-
Notifications
You must be signed in to change notification settings - Fork 233
/
Copy pathtest_cmd.rs
117 lines (100 loc) · 3.94 KB
/
test_cmd.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use std::{io::Write, path::Path};
use acvm::{acir::native_types::WitnessMap, Backend};
use clap::Args;
use nargo::ops::execute_circuit;
use noirc_driver::{compile_no_check, CompileOptions};
use noirc_frontend::{hir::Context, node_interner::FuncId};
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
use crate::{
cli::check_cmd::check_crate_and_report_errors, errors::CliError,
resolver::resolve_root_manifest,
};
use super::{compile_cmd::optimize_circuit, NargoConfig};
/// Run the tests for this program
#[derive(Debug, Clone, Args)]
pub(crate) struct TestCommand {
/// If given, only tests with names containing this string will be run
test_name: Option<String>,
#[clap(flatten)]
compile_options: CompileOptions,
}
pub(crate) fn run<B: Backend>(
backend: &B,
args: TestCommand,
config: NargoConfig,
) -> Result<(), CliError<B>> {
let test_name: String = args.test_name.unwrap_or_else(|| "".to_owned());
run_tests(backend, &config.program_dir, &test_name, &args.compile_options)
}
fn run_tests<B: Backend>(
backend: &B,
program_dir: &Path,
test_name: &str,
compile_options: &CompileOptions,
) -> Result<(), CliError<B>> {
let (mut context, crate_id) = resolve_root_manifest(program_dir, None)?;
check_crate_and_report_errors(
&mut context,
crate_id,
compile_options.deny_warnings,
compile_options.experimental_ssa,
)?;
let test_functions = match context.crate_graph.crate_type(crate_id) {
noirc_frontend::graph::CrateType::Workspace => {
context.get_all_test_functions_in_workspace_matching(test_name)
}
_ => context.get_all_test_functions_in_crate_matching(&crate_id, test_name),
};
println!("Running {} test functions...", test_functions.len());
let mut failing = 0;
let writer = StandardStream::stderr(ColorChoice::Always);
let mut writer = writer.lock();
for test_function in test_functions {
let test_name = context.function_name(&test_function);
writeln!(writer, "Testing {test_name}...").expect("Failed to write to stdout");
writer.flush().ok();
match run_test(backend, test_name, test_function, &context, compile_options) {
Ok(_) => {
writer.set_color(ColorSpec::new().set_fg(Some(Color::Green))).ok();
writeln!(writer, "ok").ok();
}
// Assume an error was already printed to stdout
Err(_) => failing += 1,
}
writer.reset().ok();
}
if failing == 0 {
writer.set_color(ColorSpec::new().set_fg(Some(Color::Green))).unwrap();
writeln!(writer, "All tests passed").ok();
} else {
let plural = if failing == 1 { "" } else { "s" };
return Err(CliError::Generic(format!("{failing} test{plural} failed")));
}
writer.reset().ok();
Ok(())
}
fn run_test<B: Backend>(
backend: &B,
test_name: &str,
main: FuncId,
context: &Context,
config: &CompileOptions,
) -> Result<(), CliError<B>> {
let mut program = compile_no_check(context, config, main)
.map_err(|_| CliError::Generic(format!("Test '{test_name}' failed to compile")))?;
// Note: We could perform this test using the unoptimized ACIR as generated by `compile_no_check`.
program.circuit = optimize_circuit(backend, program.circuit).unwrap().0;
// Run the backend to ensure the PWG evaluates functions like std::hash::pedersen,
// otherwise constraints involving these expressions will not error.
match execute_circuit(backend, program.circuit, WitnessMap::new()) {
Ok(_) => Ok(()),
Err(error) => {
let writer = StandardStream::stderr(ColorChoice::Always);
let mut writer = writer.lock();
writer.set_color(ColorSpec::new().set_fg(Some(Color::Red))).ok();
writeln!(writer, "failed").ok();
writer.reset().ok();
Err(error.into())
}
}
}