@@ -24,6 +24,15 @@ final class ProcessedCodeCoverageData
2424 */
2525 private $ lineCoverage = [];
2626
27+ /**
28+ * Function coverage data.
29+ * Maintains base format of raw data (@see https://xdebug.org/docs/code_coverage), but each 'hit' entry is an array
30+ * of testcase ids
31+ *
32+ * @var array
33+ */
34+ private $ functionCoverage = [];
35+
2736 public function initializeFilesThatAreSeenTheFirstTime (RawCodeCoverageData $ rawData ): void
2837 {
2938 foreach ($ rawData ->getLineCoverage () as $ file => $ lines ) {
@@ -35,6 +44,22 @@ public function initializeFilesThatAreSeenTheFirstTime(RawCodeCoverageData $rawD
3544 }
3645 }
3746 }
47+
48+ foreach ($ rawData ->getFunctionCoverage () as $ file => $ functions ) {
49+ if (!isset ($ this ->functionCoverage [$ file ])) {
50+ $ this ->functionCoverage [$ file ] = $ functions ;
51+
52+ foreach ($ this ->functionCoverage [$ file ] as $ functionName => $ functionData ) {
53+ foreach (\array_keys ($ this ->functionCoverage [$ file ][$ functionName ]['branches ' ]) as $ branchId ) {
54+ $ this ->functionCoverage [$ file ][$ functionName ]['branches ' ][$ branchId ]['hit ' ] = [];
55+ }
56+
57+ foreach (\array_keys ($ this ->functionCoverage [$ file ][$ functionName ]['paths ' ]) as $ pathId ) {
58+ $ this ->functionCoverage [$ file ][$ functionName ]['paths ' ][$ pathId ]['hit ' ] = [];
59+ }
60+ }
61+ }
62+ }
3863 }
3964
4065 public function markCodeAsExecutedByTestCase (string $ testCaseId , RawCodeCoverageData $ executedCode ): void
@@ -48,6 +73,26 @@ public function markCodeAsExecutedByTestCase(string $testCaseId, RawCodeCoverage
4873 }
4974 }
5075 }
76+
77+ foreach ($ executedCode ->getFunctionCoverage () as $ file => $ functions ) {
78+ foreach ($ functions as $ functionName => $ functionData ) {
79+ foreach ($ functionData ['branches ' ] as $ branchId => $ branchData ) {
80+ if ($ branchData ['hit ' ] === Driver::BRANCH_HIT ) {
81+ if (!\in_array ($ testCaseId , $ this ->functionCoverage [$ file ][$ functionName ]['branches ' ][$ branchId ]['hit ' ], true )) {
82+ $ this ->functionCoverage [$ file ][$ functionName ]['branches ' ][$ branchId ]['hit ' ][] = $ testCaseId ;
83+ }
84+ }
85+ }
86+
87+ foreach ($ functionData ['paths ' ] as $ pathId => $ pathData ) {
88+ if ($ pathData ['hit ' ] === Driver::BRANCH_HIT ) {
89+ if (!\in_array ($ testCaseId , $ this ->functionCoverage [$ file ][$ functionName ]['paths ' ][$ pathId ]['hit ' ], true )) {
90+ $ this ->functionCoverage [$ file ][$ functionName ]['paths ' ][$ pathId ]['hit ' ][] = $ testCaseId ;
91+ }
92+ }
93+ }
94+ }
95+ }
5196 }
5297
5398 public function setLineCoverage (array $ lineCoverage ): void
@@ -62,6 +107,13 @@ public function getLineCoverage(): array
62107 return $ this ->lineCoverage ;
63108 }
64109
110+ public function getFunctionCoverage (): array
111+ {
112+ \ksort ($ this ->functionCoverage );
113+
114+ return $ this ->functionCoverage ;
115+ }
116+
65117 public function getCoveredFiles (): array
66118 {
67119 return \array_keys ($ this ->lineCoverage );
@@ -70,7 +122,11 @@ public function getCoveredFiles(): array
70122 public function renameFile (string $ oldFile , string $ newFile ): void
71123 {
72124 $ this ->lineCoverage [$ newFile ] = $ this ->lineCoverage [$ oldFile ];
73- unset($ this ->lineCoverage [$ oldFile ]);
125+
126+ if (isset ($ this ->functionCoverage [$ oldFile ])) {
127+ $ this ->functionCoverage [$ newFile ] = $ this ->functionCoverage [$ oldFile ];
128+ }
129+ unset($ this ->lineCoverage [$ oldFile ], $ this ->functionCoverage [$ oldFile ]);
74130 }
75131
76132 public function merge (self $ newData ): void
@@ -103,6 +159,24 @@ public function merge(self $newData): void
103159 }
104160 }
105161 }
162+
163+ foreach ($ newData ->functionCoverage as $ file => $ functions ) {
164+ if (!isset ($ this ->functionCoverage [$ file ])) {
165+ $ this ->functionCoverage [$ file ] = $ functions ;
166+
167+ continue ;
168+ }
169+
170+ foreach ($ functions as $ functionName => $ functionData ) {
171+ foreach ($ functionData ['branches ' ] as $ branchId => $ branchData ) {
172+ $ this ->functionCoverage [$ file ][$ functionName ]['branches ' ][$ branchId ]['hit ' ] = \array_unique (\array_merge ($ this ->functionCoverage [$ file ][$ functionName ]['branches ' ][$ branchId ]['hit ' ], $ branchData ['hit ' ]));
173+ }
174+
175+ foreach ($ functionData ['paths ' ] as $ pathId => $ pathData ) {
176+ $ this ->functionCoverage [$ file ][$ functionName ]['paths ' ][$ pathId ]['hit ' ] = \array_unique (\array_merge ($ this ->functionCoverage [$ file ][$ functionName ]['paths ' ][$ pathId ]['hit ' ], $ pathData ['hit ' ]));
177+ }
178+ }
179+ }
106180 }
107181
108182 /**
0 commit comments