Skip to content

Commit 919149d

Browse files
authored
feat: nargo test -q (or nargo test --format terse) (#6776)
1 parent 68ff7bd commit 919149d

File tree

4 files changed

+431
-141
lines changed

4 files changed

+431
-141
lines changed

.github/workflows/test-js-packages.yml

+11-11
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ jobs:
534534
- name: Build list of libraries
535535
id: get_critical_libraries
536536
run: |
537-
LIBRARIES=$(grep -Po "^https://github.com/\K.+" ./CRITICAL_NOIR_LIBRARIES | jq -R -s -c 'split("\n") | map(select(. != "")) | map({ repo: ., path: "./"})')
537+
LIBRARIES=$(grep -Po "^https://github.com/\K.+" ./CRITICAL_NOIR_LIBRARIES | jq -R -s -c 'split("\n") | map(select(. != "")) | map({ repo: ., path: ""})')
538538
echo "libraries=$LIBRARIES"
539539
echo "libraries=$LIBRARIES" >> $GITHUB_OUTPUT
540540
env:
@@ -551,15 +551,15 @@ jobs:
551551
matrix:
552552
project: ${{ fromJson( needs.critical-library-list.outputs.libraries )}}
553553
include:
554-
- project: { repo: AztecProtocol/aztec-packages, path: ./noir-projects/aztec-nr }
555-
- project: { repo: AztecProtocol/aztec-packages, path: ./noir-projects/noir-contracts }
556-
- project: { repo: AztecProtocol/aztec-packages, path: ./noir-projects/noir-protocol-circuits/crates/parity-lib }
557-
- project: { repo: AztecProtocol/aztec-packages, path: ./noir-projects/noir-protocol-circuits/crates/private-kernel-lib }
558-
- project: { repo: AztecProtocol/aztec-packages, path: ./noir-projects/noir-protocol-circuits/crates/reset-kernel-lib }
559-
- project: { repo: AztecProtocol/aztec-packages, path: ./noir-projects/noir-protocol-circuits/crates/rollup-lib }
560-
- project: { repo: AztecProtocol/aztec-packages, path: ./noir-projects/noir-protocol-circuits/crates/types }
561-
562-
name: Check external repo - ${{ matrix.project.repo }}
554+
- project: { repo: AztecProtocol/aztec-packages, path: noir-projects/aztec-nr }
555+
- project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-contracts }
556+
- project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/parity-lib }
557+
- project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/private-kernel-lib }
558+
- project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/reset-kernel-lib }
559+
- project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/rollup-lib }
560+
- project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/types }
561+
562+
name: Check external repo - ${{ matrix.project.repo }}/${{ matrix.project.path }}
563563
steps:
564564
- name: Checkout
565565
uses: actions/checkout@v4
@@ -591,7 +591,7 @@ jobs:
591591
592592
- name: Run nargo test
593593
working-directory: ./test-repo/${{ matrix.project.path }}
594-
run: nargo test --silence-warnings
594+
run: nargo test -q --silence-warnings
595595
env:
596596
NARGO_IGNORE_TEST_FAILURES_FROM_FOREIGN_CALLS: true
597597

scripts/check-critical-libraries.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ for REPO in ${REPOS_TO_CHECK[@]}; do
3131
TAG=$(getLatestReleaseTagForRepo $REPO)
3232
git clone $REPO -c advice.detachedHead=false --depth 1 --branch $TAG $TMP_DIR
3333

34-
nargo test --program-dir $TMP_DIR
34+
nargo test -q --program-dir $TMP_DIR
3535

3636
rm -rf $TMP_DIR
3737
done

tooling/nargo_cli/src/cli/test_cmd.rs

+95-129
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::{
22
collections::{BTreeMap, HashMap},
3-
io::Write,
3+
fmt::Display,
44
panic::{catch_unwind, UnwindSafe},
55
path::PathBuf,
66
sync::{mpsc, Mutex},
@@ -12,19 +12,21 @@ use acvm::{BlackBoxFunctionSolver, FieldElement};
1212
use bn254_blackbox_solver::Bn254BlackBoxSolver;
1313
use clap::Args;
1414
use fm::FileManager;
15+
use formatters::{Formatter, PrettyFormatter, TerseFormatter};
1516
use nargo::{
1617
insert_all_files_for_workspace_into_file_manager, ops::TestStatus, package::Package, parse_all,
1718
prepare_package, workspace::Workspace, PrintOutput,
1819
};
1920
use nargo_toml::{get_package_manifest, resolve_workspace_from_toml};
2021
use noirc_driver::{check_crate, CompileOptions, NOIR_ARTIFACT_VERSION_STRING};
2122
use noirc_frontend::hir::{FunctionNameMatch, ParsedFiles};
22-
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, StandardStreamLock, WriteColor};
2323

2424
use crate::{cli::check_cmd::check_crate_and_report_errors, errors::CliError};
2525

2626
use super::{NargoConfig, PackageOptions};
2727

