use std::iter::Peekable;
use crate::state_machine::{ParallelTaskNode, SerialTaskNode, TaskStateMachineNode};
use integra8_components::{
BookEnd, Component, ComponentDescription, ComponentType, ConcurrencyMode, Suite,
SuiteAttributes, Test, TestParameters,
};
#[derive(Clone, Debug)]
pub enum ScheduledComponent<TParameters> {
Suite(ComponentDescription, SuiteAttributes),
Test(Test<TParameters>),
Setup(BookEnd<TParameters>),
TearDown(BookEnd<TParameters>),
}
impl<TParameters> ScheduledComponent<TParameters> {
pub fn from(component: Component<TParameters>) -> Self {
match component {
Component::Suite(c) => ScheduledComponent::Suite(c.description, c.attributes),
Component::Test(c) => ScheduledComponent::Test(c),
Component::Setup(c) => ScheduledComponent::Setup(c),
Component::TearDown(c) => ScheduledComponent::TearDown(c),
}
}
}
pub trait IntoTaskStateMachine<Payload> {
fn into_task_state_machine(self) -> TaskStateMachineNode<Payload>;
}
impl<TParameters: TestParameters> IntoTaskStateMachine<ScheduledComponent<TParameters>>
for Component<TParameters>
{
fn into_task_state_machine(self) -> TaskStateMachineNode<ScheduledComponent<TParameters>> {
match self {
Component::Suite(c) => c.into_task_state_machine(),
Component::Test(c) => TaskStateMachineNode::from(ScheduledComponent::Test(c)),
Component::Setup(c) => TaskStateMachineNode::from(ScheduledComponent::Setup(c)),
Component::TearDown(c) => TaskStateMachineNode::from(ScheduledComponent::TearDown(c)),
}
}
}
impl<TParameters: TestParameters> IntoTaskStateMachine<ScheduledComponent<TParameters>>
for Suite<TParameters>
{
fn into_task_state_machine(mut self) -> TaskStateMachineNode<ScheduledComponent<TParameters>> {
let mut root_node = SerialTaskNode::new();
root_node.enqueue(ScheduledComponent::Suite(
self.description.clone(),
self.attributes.clone(),
));
root_node.enqueue(self.setups.into_task_state_machine());
root_node.enqueue(self.tests.into_task_state_machine());
let mut parallel_suites = ParallelTaskNode::new();
let mut serial_suites = SerialTaskNode::new();
self.suites
.drain(..)
.for_each(|suite| match suite.attributes.suite_concurrency_mode {
ConcurrencyMode::Sequential => {
serial_suites.enqueue(suite.into_task_state_machine());
}
ConcurrencyMode::Parallel => {
parallel_suites.append(suite.into_task_state_machine());
}
});
root_node.enqueue(parallel_suites);
root_node.enqueue(serial_suites);
root_node.enqueue(self.tear_downs.into_task_state_machine());
root_node.enqueue(ScheduledComponent::Suite(self.description, self.attributes));
root_node.into()
}
}
impl<TParameters: TestParameters> IntoTaskStateMachine<ScheduledComponent<TParameters>>
for Vec<Test<TParameters>>
{
fn into_task_state_machine(self) -> TaskStateMachineNode<ScheduledComponent<TParameters>> {
IntoComponentTaskStepIterator::from(self.into_iter().map(|x| TaskStepComponent::Test(x)))
.fold(SerialTaskNode::new(), |mut seq, node| {
seq.enqueue(node);
seq
})
.into()
}
}
impl<TParameters: TestParameters> IntoTaskStateMachine<ScheduledComponent<TParameters>>
for Vec<BookEnd<TParameters>>
{
fn into_task_state_machine(self) -> TaskStateMachineNode<ScheduledComponent<TParameters>> {
IntoComponentTaskStepIterator::from(self.into_iter().map(|x| {
if x.description.component_type() == &ComponentType::Setup {
TaskStepComponent::Setup(x)
} else {
TaskStepComponent::TearDown(x)
}
}))
.fold(SerialTaskNode::new(), |mut seq, node| {
seq.enqueue(node);
seq
})
.into()
}
}
enum TaskStepComponent<TParameters> {
Test(Test<TParameters>),
Setup(BookEnd<TParameters>),
TearDown(BookEnd<TParameters>),
}
impl<TParameters> TaskStepComponent<TParameters> {
pub fn concurrency_mode(&self) -> &'_ ConcurrencyMode {
match self {
Self::Test(test) => &test.attributes.concurrency_mode,
Self::Setup(setup) => &setup.attributes.concurrency_mode,
Self::TearDown(tear_down) => &tear_down.attributes.concurrency_mode,
}
}
pub fn into_scheduled_component(self) -> ScheduledComponent<TParameters> {
match self {
Self::Test(test) => ScheduledComponent::Test(test),
Self::Setup(setup) => ScheduledComponent::Setup(setup),
Self::TearDown(tear_down) => ScheduledComponent::TearDown(tear_down),
}
}
}
struct IntoComponentTaskStepIterator<TParameters, I>
where
I: Iterator<Item = TaskStepComponent<TParameters>>,
{
iter: Peekable<I>,
}
impl<TParameters: TestParameters, I> IntoComponentTaskStepIterator<TParameters, I>
where
I: Iterator<Item = TaskStepComponent<TParameters>>,
{
pub fn from(iter: I) -> Self {
Self {
iter: iter.peekable(),
}
}
}
impl<TParameters: TestParameters, I> Iterator for IntoComponentTaskStepIterator<TParameters, I>
where
I: Iterator<Item = TaskStepComponent<TParameters>>,
{
type Item = TaskStateMachineNode<ScheduledComponent<TParameters>>;
fn next(&mut self) -> Option<Self::Item> {
let next = self.iter.next()?;
if next.concurrency_mode() == &ConcurrencyMode::Sequential {
return Some(next.into_scheduled_component().into());
}
let mut parallel_group = ParallelTaskNode::new();
parallel_group.append(next.into_scheduled_component());
while let Some(next) = self
.iter
.next_if(|x| x.concurrency_mode() == &ConcurrencyMode::Parallel)
{
parallel_group.append(next.into_scheduled_component());
}
Some(parallel_group.into())
}
}