Skip to content

Commit

Permalink
Test runner.
Browse files Browse the repository at this point in the history
  • Loading branch information
chriseth committed Oct 31, 2024
1 parent f3f8c60 commit 2757ee8
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 1 deletion.
6 changes: 6 additions & 0 deletions ast/src/parsed/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,12 @@ impl<R: Display> From<FunctionType<Expression<R>>> for FunctionType<u64> {
}
}

impl From<FunctionType> for Type {
fn from(value: FunctionType) -> Self {
Type::Function(value)
}
}

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize, JsonSchema)]
pub struct TypeScheme<E = u64> {
/// Type variables and their trait bounds.
Expand Down
1 change: 1 addition & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ powdr.workspace = true

clap = { version = "^4.3", features = ["derive"] }
env_logger = "0.10.0"
itertools = "0.13"
log = "0.4.17"
strum = { version = "0.24.1", features = ["derive"] }
clap-markdown = "0.1.3"
Expand Down
31 changes: 30 additions & 1 deletion cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! The powdr CLI tool

mod test_runner;
mod util;

use clap::{CommandFactory, Parser, Subcommand};
Expand Down Expand Up @@ -363,6 +364,24 @@ enum Commands {
#[arg(value_parser = clap_enum_variants!(FieldArgument))]
field: FieldArgument,
},

/// Executes all functions starting with `test_` in every module called
/// `test` starting from the given module.
Test {
/// Input file or directory.
file: String,

/// The field to use
#[arg(long)]
#[arg(default_value_t = FieldArgument::Gl)]
#[arg(value_parser = clap_enum_variants!(FieldArgument))]
field: FieldArgument,

/// Also run the tests inside the standard library.
#[arg(long)]
#[arg(default_value_t = false)]
include_std_tests: bool,
},
}

fn split_inputs<T: FieldElement>(inputs: &str) -> Vec<T> {
Expand Down Expand Up @@ -469,6 +488,13 @@ fn run_command(command: Commands) {
csv_mode
))
}
Commands::Test {
file,
field,
include_std_tests,
} => {
call_with_field!(run_test::<field>(&file, include_std_tests))
}
Commands::Prove {
file,
dir,
Expand Down Expand Up @@ -688,10 +714,13 @@ fn run<F: FieldElement>(
.compute_proof()
.unwrap();
}

Ok(())
}

fn run_test<T: FieldElement>(file: &str, include_std_tests: bool) -> Result<(), Vec<String>> {
test_runner::run::<T>(file, include_std_tests)
}

#[allow(clippy::too_many_arguments)]
fn read_and_prove<T: FieldElement>(
file: &Path,
Expand Down
63 changes: 63 additions & 0 deletions cli/src/test_runner.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use std::{
fs,
path::{Path, PathBuf},
str::FromStr,
};

use itertools::Itertools;

use powdr::ast::{
analyzed::FunctionValueDefinition,
parsed::{
asm::SymbolPath,
types::{FunctionType, Type},
},
};
use powdr::pil_analyzer::evaluator::{self, SymbolLookup};
use powdr::{FieldElement, Pipeline};

/// Executes all functions in the given file that start with `test_` and are
/// inside a module called `test`.
///
/// @param include_std_tests: Whether to run the tests inside the standard library.
pub fn run<F: FieldElement>(input: &str, include_std_tests: bool) -> Result<(), Vec<String>> {
let mut pipeline = Pipeline::<F>::default().from_file(PathBuf::from(&input));

let analyzed = pipeline.compute_analyzed_pil()?;

let mut symbols = evaluator::Definitions {
definitions: &analyzed.definitions,
solved_impls: &analyzed.solved_impls,
};

for (name, (_, val)) in analyzed
.definitions
.iter()
.filter(|(n, _)| n.starts_with("test::test_") || n.contains("::test::test_"))
.filter(|(n, _)| include_std_tests || !n.starts_with("std::"))
.filter(|(n, _)| SymbolPath::from_str(n).unwrap().name().starts_with("test_"))
.sorted_by_key(|(n, _)| *n)
{
let Some(FunctionValueDefinition::Expression(f)) = val else {
continue;
};
// Require a plain `->()` type.
let type_scheme = f.type_scheme.as_ref().unwrap();
if !type_scheme.vars.is_empty()
|| type_scheme.ty
!= (FunctionType {
params: vec![],
value: Box::new(Type::empty_tuple()),
})
.into()
{
continue;
}
print!("Running test: {name}...");
let function = symbols.lookup(name, &None).unwrap();
evaluator::evaluate_function_call::<F>(function, vec![], &mut symbols).unwrap();
println!(" ok");
}

Ok(())
}

0 comments on commit 2757ee8

Please sign in to comment.