From aab238e997fe53ead2402d271977bd7455495613 Mon Sep 17 00:00:00 2001 From: coderbants Date: Fri, 28 Aug 2026 19:58:49 +0100 Subject: [PATCH 1/3] fix(tty): isolate Unix terminal code on Windows --- Cargo.toml | 4 +- UPSTREAM_MAPPING.md | 7 ++-- src/program.rs | 3 +- src/tty.rs | 92 +++++++++++++++++--------------------------- src/tty_unix.rs | 81 ++++++++++++++++++++++++++++++++++++-- src/tty_windows.rs | 22 +++++++++-- tests/interactive.rs | 2 + tests/windows_tty.rs | 65 +++++++++++++++++++++++++++++++ 8 files changed, 206 insertions(+), 70 deletions(-) create mode 100644 tests/windows_tty.rs diff --git a/Cargo.toml b/Cargo.toml index 9310ba8..ab15af6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,10 +20,12 @@ crossterm = "0.27" futures = "0.3" [dev-dependencies] -rusty-testkit = { version = "0.1.1", path = "../rusty-testkit" } tokio-test = "0.4" rusty-bubbles = { version = "2.1.0", path = "../rusty-bubbles" } +[target.'cfg(unix)'.dev-dependencies] +rusty-testkit = { version = "0.1.1", path = "../rusty-testkit" } + [lib] name = "rusty_bubbletea" path = "src/lib.rs" diff --git a/UPSTREAM_MAPPING.md b/UPSTREAM_MAPPING.md index 9bf892b..e4ac308 100644 --- a/UPSTREAM_MAPPING.md +++ b/UPSTREAM_MAPPING.md @@ -48,9 +48,9 @@ upstream tag `v2.0.8`, checked out locally in `upstream-go/` (gitignored). | `termios_other.go` | `src/termios_other.rs` | Non-POSIX fallback | | `termios_unix.go` | `src/termios_unix.rs` | POSIX termios helper | | `termios_windows.go` | `src/termios_windows.rs` | Windows console mode helper | -| `tty.go` | `src/tty.rs` | `init_terminal` / `restore_terminal` | -| `tty_unix.go` | `src/tty_unix.rs` | Unix TTY reader + raw mode | -| `tty_windows.go` | `src/tty_windows.rs` | Windows VT console helper | +| `tty.go` | `src/tty.rs` | Public terminal facade; dispatches raw mode and restore operations to the target platform while sharing window-size queries | +| `tty_unix.go` | `src/tty_unix.rs` | Unix-only TTY state, termios, and raw-file-descriptor implementation | +| `tty_windows.go` | `src/tty_windows.rs` | Windows-only safe crossterm console-mode implementation | | `xterm.go` | `src/xterm.rs` | XTVERSION query, `TerminalVersionMsg` | ## Test Files (`*_test.go` -> `tests/`) @@ -66,6 +66,7 @@ upstream tag `v2.0.8`, checked out locally in `upstream-go/` (gitignored). | `mouse_test.go` | `tests/mouse_test.rs` | Mouse event suite | | `options_test.go` | `tests/commands_test.rs` | Program option tests | | `screen_test.go` | `tests/commands_test.rs` | Screen buffer tests | +| Rust Windows regression | `tests/windows_tty.rs` | Native Windows compile and public terminal-surface regression for the platform split | Golden files under `testdata/` are accounted for by the corresponding Rust test assertions (values verified against upstream output): `testdata/TestClearMsg/*.golden` diff --git a/src/program.rs b/src/program.rs index 844a6bd..04f4913 100644 --- a/src/program.rs +++ b/src/program.rs @@ -921,9 +921,10 @@ fn uv_mouse_to_mouse(m: rusty_ultraviolet::Mouse) -> crate::mouse::Mouse { /// CheckOptimizedMovements reads the stdin termios and reports whether hard /// tabs (TABDLY==TAB0) and backspace (BSDLY==BS0) optimizations are enabled. fn check_optimized_movements() -> (bool, bool) { - use std::os::fd::AsRawFd; #[cfg(unix)] { + use std::os::fd::AsRawFd; + let fd = std::io::stdin().as_raw_fd(); let mut t: libc::termios = unsafe { std::mem::zeroed() }; if unsafe { libc::tcgetattr(fd, &mut t) } != 0 { diff --git a/src/tty.rs b/src/tty.rs index 178b4a5..1879698 100644 --- a/src/tty.rs +++ b/src/tty.rs @@ -1,82 +1,60 @@ //! Cleanroom Rust port of upstream Go source file: `tty.go` //! Upstream Target Tag / Version: `v2.0.8` //! -//! +//! //! # TTY Terminal Management //! -//! TTY initialization, input stream setup, raw mode toggles, and window dimension queries for Bubble Tea v2.0.8. -//! +//! TTY initialization, raw mode toggles, and window dimension queries for +//! Bubble Tea v2.0.8. Unix targets use the upstream-compatible termios and +//! raw-file-descriptor implementation. Windows targets use crossterm's safe +//! console-mode API for enable, disable, initialize, and restore operations. +//! use crossterm::terminal::size as term_size; -use std::sync::{Mutex, OnceLock}; -/// Saved terminal state for the raw mode toggle, mirroring the upstream -/// `p.previousTtyInputState` (x/term `MakeRaw`/`Restore`). -static SAVED_TERMIOS: OnceLock>> = OnceLock::new(); +#[cfg(unix)] +use crate::tty_unix as platform; +#[cfg(windows)] +use crate::tty_windows as platform; +#[cfg(not(any(unix, windows)))] +use unsupported as platform; -/// Initializes terminal raw mode, mirroring the upstream `initInput` -> -/// `term.MakeRaw` path (`tty_unix.go`). Unlike a fully-zeroed `cfmakeraw`, -/// only `OPOST` is cleared from the output flags, so `TABDLY` (and thus the -/// hard-tab cursor optimization) behaves exactly as it does upstream. -pub fn enable_raw_mode() -> std::io::Result<()> { - use std::os::fd::AsRawFd; - let fd = std::io::stdin().as_raw_fd(); - let mut t: libc::termios = unsafe { std::mem::zeroed() }; - if unsafe { libc::tcgetattr(fd, &mut t) } != 0 { - return Err(std::io::Error::last_os_error()); +#[cfg(not(any(unix, windows)))] +mod unsupported { + /// Reports that raw mode is unavailable on an unsupported target. + pub(super) fn enable_raw_mode() -> std::io::Result<()> { + Err(std::io::Error::new( + std::io::ErrorKind::Unsupported, + "raw terminal mode is unsupported on this target", + )) } - *SAVED_TERMIOS - .get_or_init(|| Mutex::new(None)) - .lock() - .unwrap() = Some(t); - // This attempts to replicate the behaviour documented for cfmakeraw in - // the termios(3) manpage, as x/term's `makeRaw` does. - t.c_iflag &= !(libc::IGNBRK - | libc::BRKINT - | libc::PARMRK - | libc::ISTRIP - | libc::INLCR - | libc::IGNCR - | libc::ICRNL - | libc::IXON); - t.c_oflag &= !libc::OPOST; - t.c_lflag &= !(libc::ECHO | libc::ECHONL | libc::ICANON | libc::ISIG | libc::IEXTEN); - t.c_cflag &= !(libc::CSIZE | libc::PARENB); - t.c_cflag |= libc::CS8; - t.c_cc[libc::VMIN] = 1; - t.c_cc[libc::VTIME] = 0; - if unsafe { libc::tcsetattr(fd, libc::TCSANOW, &t) } != 0 { - return Err(std::io::Error::last_os_error()); + /// Reports that raw mode is unavailable on an unsupported target. + pub(super) fn disable_raw_mode() -> std::io::Result<()> { + Err(std::io::Error::new( + std::io::ErrorKind::Unsupported, + "raw terminal mode is unsupported on this target", + )) } - Ok(()) } -/// Restores the terminal state saved by [`enable_raw_mode`], mirroring the -/// upstream `term.Restore` path. +/// Enables terminal raw mode through the target platform implementation. +pub fn enable_raw_mode() -> std::io::Result<()> { + platform::enable_raw_mode() +} + +/// Disables terminal raw mode through the target platform implementation. pub fn disable_raw_mode() -> std::io::Result<()> { - use std::os::fd::AsRawFd; - let saved = SAVED_TERMIOS - .get_or_init(|| Mutex::new(None)) - .lock() - .unwrap() - .take(); - if let Some(t) = saved { - let fd = std::io::stdin().as_raw_fd(); - if unsafe { libc::tcsetattr(fd, libc::TCSANOW, &t) } != 0 { - return Err(std::io::Error::last_os_error()); - } - } - Ok(()) + platform::disable_raw_mode() } -/// Initializes terminal raw mode. +/// Initializes terminal raw mode through the target platform implementation. pub fn init_terminal() -> Result<(), Box> { enable_raw_mode()?; Ok(()) } -/// Restores terminal raw mode. +/// Restores terminal raw mode through the target platform implementation. pub fn restore_terminal() -> Result<(), Box> { disable_raw_mode()?; Ok(()) diff --git a/src/tty_unix.rs b/src/tty_unix.rs index 29c5e47..a3d9532 100644 --- a/src/tty_unix.rs +++ b/src/tty_unix.rs @@ -1,13 +1,86 @@ //! Cleanroom Rust port of upstream Go source file: `tty_unix.go` //! Upstream Target Tag / Version: `v2.0.8` //! -//! +//! //! # TTY (Unix) //! -//! POSIX Unix TTY handle and raw mode initialization. -//! +//! POSIX Unix TTY handle and raw mode initialization. The implementation is +//! compiled only for Unix targets so Windows never type-checks its termios or +//! raw-file-descriptor operations. +//! /// Unix TTY initialization check. pub fn is_unix_tty() -> bool { - !cfg!(target_os = "windows") + cfg!(unix) } + +#[cfg(unix)] +mod unix { + use std::io; + use std::sync::{Mutex, OnceLock}; + + /// Saved terminal state for the raw mode toggle, mirroring the upstream + /// `p.previousTtyInputState` (`term.MakeRaw`/`term.Restore`). + static SAVED_TERMIOS: OnceLock>> = OnceLock::new(); + + fn saved_termios() -> &'static Mutex> { + SAVED_TERMIOS.get_or_init(|| Mutex::new(None)) + } + + /// Enables POSIX raw mode while preserving the upstream output behavior. + pub(crate) fn enable_raw_mode() -> io::Result<()> { + use std::os::fd::AsRawFd; + + let fd = io::stdin().as_raw_fd(); + let mut termios: libc::termios = unsafe { std::mem::zeroed() }; + if unsafe { libc::tcgetattr(fd, &mut termios) } != 0 { + return Err(io::Error::last_os_error()); + } + saved_termios() + .lock() + .map_err(|_| io::Error::other("saved terminal state lock poisoned"))? + .replace(termios); + + // This replicates the behavior documented for cfmakeraw in the + // termios(3) manpage, as x/term's makeRaw does. Only OPOST is cleared + // from output flags so TABDLY retains the upstream hard-tab behavior. + termios.c_iflag &= !(libc::IGNBRK + | libc::BRKINT + | libc::PARMRK + | libc::ISTRIP + | libc::INLCR + | libc::IGNCR + | libc::ICRNL + | libc::IXON); + termios.c_oflag &= !libc::OPOST; + termios.c_lflag &= !(libc::ECHO | libc::ECHONL | libc::ICANON | libc::ISIG | libc::IEXTEN); + termios.c_cflag &= !(libc::CSIZE | libc::PARENB); + termios.c_cflag |= libc::CS8; + termios.c_cc[libc::VMIN] = 1; + termios.c_cc[libc::VTIME] = 0; + if unsafe { libc::tcsetattr(fd, libc::TCSANOW, &termios) } != 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + /// Restores the terminal state saved by [`enable_raw_mode`]. + pub(crate) fn disable_raw_mode() -> io::Result<()> { + use std::os::fd::AsRawFd; + + let saved = saved_termios() + .lock() + .map_err(|_| io::Error::other("saved terminal state lock poisoned"))? + .take(); + if let Some(termios) = saved { + let fd = io::stdin().as_raw_fd(); + if unsafe { libc::tcsetattr(fd, libc::TCSANOW, &termios) } != 0 { + return Err(io::Error::last_os_error()); + } + } + Ok(()) + } +} + +#[cfg(unix)] +pub(crate) use unix::{disable_raw_mode, enable_raw_mode}; diff --git a/src/tty_windows.rs b/src/tty_windows.rs index 490cfb9..14cc32b 100644 --- a/src/tty_windows.rs +++ b/src/tty_windows.rs @@ -1,13 +1,27 @@ //! Cleanroom Rust port of upstream Go source file: `tty_windows.go` //! Upstream Target Tag / Version: `v2.0.8` //! -//! +//! //! # TTY (Windows) //! -//! Windows VT console mode handle and raw mode initialization. -//! +//! Windows VT console mode handle and raw mode initialization. Windows uses +//! crossterm's safe console-mode operations; no Unix termios or handwritten +//! raw-file-descriptor code is compiled for this target. +//! /// Windows TTY initialization check. pub fn is_windows_tty() -> bool { - cfg!(target_os = "windows") + cfg!(windows) +} + +#[cfg(windows)] +/// Enables Windows console raw mode through crossterm's safe API. +pub(crate) fn enable_raw_mode() -> std::io::Result<()> { + crossterm::terminal::enable_raw_mode() +} + +#[cfg(windows)] +/// Disables Windows console raw mode through crossterm's safe API. +pub(crate) fn disable_raw_mode() -> std::io::Result<()> { + crossterm::terminal::disable_raw_mode() } diff --git a/tests/interactive.rs b/tests/interactive.rs index b0b982c..1fb5d14 100644 --- a/tests/interactive.rs +++ b/tests/interactive.rs @@ -7,6 +7,8 @@ //! interactive behavior (typing, navigating, mouse clicks) that a byte-level //! key-sweep cannot. +#![cfg(unix)] + use rusty_testkit::PtySession; /// The package's Cargo target directory. `cargo metadata` is authoritative: diff --git a/tests/windows_tty.rs b/tests/windows_tty.rs new file mode 100644 index 0000000..0740ba8 --- /dev/null +++ b/tests/windows_tty.rs @@ -0,0 +1,65 @@ +//! Native Windows regressions for the platform-separated terminal facade. + +#![cfg(windows)] + +use rusty_bubbletea::{termios_unix, termios_windows, tty, tty_unix, tty_windows}; + +#[test] +fn windows_selects_windows_terminal_modules() { + assert!(tty_windows::is_windows_tty()); + assert!(termios_windows::is_windows_termios()); + assert!(!tty_unix::is_unix_tty()); + assert!(!termios_unix::is_unix_termios()); +} + +#[test] +fn windows_terminal_surface_has_safe_platform_neutral_signatures() { + let _enable: fn() -> std::io::Result<()> = tty::enable_raw_mode; + let _disable: fn() -> std::io::Result<()> = tty::disable_raw_mode; + let _init: fn() -> Result<(), Box> = tty::init_terminal; + let _restore: fn() -> Result<(), Box> = tty::restore_terminal; + let _window_size: fn() -> Result<(u16, u16), Box> = tty::get_window_size; +} + +#[test] +fn windows_terminal_operations_round_trip_when_a_console_is_available() { + let enabled = tty::enable_raw_mode(); + match enabled { + Ok(()) => { + tty::disable_raw_mode().expect("a console that enables raw mode must restore it"); + } + Err(error) => { + assert!( + !error.to_string().is_empty(), + "raw-mode failure should explain why the host console is unavailable" + ); + } + } + + let initialized = tty::init_terminal(); + match initialized { + Ok(()) => { + tty::restore_terminal().expect("a console that initializes must restore"); + } + Err(error) => { + assert!( + !error.to_string().is_empty(), + "terminal initialization failure should explain why the host console is unavailable" + ); + } + } + + match tty::get_window_size() { + Ok((columns, rows)) => { + assert!( + columns > 0, + "a Windows console must report positive columns" + ); + assert!(rows > 0, "a Windows console must report positive rows"); + } + Err(error) => assert!( + !error.to_string().is_empty(), + "window-size failure should explain why the host console is unavailable" + ), + } +} From e4a27cfe70030a8586a947e4099a9428a11d7871 Mon Sep 17 00:00:00 2001 From: Jay Rodgers Date: Sat, 29 Aug 2026 07:09:00 +0100 Subject: [PATCH 2/3] fix(windows): make bubbletea TTY compile-safe (#8) --- UPSTREAM_MAPPING.md | 7 ++++++- src/program.rs | 3 ++- src/tty.rs | 24 ++++++++++++++++++++++-- tests/interactive.rs | 2 ++ tests/tea_test.rs | 10 +++++++--- tests/windows_platform.rs | 17 +++++++++++++++++ 6 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 tests/windows_platform.rs diff --git a/UPSTREAM_MAPPING.md b/UPSTREAM_MAPPING.md index 9bf892b..c44ea25 100644 --- a/UPSTREAM_MAPPING.md +++ b/UPSTREAM_MAPPING.md @@ -48,7 +48,7 @@ upstream tag `v2.0.8`, checked out locally in `upstream-go/` (gitignored). | `termios_other.go` | `src/termios_other.rs` | Non-POSIX fallback | | `termios_unix.go` | `src/termios_unix.rs` | POSIX termios helper | | `termios_windows.go` | `src/termios_windows.rs` | Windows console mode helper | -| `tty.go` | `src/tty.rs` | `init_terminal` / `restore_terminal` | +| `tty.go` | `src/tty.rs` | `init_terminal` / `restore_terminal`; Unix raw-mode implementation with compile-safe Windows no-op boundary | | `tty_unix.go` | `src/tty_unix.rs` | Unix TTY reader + raw mode | | `tty_windows.go` | `src/tty_windows.rs` | Windows VT console helper | | `xterm.go` | `src/xterm.rs` | XTVERSION query, `TerminalVersionMsg` | @@ -160,6 +160,11 @@ byte-for-byte. | `tutorials/basics/main.go` | `examples/tutorial_basics.rs` | Tutorial: counter; quits on 'q' | | `tutorials/commands/main.go` | `examples/tutorial_commands.rs` | Tutorial: commands; quits on 'q' | +Rust-only portability regression coverage is maintained in `tests/windows_platform.rs`; it +exercises the Windows raw-mode and terminal-restoration boundary that has no standalone +upstream Go test file. The PTY-driven `tests/interactive.rs` suite remains Unix-gated because +its real pseudo-terminal dependency is intentionally not claimed as native Windows support. + Example support files (`examples/go.mod`, `examples/go.sum`, `examples/table/demo.tape`, per-example `README.md`/`.gif` assets, `tutorials/go.mod`, `tutorials/go.sum`) are documented in the Support Files section. diff --git a/src/program.rs b/src/program.rs index 844a6bd..04f4913 100644 --- a/src/program.rs +++ b/src/program.rs @@ -921,9 +921,10 @@ fn uv_mouse_to_mouse(m: rusty_ultraviolet::Mouse) -> crate::mouse::Mouse { /// CheckOptimizedMovements reads the stdin termios and reports whether hard /// tabs (TABDLY==TAB0) and backspace (BSDLY==BS0) optimizations are enabled. fn check_optimized_movements() -> (bool, bool) { - use std::os::fd::AsRawFd; #[cfg(unix)] { + use std::os::fd::AsRawFd; + let fd = std::io::stdin().as_raw_fd(); let mut t: libc::termios = unsafe { std::mem::zeroed() }; if unsafe { libc::tcgetattr(fd, &mut t) } != 0 { diff --git a/src/tty.rs b/src/tty.rs index 178b4a5..3cdc5f6 100644 --- a/src/tty.rs +++ b/src/tty.rs @@ -1,23 +1,28 @@ //! Cleanroom Rust port of upstream Go source file: `tty.go` //! Upstream Target Tag / Version: `v2.0.8` //! -//! +//! //! # TTY Terminal Management //! //! TTY initialization, input stream setup, raw mode toggles, and window dimension queries for Bubble Tea v2.0.8. -//! +//! On Windows, raw-mode operations are compile-safe no-ops until the native +//! console adapter is completed; window-size queries remain available. +//! use crossterm::terminal::size as term_size; +#[cfg(unix)] use std::sync::{Mutex, OnceLock}; /// Saved terminal state for the raw mode toggle, mirroring the upstream /// `p.previousTtyInputState` (x/term `MakeRaw`/`Restore`). +#[cfg(unix)] static SAVED_TERMIOS: OnceLock>> = OnceLock::new(); /// Initializes terminal raw mode, mirroring the upstream `initInput` -> /// `term.MakeRaw` path (`tty_unix.go`). Unlike a fully-zeroed `cfmakeraw`, /// only `OPOST` is cleared from the output flags, so `TABDLY` (and thus the /// hard-tab cursor optimization) behaves exactly as it does upstream. +#[cfg(unix)] pub fn enable_raw_mode() -> std::io::Result<()> { use std::os::fd::AsRawFd; let fd = std::io::stdin().as_raw_fd(); @@ -52,8 +57,16 @@ pub fn enable_raw_mode() -> std::io::Result<()> { Ok(()) } +/// Keeps the raw-mode API available on Windows while native console mode +/// support remains deferred. +#[cfg(not(unix))] +pub fn enable_raw_mode() -> std::io::Result<()> { + Ok(()) +} + /// Restores the terminal state saved by [`enable_raw_mode`], mirroring the /// upstream `term.Restore` path. +#[cfg(unix)] pub fn disable_raw_mode() -> std::io::Result<()> { use std::os::fd::AsRawFd; let saved = SAVED_TERMIOS @@ -70,6 +83,13 @@ pub fn disable_raw_mode() -> std::io::Result<()> { Ok(()) } +/// Keeps terminal restoration deterministic on Windows when no raw mode was +/// enabled by the compile-safe fallback. +#[cfg(not(unix))] +pub fn disable_raw_mode() -> std::io::Result<()> { + Ok(()) +} + /// Initializes terminal raw mode. pub fn init_terminal() -> Result<(), Box> { enable_raw_mode()?; diff --git a/tests/interactive.rs b/tests/interactive.rs index b0b982c..546e80d 100644 --- a/tests/interactive.rs +++ b/tests/interactive.rs @@ -1,3 +1,5 @@ +#![cfg(unix)] + //! Interactive integration tests for the Bubble Tea examples, driven through //! a real pseudo-terminal (Playwright-style): keys, typing, mouse, resizing, //! and assertions on the reconstructed on-screen state. diff --git a/tests/tea_test.rs b/tests/tea_test.rs index 004437d..8c7b29f 100644 --- a/tests/tea_test.rs +++ b/tests/tea_test.rs @@ -520,9 +520,9 @@ fn test_clipboard_and_color_commands() { // TTY helpers let _ = rusty_bubbletea::tty::get_window_size(); - let (tx_sig, _rx_sig) = std::sync::mpsc::channel(); + let (_tx_sig, _rx_sig) = std::sync::mpsc::channel::>(); #[cfg(unix)] - rusty_bubbletea::signals_unix::listen_for_resize(&tx_sig); + rusty_bubbletea::signals_unix::listen_for_resize(&_tx_sig); } #[test] @@ -596,7 +596,11 @@ fn test_keyboard_and_mouse_and_env_and_logging() { nil.reset(); assert!(nil.close().is_ok()); - let mut logger = logging::log_to_file("/tmp/test_bubbletea.log", "test").unwrap(); + let log_path = std::env::temp_dir() + .join("rusty-bubbletea-test.log") + .to_string_lossy() + .into_owned(); + let mut logger = logging::log_to_file(&log_path, "test").unwrap(); logger.log("test message"); } diff --git a/tests/windows_platform.rs b/tests/windows_platform.rs new file mode 100644 index 0000000..9063f1f --- /dev/null +++ b/tests/windows_platform.rs @@ -0,0 +1,17 @@ +#![cfg(windows)] + +//! Windows regression coverage for the compile-safe terminal boundary. + +use rusty_bubbletea::tty::{disable_raw_mode, enable_raw_mode, init_terminal, restore_terminal}; + +#[test] +fn raw_mode_operations_are_safe_no_ops_on_windows() { + enable_raw_mode().expect("Windows raw-mode fallback should succeed"); + disable_raw_mode().expect("Windows raw-mode restoration fallback should succeed"); +} + +#[test] +fn terminal_lifecycle_uses_the_same_windows_boundary() { + init_terminal().expect("Windows terminal initialization fallback should succeed"); + restore_terminal().expect("Windows terminal restoration fallback should succeed"); +} From 5db17e5646dca1ceaa406b36ddaf6576cc7ad0aa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 29 Aug 2026 06:15:02 +0000 Subject: [PATCH 3/3] docs: update coverage badge (74.0%) [skip ci] --- coverage.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage.svg b/coverage.svg index 14a01d2..e9340f8 100644 --- a/coverage.svg +++ b/coverage.svg @@ -1 +1 @@ -coverage: 74.1%coverage74.1% \ No newline at end of file +coverage: 74.0%coverage74.0% \ No newline at end of file