28+
mod formatters;
29+
2830
/// Run the tests for this program
2931
#[derive(Debug, Clone, Args)]
3032
#[clap(visible_alias = "t")]
@@ -53,6 +55,40 @@ pub(crate) struct TestCommand {
5355
/// Number of threads used for running tests in parallel
5456
#[clap(long, default_value_t = rayon::current_num_threads())]
5557
test_threads: usize,
58+
59+
/// Configure formatting of output
60+
#[clap(long)]
61+
format: Option<Format>,
62+
63+
/// Display one character per test instead of one line
64+
#[clap(short = 'q', long = "quiet")]
65+
quiet: bool,
66+
}
67+
68+
#[derive(Debug, Copy, Clone, clap::ValueEnum)]
69+
enum Format {
70+
/// Print verbose output
71+
Pretty,
72+
/// Display one character per test
73+
Terse,
74+
}
75+
76+
impl Format {
77+
fn formatter(&self) -> Box<dyn Formatter> {
78+
match self {
79+
Format::Pretty => Box::new(PrettyFormatter),
80+
Format::Terse => Box::new(TerseFormatter),
81+
}
82+
}
83+
}
84+
85+
impl Display for Format {
86+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87+
match self {
88+
Format::Pretty => write!(f, "pretty"),
89+
Format::Terse => write!(f, "terse"),
90+
}
91+
}
5692
}
5793

5894
struct Test<'a> {
@@ -95,13 +131,22 @@ pub(crate) fn run(args: TestCommand, config: NargoConfig) -> Result<(), CliError
95131
None => FunctionNameMatch::Anything,
96132
};
97133

134+
let formatter: Box<dyn Formatter> = if let Some(format) = args.format {
135+
format.formatter()
136+
} else if args.quiet {
137+
Box::new(TerseFormatter)
138+
} else {
139+
Box::new(PrettyFormatter)
140+
};
141+
98142
let runner = TestRunner {
99143
file_manager: &file_manager,
100144
parsed_files: &parsed_files,
101145
workspace,
102146
args: &args,
103147
pattern,
104148
num_threads: args.test_threads,
149+
formatter,
105150
};
106151
runner.run()
107152
}
@@ -113,6 +158,7 @@ struct TestRunner<'a> {
113158
args: &'a TestCommand,
114159
pattern: FunctionNameMatch<'a>,
115160
num_threads: usize,
161+
formatter: Box<dyn Formatter>,
116162
}
117163

