use crate::{
FlakyOrRerun, NonSuccessKind, NonSuccessReruns, Property, Report, ReportUuid, TestCase,
TestCaseStatus, TestRerun, TestSuite, XmlString,
};
use chrono::{DateTime, FixedOffset};
use proptest::{
arbitrary::Arbitrary,
collection, option,
prelude::*,
strategy::{BoxedStrategy, Map, Strategy},
};
use std::time::Duration;
impl Arbitrary for XmlString {
type Parameters = <String as Arbitrary>::Parameters;
type Strategy = Map<<String as Arbitrary>::Strategy, fn(String) -> XmlString>;
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
String::arbitrary_with(args).prop_map(|s| {
XmlString::new(s.trim())
})
}
}
pub(crate) fn text_node_strategy() -> impl Strategy<Value = XmlString> {
any::<XmlString>().prop_filter("Non-empty string", |s| !s.is_empty())
}
pub(crate) fn test_name_strategy() -> impl Strategy<Value = XmlString> {
let ident = "[a-z][a-z0-9_]{0,15}";
collection::vec(ident, 1..=4).prop_map(|segments| XmlString::new(segments.join("::")))
}
pub(crate) fn xml_attr_name_strategy() -> impl Strategy<Value = XmlString> {
"[a-zA-Z_][a-zA-Z0-9_.-]{0,15}".prop_map(XmlString::new)
}
pub(crate) fn datetime_strategy() -> impl Strategy<Value = DateTime<FixedOffset>> {
(946684800i64..4102444800i64, -1440i32..1440i32).prop_map(|(secs, offset_minutes)| {
let offset_secs = offset_minutes * 60;
let offset =
FixedOffset::east_opt(offset_secs).unwrap_or(FixedOffset::east_opt(0).unwrap());
DateTime::from_timestamp(secs, 0)
.unwrap()
.with_timezone(&offset)
})
}
pub(crate) fn duration_strategy() -> impl Strategy<Value = Duration> {
(0u64..3_600_000u64).prop_map(Duration::from_millis)
}
pub(crate) fn xml_attr_index_map_strategy(
) -> impl Strategy<Value = indexmap::IndexMap<XmlString, XmlString>> {
collection::hash_map(xml_attr_name_strategy(), any::<XmlString>(), 0..3)
.prop_map(|hm| hm.into_iter().collect())
}
impl Arbitrary for NonSuccessReruns {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
(
any::<FlakyOrRerun>(),
collection::vec(any::<TestRerun>(), 0..5),
)
.prop_map(|(kind, runs)| {
let kind = if runs.is_empty() {
FlakyOrRerun::Rerun
} else {
kind
};
NonSuccessReruns { kind, runs }
})
.boxed()
}
}
impl Arbitrary for TestSuite {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
(
test_name_strategy(),
option::of(datetime_strategy()),
option::of(duration_strategy()),
collection::vec(any::<TestCase>(), 0..10),
collection::vec(any::<Property>(), 0..5),
any::<Option<XmlString>>(),
any::<Option<XmlString>>(),
collection::hash_map(xml_attr_name_strategy(), any::<XmlString>(), 0..5),
)
.prop_map(
|(name, timestamp, time, test_cases, properties, system_out, system_err, extra)| {
let tests = test_cases.len();
let mut failures = 0;
let mut errors = 0;
let mut disabled = 0;
for test_case in &test_cases {
match &test_case.status {
TestCaseStatus::Success { .. } => {}
TestCaseStatus::NonSuccess { kind, .. } => match kind {
NonSuccessKind::Failure => failures += 1,
NonSuccessKind::Error => errors += 1,
},
TestCaseStatus::Skipped { .. } => disabled += 1,
}
}
TestSuite {
name,
tests,
disabled,
errors,
failures,
timestamp,
time,
test_cases,
properties,
system_out,
system_err,
extra: extra.into_iter().collect(),
}
},
)
.boxed()
}
}
impl Arbitrary for Report {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
(
test_name_strategy(),
any::<Option<ReportUuid>>(),
option::of(datetime_strategy()),
option::of(duration_strategy()),
collection::vec(any::<TestSuite>(), 0..5),
)
.prop_map(|(name, uuid, timestamp, time, test_suites)| {
let tests = test_suites.iter().map(|ts| ts.tests).sum();
let failures = test_suites.iter().map(|ts| ts.failures).sum();
let errors = test_suites.iter().map(|ts| ts.errors).sum();
Report {
name,
uuid,
timestamp,
time,
tests,
failures,
errors,
test_suites,
}
})
.boxed()
}
}