From 176102aa278c087836867b955a528b2d7cf00c84 Mon Sep 17 00:00:00 2001 From: Emanuel Czirai Date: Mon, 10 Jun 2024 16:58:44 +0200 Subject: [PATCH] Tests support control chars widths 0 or 1 (#789) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix 'cargo test' with unicode-width 0.1.12 which would fail like this: test utils::lines::spans::tests::test_control_chars_have_width_1 ... FAILED failures: ---- utils::lines::spans::tests::test_control_chars_have_width_1 stdout ---- thread 'utils::lines::spans::tests::test_control_chars_have_width_1' panicked at /home/user/1tmp/ncurses_things/cursive/cursive-core/src/utils/lines/spans/tests.rs:55:9: assertion `left == right` failed: Width of control character \u{0000} is not 1 left: 0 right: 1 stack backtrace: 0: rust_begin_unwind at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panicking.rs:645:5 1: core::panicking::panic_fmt at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:72:14 2: core::panicking::assert_failed_inner at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:337:23 3: core::panicking::assert_failed at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:297:5 4: cursive_core::utils::lines::spans::tests::test_control_chars_have_width_1 at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/macros/mod.rs:58:21 5: cursive_core::utils::lines::spans::tests::test_control_chars_have_width_1::{{closure}} at ./src/utils/lines/spans/tests.rs:32:37 6: core::ops::function::FnOnce::call_once at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/ops/function.rs:250:5 7: core::ops::function::FnOnce::call_once at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/ops/function.rs:250:5 note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. failures: utils::lines::spans::tests::test_control_chars_have_width_1 test result: FAILED. 29 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s * fix cargo test --example select_test ... to work with unicode-width 0.1.12 as well. before this, because it was expecting only 0.1.13, it was failing with the following output: Running unittests examples/select_test.rs (target/x86_64-unknown-linux-gnu/debug/examples/select_test-d45f2e1e4022a737) running 4 tests captured piece: x01234567890123456789012345678901234567890123456789012345678901234567890123456789x 0 | 1 | 2 ┌┤ Where are you from? ├┐ | 3 │ Abidjan ▒ │ | 4 │ Abu Dhabi | │ | 5 │ Abuja | │ | 6 │ Accra | │ | 7 │ Adamstown | │ | 8 │ Addis Ababa | │ | 9 │ Algiers | │ | 0 │ Alofi | │ | 1 │ Amman | │ | 2 │ Amsterdam | │ | 3 └───────────────────────┘ | 4 | 5 | x--------------------------------------------------------------------------------x test tests::displays ... ok captured piece: x01234567890123456789012345678901234567890123456789012345678901234567890123456789x 0 | 1 | 2 ┌┤ Where are you from? ├┐ | 3 │ short nul 1str | │ | 4 │ 1thru8 | │ | 5 │ tabandnewline | │ | 6 │ bthru15 | │ | 7 │ 16thru1F | │ | 8 │ 7Fonly | │ | 9 │ 80thru89 | │ | 0 │ 8Athru93 | │ | 1 │ 94thru9D | │ | 2 │ 9Ethru9F ▒ │ | 3 └───────────────────────┘ | 4 | 5 | x--------------------------------------------------------------------------------x captured piece: x01234567890123456789012345678901234567890123456789012345678901234567890123456789x 0 | 1 | 2 ┌┤ Where are you from? ├┐ | 3 │ short nul 1str | │ | 4 │ 1thru8 | │ | 5 │ tabandnewline | │ | 6 │ bthru15 | │ | 7 │ 16thru1F | │ | 8 │ 7Fonly | │ | 9 │ 80thru89 | │ | 0 │ 8Athru93 | │ | 1 │ 94thru9D | │ | 2 │ 9Ethru9F ▒ │ | 3 └───────────────────────┘ | 4 | 5 | x--------------------------------------------------------------------------------x thread ' tests::control_chars_become_replacement_char' panicked at cursive/examples/select_test.rs:148:9: assertion `left == right` failed: tabs and newline should've been replaced with replacement char � aka \u{FFFD} left: 0 right: 1 stack backtrace: thread 'tests::nuls_become_replacement_char' panicked at cursive/examples/select_test.rs:133:9: assertion `left == right` failed: nuls aka \0 in strings are supposed to become the replacement char '�' left: 0 right: 1 captured piece: x01234567890123456789012345678901234567890123456789012345678901234567890123456789x 0 | 1 | 2 | 3 | 4 | 5 ┌────────────────────────────┐ | 6 │ Abu Dhabi is a great city! │ | 7 │ │ | 8 │ │ | 9 └────────────────────────────┘ | 0 | 1 | 2 | 3 | 4 | 5 | x--------------------------------------------------------------------------------x test tests::interacts ... ok 0: rust_begin_unwind at /rustc/a70b2ae57713ed0e7411c059d582ab382fc4166a/library/std/src/panicking.rs:652:5 1: core::panicking::panic_fmt at /rustc/a70b2ae57713ed0e7411c059d582ab382fc4166a/library/core/src/panicking.rs:72:14 2: core::panicking::assert_failed_inner at /rustc/a70b2ae57713ed0e7411c059d582ab382fc4166a/library/core/src/panicking.rs:403:23 3: core::panicking::assert_failed at /rustc/a70b2ae57713ed0e7411c059d582ab382fc4166a/library/core/src/panicking.rs:363:5 4: select_test::tests::control_chars_become_replacement_char at ./examples/select_test.rs:148:9 5: select_test::tests::control_chars_become_replacement_char::{{closure}} at ./examples/select_test.rs:142:47 6: core::ops::function::FnOnce::call_once at /rustc/a70b2ae57713ed0e7411c059d582ab382fc4166a/library/core/src/ops/function.rs:250:5 7: core::ops::function::FnOnce::call_once at /rustc/a70b2ae57713ed0e7411c059d582ab382fc4166a/library/core/src/ops/function.rs:250:5 note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. stack backtrace: 0: rust_begin_unwind at /rustc/a70b2ae57713ed0e7411c059d582ab382fc4166a/library/std/src/panicking.rs:652:5 1: core::panicking::panic_fmt at /rustc/a70b2ae57713ed0e7411c059d582ab382fc4166a/library/core/src/panicking.rs:72:14 2: core::panicking::assert_failed_inner at /rustc/a70b2ae57713ed0e7411c059d582ab382fc4166a/library/core/src/panicking.rs:403:23 3: core::panicking::assert_failed at /rustc/a70b2ae57713ed0e7411c059d582ab382fc4166a/library/core/src/panicking.rs:363:5 4: select_test::tests::nuls_become_replacement_char at ./examples/select_test.rs:133:9 5: select_test::tests::nuls_become_replacement_char::{{closure}} at ./examples/select_test.rs:128:38 6: core::ops::function::FnOnce::call_once at /rustc/a70b2ae57713ed0e7411c059d582ab382fc4166a/library/core/src/ops/function.rs:250:5 7: core::ops::function::FnOnce::call_once at /rustc/a70b2ae57713ed0e7411c059d582ab382fc4166a/library/core/src/ops/function.rs:250:5 note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. test tests::control_chars_become_replacement_char ... FAILED test tests::nuls_become_replacement_char ... FAILED failures: failures: tests::control_chars_become_replacement_char tests::nuls_become_replacement_char test result: FAILED. 2 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s error: test failed, to rerun pass `-p cursive --example select_test` * --nocapture isn't needed for github CI workflow because it will auto-show this output if it ever fails, if it doesn't fail, we don't need to see it, it's spammy * comment typo unicode_segmentation -> unicode-width refers to the crate --- .github/workflows/rust.yml | 2 +- cursive-core/src/buffer.rs | 4 +- cursive-core/src/utils/lines/spans/tests.rs | 9 +- cursive/examples/select_test.rs | 160 +++++++++++++------- 4 files changed, 110 insertions(+), 65 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 22326f4f..01979360 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -24,4 +24,4 @@ jobs: - name: Run tests run: > cargo test --features "toml markdown ansi termion-backend crossterm-backend" --no-default-features --verbose && - cargo test --example select_test -- --nocapture + cargo test --example select_test diff --git a/cursive-core/src/buffer.rs b/cursive-core/src/buffer.rs index eb555f56..a0e2585e 100644 --- a/cursive-core/src/buffer.rs +++ b/cursive-core/src/buffer.rs @@ -193,12 +193,12 @@ impl PrintBuffer { let width = g.width(); if width == 0 { // Any zero-width grapheme can be ignored. - // With unicode_segmentation < 0.1.13, this includes control chars. + // With unicode-width < 0.1.13, this includes control chars. continue; } if is_control_char(g) { - // With unicode_segmentation >= 0.1.13, control chars have non-zero width (in + // With unicode-width >= 0.1.13, control chars have non-zero width (in // practice width = 1). debug_assert_eq!( 1, width, diff --git a/cursive-core/src/utils/lines/spans/tests.rs b/cursive-core/src/utils/lines/spans/tests.rs index aa79801d..b02f17a0 100644 --- a/cursive-core/src/utils/lines/spans/tests.rs +++ b/cursive-core/src/utils/lines/spans/tests.rs @@ -29,7 +29,7 @@ fn test_replacement_char_has_width_1() { } #[test] -fn test_control_chars_have_width_1() { +fn test_control_chars_have_width_0_or_1() { use unicode_width::UnicodeWidthStr; let control_chars = [ "\u{0000}", "\u{0001}", "\u{0002}", "\u{0003}", "\u{0004}", "\u{0005}", "\u{0006}", @@ -52,10 +52,9 @@ fn test_control_chars_have_width_1() { "it's supposed to be a string of 1 char" ); let unicode_escape = format!("\\u{{{:04X}}}", c.chars().last().unwrap() as u32); - assert_eq!( - width, 1, - "Width of control character {} is not 1", - unicode_escape + assert!( + (0..=1).contains(&width), + "Width of control character {unicode_escape} is not 0 or 1, it's {width}" ); } } diff --git a/cursive/examples/select_test.rs b/cursive/examples/select_test.rs index cd32bed6..a2602e1e 100644 --- a/cursive/examples/select_test.rs +++ b/cursive/examples/select_test.rs @@ -50,6 +50,7 @@ pub mod tests { select.add_item_str("8A\u{008A}\u{008B}\u{008C}\u{008D}\u{008E}\u{008F}\u{0090}\u{0091}\u{0092}\u{0093}thru93"); select.add_item_str("94\u{0094}\u{0095}\u{0096}\u{0097}\u{0098}\u{0099}\u{009A}\u{009B}\u{009C}\u{009D}thru9D"); select.add_item_str("9E\u{009E}\u{009F}thru9F"); + //XXX: can't add more lines here, it would cause them to go off view thus fail the is-it-on-screen tests, unless the dialog is made bigger below! // Sets the callback for when "Enter" is pressed. select.set_on_submit(show_next_window); @@ -125,68 +126,113 @@ pub mod tests { } #[test] - fn nuls_become_replacement_char() { - let mut s = BasicSetup::new(); - s.hit_keystroke(Key::End); - let screen = s.last_screen().unwrap(); - s.dump_debug(); - assert_eq!( - screen - .find_occurences("short \u{fffd}nul\u{FFFD} 1str") - .len(), - 1, - "nuls aka \\0 in strings are supposed to become the replacement char '\u{fffd}'" - ); - } - #[test] - fn control_chars_become_replacement_char() { + fn control_chars_including_nul_when_on_screen() { let mut s = BasicSetup::new(); s.hit_keystroke(Key::End); let screen = s.last_screen().unwrap(); s.dump_debug(); let replacement_char = "\u{FFFD} aka \\u{FFFD}"; - assert_eq!( - screen.find_occurences("tab�and�newline").len(), - 1, - "tabs and newline should've been replaced with replacement char {}", - replacement_char - ); - assert_eq!( - screen.find_occurences("b�����������thru15").len(), - 1, - "control chars \\x0B thru \\x15 should've been replaced with the replacement char {}", - replacement_char - ); - assert_eq!( - screen.find_occurences("16����������thru1F").len(), - 1, - "control chars \\x16 thru \\x1F should've been replaced with the replacement char {}", - replacement_char - ); - assert_eq!( - screen.find_occurences("7F�only").len(), - 1, - "control char \\x7F should've been replaced with the replacement char {}", - replacement_char - ); - assert_eq!( - screen.find_occurences("80����������thru89").len(), - 1, - "control chars \\x80 thru \\x89 should've been replaced with the replacement char {}", - replacement_char - ); - assert_eq!( - screen.find_occurences("8A����������thru93").len(), - 1, - "control chars \\x8A thru \\x93 should've been replaced with the replacement char {}", - replacement_char - ); - assert_eq!( - screen.find_occurences("9E��thru9F").len(), - 1, - "control chars \\x9E thru \\x9F should've been replaced with the replacement char {}", - replacement_char - ); + use unicode_width::UnicodeWidthStr; + let width_of_nul = "\0".width(); + if !(0..=1).contains(&width_of_nul) { + panic!( + "nul aka \\0 has a width of '{width_of_nul}' instead of the expected one of 0 or 1" + ); + } + //we assume that all other control chars have same width as nul for chosing + //which test to perform on them which depends on which unicode-width crate + //version was used: the <=0.1.12 (width==0) or >=0.1.13 (width==1) + //for width==0 we expect control chars to have been deleted from on-screen output + //for width==1 we expect they were replaced with the width 1 replacement char: � + if width_of_nul == 1 { + // unicode-width version =1.1.13 or maybe later too + assert_eq!( + screen + .find_occurences("short \u{fffd}nul\u{FFFD} 1str") + .len(), + 1, + "nuls aka \\0 in strings are supposed to become the replacement char '\u{fffd}'" + ); + assert_eq!( + screen.find_occurences("tab�and�newline").len(), + 1, + "tabs and newline should've been replaced with replacement char {replacement_char}" + ); + assert_eq!( + screen.find_occurences("b�����������thru15").len(), + 1, + "control chars \\x0B thru \\x15 should've been replaced with the replacement char {replacement_char}", + ); + assert_eq!( + screen.find_occurences("16����������thru1F").len(), + 1, + "control chars \\x16 thru \\x1F should've been replaced with the replacement char {replacement_char}", + ); + assert_eq!( + screen.find_occurences("7F�only").len(), + 1, + "control char \\x7F should've been replaced with the replacement char {replacement_char}", + ); + assert_eq!( + screen.find_occurences("80����������thru89").len(), + 1, + "control chars \\x80 thru \\x89 should've been replaced with the replacement char {replacement_char}", + ); + assert_eq!( + screen.find_occurences("8A����������thru93").len(), + 1, + "control chars \\x8A thru \\x93 should've been replaced with the replacement char {replacement_char}", + ); + assert_eq!( + screen.find_occurences("9E��thru9F").len(), + 1, + "control chars \\x9E thru \\x9F should've been replaced with the replacement char {replacement_char}", + ); + } else if width_of_nul == 0 { + // unicode-width version <=1.1.12 + assert_eq!( + screen.find_occurences("short nul 1str").len(), + 1, + "nuls aka \\0 in strings are supposed to deleted from output" + ); + assert_eq!( + screen.find_occurences("tabandnewline").len(), + 1, + "tabs and newline should've been deleted from output" + ); + assert_eq!( + screen.find_occurences("bthru15").len(), + 1, + "control chars \\x0B thru \\x15 should've been deleted from output" + ); + assert_eq!( + screen.find_occurences("16thru1F").len(), + 1, + "control chars \\x16 thru \\x1F should've been deleted from output" + ); + assert_eq!( + screen.find_occurences("7Fonly").len(), + 1, + "control char \\x7F should've been deleted from output" + ); + assert_eq!( + screen.find_occurences("80thru89").len(), + 1, + "control chars \\x80 thru \\x89 should've been deleted from output" + ); + assert_eq!( + screen.find_occurences("8Athru93").len(), + 1, + "control chars \\x8A thru \\x93 should've been deleted from output" + ); + assert_eq!( + screen.find_occurences("9Ethru9F").len(), + 1, + "control chars \\x9E thru \\x9F should've been deleted from output" + ); + } else { + unreachable!(); + } } #[test]