Skip to content

Commit

Permalink
Single-compile testing framework (#158)
Browse files Browse the repository at this point in the history
## What Changed?

Adds the `InlineTestBuilder` which can produce a `PreFrg` compilation
result from a string of source code. Integrates with the existing unit
test infrastructure.

## Why Does It Need To?

This allows to define test cases without needing external files (as used
by the current `define_test!` template) or temporary directories (as
used by the `Test` integration test framework) and more importantly the
source code for the test inputs is compiled as-needed, which means e.g.
tracing output is only reported for the selected test case, and that
when test cases are filtered the tests run faster.

## Checklist

- [x] Above description has been filled out so that upon quash merge we
have a
  good record of what changed.
- [x] New functions, methods, types are documented. Old documentation is
updated
  if necessary
- [x] Documentation in Notion has been updated
- [ ] Tests for new behaviors are provided
  - [ ] New test suites (if any) ave been added to the CI tests (in
`.github/workflows/rust.yml`) either as compiler test or integration
test.
*Or* justification for their omission from CI has been provided in this
PR
    description.
  • Loading branch information
JustusAdam authored Jun 26, 2024
1 parent c7e74f8 commit b96d18d
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 28 deletions.
8 changes: 4 additions & 4 deletions Cargo.lock

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

6 changes: 2 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,5 @@ debug = true
# "rustc_utils:0.7.4-nightly-2023-08-25" = { path = "../rustc_plugin/crates/rustc_utils" }
# "rustc_plugin:0.7.4-nightly-2023-08-25" = { path = "../rustc_plugin/crates/rustc_plugin" }

"rustc_utils:0.7.4-nightly-2023-08-25" = { git = "https://github.com/JustusAdam/rustc_plugin", rev = "aa83f5740fa7eb5b8e3e1ee417b29536e87cc864", features = [
"indexical",
] }
"rustc_plugin:0.7.4-nightly-2023-08-25" = { git = "https://github.com/JustusAdam/rustc_plugin", rev = "aa83f5740fa7eb5b8e3e1ee417b29536e87cc864" }
"rustc_utils:0.7.4-nightly-2023-08-25" = { git = "https://github.com/JustusAdam/rustc_plugin", rev = "d4fefb5c0344cdf4812b4877d5b03cb19a2c4672" }
"rustc_plugin:0.7.4-nightly-2023-08-25" = { git = "https://github.com/JustusAdam/rustc_plugin", rev = "d4fefb5c0344cdf4812b4877d5b03cb19a2c4672" }
6 changes: 4 additions & 2 deletions crates/flowistry_pdg_construction/tests/pdg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ use rustc_middle::{
mir::{Terminator, TerminatorKind},
ty::TyCtxt,
};
use rustc_utils::{mir::borrowck_facts, source_map::find_bodies::find_bodies};
use rustc_utils::{
mir::borrowck_facts, source_map::find_bodies::find_bodies, test_utils::CompileResult,
};

fn get_main(tcx: TyCtxt<'_>) -> LocalDefId {
find_bodies(tcx)
Expand All @@ -36,7 +38,7 @@ fn pdg(
tests: impl for<'tcx> FnOnce(TyCtxt<'tcx>, DepGraph<'tcx>) + Send,
) {
let _ = env_logger::try_init();
rustc_utils::test_utils::compile(input, move |tcx| {
rustc_utils::test_utils::CompileBuilder::new(input).compile(move |CompileResult { tcx }| {
let def_id = get_main(tcx);
let params = configure(tcx, PdgParams::new(tcx, def_id).unwrap());
let pdg = flowistry_pdg_construction::compute_pdg(params);
Expand Down
2 changes: 1 addition & 1 deletion crates/paralegal-flow/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ description = "Extractor for precise semantic PDGs from rust code."
rustc_private = true

[features]
test = []
test = ["rustc_utils/test"]

[dependencies]
paralegal-spdg = { path = "../paralegal-spdg", features = ["rustc"] }
Expand Down
16 changes: 16 additions & 0 deletions crates/paralegal-flow/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,22 @@ impl Args {
pub fn attach_to_debugger(&self) -> Option<Debugger> {
self.attach_to_debugger
}

pub fn setup_logging(&self) {
let lvl = self.verbosity();
// //let lvl = log::LevelFilter::Debug;
if simple_logger::SimpleLogger::new()
.with_level(lvl)
.with_module_level("flowistry", lvl)
.with_module_level("rustc_utils", log::LevelFilter::Error)
.without_timestamps()
.init()
.is_ok()
&& matches!(*self.direct_debug(), LogLevelConfig::Targeted(..))
{
log::set_max_level(log::LevelFilter::Warn);
}
}
}

#[derive(serde::Serialize, serde::Deserialize, clap::Args, Default)]
Expand Down
13 changes: 2 additions & 11 deletions crates/paralegal-flow/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,17 +350,8 @@ impl rustc_plugin::RustcPlugin for DfppPlugin {
return rustc_driver::RunCompiler::new(&compiler_args, &mut NoopCallbacks {}).run();
}

let lvl = plugin_args.verbosity();
// //let lvl = log::LevelFilter::Debug;
simple_logger::SimpleLogger::new()
.with_level(lvl)
//.with_module_level("flowistry", lvl)
.with_module_level("rustc_utils", log::LevelFilter::Error)
.init()
.unwrap();
if matches!(*plugin_args.direct_debug(), LogLevelConfig::Targeted(..)) {
log::set_max_level(log::LevelFilter::Warn);
}
plugin_args.setup_logging();

let opts = Box::leak(Box::new(plugin_args));

const RERUN_VAR: &str = "RERUN_WITH_PROFILER";
Expand Down
99 changes: 93 additions & 6 deletions crates/paralegal-flow/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,89 @@ macro_rules! define_flow_test_template {
};
}

/// Builder for running test cases against a string of source code.
///
/// Start with [`InlineTestBuilder::new`], compile and run the test case with
/// [`InlineTestBuilder::check`].
pub struct InlineTestBuilder {
ctrl_name: String,
input: String,
}

impl InlineTestBuilder {
/// Constructor.
///
/// Note that this test builder does not support specifying dependencies,
/// including the `paralegal` library. As such use raw annotations like
/// `#[paralegal_flow::marker(...)]`.
///
/// By default a `main` function is used as the analysis target (even
/// without an `analyze` annotation). Use
/// [`InlineTestBuilder::with_entrypoint`] to use a different function.
pub fn new(input: impl Into<String>) -> Self {
Self {
input: input.into(),
ctrl_name: "main".into(),
}
}

/// Chose a function as analysis entrypoint. Overwrites any previous choice
/// without warning.
pub fn with_entrypoint(&mut self, name: impl Into<String>) -> &mut Self {
self.ctrl_name = name.into();
self
}

/// Compile the code, select the [`CtrlRef`] corresponding to the configured
/// entrypoint and hand it to the `check` function which should contain the
/// test predicate.
pub fn check(&self, check: impl FnOnce(CtrlRef) + Send) {
use clap::Parser;

#[derive(clap::Parser)]
struct TopLevelArgs {
#[clap(flatten)]
args: crate::ClapArgs,
}

let args = crate::Args::try_from(
TopLevelArgs::parse_from([
"".into(),
"--analyze".into(),
format!(
"{}::{}",
rustc_utils::test_utils::DUMMY_MOD_NAME,
self.ctrl_name
),
])
.args,
)
.unwrap();

args.setup_logging();

rustc_utils::test_utils::CompileBuilder::new(&self.input)
.with_args(
[
"--cfg",
"paralegal",
"-Zcrate-attr=feature(register_tool)",
"-Zcrate-attr=register_tool(paralegal_flow)",
]
.into_iter()
.map(ToOwned::to_owned),
)
.compile(move |result| {
let tcx = result.tcx;
let memo = crate::Callbacks::new(Box::leak(Box::new(args)));
let pdg = memo.run(tcx).unwrap();
let graph = PreFrg::from_description(pdg);
let cref = graph.ctrl(&self.ctrl_name);
check(cref)
});
}
}

pub trait HasGraph<'g>: Sized + Copy {
fn graph(self) -> &'g PreFrg;

Expand Down Expand Up @@ -264,14 +347,18 @@ impl PreFrg {
crate::consts::FLOW_GRAPH_OUT_NAME
))
.unwrap();
let name_map = desc
.def_info
.iter()
.map(|(def_id, info)| (info.name, *def_id))
.into_group_map();
Self { desc, name_map }
Self::from_description(desc)
})
}

pub fn from_description(desc: ProgramDescription) -> Self {
let name_map = desc
.def_info
.iter()
.map(|(def_id, info)| (info.name, *def_id))
.into_group_map();
Self { desc, name_map }
}
}

#[derive(Clone)]
Expand Down

0 comments on commit b96d18d

Please sign in to comment.