@@ -2,10 +2,12 @@ use std::collections::BTreeMap;
22use std:: sync:: Arc ;
33use std:: { borrow:: Cow , path:: Path } ;
44
5+ use anyhow:: Context as _;
56use futures:: { Stream , StreamExt , stream} ;
67use indexmap:: { IndexMap , indexmap} ;
8+ use revive_dt_common:: cached_fs:: read_to_string;
79use revive_dt_common:: types:: PlatformIdentifier ;
8- use revive_dt_config:: Context ;
10+ use revive_dt_config:: { Context , IgnoreCasesConfiguration } ;
911use revive_dt_format:: corpus:: Corpus ;
1012use serde_json:: { Value , json} ;
1113
@@ -29,7 +31,7 @@ pub async fn create_test_definitions_stream<'a>(
2931 context : & Context ,
3032 corpus : & ' a Corpus ,
3133 platforms_and_nodes : & ' a BTreeMap < PlatformIdentifier , ( & dyn Platform , NodePool ) > ,
32- only_execute_failed_tests : Option < & Report > ,
34+ test_case_ignore_configuration : & TestCaseIgnoreResolvedConfiguration ,
3335 reporter : Reporter ,
3436) -> impl Stream < Item = TestDefinition < ' a > > {
3537 let cloned_reporter = reporter. clone ( ) ;
@@ -130,7 +132,7 @@ pub async fn create_test_definitions_stream<'a>(
130132 )
131133 // Filter out the test cases which are incompatible or that can't run in the current setup.
132134 . filter_map ( move |test| async move {
133- match test. check_compatibility ( only_execute_failed_tests ) {
135+ match test. check_compatibility ( test_case_ignore_configuration ) {
134136 Ok ( ( ) ) => Some ( test) ,
135137 Err ( ( reason, additional_information) ) => {
136138 debug ! (
@@ -165,6 +167,69 @@ pub async fn create_test_definitions_stream<'a>(
165167 } )
166168}
167169
170+ #[ derive( Clone , Debug , Default ) ]
171+ pub struct TestCaseIgnoreResolvedConfiguration {
172+ /// Some existing report which will be used for a number of purposes.
173+ pub report : Option < Report > ,
174+
175+ /// Controls if test cases which succeeded from the existing report should
176+ /// be ignored or not.
177+ pub ignore_succeeding_test_cases_from_report : bool ,
178+
179+ /// Controls if test cases which contain steps expected to fail should be
180+ /// ignored or not.
181+ pub ignore_cases_with_failing_steps : bool ,
182+ }
183+
184+ impl TestCaseIgnoreResolvedConfiguration {
185+ pub fn with_report ( self , report : impl Into < Option < Report > > ) -> Self {
186+ self . mutate ( |this| this. report = report. into ( ) )
187+ }
188+
189+ pub fn with_ignore_succeeding_test_cases_from_report (
190+ self ,
191+ ignore_succeeding_test_cases_from_report : bool ,
192+ ) -> Self {
193+ self . mutate ( |this| {
194+ this. ignore_succeeding_test_cases_from_report = ignore_succeeding_test_cases_from_report
195+ } )
196+ }
197+
198+ pub fn with_ignore_cases_with_failing_steps (
199+ self ,
200+ ignore_cases_with_failing_steps : bool ,
201+ ) -> Self {
202+ self . mutate ( |this| this. ignore_cases_with_failing_steps = ignore_cases_with_failing_steps)
203+ }
204+
205+ fn mutate ( mut self , mutator : impl FnOnce ( & mut Self ) ) -> Self {
206+ mutator ( & mut self ) ;
207+ self
208+ }
209+ }
210+
211+ impl TryFrom < IgnoreCasesConfiguration > for TestCaseIgnoreResolvedConfiguration {
212+ type Error = anyhow:: Error ;
213+
214+ fn try_from ( value : IgnoreCasesConfiguration ) -> Result < Self , Self :: Error > {
215+ let mut this = Self :: default ( )
216+ . with_ignore_cases_with_failing_steps ( value. ignore_cases_with_failing_steps ) ;
217+
218+ this = if let Some ( succeeding_cases_from_report) = value. succeeding_cases_from_report {
219+ let content = read_to_string ( succeeding_cases_from_report)
220+ . context ( "Failed to read report at the specified path" ) ?;
221+ let report =
222+ serde_json:: from_str :: < Report > ( & content) . context ( "Failed to deserialize report" ) ?;
223+ this. with_report ( report)
224+ . with_ignore_succeeding_test_cases_from_report ( true )
225+ } else {
226+ this
227+ } ;
228+
229+ Ok ( this)
230+ }
231+ }
232+
168233/// This is a full description of a differential test to run alongside the full metadata file, the
169234/// specific case to be tested, the platforms that the tests should run on, the specific nodes of
170235/// these platforms that they should run on, the compilers to use, and everything else needed making
@@ -192,14 +257,14 @@ impl<'a> TestDefinition<'a> {
192257 /// Checks if this test can be ran with the current configuration.
193258 pub fn check_compatibility (
194259 & self ,
195- only_execute_failed_tests : Option < & Report > ,
260+ test_case_ignore_configuration : & TestCaseIgnoreResolvedConfiguration ,
196261 ) -> TestCheckFunctionResult {
197262 self . check_metadata_file_ignored ( ) ?;
198263 self . check_case_file_ignored ( ) ?;
199264 self . check_target_compatibility ( ) ?;
200265 self . check_evm_version_compatibility ( ) ?;
201266 self . check_compiler_compatibility ( ) ?;
202- self . check_ignore_succeeded ( only_execute_failed_tests ) ?;
267+ self . check_ignore_configuration ( test_case_ignore_configuration ) ?;
203268 Ok ( ( ) )
204269 }
205270
@@ -317,32 +382,49 @@ impl<'a> TestDefinition<'a> {
317382
318383 /// Checks if the test case should be executed or not based on the passed report and whether the
319384 /// user has instructed the tool to ignore the already succeeding test cases.
320- fn check_ignore_succeeded (
385+ fn check_ignore_configuration (
321386 & self ,
322- only_execute_failed_tests : Option < & Report > ,
387+ test_case_ignore_configuration : & TestCaseIgnoreResolvedConfiguration ,
323388 ) -> TestCheckFunctionResult {
324- let Some ( report) = only_execute_failed_tests else {
325- return Ok ( ( ) ) ;
326- } ;
327-
328- let test_case_status = report
329- . execution_information
330- . get ( & ( self . metadata_file_path . to_path_buf ( ) . into ( ) ) )
331- . and_then ( |obj| obj. case_reports . get ( & self . case_idx ) )
332- . and_then ( |obj| obj. mode_execution_reports . get ( & self . mode ) )
333- . and_then ( |obj| obj. status . as_ref ( ) ) ;
334-
335- match test_case_status {
336- Some ( TestCaseStatus :: Failed { .. } ) => Ok ( ( ) ) ,
337- Some ( TestCaseStatus :: Ignored { .. } ) => Err ( (
338- "Ignored since it was ignored in a previous run" ,
389+ // Check if the test case should be ignored based on it containing
390+ // failing steps.
391+ if test_case_ignore_configuration. ignore_cases_with_failing_steps
392+ && self . case . any_step_expected_to_fail ( )
393+ {
394+ return Err ( (
395+ "Ignored since it contains steps which are expected to fail" ,
339396 indexmap ! { } ,
340- ) ) ,
341- Some ( TestCaseStatus :: Succeeded { .. } ) => {
342- Err ( ( "Ignored since it succeeded in a prior run" , indexmap ! { } ) )
397+ ) ) ;
398+ }
399+
400+ // Check if the succeeding cases from the provided report should be
401+ // ignored or not.
402+ if let ( Some ( report) , true ) = (
403+ test_case_ignore_configuration. report . as_ref ( ) ,
404+ test_case_ignore_configuration. ignore_succeeding_test_cases_from_report ,
405+ ) {
406+ let test_case_status = report
407+ . execution_information
408+ . get ( & ( self . metadata_file_path . to_path_buf ( ) . into ( ) ) )
409+ . and_then ( |obj| obj. case_reports . get ( & self . case_idx ) )
410+ . and_then ( |obj| obj. mode_execution_reports . get ( & self . mode ) )
411+ . and_then ( |obj| obj. status . as_ref ( ) ) ;
412+
413+ match test_case_status {
414+ Some ( TestCaseStatus :: Failed { .. } ) | None => { }
415+ Some ( TestCaseStatus :: Ignored { .. } ) => {
416+ return Err ( (
417+ "Ignored since it was ignored in a previous run" ,
418+ indexmap ! { } ,
419+ ) ) ;
420+ }
421+ Some ( TestCaseStatus :: Succeeded { .. } ) => {
422+ return Err ( ( "Ignored since it succeeded in a prior run" , indexmap ! { } ) ) ;
423+ }
343424 }
344- None => Ok ( ( ) ) ,
345425 }
426+
427+ Ok ( ( ) )
346428 }
347429}
348430
0 commit comments