@@ -348,17 +348,29 @@ public function append(array $data, $id = null, bool $append = true, $linesToBeC
348348
349349 $ this ->tests [$ id ] = ['size ' => $ size , 'status ' => $ status ];
350350
351- foreach ($ data as $ file => $ lines ) {
351+ foreach ($ data as $ file => $ fileData ) {
352352 if (!$ this ->filter ->isFile ($ file )) {
353353 continue ;
354354 }
355355
356- foreach ($ lines as $ k => $ v ) {
357- if ($ v === Driver::LINE_EXECUTED ) {
358- if (empty ($ this ->data [$ file ][$ k ]) || !\in_array ($ id , $ this ->data [$ file ][$ k ])) {
359- $ this ->data [$ file ][$ k ][] = $ id ;
356+ foreach ($ fileData ['lines ' ] as $ line => $ lineCoverage ) {
357+ if ($ lineCoverage === Driver::LINE_EXECUTED ) {
358+ $ this ->addCoverageLinePathCovered ($ file , $ line , true );
359+ $ this ->addCoverageLineTest ($ file , $ line , $ id );
360+ }
361+ }
362+
363+ foreach ($ fileData ['functions ' ] as $ function => $ functionCoverage ) {
364+ foreach ($ functionCoverage ['branches ' ] as $ branch => $ branchCoverage ) {
365+ if (($ branchCoverage ['hit ' ] ?? 0 ) === 1 ) {
366+ $ this ->addCoverageBranchHit ($ file , $ function , $ branch , $ branchCoverage ['hit ' ] ?? 0 );
367+ $ this ->addCoverageBranchTest ($ file , $ function , $ branch , $ id );
360368 }
361369 }
370+
371+ foreach ($ functionCoverage ['paths ' ] as $ path => $ pathCoverage ) {
372+ $ this ->addCoveragePathHit ($ file , $ function , $ path , $ pathCoverage ['hit ' ] ?? 0 );
373+ }
362374 }
363375 }
364376
@@ -376,10 +388,13 @@ public function merge(self $that): void
376388 \array_merge ($ this ->filter ->getWhitelistedFiles (), $ that ->filter ()->getWhitelistedFiles ())
377389 );
378390
379- foreach ($ that ->data as $ file => $ lines ) {
380- if (!isset ($ this ->data [$ file ])) {
391+ $ thisData = $ this ->getData ();
392+ $ thatData = $ that ->getData ();
393+
394+ foreach ($ thatData as $ file => $ fileData ) {
395+ if (!isset ($ thisData [$ file ])) {
381396 if (!$ this ->filter ->isFiltered ($ file )) {
382- $ this -> data [$ file ] = $ lines ;
397+ $ thisData [$ file ] = $ fileData ;
383398 }
384399
385400 continue ;
@@ -388,27 +403,30 @@ public function merge(self $that): void
388403 // we should compare the lines if any of two contains data
389404 $ compareLineNumbers = \array_unique (
390405 \array_merge (
391- \array_keys ($ this -> data [$ file ]),
392- \array_keys ($ that -> data [$ file ])
406+ \array_keys ($ thisData [$ file][ ' lines ' ]),
407+ \array_keys ($ thatData [$ file ][ ' lines ' ]) // can this be $fileData?
393408 )
394409 );
395410
396411 foreach ($ compareLineNumbers as $ line ) {
397- $ thatPriority = $ this ->getLinePriority ($ that -> data [$ file ], $ line );
398- $ thisPriority = $ this ->getLinePriority ($ this -> data [$ file ], $ line );
412+ $ thatPriority = $ this ->getLinePriority ($ thatData [$ file][ ' lines ' ], $ line );
413+ $ thisPriority = $ this ->getLinePriority ($ thisData [$ file][ ' lines ' ], $ line );
399414
400415 if ($ thatPriority > $ thisPriority ) {
401- $ this ->data [$ file ][$ line ] = $ that ->data [$ file ][$ line ];
402- } elseif ($ thatPriority === $ thisPriority && \is_array ($ this ->data [$ file ][$ line ])) {
403- $ this ->data [$ file ][$ line ] = \array_unique (
404- \array_merge ($ this ->data [$ file ][$ line ], $ that ->data [$ file ][$ line ])
416+ $ thisData [$ file ]['lines ' ][$ line ] = $ thatData [$ file ]['lines ' ][$ line ];
417+ } elseif ($ thatPriority === $ thisPriority && \is_array ($ thisData [$ file ]['lines ' ][$ line ])) {
418+ if ($ line ['pathCovered ' ] === true ) {
419+ $ thisData [$ file ]['lines ' ][$ line ]['pathCovered ' ] = $ line ['pathCovered ' ];
420+ }
421+ $ thisData [$ file ]['lines ' ][$ line ] = \array_unique (
422+ \array_merge ($ thisData [$ file ]['lines ' ][$ line ], $ thatData [$ file ]['lines ' ][$ line ])
405423 );
406424 }
407425 }
408426 }
409427
410- $ this ->tests = \array_merge ($ this ->tests , $ that ->getTests ());
411- $ this ->report = null ;
428+ $ this ->tests = \array_merge ($ this ->tests , $ that ->getTests ());
429+ $ this ->setData ( $ thisData ) ;
412430 }
413431
414432 public function setCacheTokens (bool $ flag ): void
@@ -493,12 +511,9 @@ public function setDetermineBranchCoverage(bool $flag): void
493511 *
494512 * During a merge, a higher number is better.
495513 *
496- * @param array $data
497- * @param int $line
498- *
499514 * @return int
500515 */
501- private function getLinePriority ($ data , $ line )
516+ private function getLinePriority (array $ data , int $ line )
502517 {
503518 if (!\array_key_exists ($ line , $ data )) {
504519 return 1 ;
@@ -533,7 +548,10 @@ private function applyCoversAnnotationFilter(array &$data, $linesToBeCovered, ar
533548 throw new MissingCoversAnnotationException ;
534549 }
535550
536- $ data = [];
551+ $ data = [
552+ 'lines ' => [],
553+ 'functions ' => [],
554+ ];
537555
538556 return ;
539557 }
@@ -544,7 +562,7 @@ private function applyCoversAnnotationFilter(array &$data, $linesToBeCovered, ar
544562
545563 if ($ this ->checkForUnintentionallyCoveredCode &&
546564 (!$ this ->currentId instanceof TestCase ||
547- (!$ this ->currentId ->isMedium () && !$ this ->currentId ->isLarge ()))) {
565+ (!$ this ->currentId ->isMedium () && !$ this ->currentId ->isLarge ()))) {
548566 $ this ->performUnintentionallyCoveredCodeCheck ($ data , $ linesToBeCovered , $ linesToBeUsed );
549567 }
550568
@@ -556,7 +574,11 @@ private function applyCoversAnnotationFilter(array &$data, $linesToBeCovered, ar
556574
557575 foreach (\array_keys ($ data ) as $ filename ) {
558576 $ _linesToBeCovered = \array_flip ($ linesToBeCovered [$ filename ]);
559- $ data [$ filename ] = \array_intersect_key ($ data [$ filename ], $ _linesToBeCovered );
577+
578+ $ data [$ filename ]['lines ' ] = \array_intersect_key (
579+ $ data [$ filename ],
580+ $ _linesToBeCovered
581+ );
560582 }
561583 }
562584
@@ -580,22 +602,208 @@ private function applyIgnoredLinesFilter(array &$data): void
580602 }
581603
582604 foreach ($ this ->getLinesToBeIgnored ($ filename ) as $ line ) {
583- unset($ data [$ filename ][$ line ]);
605+ unset($ data [$ filename ][' lines ' ][ $ line ]);
584606 }
585607 }
586608 }
587609
588610 private function initializeFilesThatAreSeenTheFirstTime (array $ data ): void
589611 {
590- foreach ($ data as $ file => $ lines ) {
591- if (!isset ($ this ->data [$ file ]) && $ this ->filter ->isFile ($ file )) {
592- $ this ->data [$ file ] = [];
612+ foreach ($ data as $ file => $ fileData ) {
613+ if (isset ($ this ->data [$ file ]) || !$ this ->filter ->isFile ($ file )) {
614+ continue ;
615+ }
616+ $ this ->initializeFileCoverageData ($ file );
593617
594- foreach ($ lines as $ k => $ v ) {
595- $ this ->data [$ file ][$ k ] = $ v === -2 ? null : [];
618+ // If this particular line is identified as not covered, mark it as null
619+ foreach ($ fileData ['lines ' ] as $ lineNumber => $ flag ) {
620+ if ($ flag === Driver::LINE_NOT_EXECUTABLE ) {
621+ $ this ->data [$ file ]['lines ' ][$ lineNumber ] = null ;
596622 }
597623 }
624+
625+ foreach ($ fileData ['functions ' ] as $ functionName => $ functionData ) {
626+ // @todo - should this have a helper to merge covered paths?
627+ $ this ->data [$ file ]['paths ' ][$ functionName ] = $ functionData ['paths ' ];
628+
629+ foreach ($ functionData ['branches ' ] as $ branchIndex => $ branchData ) {
630+ $ this ->addCoverageBranchHit ($ file , $ functionName , $ branchIndex , $ branchData ['hit ' ]);
631+ $ this ->addCoverageBranchLineStart ($ file , $ functionName , $ branchIndex , $ branchData ['line_start ' ]);
632+ $ this ->addCoverageBranchLineEnd ($ file , $ functionName , $ branchIndex , $ branchData ['line_end ' ]);
633+
634+ for ($ curLine = $ branchData ['line_start ' ]; $ curLine < $ branchData ['line_end ' ]; $ curLine ++) {
635+ if (isset ($ this ->data [$ file ]['lines ' ][$ curLine ])) {
636+ $ this ->addCoverageLinePathCovered ($ file , $ curLine , (bool ) $ branchData ['hit ' ]);
637+ }
638+ }
639+ }
640+ }
641+ }
642+ }
643+
644+ private function initializeFileCoverageData (string $ file ): void
645+ {
646+ if (!isset ($ this ->data [$ file ]) && $ this ->filter ->isFile ($ file )) {
647+ $ this ->data [$ file ] = [
648+ 'lines ' => [],
649+ 'branches ' => [],
650+ 'paths ' => [],
651+ ];
652+ }
653+ }
654+
655+ private function addCoverageLinePathCovered (string $ file , int $ lineNumber , bool $ isCovered ): void
656+ {
657+ $ this ->initializeFileCoverageData ($ file );
658+
659+ // Initialize the data coverage array for this line
660+ if (!isset ($ this ->data [$ file ]['lines ' ][$ lineNumber ])) {
661+ $ this ->data [$ file ]['lines ' ][$ lineNumber ] = [
662+ 'pathCovered ' => false ,
663+ 'tests ' => [],
664+ ];
665+ }
666+
667+ $ this ->data [$ file ]['lines ' ][$ lineNumber ]['pathCovered ' ] = $ isCovered ;
668+ }
669+
670+ private function addCoverageLineTest (string $ file , int $ lineNumber , string $ testId ): void
671+ {
672+ $ this ->initializeFileCoverageData ($ file );
673+
674+ // Initialize the data coverage array for this line
675+ if (!isset ($ this ->data [$ file ]['lines ' ][$ lineNumber ])) {
676+ $ this ->data [$ file ]['lines ' ][$ lineNumber ] = [
677+ 'pathCovered ' => false ,
678+ 'tests ' => [],
679+ ];
680+ }
681+
682+ if (!\in_array ($ testId , $ this ->data [$ file ]['lines ' ][$ lineNumber ]['tests ' ], true )) {
683+ $ this ->data [$ file ]['lines ' ][$ lineNumber ]['tests ' ][] = $ testId ;
684+ }
685+ }
686+
687+ private function addCoverageBranchHit (string $ file , string $ functionName , int $ branchIndex , int $ hit ): void
688+ {
689+ $ this ->initializeFileCoverageData ($ file );
690+
691+ if (!\array_key_exists ($ functionName , $ this ->data [$ file ]['branches ' ])) {
692+ $ this ->data [$ file ]['branches ' ][$ functionName ] = [];
693+ }
694+
695+ if (!\array_key_exists ($ branchIndex , $ this ->data [$ file ]['branches ' ][$ functionName ])) {
696+ $ this ->data [$ file ]['branches ' ][$ functionName ][$ branchIndex ] = [
697+ 'hit ' => 0 ,
698+ 'line_start ' => 0 ,
699+ 'line_end ' => 0 ,
700+ 'tests ' => [],
701+ ];
598702 }
703+
704+ $ this ->data [$ file ]['branches ' ][$ functionName ][$ branchIndex ]['hit ' ] = \max (
705+ $ this ->data [$ file ]['branches ' ][$ functionName ][$ branchIndex ]['hit ' ],
706+ $ hit
707+ );
708+ }
709+
710+ private function addCoverageBranchLineStart (
711+ string $ file ,
712+ string $ functionName ,
713+ int $ branchIndex ,
714+ int $ lineStart
715+ ): void {
716+ $ this ->initializeFileCoverageData ($ file );
717+
718+ if (!\array_key_exists ($ functionName , $ this ->data [$ file ]['branches ' ])) {
719+ $ this ->data [$ file ]['branches ' ][$ functionName ] = [];
720+ }
721+
722+ if (!\array_key_exists ($ branchIndex , $ this ->data [$ file ]['branches ' ][$ functionName ])) {
723+ $ this ->data [$ file ]['branches ' ][$ functionName ][$ branchIndex ] = [
724+ 'hit ' => 0 ,
725+ 'line_start ' => 0 ,
726+ 'line_end ' => 0 ,
727+ 'tests ' => [],
728+ ];
729+ }
730+
731+ $ this ->data [$ file ]['branches ' ][$ functionName ][$ branchIndex ]['line_start ' ] = $ lineStart ;
732+ }
733+
734+ private function addCoverageBranchLineEnd (
735+ string $ file ,
736+ string $ functionName ,
737+ int $ branchIndex ,
738+ int $ lineEnd
739+ ): void {
740+ $ this ->initializeFileCoverageData ($ file );
741+
742+ if (!\array_key_exists ($ functionName , $ this ->data [$ file ]['branches ' ])) {
743+ $ this ->data [$ file ]['branches ' ][$ functionName ] = [];
744+ }
745+
746+ if (!\array_key_exists ($ branchIndex , $ this ->data [$ file ]['branches ' ][$ functionName ])) {
747+ $ this ->data [$ file ]['branches ' ][$ functionName ][$ branchIndex ] = [
748+ 'hit ' => 0 ,
749+ 'line_start ' => 0 ,
750+ 'line_end ' => 0 ,
751+ 'tests ' => [],
752+ ];
753+ }
754+
755+ $ this ->data [$ file ]['branches ' ][$ functionName ][$ branchIndex ]['line_end ' ] = $ lineEnd ;
756+ }
757+
758+ private function addCoverageBranchTest (
759+ string $ file ,
760+ string $ functionName ,
761+ int $ branchIndex ,
762+ string $ testId
763+ ): void {
764+ $ this ->initializeFileCoverageData ($ file );
765+
766+ if (!\array_key_exists ($ functionName , $ this ->data [$ file ]['branches ' ])) {
767+ $ this ->data [$ file ]['branches ' ][$ functionName ] = [];
768+ }
769+
770+ if (!\array_key_exists ($ branchIndex , $ this ->data [$ file ]['branches ' ][$ functionName ])) {
771+ $ this ->data [$ file ]['branches ' ][$ functionName ][$ branchIndex ] = [
772+ 'hit ' => 0 ,
773+ 'line_start ' => 0 ,
774+ 'line_end ' => 0 ,
775+ 'tests ' => [],
776+ ];
777+ }
778+
779+ if (!\in_array ($ testId , $ this ->data [$ file ]['branches ' ][$ functionName ][$ branchIndex ]['tests ' ], true )) {
780+ $ this ->data [$ file ]['branches ' ][$ functionName ][$ branchIndex ]['tests ' ][] = $ testId ;
781+ }
782+ }
783+
784+ private function addCoveragePathHit (
785+ string $ file ,
786+ string $ functionName ,
787+ int $ pathId ,
788+ int $ hit
789+ ): void {
790+ $ this ->initializeFileCoverageData ($ file );
791+
792+ if (!\array_key_exists ($ functionName , $ this ->data [$ file ]['paths ' ])) {
793+ $ this ->data [$ file ]['paths ' ][$ functionName ] = [];
794+ }
795+
796+ if (!\array_key_exists ($ pathId , $ this ->data [$ file ]['paths ' ][$ functionName ])) {
797+ $ this ->data [$ file ]['paths ' ][$ functionName ][$ pathId ] = [
798+ 'hit ' => 0 ,
799+ 'path ' => [],
800+ ];
801+ }
802+
803+ $ this ->data [$ file ]['paths ' ][$ functionName ][$ pathId ]['hit ' ] = \max (
804+ $ this ->data [$ file ]['paths ' ][$ functionName ][$ pathId ]['hit ' ],
805+ $ hit
806+ );
599807 }
600808
601809 /**
@@ -619,13 +827,17 @@ private function addUncoveredFilesFromWhitelist(): void
619827 continue ;
620828 }
621829
622- $ data [$ uncoveredFile ] = [];
830+ $ data [$ uncoveredFile ] = [
831+ 'lines ' => [],
832+ 'functions ' => [],
833+ ];
623834
624835 $ lines = \count (\file ($ uncoveredFile ));
625836
626- for ($ i = 1 ; $ i <= $ lines ; $ i ++) {
627- $ data [$ uncoveredFile ][$ i ] = Driver::LINE_NOT_EXECUTED ;
837+ for ($ line = 1 ; $ line <= $ lines ; $ line ++) {
838+ $ data [$ uncoveredFile ][' lines ' ][ $ line ] = Driver::LINE_NOT_EXECUTED ;
628839 }
840+ // @todo - do the same here with functions and paths
629841 }
630842
631843 $ this ->append ($ data , 'UNCOVERED_FILES_FROM_WHITELIST ' );
@@ -817,10 +1029,10 @@ private function performUnintentionallyCoveredCodeCheck(array &$data, array $lin
8171029
8181030 $ unintentionallyCoveredUnits = [];
8191031
820- foreach ($ data as $ file => $ _data ) {
821- foreach ($ _data as $ line => $ flag ) {
822- if ($ flag === 1 && !isset ($ allowedLines [$ file ][$ line ])) {
823- $ unintentionallyCoveredUnits [] = $ this ->wizard ->lookup ($ file , $ line );
1032+ foreach ($ data as $ file => $ fileData ) {
1033+ foreach ($ fileData [ ' lines ' ] as $ lineNumber => $ flag ) {
1034+ if ($ flag === 1 && !isset ($ allowedLines [$ file ][$ lineNumber ])) {
1035+ $ unintentionallyCoveredUnits [] = $ this ->wizard ->lookup ($ file , $ lineNumber );
8241036 }
8251037 }
8261038 }
0 commit comments