1use crate::{
9 FlakyOrRerun, NonSuccessKind, NonSuccessReruns, Property, Report, ReportUuid, TestCase,
10 TestCaseStatus, TestRerun, TestSuite, XmlString,
11};
12use chrono::{DateTime, FixedOffset};
13use proptest::{
14 arbitrary::Arbitrary,
15 collection, option,
16 prelude::*,
17 strategy::{BoxedStrategy, Map, Strategy},
18};
19use std::time::Duration;
20
21impl Arbitrary for XmlString {
22 type Parameters = <String as Arbitrary>::Parameters;
23 type Strategy = Map<<String as Arbitrary>::Strategy, fn(String) -> XmlString>;
24
25 fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
26 String::arbitrary_with(args).prop_map(|s| {
27 XmlString::new(s.trim())
30 })
31 }
32}
33
34pub(crate) fn text_node_strategy() -> impl Strategy<Value = XmlString> {
35 any::<XmlString>().prop_filter("Non-empty string", |s| !s.is_empty())
36}
37
38pub(crate) fn test_name_strategy() -> impl Strategy<Value = XmlString> {
40 let ident = "[a-z][a-z0-9_]{0,15}";
42
43 collection::vec(ident, 1..=4).prop_map(|segments| XmlString::new(segments.join("::")))
45}
46
47pub(crate) fn xml_attr_name_strategy() -> impl Strategy<Value = XmlString> {
49 "[a-zA-Z_][a-zA-Z0-9_.-]{0,15}".prop_map(XmlString::new)
51}
52
53pub(crate) fn datetime_strategy() -> impl Strategy<Value = DateTime<FixedOffset>> {
55 (946684800i64..4102444800i64, -1440i32..1440i32).prop_map(|(secs, offset_minutes)| {
59 let offset_secs = offset_minutes * 60;
60 let offset =
61 FixedOffset::east_opt(offset_secs).unwrap_or(FixedOffset::east_opt(0).unwrap());
62 DateTime::from_timestamp(secs, 0)
63 .unwrap()
64 .with_timezone(&offset)
65 })
66}
67
68pub(crate) fn duration_strategy() -> impl Strategy<Value = Duration> {
70 (0u64..3_600_000u64).prop_map(Duration::from_millis)
72}
73
74pub(crate) fn xml_attr_index_map_strategy(
76) -> impl Strategy<Value = indexmap::IndexMap<XmlString, XmlString>> {
77 collection::hash_map(xml_attr_name_strategy(), any::<XmlString>(), 0..3)
78 .prop_map(|hm| hm.into_iter().collect())
79}
80
81impl Arbitrary for NonSuccessReruns {
82 type Parameters = ();
83 type Strategy = BoxedStrategy<Self>;
84
85 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
86 (
87 any::<FlakyOrRerun>(),
88 collection::vec(any::<TestRerun>(), 0..5),
89 )
90 .prop_map(|(kind, runs)| {
91 let kind = if runs.is_empty() {
94 FlakyOrRerun::Rerun
95 } else {
96 kind
97 };
98 NonSuccessReruns { kind, runs }
99 })
100 .boxed()
101 }
102}
103
104impl Arbitrary for TestSuite {
105 type Parameters = ();
106 type Strategy = BoxedStrategy<Self>;
107
108 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
109 (
110 test_name_strategy(),
111 option::of(datetime_strategy()),
112 option::of(duration_strategy()),
113 collection::vec(any::<TestCase>(), 0..10),
114 collection::vec(any::<Property>(), 0..5),
115 any::<Option<XmlString>>(),
116 any::<Option<XmlString>>(),
117 collection::hash_map(xml_attr_name_strategy(), any::<XmlString>(), 0..5),
118 )
119 .prop_map(
120 |(name, timestamp, time, test_cases, properties, system_out, system_err, extra)| {
121 let tests = test_cases.len();
123 let mut failures = 0;
124 let mut errors = 0;
125 let mut disabled = 0;
126
127 for test_case in &test_cases {
128 match &test_case.status {
129 TestCaseStatus::Success { .. } => {}
130 TestCaseStatus::NonSuccess { kind, .. } => match kind {
131 NonSuccessKind::Failure => failures += 1,
132 NonSuccessKind::Error => errors += 1,
133 },
134 TestCaseStatus::Skipped { .. } => disabled += 1,
135 }
136 }
137
138 TestSuite {
139 name,
140 tests,
141 disabled,
142 errors,
143 failures,
144 timestamp,
145 time,
146 test_cases,
147 properties,
148 system_out,
149 system_err,
150 extra: extra.into_iter().collect(),
151 }
152 },
153 )
154 .boxed()
155 }
156}
157
158impl Arbitrary for Report {
159 type Parameters = ();
160 type Strategy = BoxedStrategy<Self>;
161
162 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
163 (
164 test_name_strategy(),
165 any::<Option<ReportUuid>>(),
166 option::of(datetime_strategy()),
167 option::of(duration_strategy()),
168 collection::vec(any::<TestSuite>(), 0..5),
169 )
170 .prop_map(|(name, uuid, timestamp, time, test_suites)| {
171 let tests = test_suites.iter().map(|ts| ts.tests).sum();
173 let failures = test_suites.iter().map(|ts| ts.failures).sum();
174 let errors = test_suites.iter().map(|ts| ts.errors).sum();
175
176 Report {
177 name,
178 uuid,
179 timestamp,
180 time,
181 tests,
182 failures,
183 errors,
184 test_suites,
185 }
186 })
187 .boxed()
188 }
189}