From 8e7eedebe7d3578289b54dce7f69668840b74a23 Mon Sep 17 00:00:00 2001 From: Michael Debertol Date: Fri, 11 Jun 2021 22:00:26 +0200 Subject: [PATCH 1/4] tests: take slices in `stdout_is_fixture` --- tests/by-util/test_pr.rs | 132 +++++++++------------------------------ tests/common/util.rs | 2 +- 2 files changed, 31 insertions(+), 103 deletions(-) diff --git a/tests/by-util/test_pr.rs b/tests/by-util/test_pr.rs index def361fab..c1dee2a6c 100644 --- a/tests/by-util/test_pr.rs +++ b/tests/by-util/test_pr.rs @@ -33,10 +33,7 @@ fn test_without_any_options() { scenario .args(&[test_file_path]) .succeeds() - .stdout_is_templated_fixture( - expected_test_file_path, - vec![(&"{last_modified_time}".to_string(), &value)], - ); + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); } #[test] @@ -48,10 +45,7 @@ fn test_with_numbering_option_with_number_width() { scenario .args(&["-n", "2", test_file_path]) .succeeds() - .stdout_is_templated_fixture( - expected_test_file_path, - vec![(&"{last_modified_time}".to_string(), &value)], - ); + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); } #[test] @@ -66,10 +60,7 @@ fn test_with_long_header_option() { .succeeds() .stdout_is_templated_fixture( expected_test_file_path, - vec![ - (&"{last_modified_time}".to_string(), &value), - (&"{header}".to_string(), &header.to_string()), - ], + &[("{last_modified_time}", &value), ("{header}", header)], ); new_ucmd!() @@ -77,10 +68,7 @@ fn test_with_long_header_option() { .succeeds() .stdout_is_templated_fixture( expected_test_file_path, - vec![ - (&"{last_modified_time}".to_string(), &value), - (&"{header}".to_string(), &header.to_string()), - ], + &[("{last_modified_time}", &value), ("{header}", header)], ); } @@ -93,18 +81,12 @@ fn test_with_double_space_option() { scenario .args(&["-d", test_file_path]) .succeeds() - .stdout_is_templated_fixture( - expected_test_file_path, - vec![(&"{last_modified_time}".to_string(), &value)], - ); + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); new_ucmd!() .args(&["--double-space", test_file_path]) .succeeds() - .stdout_is_templated_fixture( - expected_test_file_path, - vec![(&"{last_modified_time}".to_string(), &value)], - ); + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); } #[test] @@ -116,10 +98,7 @@ fn test_with_first_line_number_option() { scenario .args(&["-N", "5", "-n", test_file_path]) .succeeds() - .stdout_is_templated_fixture( - expected_test_file_path, - vec![(&"{last_modified_time}".to_string(), &value)], - ); + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); } #[test] @@ -131,10 +110,7 @@ fn test_with_first_line_number_long_option() { scenario .args(&["--first-line-number=5", "-n", test_file_path]) .succeeds() - .stdout_is_templated_fixture( - expected_test_file_path, - vec![(&"{last_modified_time}".to_string(), &value)], - ); + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); } #[test] @@ -146,10 +122,7 @@ fn test_with_number_option_with_custom_separator_char() { scenario .args(&["-nc", test_file_path]) .succeeds() - .stdout_is_templated_fixture( - expected_test_file_path, - vec![(&"{last_modified_time}".to_string(), &value)], - ); + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); } #[test] @@ -161,10 +134,7 @@ fn test_with_number_option_with_custom_separator_char_and_width() { scenario .args(&["-nc1", test_file_path]) .succeeds() - .stdout_is_templated_fixture( - expected_test_file_path, - vec![(&"{last_modified_time}".to_string(), &value)], - ); + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); } #[test] @@ -207,25 +177,19 @@ fn test_with_page_range() { scenario .args(&["--pages=15", test_file_path]) .succeeds() - .stdout_is_templated_fixture( - expected_test_file_path, - vec![(&"{last_modified_time}".to_string(), &value)], - ); + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); new_ucmd!() .args(&["+15", test_file_path]) .succeeds() - .stdout_is_templated_fixture( - expected_test_file_path, - vec![(&"{last_modified_time}".to_string(), &value)], - ); + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); new_ucmd!() .args(&["--pages=15:17", test_file_path]) .succeeds() .stdout_is_templated_fixture( expected_test_file_path1, - vec![(&"{last_modified_time}".to_string(), &value)], + &[("{last_modified_time}", &value)], ); new_ucmd!() @@ -233,7 +197,7 @@ fn test_with_page_range() { .succeeds() .stdout_is_templated_fixture( expected_test_file_path1, - vec![(&"{last_modified_time}".to_string(), &value)], + &[("{last_modified_time}", &value)], ); } @@ -246,10 +210,7 @@ fn test_with_no_header_trailer_option() { scenario .args(&["-t", test_file_path]) .succeeds() - .stdout_is_templated_fixture( - expected_test_file_path, - vec![(&"{last_modified_time}".to_string(), &value)], - ); + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); } #[test] @@ -262,10 +223,7 @@ fn test_with_page_length_option() { scenario .args(&["--pages=2:3", "-l", "100", "-n", test_file_path]) .succeeds() - .stdout_is_templated_fixture( - expected_test_file_path, - vec![(&"{last_modified_time}".to_string(), &value)], - ); + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); new_ucmd!() .args(&["--pages=2:3", "-l", "5", "-n", test_file_path]) @@ -293,10 +251,7 @@ fn test_with_stdin() { .pipe_in_fixture("stdin.log") .args(&["--pages=1:2", "-n", "-"]) .run() - .stdout_is_templated_fixture( - expected_file_path, - vec![(&"{last_modified_time}".to_string(), &now)], - ); + .stdout_is_templated_fixture(expected_file_path, &[("{last_modified_time}", &now)]); } #[test] @@ -308,18 +263,12 @@ fn test_with_column() { scenario .args(&["--pages=3:5", "--column=3", "-n", test_file_path]) .succeeds() - .stdout_is_templated_fixture( - expected_test_file_path, - vec![(&"{last_modified_time}".to_string(), &value)], - ); + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); new_ucmd!() .args(&["--pages=3:5", "-3", "-n", test_file_path]) .succeeds() - .stdout_is_templated_fixture( - expected_test_file_path, - vec![(&"{last_modified_time}".to_string(), &value)], - ); + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); } #[test] @@ -331,10 +280,7 @@ fn test_with_column_across_option() { scenario .args(&["--pages=3:5", "--column=3", "-a", "-n", test_file_path]) .succeeds() - .stdout_is_templated_fixture( - expected_test_file_path, - vec![(&"{last_modified_time}".to_string(), &value)], - ); + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); } #[test] @@ -354,10 +300,7 @@ fn test_with_column_across_option_and_column_separator() { test_file_path, ]) .succeeds() - .stdout_is_templated_fixture( - expected_test_file_path, - vec![(&"{last_modified_time}".to_string(), &value)], - ); + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); new_ucmd!() .args(&[ @@ -371,7 +314,7 @@ fn test_with_column_across_option_and_column_separator() { .succeeds() .stdout_is_templated_fixture( expected_test_file_path1, - vec![(&"{last_modified_time}".to_string(), &value)], + &[("{last_modified_time}", &value)], ); } @@ -386,19 +329,13 @@ fn test_with_mpr() { new_ucmd!() .args(&["--pages=1:2", "-m", "-n", test_file_path, test_file_path1]) .succeeds() - .stdout_is_templated_fixture( - expected_test_file_path, - vec![(&"{last_modified_time}".to_string(), &now)], - ); + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &now)]); let now = now_time(); new_ucmd!() .args(&["--pages=2:4", "-m", "-n", test_file_path, test_file_path1]) .succeeds() - .stdout_is_templated_fixture( - expected_test_file_path1, - vec![(&"{last_modified_time}".to_string(), &now)], - ); + .stdout_is_templated_fixture(expected_test_file_path1, &[("{last_modified_time}", &now)]); let now = now_time(); new_ucmd!() @@ -413,10 +350,7 @@ fn test_with_mpr() { test_file_path, ]) .succeeds() - .stdout_is_templated_fixture( - expected_test_file_path2, - vec![(&"{last_modified_time}".to_string(), &now)], - ); + .stdout_is_templated_fixture(expected_test_file_path2, &[("{last_modified_time}", &now)]); } #[test] @@ -452,10 +386,7 @@ fn test_with_offset_space_option() { test_file_path, ]) .succeeds() - .stdout_is_templated_fixture( - expected_test_file_path, - vec![(&"{last_modified_time}".to_string(), &value)], - ); + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); } #[test] @@ -497,9 +428,9 @@ fn test_with_pr_core_utils_tests() { scenario_with_expected_status.stdout_is_templated_fixture( test_file_path, - vec![ - (&"{last_modified_time}".to_string(), &value), - (&"{file_name}".to_string(), &input_file_path.to_string()), + &[ + ("{last_modified_time}", &value), + ("{file_name}", input_file_path), ], ); } @@ -515,8 +446,5 @@ fn test_with_join_lines_option() { scenario .args(&["+1:2", "-J", "-m", test_file_1, test_file_2]) .run() - .stdout_is_templated_fixture( - expected_file_path, - vec![(&"{last_modified_time}".to_string(), &now)], - ); + .stdout_is_templated_fixture(expected_file_path, &[("{last_modified_time}", &now)]); } diff --git a/tests/common/util.rs b/tests/common/util.rs index 922d2ba36..52911912e 100644 --- a/tests/common/util.rs +++ b/tests/common/util.rs @@ -247,7 +247,7 @@ impl CmdResult { pub fn stdout_is_templated_fixture>( &self, file_rel_path: T, - template_vars: Vec<(&String, &String)>, + template_vars: &[(&str, &str)], ) -> &CmdResult { let mut contents = String::from_utf8(read_scenario_fixture(&self.tmpd, file_rel_path)).unwrap(); From 98088db9ff09d40ee9f61aa697dedf41e2ae71ce Mon Sep 17 00:00:00 2001 From: Michael Debertol Date: Fri, 11 Jun 2021 22:02:10 +0200 Subject: [PATCH 2/4] tests: add `_any` functions This should make it easier to write tests that could have different valid outputs depending on timing. --- tests/common/util.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/common/util.rs b/tests/common/util.rs index 52911912e..f881cff21 100644 --- a/tests/common/util.rs +++ b/tests/common/util.rs @@ -223,6 +223,18 @@ impl CmdResult { self } + /// like `stdout_is`, but succeeds if any elements of `expected` matches stdout. + pub fn stdout_is_any + std::fmt::Debug>(&self, expected: Vec) -> &CmdResult { + if !expected.iter().any(|msg| self.stdout_str() == msg.as_ref()) { + panic!( + "stdout was {}\nExpected any of {:#?}", + self.stdout_str(), + expected + ) + } + self + } + /// Like `stdout_is` but newlines are normalized to `\n`. pub fn normalized_newlines_stdout_is>(&self, msg: T) -> &CmdResult { let msg = msg.as_ref().replace("\r\n", "\n"); @@ -257,6 +269,23 @@ impl CmdResult { self.stdout_is(contents) } + /// like `stdout_is_templated_fixture`, but succeeds if any replacement by `template_vars` results in the actual stdout. + pub fn stdout_is_templated_fixture_any>( + &self, + file_rel_path: T, + template_vars: &[Vec<(String, String)>], + ) { + let contents = String::from_utf8(read_scenario_fixture(&self.tmpd, file_rel_path)).unwrap(); + let possible_values = template_vars.iter().map(|vars| { + let mut contents = contents.clone(); + for kv in vars.iter() { + contents = contents.replace(&kv.0, &kv.1); + } + contents + }); + self.stdout_is_any(possible_values.collect()); + } + /// asserts that the command resulted in stderr stream output that equals the /// passed in value, when both are trimmed of trailing whitespace /// stderr_only is a better choice unless stdout may or will be non-empty From bb029193e2b281f07f5fae69985a42d028f4f6a6 Mon Sep 17 00:00:00 2001 From: Michael Debertol Date: Fri, 11 Jun 2021 22:29:16 +0200 Subject: [PATCH 3/4] tests/pr: prevent races Allow any timestamp from the start of the command to its end to show up in stdout. --- tests/by-util/test_pr.rs | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/tests/by-util/test_pr.rs b/tests/by-util/test_pr.rs index c1dee2a6c..2391bc37a 100644 --- a/tests/by-util/test_pr.rs +++ b/tests/by-util/test_pr.rs @@ -3,6 +3,7 @@ use crate::common::util::*; use chrono::offset::Local; use chrono::DateTime; +use chrono::Duration; use std::fs::metadata; fn file_last_modified_time(ucmd: &UCommand, path: &str) -> String { @@ -20,8 +21,22 @@ fn file_last_modified_time(ucmd: &UCommand, path: &str) -> String { .unwrap_or_default() } -fn now_time() -> String { - Local::now().format("%b %d %H:%M %Y").to_string() +fn all_minutes(from: DateTime, to: DateTime) -> Vec { + const FORMAT: &str = "%b %d %H:%M %Y"; + let mut vec = vec![]; + let mut current = from; + while current < to { + vec.push(current.format(FORMAT).to_string()); + current = current + Duration::minutes(1); + } + vec +} + +fn valid_last_modified_template_vars(from: DateTime) -> Vec> { + all_minutes(from, Local::now()) + .into_iter() + .map(|time| vec![("{last_modified_time}".to_string(), time)]) + .collect() } #[test] @@ -246,12 +261,12 @@ fn test_with_suppress_error_option() { fn test_with_stdin() { let expected_file_path = "stdin.log.expected"; let mut scenario = new_ucmd!(); - let now = now_time(); + let start = Local::now(); scenario .pipe_in_fixture("stdin.log") .args(&["--pages=1:2", "-n", "-"]) .run() - .stdout_is_templated_fixture(expected_file_path, &[("{last_modified_time}", &now)]); + .stdout_is_templated_fixture_any(expected_file_path, &valid_last_modified_template_vars(start)); } #[test] @@ -325,19 +340,19 @@ fn test_with_mpr() { let expected_test_file_path = "mpr.log.expected"; let expected_test_file_path1 = "mpr1.log.expected"; let expected_test_file_path2 = "mpr2.log.expected"; - let now = now_time(); + let start = Local::now(); new_ucmd!() .args(&["--pages=1:2", "-m", "-n", test_file_path, test_file_path1]) .succeeds() - .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &now)]); + .stdout_is_templated_fixture_any(expected_test_file_path, &valid_last_modified_template_vars(start)); - let now = now_time(); + let start = Local::now(); new_ucmd!() .args(&["--pages=2:4", "-m", "-n", test_file_path, test_file_path1]) .succeeds() - .stdout_is_templated_fixture(expected_test_file_path1, &[("{last_modified_time}", &now)]); + .stdout_is_templated_fixture_any(expected_test_file_path1, &valid_last_modified_template_vars(start)); - let now = now_time(); + let start = Local::now(); new_ucmd!() .args(&[ "--pages=1:2", @@ -350,7 +365,7 @@ fn test_with_mpr() { test_file_path, ]) .succeeds() - .stdout_is_templated_fixture(expected_test_file_path2, &[("{last_modified_time}", &now)]); + .stdout_is_templated_fixture_any(expected_test_file_path2, &valid_last_modified_template_vars(start)); } #[test] @@ -442,9 +457,9 @@ fn test_with_join_lines_option() { let test_file_2 = "test.log"; let expected_file_path = "joined.log.expected"; let mut scenario = new_ucmd!(); - let now = now_time(); + let start = Local::now(); scenario .args(&["+1:2", "-J", "-m", test_file_1, test_file_2]) .run() - .stdout_is_templated_fixture(expected_file_path, &[("{last_modified_time}", &now)]); + .stdout_is_templated_fixture_any(expected_file_path, &valid_last_modified_template_vars(start)); } From d8c8e6774ff5c66783ff875635282e7fb5468162 Mon Sep 17 00:00:00 2001 From: Michael Debertol Date: Sat, 12 Jun 2021 12:35:50 +0200 Subject: [PATCH 4/4] tests/pr: formatting --- tests/by-util/test_pr.rs | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/tests/by-util/test_pr.rs b/tests/by-util/test_pr.rs index 2391bc37a..fb6703f28 100644 --- a/tests/by-util/test_pr.rs +++ b/tests/by-util/test_pr.rs @@ -266,7 +266,10 @@ fn test_with_stdin() { .pipe_in_fixture("stdin.log") .args(&["--pages=1:2", "-n", "-"]) .run() - .stdout_is_templated_fixture_any(expected_file_path, &valid_last_modified_template_vars(start)); + .stdout_is_templated_fixture_any( + expected_file_path, + &valid_last_modified_template_vars(start), + ); } #[test] @@ -344,13 +347,19 @@ fn test_with_mpr() { new_ucmd!() .args(&["--pages=1:2", "-m", "-n", test_file_path, test_file_path1]) .succeeds() - .stdout_is_templated_fixture_any(expected_test_file_path, &valid_last_modified_template_vars(start)); + .stdout_is_templated_fixture_any( + expected_test_file_path, + &valid_last_modified_template_vars(start), + ); let start = Local::now(); new_ucmd!() .args(&["--pages=2:4", "-m", "-n", test_file_path, test_file_path1]) .succeeds() - .stdout_is_templated_fixture_any(expected_test_file_path1, &valid_last_modified_template_vars(start)); + .stdout_is_templated_fixture_any( + expected_test_file_path1, + &valid_last_modified_template_vars(start), + ); let start = Local::now(); new_ucmd!() @@ -365,7 +374,10 @@ fn test_with_mpr() { test_file_path, ]) .succeeds() - .stdout_is_templated_fixture_any(expected_test_file_path2, &valid_last_modified_template_vars(start)); + .stdout_is_templated_fixture_any( + expected_test_file_path2, + &valid_last_modified_template_vars(start), + ); } #[test] @@ -461,5 +473,8 @@ fn test_with_join_lines_option() { scenario .args(&["+1:2", "-J", "-m", test_file_1, test_file_2]) .run() - .stdout_is_templated_fixture_any(expected_file_path, &valid_last_modified_template_vars(start)); + .stdout_is_templated_fixture_any( + expected_file_path, + &valid_last_modified_template_vars(start), + ); }