pub mod backtrack;
pub mod builder;
pub mod environment;
use super::ExecutionStrategy;
use crate::{
context::ResourceId,
runner::{result_map::ExecutionResultMap, test_repo::TestRepo, Runner},
TestCase,
};
pub use builder::*;
pub use environment::*;
pub trait Process {
type I: IntoIterator<Item = ResourceId>;
fn tools(&self) -> Self::I;
fn inputs(&self) -> Self::I;
fn outputs(&self) -> Self::I;
fn cost(&self) -> u64;
fn id(&self) -> ProcessId;
fn name(&self) -> &str { "" }
}
pub type ProcessId = u64;
pub trait Resource {
fn id(&self) -> ResourceId;
fn name(&self) -> &str { "" }
}
#[derive(Default)]
pub struct DepdencyStrategy {}
struct TestResource {
name: ResourceId,
}
impl Resource for TestResource {
fn id(&self) -> ResourceId { self.name.clone() }
fn name(&self) -> &str { &self.name }
}
struct TestProcess<'a>(&'a TestCase);
impl<'a> Process for TestProcess<'a> {
type I = Vec<ResourceId>;
fn tools(&self) -> Self::I { self.0.info.reference_resources.clone() }
fn inputs(&self) -> Self::I { self.0.info.input_resources.clone() }
fn outputs(&self) -> Self::I { self.0.info.output_resources.clone() }
fn cost(&self) -> u64 { 0 }
fn id(&self) -> ProcessId { self.0.info.test_id }
fn name(&self) -> &str { &self.0.info.display_name }
}
impl ExecutionStrategy for DepdencyStrategy {
fn run(&self, repo: &mut TestRepo, initial: &[ResourceId]) -> ExecutionResultMap {
let mut result_map = ExecutionResultMap::default();
let mut env = EnvironmentBuilder::default();
let mut resources = repo.cases().values().fold(Vec::new(), |mut data, c: &TestCase| {
data.append(&mut c.info.reference_resources.clone());
data.append(&mut c.info.input_resources.clone());
data.append(&mut c.info.output_resources.clone());
data
});
resources.sort();
resources.dedup();
let resources = resources.into_iter().map(|r| TestResource { name: r });
for r in resources {
env.add_resource(r);
}
let mut processes: Vec<_> = repo.cases().values().collect();
processes.sort_by_key(|p| p.info.test_id);
for c in processes {
env.add_process(TestProcess(c))
}
for r in initial {
env.add_initial(r.clone())
}
let env = env.build();
let params = repo.runner_params();
let mut path = Path::default();
'outer: for rand in 0usize..1000 {
let (_reason, mut nexts) = backtrack::next(&env, &path);
if nexts.is_empty() {
break;
}
let i = rand.rem_euclid(nexts.len());
let step = nexts.remove(i);
let tc_id = step.process;
let tc = repo.cases().get(&tc_id).unwrap();
if let Ok(res) = Runner::exec_testcase(tc, ¶ms) {
result_map.record(tc_id, res);
path.push(step);
if result_map.did_complete(repo) {
break 'outer;
}
}
}
result_map
}
}
impl DepdencyStrategy {}
#[cfg(test)]
mod tests {
use crate::{
algos::ExecutionStrategy, call_handler, Context, HandlerParams, Resource, ResourceId, RunConfig, TestError,
TestRepo,
};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ExampleError {}
impl TestError for ExampleError {}
struct A {}
struct B {}
impl Resource for A {
type Context = Context;
fn from_context(context: &Self::Context) -> Option<Self> { context.extract::<A>() }
fn into_context(context: &Self::Context, resource: A) { context.inject(resource) }
fn get_resource_id() -> ResourceId { "A".to_string() }
}
impl Resource for B {
type Context = Context;
fn from_context(context: &Self::Context) -> Option<Self> { context.extract::<B>() }
fn into_context(context: &Self::Context, resource: B) { context.inject(resource) }
fn get_resource_id() -> ResourceId { "B".to_string() }
}
fn test_1() -> std::result::Result<A, ExampleError> { Ok(A {}) }
fn test_2(_: A) -> std::result::Result<B, ExampleError> { Ok(B {}) }
fn test_3(_: B) -> std::result::Result<(), ExampleError> { Ok(()) }
#[test]
fn dependency_with_2_resources() {
let mut repo = TestRepo::default();
let runconfig = RunConfig {
context: Context::default(),
..Default::default()
};
let context = runconfig.context.clone();
let handler_params = HandlerParams::from(&runconfig);
repo.add(
0,
"test_1",
Box::new(move || call_handler(context.clone(), &mut test_1, &handler_params)),
Default::default(),
vec![],
vec![],
vec!["A".to_string()],
);
let context = runconfig.context.clone();
let handler_params = HandlerParams::from(&runconfig);
repo.add(
1,
"test_2",
Box::new(move || call_handler(context.clone(), &mut test_2, &handler_params)),
Default::default(),
vec![],
vec!["A".to_string()],
vec!["B".to_string()],
);
let context = runconfig.context.clone();
let handler_params = HandlerParams::from(&runconfig);
repo.add(
2,
"test_3",
Box::new(move || call_handler(context.clone(), &mut test_3, &handler_params)),
Default::default(),
vec![],
vec!["B".to_string()],
vec![],
);
let strategy = super::DepdencyStrategy::default();
let result = strategy.run(&mut repo, &[]);
assert_eq!(result.successes(), 3);
assert_eq!(result.errors(), 0);
let records = result.into_records();
assert_eq!(records.len(), 3);
assert_eq!(records[0].test_id, 0);
assert_eq!(records[1].test_id, 1);
assert_eq!(records[2].test_id, 2);
}
}