Skip to content

Commit f39468a

Browse files
committed
Fix live printing (#880)
<!-- Reference any GitHub issues resolved by this PR --> Closes #860 Closes #843 ## Introduced changes <!-- A brief description of the changes --> - Test from different crates no longer run in parallel - Tests from crate are now run unordered. - Outputs from these tests are printed immediately after test case execution completed - Added new macro for testing e2e tests outputs that does not depend on test order. This macro is limited right now in that it asserts the output line by line. This is problematic for outputs that are longer than one line, like the whole output of `[FAIL]` test. This will be extended in future PRs. ## Breaking changes <!-- List of all breaking changes, if applicable --> ## Checklist <!-- Make sure all of these are complete --> - [x] Linked relevant issue - [x] Updated relevant documentation - [x] Added relevant tests - [x] Performed self-review of the code - [x] Added changes to `CHANGELOG.md` (cherry picked from commit 3aba230)
1 parent 6db50de commit f39468a

File tree

10 files changed

+415
-320
lines changed

10 files changed

+415
-320
lines changed

crates/forge/src/lib.rs

Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use cairo_lang_sierra::ids::ConcreteTypeId;
2424
use cairo_lang_sierra::program::{Function, Program};
2525
use cairo_lang_sierra_to_casm::metadata::MetadataComputationConfig;
2626
use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
27-
use futures::stream::{FuturesOrdered, FuturesUnordered};
27+
use futures::stream::FuturesUnordered;
2828

2929
use once_cell::sync::Lazy;
3030
use rand::{thread_rng, RngCore};
@@ -345,38 +345,26 @@ pub async fn run(
345345

346346
let mut fuzzing_happened = false;
347347
let mut summaries = vec![];
348-
let mut tasks = vec![];
349348

350349
for tests_from_crate in tests {
351350
let tests_from_crate = Arc::new(tests_from_crate);
352351
let runner_config = runner_config.clone();
353-
let test_crate_type = tests_from_crate.test_crate_type;
354-
let number_of_test_cases = tests_from_crate.test_cases.len();
355352
let runner_params = runner_params.clone();
356353
let cancellation_tokens = cancellation_tokens.clone();
357-
tasks.push((
358-
test_crate_type,
359-
number_of_test_cases,
360-
task::spawn({
361-
async move {
362-
run_tests_from_crate(
363-
tests_from_crate,
364-
runner_config,
365-
runner_params,
366-
cancellation_tokens,
367-
)
368-
.await
369-
}
370-
}),
371-
));
372-
}
373-
for (test_crate_type, tests_len, task) in tasks {
374-
pretty_printing::print_running_tests(test_crate_type, tests_len);
375354

376-
let (summary, was_fuzzed) = task.await??;
377-
for test_case_summary in &summary.test_case_summaries {
378-
pretty_printing::print_test_result(test_case_summary);
379-
}
355+
pretty_printing::print_running_tests(
356+
tests_from_crate.test_crate_type,
357+
tests_from_crate.test_cases.len(),
358+
);
359+
360+
let (summary, was_fuzzed) = run_tests_from_crate(
361+
tests_from_crate,
362+
runner_config,
363+
runner_params,
364+
cancellation_tokens,
365+
)
366+
.await?;
367+
380368
fuzzing_happened |= was_fuzzed;
381369
summaries.push(summary.clone());
382370
}
@@ -406,7 +394,7 @@ async fn run_tests_from_crate(
406394
);
407395

408396
let mut was_fuzzed = false;
409-
let mut tasks = FuturesOrdered::new();
397+
let mut tasks = FuturesUnordered::new();
410398
let test_cases = &tests.test_cases;
411399

412400
for case in test_cases.iter() {
@@ -419,7 +407,7 @@ async fn run_tests_from_crate(
419407
let args: Vec<ConcreteTypeId> = args.into_iter().cloned().collect();
420408
let runner = runner.clone();
421409

422-
tasks.push_back(choose_test_strategy_and_run(
410+
tasks.push(choose_test_strategy_and_run(
423411
args,
424412
case.clone(),
425413
runner,
@@ -434,6 +422,8 @@ async fn run_tests_from_crate(
434422
while let Some(task) = tasks.next().await {
435423
let result = task??;
436424

425+
pretty_printing::print_test_result(&result);
426+
437427
if result.runs().is_some() {
438428
was_fuzzed = true;
439429
}
@@ -536,7 +526,7 @@ fn run_with_fuzzing(
536526
arg.debug_name
537527
.as_ref()
538528
.ok_or_else(|| anyhow!("Type {arg:?} does not have a debug name"))
539-
.map(smol_str::SmolStr::as_str)
529+
.map(SmolStr::as_str)
540530
})
541531
.collect::<Result<Vec<_>>>()?;
542532

@@ -623,6 +613,7 @@ fn run_with_fuzzing(
623613
.clone();
624614

625615
let result = result.with_runs(runs);
616+
626617
Ok(result)
627618
})
628619
}

crates/forge/tests/e2e/collection.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::assert_stdout_contains;
12
use assert_fs::fixture::PathCopy;
23
use indoc::indoc;
34

@@ -14,11 +15,11 @@ fn collection_with_lib() {
1415

1516
let snapbox = runner();
1617

17-
snapbox
18-
.current_dir(&temp)
19-
.assert()
20-
.success()
21-
.stdout_matches(indoc! {r#"
18+
let output = snapbox.current_dir(&temp).assert().success();
19+
20+
assert_stdout_contains!(
21+
output,
22+
indoc! {r#"
2223
[..]Compiling[..]
2324
[..]Finished[..]
2425
@@ -44,7 +45,8 @@ fn collection_with_lib() {
4445
[PASS] tests::fab::test_fab
4546
[PASS] tests::fab::fab_mod::test_fab
4647
Tests: 17 passed, 0 failed, 0 skipped
47-
"#});
48+
"#}
49+
);
4850
}
4951

5052
#[test]
@@ -58,11 +60,10 @@ fn collection_without_lib() {
5860

5961
let snapbox = runner();
6062

61-
snapbox
62-
.current_dir(&temp)
63-
.assert()
64-
.success()
65-
.stdout_matches(indoc! {r#"
63+
let output = snapbox.current_dir(&temp).assert().success();
64+
assert_stdout_contains!(
65+
output,
66+
indoc! {r#"
6667
[..]Compiling[..]
6768
[..]Finished[..]
6869
@@ -88,5 +89,6 @@ fn collection_without_lib() {
8889
[PASS] tests::fibfabfob::test_fob
8990
[PASS] tests::fibfabfob::test_fab
9091
Tests: 17 passed, 0 failed, 0 skipped
91-
"#});
92+
"#}
93+
);
9294
}

crates/forge/tests/e2e/common/runner.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
use ark_std::iterable::Iterable;
12
use assert_fs::fixture::{FileWriteStr, PathChild, PathCopy};
23
use assert_fs::TempDir;
34
use camino::Utf8PathBuf;
45
use indoc::formatdoc;
6+
use regex::Regex;
57
use snapbox::cmd::{cargo_bin, Command as SnapboxCommand};
68
use std::env;
79
use std::process::Command;
@@ -205,3 +207,72 @@ pub(crate) fn get_current_branch() -> String {
205207
String::from_utf8(output.stdout).unwrap().trim().to_string()
206208
}
207209
}
210+
211+
pub(crate) fn find_with_wildcard(line: &str, actual: &Vec<String>) -> Option<usize> {
212+
let escaped = regex::escape(line);
213+
let replaced = escaped.replace("\\[\\.\\.\\]", ".*");
214+
let wrapped = format!("^{replaced}$");
215+
let re = Regex::new(wrapped.as_str()).unwrap();
216+
217+
actual.iter().position(|other| re.is_match(other))
218+
}
219+
220+
pub(crate) fn is_present(line: &str, actual: &mut Vec<String>) -> bool {
221+
let position = find_with_wildcard(line, actual);
222+
if let Some(position) = position {
223+
actual.remove(position);
224+
return true;
225+
}
226+
false
227+
}
228+
229+
pub(crate) fn assert_output_contains(output: &str, lines: &str) {
230+
let asserted_lines: Vec<String> = lines.lines().map(std::convert::Into::into).collect();
231+
let mut actual_lines: Vec<String> = output.lines().map(std::convert::Into::into).collect();
232+
233+
let mut matches = true;
234+
let mut out = String::new();
235+
236+
for line in &asserted_lines {
237+
if is_present(line, &mut actual_lines) {
238+
out.push_str("| ");
239+
} else {
240+
matches = false;
241+
out.push_str("- ");
242+
}
243+
out.push_str(line);
244+
out.push('\n');
245+
}
246+
for remaining_line in actual_lines {
247+
matches = false;
248+
out.push_str("+ ");
249+
out.push_str(&remaining_line);
250+
out.push('\n');
251+
}
252+
253+
assert!(matches, "Stdout does not match:\n\n{out}");
254+
}
255+
256+
#[macro_export]
257+
macro_rules! assert_stdout_contains {
258+
( $output:expr, $lines:expr ) => {{
259+
use $crate::e2e::common::runner::assert_output_contains;
260+
261+
let output = $output.get_output();
262+
let stdout = String::from_utf8(output.stdout.clone()).unwrap();
263+
264+
assert_output_contains(&stdout, $lines);
265+
}};
266+
}
267+
268+
#[macro_export]
269+
macro_rules! assert_stderr_contains {
270+
( $output:expr, $lines:expr ) => {{
271+
use $crate::e2e::common::runner::assert_output_contains;
272+
273+
let output = $output.get_output();
274+
let stderr = String::from_utf8(output.stderr.clone()).unwrap();
275+
276+
assert_output_contains(&stderr, $lines);
277+
}};
278+
}

crates/forge/tests/e2e/diagnostics_and_plugins.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::assert_stderr_contains;
12
use indoc::indoc;
23

34
use crate::e2e::common::runner::{runner, setup_package};
@@ -7,12 +8,10 @@ fn print_error_if_attributes_incorrect() {
78
let temp = setup_package("diagnostics_and_plugins");
89
let snapbox = runner();
910

10-
snapbox
11-
.current_dir(&temp)
12-
.assert()
13-
.code(2)
14-
.stderr_matches(indoc!
15-
{r#"
11+
let output = snapbox.current_dir(&temp).assert().code(2);
12+
assert_stderr_contains!(
13+
output,
14+
indoc! {r#"
1615
error: Plugin diagnostic: Expected fork config must be of the form `url: <double quote string>, block_id: <snforge_std::BlockId>`.
1716
--> lib.cairo:2:7
1817
#[fork(url: "https://lib.com")]
@@ -43,5 +42,6 @@ fn print_error_if_attributes_incorrect() {
4342
#[should_panic(url: "https://test.com")]
4443
^***********************^
4544
46-
"#});
45+
"#}
46+
);
4747
}

crates/forge/tests/e2e/env.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::assert_stdout_contains;
12
use crate::e2e::common::runner::{runner, setup_package};
23
use indoc::indoc;
34

@@ -6,13 +7,15 @@ fn env_var_reading() {
67
let temp = setup_package("env");
78
let snapbox = runner();
89

9-
snapbox
10+
let output = snapbox
1011
.current_dir(&temp)
1112
.env("FELT_ENV_VAR", "987654321")
1213
.env("STRING_ENV_VAR", "'abcde'")
1314
.assert()
14-
.code(0)
15-
.stdout_matches(indoc! {r#"
15+
.code(0);
16+
assert_stdout_contains!(
17+
output,
18+
indoc! {r#"
1619
[..]Compiling[..]
1720
[..]Finished[..]
1821
@@ -21,5 +24,6 @@ fn env_var_reading() {
2124
Running 1 test(s) from src/
2225
[PASS] env::reading_env_vars
2326
Tests: 1 passed, 0 failed, 0 skipped
24-
"#});
27+
"#}
28+
);
2529
}

crates/forge/tests/e2e/forking.rs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::assert_stdout_contains;
12
use crate::e2e::common::runner::{
23
runner, setup_package, setup_package_with_file_patterns, BASE_FILE_PATTERNS,
34
};
@@ -8,12 +9,14 @@ fn without_cache() {
89
let temp = setup_package("forking");
910
let snapbox = runner();
1011

11-
snapbox
12+
let output = snapbox
1213
.current_dir(&temp)
1314
.args(["--exact", "forking::test_fork_simple"])
1415
.assert()
15-
.code(0)
16-
.stdout_matches(indoc! {r#"
16+
.code(0);
17+
assert_stdout_contains!(
18+
output,
19+
indoc! {r#"
1720
[..]Compiling[..]
1821
[..]Finished[..]
1922
@@ -22,7 +25,8 @@ fn without_cache() {
2225
Running 1 test(s) from src/
2326
[PASS] forking::test_fork_simple
2427
Tests: 1 passed, 0 failed, 0 skipped
25-
"#});
28+
"#}
29+
);
2630
}
2731

2832
#[test]
@@ -37,11 +41,10 @@ fn with_cache() {
3741
);
3842
let snapbox = runner();
3943

40-
snapbox
41-
.current_dir(&temp)
42-
.assert()
43-
.code(1)
44-
.stdout_matches(indoc! {r#"
44+
let output = snapbox.current_dir(&temp).assert().code(1);
45+
assert_stdout_contains!(
46+
output,
47+
indoc! {r#"
4548
[..]Compiling[..]
4649
[..]Finished[..]
4750
@@ -57,7 +60,8 @@ fn with_cache() {
5760
5861
Failures:
5962
forking::test_fork_simple
60-
"#});
63+
"#}
64+
);
6165
}
6266

6367
#[test]
@@ -68,12 +72,14 @@ fn with_clean_cache() {
6872
);
6973
let snapbox = runner();
7074

71-
snapbox
75+
let output = snapbox
7276
.current_dir(&temp)
7377
.arg("--clean-cache")
7478
.assert()
75-
.code(0)
76-
.stdout_matches(indoc! {r#"
79+
.code(0);
80+
assert_stdout_contains!(
81+
output,
82+
indoc! {r#"
7783
[..]Compiling[..]
7884
[..]Finished[..]
7985
@@ -82,5 +88,6 @@ fn with_clean_cache() {
8288
Running 1 test(s) from src/
8389
[PASS] forking::test_fork_simple
8490
Tests: 1 passed, 0 failed, 0 skipped
85-
"#});
91+
"#}
92+
);
8693
}

0 commit comments

Comments
 (0)