Add file-in-file (macro) support to json test runner
This commit is contained in:
parent
ba7e99f482
commit
bd91de42f0
3 changed files with 64 additions and 12 deletions
|
@ -1,4 +1,5 @@
|
|||
/// Harness information for a test runner
|
||||
#[derive(Clone)]
|
||||
pub enum Feedback {
|
||||
/// Start of run (no feedback to provide)
|
||||
Start,
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use super::super::{Instruction, Feedback, TestRunner, TestMetadata};
|
||||
use super::{Test, FailureMode};
|
||||
use super::{Test, FailureMode, TestStepType};
|
||||
|
||||
/// Test runner for specific JSON data structures.
|
||||
pub struct JsonRunner {
|
||||
|
@ -7,11 +9,13 @@ pub struct JsonRunner {
|
|||
step_i: usize,
|
||||
op_i: usize,
|
||||
success: bool,
|
||||
filepath: Option<PathBuf>,
|
||||
current_macro: Option<Box<Self>>,
|
||||
}
|
||||
|
||||
impl JsonRunner {
|
||||
/// Load test information from file
|
||||
pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> std::io::Result<Self> {
|
||||
pub fn from_file<P: AsRef<Path>>(path: P) -> std::io::Result<Self> {
|
||||
let file = std::io::BufReader::new(std::fs::File::open(path.as_ref())?);
|
||||
let test = serde_json::from_reader(file)?;
|
||||
Ok(Self {
|
||||
|
@ -19,6 +23,8 @@ impl JsonRunner {
|
|||
step_i: 0,
|
||||
op_i: 0,
|
||||
success: true,
|
||||
filepath: Some(path.as_ref().into()),
|
||||
current_macro: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -29,6 +35,8 @@ impl JsonRunner {
|
|||
step_i: 0,
|
||||
op_i: 0,
|
||||
success: true,
|
||||
filepath: None,
|
||||
current_macro: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,18 +48,50 @@ impl TestRunner for JsonRunner {
|
|||
if matches!(fail_mode, FailureMode::FastFail) && !feedback.is_ok() {
|
||||
return None;
|
||||
}
|
||||
// handle in-progress macro execution
|
||||
if let Some(current_macro) = &mut self.current_macro {
|
||||
if let Some(instr) = current_macro.next(feedback.clone()) {
|
||||
return Some(instr);
|
||||
} else {
|
||||
self.current_macro = None;
|
||||
}
|
||||
}
|
||||
#[allow(clippy::never_loop)]
|
||||
'step_loop: while self.step_i < self.test_data.test.len() {
|
||||
let step = &self.test_data.test[self.step_i];
|
||||
'op_loop: while self.op_i < step.operations.len() {
|
||||
if matches!(fail_mode, FailureMode::SkipInstructions) && !feedback.is_ok() {
|
||||
log::info!("{:?} Failing instruction, going to next step", fail_mode);
|
||||
break 'op_loop;
|
||||
match step {
|
||||
TestStepType::Regular(step) => {
|
||||
'op_loop: while self.op_i < step.operations.len() {
|
||||
if matches!(fail_mode, FailureMode::SkipInstructions) && !feedback.is_ok() {
|
||||
log::info!("{:?} Failing instruction, going to next step", fail_mode);
|
||||
break 'op_loop;
|
||||
}
|
||||
let instruction = &step.operations[self.op_i];
|
||||
log::debug!("Performing step {}, operation {}", self.step_i, self.op_i);
|
||||
self.op_i += 1;
|
||||
return Some(instruction.clone().into_instruction(step.tab.clone()));
|
||||
}
|
||||
},
|
||||
TestStepType::Macro { name, file } => {
|
||||
let path = if let Some(filepath) = &self.filepath {
|
||||
filepath.join(file)
|
||||
} else {
|
||||
file.to_owned()
|
||||
};
|
||||
log::debug!("Macro {} expanded to {}", name, path.display());
|
||||
self.step_i += 1;
|
||||
match Self::from_file(&path) {
|
||||
Ok(mut new_macro) => {
|
||||
if let Some(instr) = new_macro.next(feedback.clone()) {
|
||||
self.current_macro = Some(Box::new(new_macro));
|
||||
return Some(instr);
|
||||
} else {
|
||||
log::warn!("Macro {} ({}) is empty!", name, file.display());
|
||||
}
|
||||
},
|
||||
Err(e) => log::error!("Macro {} ({}) failed to load: {}", name, file.display(), e),
|
||||
}
|
||||
}
|
||||
let instruction = &step.operations[self.op_i];
|
||||
log::debug!("Performing step {}, operation {}", self.step_i, self.op_i);
|
||||
self.op_i += 1;
|
||||
return Some(instruction.clone().into_instruction(step.tab.clone()));
|
||||
}
|
||||
if matches!(fail_mode, FailureMode::SkipSteps) && !self.success {
|
||||
log::info!("{:?} Failing step complete, ending test", fail_mode);
|
||||
|
|
|
@ -8,7 +8,7 @@ use super::super::{TabSelector, ElementSelector, ElementOpType, ElementOp, TabOp
|
|||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Test {
|
||||
pub(super) info: TestInfo,
|
||||
pub(super) test: Vec<TestStep>,
|
||||
pub(super) test: Vec<TestStepType>,
|
||||
}
|
||||
|
||||
/// Test metadata
|
||||
|
@ -41,7 +41,18 @@ impl std::convert::From<TestInfo> for TestMetadata {
|
|||
}
|
||||
}
|
||||
|
||||
/// Test step
|
||||
/// Test Step type
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(untagged)]
|
||||
pub enum TestStepType {
|
||||
Regular(TestStep),
|
||||
Macro {
|
||||
name: String,
|
||||
file: std::path::PathBuf,
|
||||
}
|
||||
}
|
||||
|
||||
/// Regular test step
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct TestStep {
|
||||
pub tab: TabDescriptor,
|
||||
|
|
Loading…
Reference in a new issue