Skip to content

Commit

Permalink
Notify if test passed or failed
Browse files Browse the repository at this point in the history
- add TestFunction to dap
- add Abi dependency to debugger crate
  • Loading branch information
anaPerezGhiglia committed Aug 7, 2024
1 parent 5a1625e commit 9eacfe6
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 17 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tooling/debugger/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ noirc_printable_type.workspace = true
noirc_errors.workspace = true
noirc_driver.workspace = true
noirc_artifacts.workspace = true
noirc_abi.workspace = true
thiserror.workspace = true
codespan-reporting.workspace = true
dap.workspace = true
Expand Down
5 changes: 5 additions & 0 deletions tooling/debugger/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,14 @@ impl std::str::FromStr for DebugLocation {

#[derive(Debug)]
pub(super) enum DebugCommandResult {
// TODO: validate comments
// The debugging session is over successfully
Done,
// The session is active and we should continue with the execution
Ok,
// Execution should be paused since we reached a Breakpoint
BreakpointReached(DebugLocation),
// Session is over with an error
Error(NargoError<FieldElement>),
}

Expand Down
76 changes: 72 additions & 4 deletions tooling/debugger/src/dap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ use acvm::acir::circuit::brillig::BrilligBytecode;
use acvm::acir::circuit::Circuit;
use acvm::acir::native_types::WitnessMap;
use acvm::{BlackBoxFunctionSolver, FieldElement};
use nargo::errors::try_to_diagnose_runtime_error;
use nargo::ops::check_expected_failure_message;
use noirc_abi::Abi;
use noirc_frontend::hir::def_map::TestFunction;

use crate::context::DebugContext;
use crate::context::{DebugCommandResult, DebugLocation};
Expand All @@ -21,8 +25,8 @@ use dap::responses::{
};
use dap::server::Server;
use dap::types::{
Breakpoint, DisassembledInstruction, Scope, Source, StackFrame, SteppingGranularity,
StoppedEventReason, Thread, Variable,
Breakpoint, DisassembledInstruction, OutputEventCategory, Scope, Source, StackFrame,
SteppingGranularity, StoppedEventReason, Thread, Variable,
};
use noirc_artifacts::debug::DebugArtifact;

Expand All @@ -39,6 +43,8 @@ pub struct DapSession<'a, R: Read, W: Write, B: BlackBoxFunctionSolver<FieldElem
next_breakpoint_id: BreakpointId,
instruction_breakpoints: Vec<(DebugLocation, BreakpointId)>,
source_breakpoints: BTreeMap<FileId, Vec<(DebugLocation, BreakpointId)>>,
test_function: Option<TestFunction>,
abi: &'a Abi,
}

enum ScopeReferences {
Expand All @@ -65,6 +71,8 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver<FieldElement>> DapSession<
debug_artifact: &'a DebugArtifact,
initial_witness: WitnessMap<FieldElement>,
unconstrained_functions: &'a [BrilligBytecode<FieldElement>],
test_function: Option<TestFunction>,
abi: &'a Abi,
) -> Self {
let context = DebugContext::new(
solver,
Expand All @@ -82,6 +90,8 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver<FieldElement>> DapSession<
next_breakpoint_id: 1,
instruction_breakpoints: vec![],
source_breakpoints: BTreeMap::new(),
test_function,
abi,
}
}

