Accept an optional --seed parameter for check, otherwise default to random.

Also, show the seed on failure.
This commit is contained in:
KtorZ 2024-03-03 20:36:01 +01:00
parent a7b9d4bb22
commit 7a2537432a
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
14 changed files with 100 additions and 55 deletions

1
Cargo.lock generated vendored
View File

@ -67,6 +67,7 @@ dependencies = [
"ordinal",
"owo-colors",
"pallas",
"rand",
"regex",
"serde_json",
"thiserror",

View File

@ -1,7 +1,6 @@
use std::{collections::HashMap, path::PathBuf};
use aiken_lang::{ast::Tracing, line_numbers::LineNumbers};
use aiken_project::{config::Config, error::Error as ProjectError, module::CheckedModule, Project};
use std::{collections::HashMap, path::PathBuf};
#[derive(Debug)]
pub struct SourceInfo {
@ -30,9 +29,9 @@ impl LspProject {
pub fn compile(&mut self) -> Result<(), Vec<ProjectError>> {
let checkpoint = self.project.checkpoint();
let result = self
.project
.check(true, None, false, false, Tracing::silent());
let result =
self.project
.check(true, None, false, false, u32::default(), Tracing::silent());
self.project.restore(checkpoint);

View File

@ -226,6 +226,7 @@ where
match_tests: Option<Vec<String>>,
verbose: bool,
exact_match: bool,
seed: u32,
tracing: Tracing,
) -> Result<(), Vec<Error>> {
let options = Options {
@ -237,6 +238,7 @@ where
match_tests,
verbose,
exact_match,
seed,
}
},
};
@ -322,6 +324,7 @@ where
match_tests,
verbose,
exact_match,
seed,
} => {
let tests =
self.collect_tests(verbose, match_tests, exact_match, options.tracing)?;
@ -330,7 +333,7 @@ where
self.event_listener.handle_event(Event::RunningTests);
}
let tests = self.run_tests(tests);
let tests = self.run_tests(tests, seed);
let errors: Vec<Error> = tests
.iter()
@ -786,7 +789,7 @@ where
Ok(tests)
}
fn run_tests(&self, tests: Vec<Test>) -> Vec<TestResult<UntypedExpr>> {
fn run_tests(&self, tests: Vec<Test>, seed: u32) -> Vec<TestResult<UntypedExpr>> {
use rayon::prelude::*;
let data_types = utils::indexmap::as_ref_values(&self.data_types);
@ -797,7 +800,7 @@ where
Test::UnitTest(unit_test) => unit_test.run(),
// TODO: Get the seed from the command-line, defaulting to a random one when not
// provided.
Test::PropertyTest(property_test) => property_test.run(42),
Test::PropertyTest(property_test) => property_test.run(seed),
})
.collect::<Vec<TestResult<PlutusData>>>()
.into_iter()

View File

@ -10,6 +10,7 @@ pub enum CodeGenMode {
match_tests: Option<Vec<String>>,
verbose: bool,
exact_match: bool,
seed: u32,
},
Build(bool),
NoOp,

View File

@ -267,8 +267,6 @@ impl PropertyTest {
} = next_prng
{
if result.failed(self.can_error) {
println!("{:#?}", result.result());
let mut counterexample = Counterexample {
value,
choices: next_prng.choices(),

View File

@ -1,4 +1,4 @@
use crate::{telemetry::Terminal, Project};
use crate::{telemetry::Terminal, Error, Project};
use miette::{Diagnostic, IntoDiagnostic};
use notify::{Event, RecursiveMode, Watcher};
use owo_colors::{OwoColorize, Stream::Stderr};
@ -75,7 +75,12 @@ pub fn default_filter(evt: &Event) -> bool {
}
}
pub fn with_project<A>(directory: Option<&Path>, deny: bool, mut action: A) -> miette::Result<()>
pub fn with_project<A>(
directory: Option<&Path>,
seed: u32,
deny: bool,
mut action: A,
) -> miette::Result<()>
where
A: FnMut(&mut Project<Terminal>) -> Result<(), Vec<crate::error::Error>>,
{
@ -116,17 +121,24 @@ where
}
);
if errs.iter().any(|e| matches!(e, Error::TestFailure { .. })) {
eprintln!(
" ━━━━━━\n ╰─▶ use {} {} to replay",
"--seed".if_supports_color(Stderr, |s| s.bold()),
format!("{seed}").if_supports_color(Stderr, |s| s.bold())
);
}
return Err(ExitFailure::into_report());
} else {
eprintln!(
"{}",
Summary {
error_count: 0,
warning_count
}
);
}
eprintln!(
"{}",
Summary {
error_count: 0,
warning_count
}
);
if warning_count > 0 && deny {
Err(ExitFailure::into_report())
} else {
@ -148,6 +160,7 @@ where
pub fn watch_project<F, A>(
directory: Option<&Path>,
filter: F,
seed: u32,
debounce: u32,
mut action: A,
) -> miette::Result<()>
@ -219,7 +232,7 @@ where
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple()),
);
with_project(directory, false, &mut action).unwrap_or(())
with_project(directory, seed, false, &mut action).unwrap_or(())
}
}
}

View File

@ -38,3 +38,4 @@ clap_complete = "4.3.2"
inquire = "0.6.2"
num-bigint = "0.4.3"
ordinal = "0.3.2"
rand = "0.8.5"

View File

