@@ -325,7 +325,24 @@ fn run(cli: Cli) -> ExitCode {
325325 files
326326 } ;
327327
328+ #[ derive( Debug , Default ) ]
329+ struct EntryByCtsPath < ' a > {
330+ metadata_path : Option < TestPath < ' a > > ,
331+ reported_path : Option < TestPath < ' a > > ,
332+ entry : TestEntry ,
333+ }
334+
335+ fn cts_path ( test_path : & TestPath < ' _ > ) -> Option < String > {
336+ test_path
337+ . variant
338+ . as_ref ( )
339+ . filter ( |v| v. starts_with ( "?q=webgpu:" ) )
340+ . map ( |v| v. strip_prefix ( "?q=" ) . unwrap ( ) . to_owned ( ) )
341+ . filter ( |_q| test_path. path . ends_with ( "cts.https.html" ) )
342+ }
343+
328344 let mut file_props_by_file = IndexMap :: < Utf8PathBuf , FileProps > :: default ( ) ;
345+ let mut entries_by_cts_path = IndexMap :: < String , EntryByCtsPath < ' _ > > :: default ( ) ;
329346 let mut other_entries_by_test = IndexMap :: < TestPath < ' _ > , TestEntry > :: default ( ) ;
330347 let old_meta_file_paths = meta_files_by_path. keys ( ) . cloned ( ) . collect :: < Vec < _ > > ( ) ;
331348
@@ -370,9 +387,19 @@ fn run(cli: Cli) -> ExitCode {
370387 let TestEntry {
371388 entry : test_entry,
372389 subtests : subtest_entries,
373- } = other_entries_by_test
374- . entry ( test_path. clone ( ) . into_owned ( ) )
375- . or_default ( ) ;
390+ } = if let Some ( cts_path) = cts_path ( & test_path) {
391+ let entry = entries_by_cts_path. entry ( cts_path) . or_default ( ) ;
392+ if let Some ( _old) =
393+ entry. metadata_path . replace ( test_path. clone ( ) . into_owned ( ) )
394+ {
395+ dupe_err ( ) ;
396+ }
397+ & mut entry. entry
398+ } else {
399+ other_entries_by_test
400+ . entry ( test_path. clone ( ) . into_owned ( ) )
401+ . or_default ( )
402+ } ;
376403
377404 let test_path = & test_path;
378405
@@ -451,9 +478,32 @@ fn run(cli: Cli) -> ExitCode {
451478 let TestEntry {
452479 entry : test_entry,
453480 subtests : subtest_entries,
454- } = other_entries_by_test
455- . entry ( test_path. clone ( ) . into_owned ( ) )
456- . or_default ( ) ;
481+ } = if let Some ( cts_path) = cts_path ( & test_path) {
482+ let entry = entries_by_cts_path. entry ( cts_path) . or_default ( ) ;
483+ if let Some ( old) =
484+ entry. reported_path . replace ( test_path. clone ( ) . into_owned ( ) )
485+ {
486+ if old != test_path {
487+ log:: warn!(
488+ concat!(
489+ "found test execution entry containing the same " ,
490+ "CTS test path as another, " ,
491+ "discarding previous entries with " ,
492+ "this and further dupes; entries:\n " ,
493+ "older: {:#?}\n " ,
494+ "newer: {:#?}\n " ,
495+ ) ,
496+ old,
497+ test_path
498+ )
499+ }
500+ }
501+ & mut entry. entry
502+ } else {
503+ other_entries_by_test
504+ . entry ( test_path. clone ( ) . into_owned ( ) )
505+ . or_default ( )
506+ } ;
457507
458508 let ( reported_outcome, reported_subtests) = match result {
459509 TestExecutionResult :: Complete { outcome, subtests } => ( outcome, subtests) ,
@@ -518,119 +568,150 @@ fn run(cli: Cli) -> ExitCode {
518568 log:: info!( "metadata and reports gathered, now reconciling outcomes…" ) ;
519569
520570 let mut found_reconciliation_err = false ;
521- let recombined_tests_iter =
522- other_entries_by_test
523- . into_iter ( )
524- . filter_map ( |( test_path, test_entry) | {
525- fn reconcile < Out > (
526- entry : Entry < Out > ,
527- preset : ReportProcessingPreset ,
528- ) -> TestProps < Out >
529- where
530- Out : Debug + Default + EnumSetType ,
531- {
532- let Entry {
533- meta_props,
534- reported,
535- } = entry;
536- let normalize = NormalizedExpectationPropertyValue :: from_fully_expanded;
537-
538- let mut meta_props = meta_props. unwrap_or_default ( ) ;
539- meta_props. expectations = Some ( ' resolve: {
540- let resolve = match preset {
541- ReportProcessingPreset :: ResetAll => {
542- break ' resolve normalize ( reported) ;
543- }
544- ReportProcessingPreset :: ResetContradictory => {
545- |meta : Expectation < _ > , rep : Option < Expectation < _ > > | {
546- rep. filter ( |rep| !meta. is_superset ( rep) ) . unwrap_or ( meta)
547- }
571+ let entries_by_cts_path = entries_by_cts_path. into_iter ( ) . map ( |( _name, entry) | {
572+ let EntryByCtsPath {
573+ metadata_path,
574+ reported_path,
575+ entry,
576+ } = entry;
577+ let output_path = if let Some ( ( meta, rep) ) = metadata_path
578+ . as_ref ( )
579+ . zip ( reported_path. as_ref ( ) )
580+ . filter ( |( meta, rep) | meta != rep)
581+ {
582+ log:: info!(
583+ concat!(
584+ "metadata path for test is different from " ,
585+ "reported execution; relocating…\n " ,
586+ "…metadata: {:#?}\n " ,
587+ "…reported: {:#?}\n "
588+ ) ,
589+ meta,
590+ rep
591+ ) ;
592+ reported_path
593+ } else {
594+ metadata_path. or ( reported_path)
595+ } ;
596+
597+ (
598+ output_path. expect ( concat ! (
599+ "internal error: CTS path entry created without at least one " ,
600+ "report or metadata path specified"
601+ ) ) ,
602+ entry,
603+ )
604+ } ) ;
605+ let recombined_tests_iter = entries_by_cts_path
606+ . chain ( other_entries_by_test)
607+ . filter_map ( |( test_path, test_entry) | {
608+ fn reconcile < Out > (
609+ entry : Entry < Out > ,
610+ preset : ReportProcessingPreset ,
611+ ) -> TestProps < Out >
612+ where
613+ Out : Debug + Default + EnumSetType ,
614+ {
615+ let Entry {
616+ meta_props,
617+ reported,
618+ } = entry;
619+ let normalize = NormalizedExpectationPropertyValue :: from_fully_expanded;
620+
621+ let mut meta_props = meta_props. unwrap_or_default ( ) ;
622+ meta_props. expectations = Some ( ' resolve: {
623+ let resolve = match preset {
624+ ReportProcessingPreset :: ResetAll => {
625+ break ' resolve normalize ( reported) ;
626+ }
627+ ReportProcessingPreset :: ResetContradictory => {
628+ |meta : Expectation < _ > , rep : Option < Expectation < _ > > | {
629+ rep. filter ( |rep| !meta. is_superset ( rep) ) . unwrap_or ( meta)
548630 }
549- ReportProcessingPreset :: Merge => |meta, rep| match rep {
550- Some ( rep) => meta | rep,
551- None => meta,
552- } ,
553- } ;
554-
555- normalize (
556- Platform :: iter ( )
557- . map ( |platform| {
558- let build_profiles = BuildProfile :: iter ( )
559- . map ( |build_profile| {
560- (
561- build_profile,
562- resolve (
563- meta_props
564- . expectations
565- . as_ref ( )
566- . unwrap_or ( & Default :: default ( ) )
567- . get ( platform, build_profile) ,
568- reported
569- . get ( & platform)
570- . and_then ( |rep| {
571- rep. get ( & build_profile)
572- } )
573- . copied ( ) ,
574- ) ,
575- )
576- } )
577- . collect ( ) ;
578- ( platform, build_profiles)
579- } )
580- . collect ( ) ,
581- )
582- } ) ;
583- meta_props
584- }
631+ }
632+ ReportProcessingPreset :: Merge => |meta, rep| match rep {
633+ Some ( rep) => meta | rep,
634+ None => meta,
635+ } ,
636+ } ;
585637
586- let TestEntry {
587- entry : test_entry,
588- subtests : subtest_entries,
589- } = test_entry;
638+ normalize (
639+ Platform :: iter ( )
640+ . map ( |platform| {
641+ let build_profiles = BuildProfile :: iter ( )
642+ . map ( |build_profile| {
643+ (
644+ build_profile,
645+ resolve (
646+ meta_props
647+ . expectations
648+ . as_ref ( )
649+ . unwrap_or ( & Default :: default ( ) )
650+ . get ( platform, build_profile) ,
651+ reported
652+ . get ( & platform)
653+ . and_then ( |rep| rep. get ( & build_profile) )
654+ . copied ( ) ,
655+ ) ,
656+ )
657+ } )
658+ . collect ( ) ;
659+ ( platform, build_profiles)
660+ } )
661+ . collect ( ) ,
662+ )
663+ } ) ;
664+ meta_props
665+ }
590666
591- if test_entry. meta_props . is_none ( ) {
592- log:: info!( "new test entry: {test_path:?}" )
593- }
667+ let TestEntry {
668+ entry : test_entry,
669+ subtests : subtest_entries,
670+ } = test_entry;
594671
595- if test_entry. reported . is_empty ( ) {
596- match preset {
597- ReportProcessingPreset :: Merge => {
598- log:: warn!( "no entries found in reports for {test_path:?}" )
599- }
600- ReportProcessingPreset :: ResetAll
601- | ReportProcessingPreset :: ResetContradictory => {
602- log:: warn!(
603- "removing entry after finding no entries in reports: {:?}" ,
604- test_path
605- ) ;
606- return None ;
607- }
672+ if test_entry. meta_props . is_none ( ) {
673+ log:: info!( "new test entry: {test_path:?}" )
674+ }
675+
676+ if test_entry. reported . is_empty ( ) {
677+ match preset {
678+ ReportProcessingPreset :: Merge => {
679+ log:: warn!( "no entries found in reports for {test_path:?}" )
680+ }
681+ ReportProcessingPreset :: ResetAll
682+ | ReportProcessingPreset :: ResetContradictory => {
683+ log:: warn!(
684+ "removing entry after finding no entries in reports: {:?}" ,
685+ test_path
686+ ) ;
687+ return None ;
608688 }
609689 }
690+ }
610691
611- let properties = reconcile ( test_entry, preset) ;
692+ let properties = reconcile ( test_entry, preset) ;
612693
613- let mut subtests = BTreeMap :: new ( ) ;
614- for ( subtest_name, subtest) in subtest_entries {
615- let subtest_name = SectionHeader ( subtest_name) ;
616- if subtests. get ( & subtest_name) . is_some ( ) {
617- found_reconciliation_err = true ;
618- log:: error!( "internal error: duplicate test path {test_path:?}" ) ;
619- }
620- subtests. insert (
621- subtest_name,
622- Subtest {
623- properties : reconcile ( subtest, preset) ,
624- } ,
625- ) ;
694+ let mut subtests = BTreeMap :: new ( ) ;
695+ for ( subtest_name, subtest) in subtest_entries {
696+ let subtest_name = SectionHeader ( subtest_name) ;
697+ if subtests. get ( & subtest_name) . is_some ( ) {
698+ found_reconciliation_err = true ;
699+ log:: error!( "internal error: duplicate test path {test_path:?}" ) ;
626700 }
701+ subtests. insert (
702+ subtest_name,
703+ Subtest {
704+ properties : reconcile ( subtest, preset) ,
705+ } ,
706+ ) ;
707+ }
627708
628- if subtests. is_empty ( ) && properties == Default :: default ( ) {
629- None
630- } else {
631- Some ( ( test_path, ( properties, subtests) ) )
632- }
633- } ) ;
709+ if subtests. is_empty ( ) && properties == Default :: default ( ) {
710+ None
711+ } else {
712+ Some ( ( test_path, ( properties, subtests) ) )
713+ }
714+ } ) ;
634715
635716 log:: info!(
636717 "outcome reconciliation complete, gathering tests back into new metadata files…"
0 commit comments