Expand Down Expand Up @@ -341,6 +351,14 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver<FieldElement>> DapSession<
match result {
DebugCommandResult::Done => {
self.running = false;
if let Some(test_function) = self.test_function.as_ref() {
let test_result = if test_function.should_fail() {
"x Test failed: Test passed when it should have failed"
} else {
"✓ Test passed"
};
self.send_debug_output_message(String::from(test_result))?;
}
}
DebugCommandResult::Ok => {
self.server.send_event(Event::Stopped(StoppedEventBody {
Expand Down Expand Up @@ -368,18 +386,65 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver<FieldElement>> DapSession<
DebugCommandResult::Error(err) => {
self.server.send_event(Event::Stopped(StoppedEventBody {
reason: StoppedEventReason::Exception,
description: Some(format!("{err:?}")),
description: Some(String::from("Paused on exception")), // This is shown in callstack section of the debug panel
thread_id: Some(0),
preserve_focus_hint: Some(false),
text: None,
text: Some(format!("{err}\n{err:?}")), // This is shown in the exception panel
all_threads_stopped: Some(false),
hit_breakpoint_ids: None,
}))?;
if let Some(test_function) = self.test_function.as_ref() {
let test_should_have_passed = !test_function.should_fail();
if test_should_have_passed {
// Do not finish the execution of the debugger
// Since the test shouldn't have failed, so that user can
// - see the error on the exception panel
// - restart the debug session
let message = format!("x Text failed: {}", err.to_string());
self.send_debug_output_message(message)?;
} else {
// Finish the execution of the debugger since the test reached
// the expected state (failed)
self.running = false;
let diagnostic = try_to_diagnose_runtime_error(
&err,
&self.abi,
&self.debug_artifact.debug_symbols,
);
let message = match check_expected_failure_message(
test_function,
err.user_defined_failure_message(&self.abi.error_types),
diagnostic,
) {
nargo::ops::TestStatus::Pass => String::from("✓ Test passed"),
nargo::ops::TestStatus::Fail { message, .. } => {
format!("x Test failed: {}", message)
}
nargo::ops::TestStatus::CompileError(..) => {
String::from("x Test failed: Something went wrong")
} // TODO: this shouldn't happen. Should we panic?
};
self.send_debug_output_message(message)?;
};
}
}
}
Ok(())
}

fn send_debug_output_message(&mut self, message: String) -> Result<(), ServerError> {
self.server.send_event(Event::Output(dap::events::OutputEventBody {
category: Some(OutputEventCategory::Console),
output: message,
group: None,
variables_reference: None,
source: None,
line: None,
column: None,
data: None,
}))
}

fn get_next_breakpoint_id(&mut self) -> BreakpointId {
let id = self.next_breakpoint_id;
self.next_breakpoint_id += 1;
Expand Down Expand Up @@ -607,6 +672,7 @@ pub fn run_session<R: Read, W: Write, B: BlackBoxFunctionSolver<FieldElement>>(
solver: &B,
program: CompiledProgram,
initial_witness: WitnessMap<FieldElement>,
test_function: Option<TestFunction>,
) -> Result<(), ServerError> {
let debug_artifact = DebugArtifact { debug_symbols: program.debug, file_map: program.file_map };
let mut session = DapSession::new(
Expand All @@ -616,6 +682,8 @@ pub fn run_session<R: Read, W: Write, B: BlackBoxFunctionSolver<FieldElement>>(
&debug_artifact,
initial_witness,
&program.program.unconstrained_functions,
test_function,
&program.abi,
);

session.run_loop()
Expand Down
4 changes: 3 additions & 1 deletion tooling/debugger/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use acvm::{BlackBoxFunctionSolver, FieldElement};

use nargo::NargoError;
use noirc_driver::CompiledProgram;
use noirc_frontend::hir::def_map::TestFunction;

pub fn run_repl_session<B: BlackBoxFunctionSolver<FieldElement>>(
solver: &B,
Expand All @@ -28,6 +29,7 @@ pub fn run_dap_loop<R: Read, W: Write, B: BlackBoxFunctionSolver<FieldElement>>(
solver: &B,
program: CompiledProgram,
initial_witness: WitnessMap<FieldElement>,
test_function: Option<TestFunction>,
) -> Result<(), ServerError> {
dap::run_session(server, solver, program, initial_witness)
dap::run_session(server, solver, program, initial_witness, test_function)
}
3 changes: 2 additions & 1 deletion tooling/nargo/src/ops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ pub use self::optimize::{optimize_contract, optimize_program};
pub use self::transform::{transform_contract, transform_program};

pub use self::test::{
run_test, test_status_program_compile_fail, test_status_program_compile_pass, TestStatus,
check_expected_failure_message, run_test, test_status_program_compile_fail,
test_status_program_compile_pass, TestStatus,
};

mod compile;
Expand Down
2 changes: 1 addition & 1 deletion tooling/nargo/src/ops/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ pub fn test_status_program_compile_pass(
)
}

fn check_expected_failure_message(
pub fn check_expected_failure_message(
test_function: &TestFunction,
failed_assertion: Option<String>,
error_diagnostic: Option<FileDiagnostic>,
Expand Down
25 changes: 15 additions & 10 deletions tooling/nargo_cli/src/cli/dap_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use nargo::workspace::Workspace;
use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection};
use noirc_abi::input_parser::Format;
use noirc_driver::{check_crate, CompileOptions, CompiledProgram, NOIR_ARTIFACT_VERSION_STRING};
use noirc_frontend::hir::def_map::TestFunction;
use noirc_frontend::{graph::CrateName, hir::FunctionNameMatch};

use std::io::{BufReader, BufWriter, Read, Write};
Expand Down Expand Up @@ -109,7 +110,7 @@ fn load_and_compile_project(
acir_mode: bool,
skip_instrumentation: bool,
test_name: Option<&str>,
) -> Result<(CompiledProgram, WitnessMap<FieldElement>), LoadError> {
) -> Result<(CompiledProgram, WitnessMap<FieldElement>, Option<TestFunction>), LoadError> {
let workspace = find_workspace(project_folder, package)
.ok_or(LoadError::Generic(workspace_not_found_error_msg(project_folder, package)))?;
let package = workspace
Expand All @@ -121,12 +122,15 @@ fn load_and_compile_project(
let compile_options =
compile_options_for_debugging(acir_mode, skip_instrumentation, CompileOptions::default());

let compiled_program = match test_name {
let (compiled_program, test_function) = match test_name {
Some("") | None => (
compile_bin_package_for_debugging(&workspace, &package, &compile_options)
.map_err(|_| LoadError::Generic("Failed to compile project".into()))?,
None,
),
Some(test_name) => {
load_and_compile_test_function(test_name, workspace, &package, &compile_options)?
}
None => compile_bin_package_for_debugging(&workspace, &package, &compile_options)
.map_err(|_| LoadError::Generic("Failed to compile project".into()))?,
};
let compiled_program = nargo::ops::transform_program(compiled_program, expression_width);

Expand All @@ -140,15 +144,15 @@ fn load_and_compile_project(
.encode(&inputs_map, None)
.map_err(|_| LoadError::Generic("Failed to encode inputs".into()))?;

Ok((compiled_program, initial_witness))
Ok((compiled_program, initial_witness, test_function))
}

fn load_and_compile_test_function(
test_name: &str,
workspace: Workspace,
package: &Package,
compile_options: &CompileOptions,
) -> Result<CompiledProgram, LoadError> {
) -> Result<(CompiledProgram, Option<TestFunction>), LoadError> {
let (workspace_file_manager, mut parsed_files) =
file_manager_and_files_from(&workspace.root_dir, &workspace);

Expand Down Expand Up @@ -183,11 +187,11 @@ fn load_and_compile_test_function(

let test_functions = context
.get_all_test_functions_in_crate_matching(&crate_id, FunctionNameMatch::Exact(test_name));
let (_, test_function) = test_functions.first().expect("Test function should exist");
let (_, test_function) = test_functions.into_iter().nth(0).expect("Test function should exist");

let compiled = compile_no_check_for_debug(&mut context, test_function, &compile_options)
let compiled = compile_no_check_for_debug(&mut context, &test_function, &compile_options)
.map_err(|_| LoadError::Generic("Failed to compile project".into()))?;
Ok(compiled)
Ok((compiled, Some(test_function)))
}

fn loop_uninitialized_dap<R: Read, W: Write>(
Expand Down Expand Up @@ -250,14 +254,15 @@ fn loop_uninitialized_dap<R: Read, W: Write>(
skip_instrumentation,
test_name,
) {
Ok((compiled_program, initial_witness)) => {
Ok((compiled_program, initial_witness, test_function)) => {
server.respond(req.ack()?)?;

noir_debugger::run_dap_loop(
server,
&Bn254BlackBoxSolver,
compiled_program,
initial_witness,
test_function,
)?;
break;
}
Expand Down

0 comments on commit 9eacfe6

Please sign in to comment.