Accept an optional --seed parameter for check, otherwise default to random.
Also, show the seed on failure.
This commit is contained in:
parent
a7b9d4bb22
commit
7a2537432a
|
@ -67,6 +67,7 @@ dependencies = [
|
||||||
"ordinal",
|
"ordinal",
|
||||||
"owo-colors",
|
"owo-colors",
|
||||||
"pallas",
|
"pallas",
|
||||||
|
"rand",
|
||||||
"regex",
|
"regex",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use std::{collections::HashMap, path::PathBuf};
|
|
||||||
|
|
||||||
use aiken_lang::{ast::Tracing, line_numbers::LineNumbers};
|
use aiken_lang::{ast::Tracing, line_numbers::LineNumbers};
|
||||||
use aiken_project::{config::Config, error::Error as ProjectError, module::CheckedModule, Project};
|
use aiken_project::{config::Config, error::Error as ProjectError, module::CheckedModule, Project};
|
||||||
|
use std::{collections::HashMap, path::PathBuf};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SourceInfo {
|
pub struct SourceInfo {
|
||||||
|
@ -30,9 +29,9 @@ impl LspProject {
|
||||||
pub fn compile(&mut self) -> Result<(), Vec<ProjectError>> {
|
pub fn compile(&mut self) -> Result<(), Vec<ProjectError>> {
|
||||||
let checkpoint = self.project.checkpoint();
|
let checkpoint = self.project.checkpoint();
|
||||||
|
|
||||||
let result = self
|
let result =
|
||||||
.project
|
self.project
|
||||||
.check(true, None, false, false, Tracing::silent());
|
.check(true, None, false, false, u32::default(), Tracing::silent());
|
||||||
|
|
||||||
self.project.restore(checkpoint);
|
self.project.restore(checkpoint);
|
||||||
|
|
||||||
|
|
|
@ -226,6 +226,7 @@ where
|
||||||
match_tests: Option<Vec<String>>,
|
match_tests: Option<Vec<String>>,
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
exact_match: bool,
|
exact_match: bool,
|
||||||
|
seed: u32,
|
||||||
tracing: Tracing,
|
tracing: Tracing,
|
||||||
) -> Result<(), Vec<Error>> {
|
) -> Result<(), Vec<Error>> {
|
||||||
let options = Options {
|
let options = Options {
|
||||||
|
@ -237,6 +238,7 @@ where
|
||||||
match_tests,
|
match_tests,
|
||||||
verbose,
|
verbose,
|
||||||
exact_match,
|
exact_match,
|
||||||
|
seed,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -322,6 +324,7 @@ where
|
||||||
match_tests,
|
match_tests,
|
||||||
verbose,
|
verbose,
|
||||||
exact_match,
|
exact_match,
|
||||||
|
seed,
|
||||||
} => {
|
} => {
|
||||||
let tests =
|
let tests =
|
||||||
self.collect_tests(verbose, match_tests, exact_match, options.tracing)?;
|
self.collect_tests(verbose, match_tests, exact_match, options.tracing)?;
|
||||||
|
@ -330,7 +333,7 @@ where
|
||||||
self.event_listener.handle_event(Event::RunningTests);
|
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
|
let errors: Vec<Error> = tests
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -786,7 +789,7 @@ where
|
||||||
Ok(tests)
|
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::*;
|
use rayon::prelude::*;
|
||||||
|
|
||||||
let data_types = utils::indexmap::as_ref_values(&self.data_types);
|
let data_types = utils::indexmap::as_ref_values(&self.data_types);
|
||||||
|
@ -797,7 +800,7 @@ where
|
||||||
Test::UnitTest(unit_test) => unit_test.run(),
|
Test::UnitTest(unit_test) => unit_test.run(),
|
||||||
// TODO: Get the seed from the command-line, defaulting to a random one when not
|
// TODO: Get the seed from the command-line, defaulting to a random one when not
|
||||||
// provided.
|
// provided.
|
||||||
Test::PropertyTest(property_test) => property_test.run(42),
|
Test::PropertyTest(property_test) => property_test.run(seed),
|
||||||
})
|
})
|
||||||
.collect::<Vec<TestResult<PlutusData>>>()
|
.collect::<Vec<TestResult<PlutusData>>>()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
|
@ -10,6 +10,7 @@ pub enum CodeGenMode {
|
||||||
match_tests: Option<Vec<String>>,
|
match_tests: Option<Vec<String>>,
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
exact_match: bool,
|
exact_match: bool,
|
||||||
|
seed: u32,
|
||||||
},
|
},
|
||||||
Build(bool),
|
Build(bool),
|
||||||
NoOp,
|
NoOp,
|
||||||
|
|
|
@ -267,8 +267,6 @@ impl PropertyTest {
|
||||||
} = next_prng
|
} = next_prng
|
||||||
{
|
{
|
||||||
if result.failed(self.can_error) {
|
if result.failed(self.can_error) {
|
||||||
println!("{:#?}", result.result());
|
|
||||||
|
|
||||||
let mut counterexample = Counterexample {
|
let mut counterexample = Counterexample {
|
||||||
value,
|
value,
|
||||||
choices: next_prng.choices(),
|
choices: next_prng.choices(),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{telemetry::Terminal, Project};
|
use crate::{telemetry::Terminal, Error, Project};
|
||||||
use miette::{Diagnostic, IntoDiagnostic};
|
use miette::{Diagnostic, IntoDiagnostic};
|
||||||
use notify::{Event, RecursiveMode, Watcher};
|
use notify::{Event, RecursiveMode, Watcher};
|
||||||
use owo_colors::{OwoColorize, Stream::Stderr};
|
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
|
where
|
||||||
A: FnMut(&mut Project<Terminal>) -> Result<(), Vec<crate::error::Error>>,
|
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());
|
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 {
|
if warning_count > 0 && deny {
|
||||||
Err(ExitFailure::into_report())
|
Err(ExitFailure::into_report())
|
||||||
} else {
|
} else {
|
||||||
|
@ -148,6 +160,7 @@ where
|
||||||
pub fn watch_project<F, A>(
|
pub fn watch_project<F, A>(
|
||||||
directory: Option<&Path>,
|
directory: Option<&Path>,
|
||||||
filter: F,
|
filter: F,
|
||||||
|
seed: u32,
|
||||||
debounce: u32,
|
debounce: u32,
|
||||||
mut action: A,
|
mut action: A,
|
||||||
) -> miette::Result<()>
|
) -> miette::Result<()>
|
||||||
|
@ -219,7 +232,7 @@ where
|
||||||
.if_supports_color(Stderr, |s| s.bold())
|
.if_supports_color(Stderr, |s| s.bold())
|
||||||
.if_supports_color(Stderr, |s| s.purple()),
|
.if_supports_color(Stderr, |s| s.purple()),
|
||||||
);
|
);
|
||||||
with_project(directory, false, &mut action).unwrap_or(())
|
with_project(directory, seed, false, &mut action).unwrap_or(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,3 +38,4 @@ clap_complete = "4.3.2"
|
||||||
inquire = "0.6.2"
|
inquire = "0.6.2"
|
||||||
num-bigint = "0.4.3"
|
num-bigint = "0.4.3"
|
||||||
ordinal = "0.3.2"
|
ordinal = "0.3.2"
|
||||||
|
rand = "0.8.5"
|
||||||
|
|
|
@ -39,7 +39,7 @@ pub fn exec(
|
||||||
mainnet,
|
mainnet,
|
||||||
}: Args,
|
}: Args,
|
||||||
) -> miette::Result<()> {
|
) -> miette::Result<()> {
|
||||||
with_project(directory.as_deref(), false, |p| {
|
with_project(directory.as_deref(), u32::default(), false, |p| {
|
||||||
if rebuild {
|
if rebuild {
|
||||||
p.build(false, Tracing::silent())?;
|
p.build(false, Tracing::silent())?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ pub fn exec(
|
||||||
validator,
|
validator,
|
||||||
}: Args,
|
}: Args,
|
||||||
) -> miette::Result<()> {
|
) -> miette::Result<()> {
|
||||||
with_project(None, false, |p| {
|
with_project(None, u32::default(), false, |p| {
|
||||||
let title = module.as_ref().map(|m| {
|
let title = module.as_ref().map(|m| {
|
||||||
format!(
|
format!(
|
||||||
"{m}{}",
|
"{m}{}",
|
||||||
|
|
|
@ -29,7 +29,7 @@ pub fn exec(
|
||||||
rebuild,
|
rebuild,
|
||||||
}: Args,
|
}: Args,
|
||||||
) -> miette::Result<()> {
|
) -> miette::Result<()> {
|
||||||
with_project(directory.as_deref(), false, |p| {
|
with_project(directory.as_deref(), u32::default(), false, |p| {
|
||||||
if rebuild {
|
if rebuild {
|
||||||
p.build(false, Tracing::silent())?;
|
p.build(false, Tracing::silent())?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ pub fn exec(
|
||||||
rebuild,
|
rebuild,
|
||||||
}: Args,
|
}: Args,
|
||||||
) -> miette::Result<()> {
|
) -> miette::Result<()> {
|
||||||
with_project(directory.as_deref(), false, |p| {
|
with_project(directory.as_deref(), u32::default(), false, |p| {
|
||||||
if rebuild {
|
if rebuild {
|
||||||
p.build(false, Tracing::silent())?;
|
p.build(false, Tracing::silent())?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use aiken_lang::ast::{TraceLevel, Tracing};
|
use aiken_lang::ast::{TraceLevel, Tracing};
|
||||||
use aiken_project::watch::{self, watch_project, with_project};
|
use aiken_project::watch::{self, watch_project, with_project};
|
||||||
use clap::builder::MapValueParser;
|
use clap::builder::{MapValueParser, PossibleValuesParser, TypedValueParser};
|
||||||
use clap::builder::{PossibleValuesParser, TypedValueParser};
|
|
||||||
use std::{path::PathBuf, process};
|
use std::{path::PathBuf, process};
|
||||||
|
|
||||||
#[derive(clap::Args)]
|
#[derive(clap::Args)]
|
||||||
|
@ -52,17 +51,23 @@ pub fn exec(
|
||||||
}: Args,
|
}: Args,
|
||||||
) -> miette::Result<()> {
|
) -> miette::Result<()> {
|
||||||
let result = if watch {
|
let result = if watch {
|
||||||
watch_project(directory.as_deref(), watch::default_filter, 500, |p| {
|
watch_project(
|
||||||
p.build(
|
directory.as_deref(),
|
||||||
uplc,
|
watch::default_filter,
|
||||||
match filter_traces {
|
u32::default(),
|
||||||
Some(filter_traces) => filter_traces(trace_level),
|
500,
|
||||||
None => Tracing::All(trace_level),
|
|p| {
|
||||||
},
|
p.build(
|
||||||
)
|
uplc,
|
||||||
})
|
match filter_traces {
|
||||||
|
Some(filter_traces) => filter_traces(trace_level),
|
||||||
|
None => Tracing::All(trace_level),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
with_project(directory.as_deref(), deny, |p| {
|
with_project(directory.as_deref(), u32::default(), deny, |p| {
|
||||||
p.build(
|
p.build(
|
||||||
uplc,
|
uplc,
|
||||||
match filter_traces {
|
match filter_traces {
|
||||||
|
@ -77,8 +82,8 @@ pub fn exec(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
pub fn filter_traces_parser(
|
pub fn filter_traces_parser()
|
||||||
) -> MapValueParser<PossibleValuesParser, fn(String) -> fn(TraceLevel) -> Tracing> {
|
-> MapValueParser<PossibleValuesParser, fn(String) -> fn(TraceLevel) -> Tracing> {
|
||||||
PossibleValuesParser::new(["user-defined", "compiler-generated", "all"]).map(
|
PossibleValuesParser::new(["user-defined", "compiler-generated", "all"]).map(
|
||||||
|s: String| match s.as_str() {
|
|s: String| match s.as_str() {
|
||||||
"user-defined" => Tracing::UserDefined,
|
"user-defined" => Tracing::UserDefined,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use super::build::{filter_traces_parser, trace_level_parser};
|
use super::build::{filter_traces_parser, trace_level_parser};
|
||||||
use aiken_lang::ast::{TraceLevel, Tracing};
|
use aiken_lang::ast::{TraceLevel, Tracing};
|
||||||
use aiken_project::watch::{self, watch_project, with_project};
|
use aiken_project::watch::{self, watch_project, with_project};
|
||||||
|
use rand::prelude::*;
|
||||||
use std::{path::PathBuf, process};
|
use std::{path::PathBuf, process};
|
||||||
|
|
||||||
#[derive(clap::Args)]
|
#[derive(clap::Args)]
|
||||||
|
@ -25,6 +26,10 @@ pub struct Args {
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
watch: bool,
|
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.
|
/// 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 module with `-m aiken/list` or `-m list`.
|
||||||
/// You can match a test with `-m "aiken/list.{map}"` or `-m "aiken/option.{flatten_1}"`
|
/// You can match a test with `-m "aiken/list.{map}"` or `-m "aiken/option.{flatten_1}"`
|
||||||
|
@ -66,28 +71,41 @@ pub fn exec(
|
||||||
watch,
|
watch,
|
||||||
filter_traces,
|
filter_traces,
|
||||||
trace_level,
|
trace_level,
|
||||||
|
seed,
|
||||||
}: Args,
|
}: Args,
|
||||||
) -> miette::Result<()> {
|
) -> miette::Result<()> {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
|
let seed = seed.unwrap_or_else(|| rng.gen());
|
||||||
|
|
||||||
let result = if watch {
|
let result = if watch {
|
||||||
watch_project(directory.as_deref(), watch::default_filter, 500, |p| {
|
watch_project(
|
||||||
p.check(
|
directory.as_deref(),
|
||||||
skip_tests,
|
watch::default_filter,
|
||||||
match_tests.clone(),
|
seed,
|
||||||
debug,
|
500,
|
||||||
exact_match,
|
|p| {
|
||||||
match filter_traces {
|
p.check(
|
||||||
Some(filter_traces) => filter_traces(trace_level),
|
skip_tests,
|
||||||
None => Tracing::All(trace_level),
|
match_tests.clone(),
|
||||||
},
|
debug,
|
||||||
)
|
exact_match,
|
||||||
})
|
seed,
|
||||||
|
match filter_traces {
|
||||||
|
Some(filter_traces) => filter_traces(trace_level),
|
||||||
|
None => Tracing::All(trace_level),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
with_project(directory.as_deref(), deny, |p| {
|
with_project(directory.as_deref(), seed, deny, |p| {
|
||||||
p.check(
|
p.check(
|
||||||
skip_tests,
|
skip_tests,
|
||||||
match_tests.clone(),
|
match_tests.clone(),
|
||||||
debug,
|
debug,
|
||||||
exact_match,
|
exact_match,
|
||||||
|
seed,
|
||||||
match filter_traces {
|
match filter_traces {
|
||||||
Some(filter_traces) => filter_traces(trace_level),
|
Some(filter_traces) => filter_traces(trace_level),
|
||||||
None => Tracing::All(trace_level),
|
None => Tracing::All(trace_level),
|
||||||
|
|
|
@ -29,11 +29,17 @@ pub fn exec(
|
||||||
}: Args,
|
}: Args,
|
||||||
) -> miette::Result<()> {
|
) -> miette::Result<()> {
|
||||||
let result = if watch {
|
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())
|
p.docs(destination.clone())
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
with_project(directory.as_deref(), deny, |p| p.docs(destination.clone()))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
result.map_err(|_| process::exit(1))
|
result.map_err(|_| process::exit(1))
|
||||||
|
|
Loading…
Reference in New Issue