@@ -447,3 +447,264 @@ def test_skips_test_with_missing_build_but_processes_others(self):
447447 self .assertEqual (len (result ), 1 )
448448 record = next (iter (result .values ()))
449449 self .assertEqual (record ["total_tests" ], 1 )
450+
451+
452+ class TestAccumulateRollupEntryCorrection (SimpleTestCase ):
453+ """Test is_correction=True behavior: null -> non-null transitions."""
454+
455+ def _make_rollup_data_with_null_entry (self , initial_null_count = 1 ):
456+ """Pre-seed rollup_data with a record having some null_tests."""
457+ checkout = _make_checkout ()
458+ entry = _make_rollup_entry (checkout = checkout , status = None )
459+ rollup_key = RollupKey (
460+ origin = checkout .origin ,
461+ tree_name = checkout .tree_name ,
462+ git_repository_branch = checkout .git_repository_branch ,
463+ git_repository_url = checkout .git_repository_url ,
464+ git_commit_hash = checkout .git_commit_hash ,
465+ path_group = entry ["path_group" ],
466+ config = entry ["config" ],
467+ arch = entry ["arch" ],
468+ compiler = entry ["compiler" ],
469+ hardware_key = entry ["hardware_key" ],
470+ platform = entry ["platform" ],
471+ lab = entry ["lab" ],
472+ test_origin = entry ["origin" ],
473+ issue_id = entry ["issue_id" ],
474+ issue_version = entry ["issue_version" ],
475+ issue_uncategorized = entry ["issue_uncategorized" ],
476+ is_boot = entry ["is_boot" ],
477+ )
478+ rollup_data = {
479+ rollup_key : {
480+ "pass_tests" : 0 ,
481+ "fail_tests" : 0 ,
482+ "skip_tests" : 0 ,
483+ "error_tests" : 0 ,
484+ "miss_tests" : 0 ,
485+ "done_tests" : 0 ,
486+ "null_tests" : initial_null_count ,
487+ "total_tests" : initial_null_count ,
488+ }
489+ }
490+ return rollup_data , entry , rollup_key
491+
492+ def test_correction_decrements_null_tests (self ):
493+ """Correction moves count from null_tests to the new status bucket."""
494+ rollup_data , entry , rollup_key = self ._make_rollup_data_with_null_entry ()
495+ entry ["status" ] = StatusChoices .PASS
496+
497+ accumulate_rollup_entry (rollup_data , entry , is_correction = True )
498+
499+ record = rollup_data [rollup_key ]
500+ self .assertEqual (record ["null_tests" ], 0 )
501+
502+ def test_correction_increments_new_status_bucket (self ):
503+ """The new status bucket gets the count moved from null_tests."""
504+ rollup_data , entry , rollup_key = self ._make_rollup_data_with_null_entry ()
505+ entry ["status" ] = StatusChoices .PASS
506+
507+ accumulate_rollup_entry (rollup_data , entry , is_correction = True )
508+
509+ record = rollup_data [rollup_key ]
510+ self .assertEqual (record ["pass_tests" ], 1 )
511+
512+ def test_correction_does_not_change_total_tests (self ):
513+ """Total should remain unchanged - just moving from null to bucket."""
514+ rollup_data , entry , rollup_key = self ._make_rollup_data_with_null_entry ()
515+ entry ["status" ] = StatusChoices .PASS
516+
517+ accumulate_rollup_entry (rollup_data , entry , is_correction = True )
518+
519+ record = rollup_data [rollup_key ]
520+ self .assertEqual (record ["total_tests" ], 1 )
521+
522+ def test_correction_with_fail_status (self ):
523+ """Correction works with fail status too."""
524+ rollup_data , entry , rollup_key = self ._make_rollup_data_with_null_entry ()
525+ entry ["status" ] = StatusChoices .FAIL
526+
527+ accumulate_rollup_entry (rollup_data , entry , is_correction = True )
528+
529+ record = rollup_data [rollup_key ]
530+ self .assertEqual (record ["null_tests" ], 0 )
531+ self .assertEqual (record ["fail_tests" ], 1 )
532+ self .assertEqual (record ["total_tests" ], 1 )
533+
534+ def test_correction_with_skip_status (self ):
535+ """Correction works with skip status."""
536+ rollup_data , entry , rollup_key = self ._make_rollup_data_with_null_entry ()
537+ entry ["status" ] = StatusChoices .SKIP
538+
539+ accumulate_rollup_entry (rollup_data , entry , is_correction = True )
540+
541+ record = rollup_data [rollup_key ]
542+ self .assertEqual (record ["null_tests" ], 0 )
543+ self .assertEqual (record ["skip_tests" ], 1 )
544+ self .assertEqual (record ["total_tests" ], 1 )
545+
546+ def test_correction_with_null_as_new_status (self ):
547+ """If new status is also null, both operations hit null_tests (net zero)."""
548+ rollup_data , entry , rollup_key = self ._make_rollup_data_with_null_entry ()
549+ entry ["status" ] = None
550+
551+ accumulate_rollup_entry (rollup_data , entry , is_correction = True )
552+
553+ record = rollup_data [rollup_key ]
554+ # Decrement then increment null_tests: net change is 0
555+ self .assertEqual (record ["null_tests" ], 1 )
556+ self .assertEqual (record ["total_tests" ], 1 )
557+
558+ def test_multiple_corrections_on_same_rollup_key (self ):
559+ """Multiple corrections on the same key accumulate correctly."""
560+ checkout = _make_checkout ()
561+ base_entry = _make_rollup_entry (checkout = checkout , status = None )
562+ rollup_key = RollupKey (
563+ origin = checkout .origin ,
564+ tree_name = checkout .tree_name ,
565+ git_repository_branch = checkout .git_repository_branch ,
566+ git_repository_url = checkout .git_repository_url ,
567+ git_commit_hash = checkout .git_commit_hash ,
568+ path_group = base_entry ["path_group" ],
569+ config = base_entry ["config" ],
570+ arch = base_entry ["arch" ],
571+ compiler = base_entry ["compiler" ],
572+ hardware_key = base_entry ["hardware_key" ],
573+ platform = base_entry ["platform" ],
574+ lab = base_entry ["lab" ],
575+ test_origin = base_entry ["origin" ],
576+ issue_id = base_entry ["issue_id" ],
577+ issue_version = base_entry ["issue_version" ],
578+ issue_uncategorized = base_entry ["issue_uncategorized" ],
579+ is_boot = base_entry ["is_boot" ],
580+ )
581+ rollup_data = {
582+ rollup_key : {
583+ "pass_tests" : 0 ,
584+ "fail_tests" : 0 ,
585+ "skip_tests" : 0 ,
586+ "error_tests" : 0 ,
587+ "miss_tests" : 0 ,
588+ "done_tests" : 0 ,
589+ "null_tests" : 3 ,
590+ "total_tests" : 3 ,
591+ }
592+ }
593+
594+ # Three corrections: PASS, FAIL, SKIP
595+ entry1 = _make_rollup_entry (checkout = checkout , status = StatusChoices .PASS )
596+ entry2 = _make_rollup_entry (checkout = checkout , status = StatusChoices .FAIL )
597+ entry3 = _make_rollup_entry (checkout = checkout , status = StatusChoices .SKIP )
598+
599+ accumulate_rollup_entry (rollup_data , entry1 , is_correction = True )
600+ accumulate_rollup_entry (rollup_data , entry2 , is_correction = True )
601+ accumulate_rollup_entry (rollup_data , entry3 , is_correction = True )
602+
603+ record = rollup_data [rollup_key ]
604+ self .assertEqual (record ["null_tests" ], 0 )
605+ self .assertEqual (record ["pass_tests" ], 1 )
606+ self .assertEqual (record ["fail_tests" ], 1 )
607+ self .assertEqual (record ["skip_tests" ], 1 )
608+ self .assertEqual (record ["total_tests" ], 3 )
609+
610+
611+ class TestAggregateTestsRollupWithReprocess (SimpleTestCase ):
612+ """Test aggregate_tests_rollup with reprocess_test_ids parameter."""
613+
614+ def test_reprocess_test_id_applies_correction (self ):
615+ """Test in reprocess_test_ids gets correction: null_tests decremented."""
616+ checkout = _make_checkout ()
617+ build = _make_build (checkout = checkout )
618+ # This test will be marked as reprocess
619+ test = _make_pending_test (
620+ test_id = "test-1" ,
621+ build_id = "build-1" ,
622+ full_status = StatusChoices .PASS ,
623+ )
624+
625+ result = aggregate_tests_rollup (
626+ [test ],
627+ {"build-1" : build },
628+ {},
629+ reprocess_test_ids = {"test-1" }, # Mark as correction
630+ )
631+
632+ record = next (iter (result .values ()))
633+ # Correction: total should not increment, pass_tests should be 1
634+ # But since there's no prior null_tests to decrement, it goes negative
635+ self .assertEqual (record ["pass_tests" ], 1 )
636+ self .assertEqual (record ["null_tests" ], - 1 )
637+ self .assertEqual (record ["total_tests" ], 0 )
638+
639+ def test_normal_test_not_in_reprocess (self ):
640+ """Test not in reprocess_test_ids behaves like normal."""
641+ checkout = _make_checkout ()
642+ build = _make_build (checkout = checkout )
643+ test = _make_pending_test (
644+ test_id = "test-1" ,
645+ build_id = "build-1" ,
646+ full_status = StatusChoices .PASS ,
647+ )
648+
649+ result = aggregate_tests_rollup (
650+ [test ],
651+ {"build-1" : build },
652+ {},
653+ reprocess_test_ids = set (), # Empty set
654+ )
655+
656+ record = next (iter (result .values ()))
657+ self .assertEqual (record ["pass_tests" ], 1 )
658+ self .assertEqual (record ["total_tests" ], 1 )
659+ self .assertEqual (record ["null_tests" ], 0 )
660+
661+ def test_mixed_batch_corrections_and_new (self ):
662+ """Two tests same rollup key: one correction + one normal."""
663+ checkout = _make_checkout ()
664+ build = _make_build (checkout = checkout )
665+ # Correction: was counted as null, now becoming PASS
666+ test_correction = _make_pending_test (
667+ test_id = "t1" ,
668+ build_id = "build-1" ,
669+ full_status = StatusChoices .PASS ,
670+ )
671+ # New test, fresh PASS
672+ test_new = _make_pending_test (
673+ test_id = "t2" ,
674+ build_id = "build-1" ,
675+ full_status = StatusChoices .PASS ,
676+ )
677+
678+ result = aggregate_tests_rollup (
679+ [test_correction , test_new ],
680+ {"build-1" : build },
681+ {},
682+ reprocess_test_ids = {"t1" }, # Only first is correction
683+ )
684+
685+ self .assertEqual (len (result ), 1 )
686+ record = next (iter (result .values ()))
687+ # Correction: null_tests -1, pass_tests +1, total_tests 0
688+ # New: pass_tests +1, total_tests +1
689+ # Result: null_tests -1, pass_tests 2, total_tests 1
690+ self .assertEqual (record ["null_tests" ], - 1 )
691+ self .assertEqual (record ["pass_tests" ], 2 )
692+ self .assertEqual (record ["total_tests" ], 1 )
693+
694+ def test_default_reprocess_test_ids_is_empty (self ):
695+ """Not passing reprocess_test_ids defaults to empty set."""
696+ checkout = _make_checkout ()
697+ build = _make_build (checkout = checkout )
698+ test = _make_pending_test (
699+ test_id = "test-1" ,
700+ build_id = "build-1" ,
701+ full_status = StatusChoices .FAIL ,
702+ )
703+
704+ # Call without the reprocess_test_ids parameter
705+ result = aggregate_tests_rollup ([test ], {"build-1" : build }, {})
706+
707+ record = next (iter (result .values ()))
708+ self .assertEqual (record ["fail_tests" ], 1 )
709+ self .assertEqual (record ["total_tests" ], 1 )
710+ self .assertEqual (record ["null_tests" ], 0 )
0 commit comments