@ -39,7 +39,7 @@ pub fn exec(
mainnet,
}: Args,
) -> miette::Result<()> {
with_project(directory.as_deref(), false, |p| {
with_project(directory.as_deref(), u32::default(), false, |p| {
if rebuild {
p.build(false, Tracing::silent())?;
}

View File

@ -47,7 +47,7 @@ pub fn exec(
validator,
}: Args,
) -> miette::Result<()> {
with_project(None, false, |p| {
with_project(None, u32::default(), false, |p| {
let title = module.as_ref().map(|m| {
format!(
"{m}{}",

View File

@ -29,7 +29,7 @@ pub fn exec(
rebuild,
}: Args,
) -> miette::Result<()> {
with_project(directory.as_deref(), false, |p| {
with_project(directory.as_deref(), u32::default(), false, |p| {
if rebuild {
p.build(false, Tracing::silent())?;
}

View File

@ -29,7 +29,7 @@ pub fn exec(
rebuild,
}: Args,
) -> miette::Result<()> {
with_project(directory.as_deref(), false, |p| {
with_project(directory.as_deref(), u32::default(), false, |p| {
if rebuild {
p.build(false, Tracing::silent())?;
}

View File

@ -1,7 +1,6 @@
use aiken_lang::ast::{TraceLevel, Tracing};
use aiken_project::watch::{self, watch_project, with_project};
use clap::builder::MapValueParser;
use clap::builder::{PossibleValuesParser, TypedValueParser};
use clap::builder::{MapValueParser, PossibleValuesParser, TypedValueParser};
use std::{path::PathBuf, process};
#[derive(clap::Args)]
@ -52,17 +51,23 @@ pub fn exec(
}: Args,
) -> miette::Result<()> {
let result = if watch {
watch_project(directory.as_deref(), watch::default_filter, 500, |p| {
p.build(
uplc,
match filter_traces {
Some(filter_traces) => filter_traces(trace_level),
None => Tracing::All(trace_level),
},
)
})
watch_project(
directory.as_deref(),
watch::default_filter,
u32::default(),
500,
|p| {
p.build(
uplc,
match filter_traces {
Some(filter_traces) => filter_traces(trace_level),
None => Tracing::All(trace_level),
},
)
},
)
} else {
with_project(directory.as_deref(), deny, |p| {
with_project(directory.as_deref(), u32::default(), deny, |p| {
p.build(
uplc,
match filter_traces {
@ -77,8 +82,8 @@ pub fn exec(
}
#[allow(clippy::type_complexity)]
pub fn filter_traces_parser(
) -> MapValueParser<PossibleValuesParser, fn(String) -> fn(TraceLevel) -> Tracing> {
pub fn filter_traces_parser()
-> MapValueParser<PossibleValuesParser, fn(String) -> fn(TraceLevel) -> Tracing> {
PossibleValuesParser::new(["user-defined", "compiler-generated", "all"]).map(
|s: String| match s.as_str() {
"user-defined" => Tracing::UserDefined,

View File

@ -1,6 +1,7 @@
use super::build::{filter_traces_parser, trace_level_parser};
use aiken_lang::ast::{TraceLevel, Tracing};
use aiken_project::watch::{self, watch_project, with_project};
use rand::prelude::*;
use std::{path::PathBuf, process};
#[derive(clap::Args)]
@ -25,6 +26,10 @@ pub struct Args {
#[clap(long)]
watch: bool,
/// An initial seed to initialize the pseudo-random generator for property-tests.
#[clap(long)]
seed: Option<u32>,
/// Only run tests if they match any of these strings.
/// You can match a module with `-m aiken/list` or `-m list`.
/// You can match a test with `-m "aiken/list.{map}"` or `-m "aiken/option.{flatten_1}"`
@ -66,28 +71,41 @@ pub fn exec(
watch,
filter_traces,
trace_level,
seed,
}: Args,
) -> miette::Result<()> {
let mut rng = rand::thread_rng();
let seed = seed.unwrap_or_else(|| rng.gen());
let result = if watch {
watch_project(directory.as_deref(), watch::default_filter, 500, |p| {
p.check(
skip_tests,
match_tests.clone(),
debug,
exact_match,
match filter_traces {
Some(filter_traces) => filter_traces(trace_level),
None => Tracing::All(trace_level),
},
)
})
watch_project(
directory.as_deref(),
watch::default_filter,
seed,
500,
|p| {
p.check(
skip_tests,
match_tests.clone(),
debug,
exact_match,
seed,
match filter_traces {
Some(filter_traces) => filter_traces(trace_level),
None => Tracing::All(trace_level),
},
)
},
)
} else {
with_project(directory.as_deref(), deny, |p| {
with_project(directory.as_deref(), seed, deny, |p| {
p.check(
skip_tests,
match_tests.clone(),
debug,
exact_match,
seed,
match filter_traces {
Some(filter_traces) => filter_traces(trace_level),
None => Tracing::All(trace_level),

View File

@ -29,11 +29,17 @@ pub fn exec(
}: Args,
) -> miette::Result<()> {
let result = if watch {
watch_project(directory.as_deref(), watch::default_filter, 500, |p| {
watch_project(
directory.as_deref(),
watch::default_filter,
u32::default(),
500,
|p| p.docs(destination.clone()),
)
} else {
with_project(directory.as_deref(), u32::default(), deny, |p| {
p.docs(destination.clone())
})
} else {
with_project(directory.as_deref(), deny, |p| p.docs(destination.clone()))
};
result.map_err(|_| process::exit(1))