118164
impl<'a> TestRunner<'a> {
@@ -222,27 +268,31 @@ impl<'a> TestRunner<'a> {
222268
// We'll go package by package, but we might get test results from packages ahead of us.
223269
// We'll buffer those here and show them all at once when we get to those packages.
224270
let mut buffer: HashMap<String, Vec<TestResult>> = HashMap::new();
225-
for (package_name, test_count) in test_count_per_package {
226-
let plural = if *test_count == 1 { "" } else { "s" };
227-
println!("[{package_name}] Running {test_count} test function{plural}");
228-
271+
for (package_name, total_test_count) in test_count_per_package {
229272
let mut test_report = Vec::new();
230273

231-
// How many tests are left to receive for this package
232-
let mut remaining_test_count = *test_count;
274+
let mut current_test_count = 0;
275+
let total_test_count = *total_test_count;
276+
277+
self.formatter
278+
.package_start(package_name, total_test_count)
279+
.expect("Could not display package start");
233280

234281
// Check if we have buffered test results for this package
235282
if let Some(buffered_tests) = buffer.remove(package_name) {
236-
remaining_test_count -= buffered_tests.len();
237-
238283
for test_result in buffered_tests {
239-
self.display_test_result(&test_result)
240-
.expect("Could not display test status");
284+
self.display_test_result(
285+
&test_result,
286+
current_test_count + 1,
287+
total_test_count,
288+
)
289+
.expect("Could not display test status");
241290
test_report.push(test_result);
291+
current_test_count += 1;
242292
}
243293
}
244294

245-
if remaining_test_count > 0 {
295+
if current_test_count < total_test_count {
246296
while let Ok(test_result) = receiver.recv() {
247297
if test_result.status.failed() {
248298
all_passed = false;
@@ -257,17 +307,29 @@ impl<'a> TestRunner<'a> {
257307
continue;
258308
}
259309

260-
self.display_test_result(&test_result)
261-
.expect("Could not display test status");
310+
self.display_test_result(
311+
&test_result,
312+
current_test_count + 1,
313+
total_test_count,
314+
)
315+
.expect("Could not display test status");
262316
test_report.push(test_result);
263-
remaining_test_count -= 1;
264-
if remaining_test_count == 0 {
317+
current_test_count += 1;
318+
if current_test_count == total_test_count {
265319
break;
266320
}
267321
}
268322
}
269323

270-
display_test_report(package_name, &test_report)
324+
self.formatter
325+
.package_end(
326+
package_name,
327+
&test_report,
328+
self.file_manager,
329+
self.args.show_output,
330+
self.args.compile_options.deny_warnings,
331+
self.args.compile_options.silence_warnings,
332+
)
271333
.expect("Could not display test report");
272334
}
273335
});
@@ -417,116 +479,20 @@ impl<'a> TestRunner<'a> {
417479
}
418480

419481
/// Display the status of a single test
420-
fn display_test_result(&'a self, test_result: &'a TestResult) -> std::io::Result<()> {
421-
let writer = StandardStream::stderr(ColorChoice::Always);
422-
let mut writer = writer.lock();
423-
424-
let is_slow = test_result.time_to_run >= Duration::from_secs(30);
425-
let show_time = |writer: &mut StandardStreamLock<'_>| {
426-
if is_slow {
427-
write!(writer, " <{:.3}s>", test_result.time_to_run.as_secs_f64())
428-
} else {
429-
Ok(())
430-
}
431-
};
432-
433-
write!(writer, "[{}] Testing {}... ", &test_result.package_name, &test_result.name)?;
434-
writer.flush()?;
435-
436-
match &test_result.status {
437-
TestStatus::Pass { .. } => {
438-
writer.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?;
439-
write!(writer, "ok")?;
440-
writer.reset()?;
441-
show_time(&mut writer)?;
442-
writeln!(writer)?;
443-
}
444-
TestStatus::Fail { message, error_diagnostic } => {
445-
writer.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?;
446-
write!(writer, "FAIL\n{message}\n")?;
447-
writer.reset()?;
448-
show_time(&mut writer)?;
449-
writeln!(writer)?;
450-
if let Some(diag) = error_diagnostic {
451-
noirc_errors::reporter::report_all(
452-
self.file_manager.as_file_map(),
453-
&[diag.clone()],
454-
self.args.compile_options.deny_warnings,
455-
self.args.compile_options.silence_warnings,
456-
);
457-
}
458-
}
459-
TestStatus::Skipped { .. } => {
460-
writer.set_color(ColorSpec::new().set_fg(Some(Color::Yellow)))?;
461-
write!(writer, "skipped")?;
462-
writer.reset()?;
463-
show_time(&mut writer)?;
464-
writeln!(writer)?;
465-
}
466-
TestStatus::CompileError(err) => {
467-
noirc_errors::reporter::report_all(
468-
self.file_manager.as_file_map(),
469-
&[err.clone()],
470-
self.args.compile_options.deny_warnings,
471-
self.args.compile_options.silence_warnings,
472-
);
473-
}
474-
}
475-
476-
if self.args.show_output && !test_result.output.is_empty() {
477-
writeln!(writer, "--- {} stdout ---", test_result.name)?;
478-
write!(writer, "{}", test_result.output)?;
479-
let name_len = test_result.name.len();
480-
writeln!(writer, "{}", "-".repeat(name_len + "--- stdout ---".len()))
481-
} else {
482-
Ok(())
483-
}
484-
}
485-
}
486-
487-
/// Display a report for all tests in a package
488-
fn display_test_report(package_name: &String, test_report: &[TestResult]) -> std::io::Result<()> {
489-
let writer = StandardStream::stderr(ColorChoice::Always);
490-
let mut writer = writer.lock();
491-
492-
let failed_tests: Vec<_> = test_report
493-
.iter()
494-
.filter_map(|test_result| test_result.status.failed().then_some(&test_result.name))
495-
.collect();
496-
497-
if !failed_tests.is_empty() {
498-
writeln!(writer)?;
499-
writeln!(writer, "[{}] Failures:", package_name)?;
500-
for failed_test in failed_tests {
501-
writeln!(writer, " {}", failed_test)?;
502-
}
503-
writeln!(writer)?;
504-
}
505-
506-
write!(writer, "[{}] ", package_name)?;
507-
508-
let count_all = test_report.len();
509-
let count_failed = test_report.iter().filter(|test_result| test_result.status.failed()).count();
510-
let plural = if count_all == 1 { "" } else { "s" };
511-
if count_failed == 0 {
512-
writer.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?;
513-
write!(writer, "{count_all} test{plural} passed")?;
514-
writer.reset()?;
515-
writeln!(writer)?;
516-
} else {
517-
let count_passed = count_all - count_failed;
518-
let plural_failed = if count_failed == 1 { "" } else { "s" };
519-
let plural_passed = if count_passed == 1 { "" } else { "s" };
520-
521-
if count_passed != 0 {
522-
writer.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?;
523-
write!(writer, "{count_passed} test{plural_passed} passed, ")?;
524-
}
525-
526-
writer.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?;
527-
writeln!(writer, "{count_failed} test{plural_failed} failed")?;
528-
writer.reset()?;
482+
fn display_test_result(
483+
&'a self,
484+
test_result: &'a TestResult,
485+
current_test_count: usize,
486+
total_test_count: usize,
487+
) -> std::io::Result<()> {
488+
self.formatter.test_end(
489+
test_result,
490+
current_test_count,
491+
total_test_count,
492+
self.file_manager,
493+
self.args.show_output,
494+
self.args.compile_options.deny_warnings,
495+
self.args.compile_options.silence_warnings,
496+
)
529497
}
530-
531-
Ok(())
532498
}

0 commit comments

Comments
 (0)