Skip to content

Commit

Permalink
chore(debugger): Integration tests (#3938)
Browse files Browse the repository at this point in the history
# Description

Adds integration tests for the debugger. 

## Problem

Related to #3015. Generates a new suite of tests that uses the examples
at test_programs/execution_success to verify that the debugger can be
started and stepped to completion with those examples.

## Summary

We take the same approach used for the `nargo execute` cmd: generating
test cases from those at test_programs/execution_success. This means
future extensions to the Noir language captured in those examples will
automatically be included in the debugger integration suite, and will
help detect any potential divergence between the language runtime and
the debugger instrumentation.

## Documentation

Check one:
- [x] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[Exceptional Case]** Documentation to be submitted in a separate
PR.

# PR Checklist\*

- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
  • Loading branch information
mverzilli authored Jan 4, 2024
1 parent 219423e commit a99e1f7
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 3 deletions.
38 changes: 38 additions & 0 deletions Cargo.lock

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

11 changes: 8 additions & 3 deletions tooling/debugger/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ authors.workspace = true
edition.workspace = true
license.workspace = true

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
rustc_version = "0.4.0"
build-data.workspace = true

[dependencies]
acvm.workspace = true
Expand All @@ -22,5 +24,8 @@ easy-repl = "0.2.1"
owo-colors = "3"
serde_json.workspace = true

[dev_dependencies]
tempfile.workspace = true
[dev-dependencies]
assert_cmd = "2.0.12"
rexpect = "0.5.0"
test-binary = "3.0.1"
tempfile.workspace = true
74 changes: 74 additions & 0 deletions tooling/debugger/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use rustc_version::{version, Version};
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::{env, fs};

fn check_rustc_version() {
assert!(
version().unwrap() >= Version::parse("1.71.1").unwrap(),
"The minimal supported rustc version is 1.71.1."
);
}

const GIT_COMMIT: &&str = &"GIT_COMMIT";

fn main() {
// Rebuild if the tests have changed
println!("cargo:rerun-if-changed=tests");

check_rustc_version();

// Only use build_data if the environment variable isn't set
// The environment variable is always set when working via Nix
if std::env::var(GIT_COMMIT).is_err() {
build_data::set_GIT_COMMIT();
build_data::set_GIT_DIRTY();
build_data::no_debug_rebuilds();
}

let out_dir = env::var("OUT_DIR").unwrap();
let destination = Path::new(&out_dir).join("debug.rs");
let mut test_file = File::create(destination).unwrap();

// Try to find the directory that Cargo sets when it is running; otherwise fallback to assuming the CWD
// is the root of the repository and append the crate path
let root_dir = match std::env::var("CARGO_MANIFEST_DIR") {
Ok(dir) => PathBuf::from(dir).parent().unwrap().parent().unwrap().to_path_buf(),
Err(_) => std::env::current_dir().unwrap(),
};
let test_dir = root_dir.join("test_programs");

generate_debugger_tests(&mut test_file, &test_dir);
}

fn generate_debugger_tests(test_file: &mut File, test_data_dir: &Path) {
let test_sub_dir = "execution_success";
let test_data_dir = test_data_dir.join(test_sub_dir);

let test_case_dirs =
fs::read_dir(test_data_dir).unwrap().flatten().filter(|c| c.path().is_dir());

for test_dir in test_case_dirs {
let test_name =
test_dir.file_name().into_string().expect("Directory can't be converted to string");
if test_name.contains('-') {
panic!(
"Invalid test directory: {test_name}. Cannot include `-`, please convert to `_`"
);
};
let test_dir = &test_dir.path();

write!(
test_file,
r#"
#[test]
fn debug_{test_name}() {{
debugger_execution_success("{test_dir}");
}}
"#,
test_dir = test_dir.display(),
)
.expect("Could not write templated test file.");
}
}
1 change: 1 addition & 0 deletions tooling/debugger/src/repl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> {
);
}
}

print_source_code_location(self.debug_artifact, &location);
}
}
Expand Down
55 changes: 55 additions & 0 deletions tooling/debugger/tests/debug.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#[cfg(test)]
mod tests {
// Some of these imports are consumed by the injected tests
use assert_cmd::cargo::cargo_bin;

use rexpect::spawn_bash;

test_binary::build_test_binary_once!(mock_backend, "../backend_interface/test-binaries");

// include tests generated by `build.rs`
include!(concat!(env!("OUT_DIR"), "/debug.rs"));

pub fn debugger_execution_success(test_program_dir: &str) {
let nargo_bin =
cargo_bin("nargo").into_os_string().into_string().expect("Cannot parse nargo path");

let mock_backend_path =
path_to_mock_backend().into_string().expect("Cannot parse mock_backend path");

let mut dbg_session = spawn_bash(Some(10000)).expect("Could not start bash session");

dbg_session
.send_line(&format!("export NARGO_BACKEND_PATH={}", mock_backend_path))
.expect("Could not export NARGO_BACKEND_PATH.");
dbg_session.wait_for_prompt().expect("Could not export NARGO_BACKEND_PATH.");

// Start debugger and test that it loads for the given program.
dbg_session
.execute(
&format!("{} debug --program-dir {}", nargo_bin, test_program_dir),
&format!(".*\\Starting debugger.*"),
)
.expect("Could not start debugger");

// While running the debugger, issue a "continue" cmd,
// which should run to the program to end given
// we haven't set any breakpoints.
// ">" is the debugger's prompt, so finding one
// after running "continue" indicates that the
// debugger has not panicked until the end of the program.
dbg_session
.send_line("c")
.expect("Debugger panicked while attempting to step through program.");
dbg_session
.exp_string(">")
.expect("Failed while waiting for debugger to step through program.");

// Run the "quit" command, then check that the debugger confirms
// having successfully solved the circuit witness.
dbg_session.send_line("quit").expect("Failed to quit debugger");
dbg_session
.exp_regex(&format!(".*Circuit witness successfully solved.*"))
.expect("Expected circuit witness to be successfully solved.");
}
}

0 comments on commit a99e1f7

Please sign in to comment.