Skip to content

Commit cabfd67

Browse files
Merge pull request #7 from robiningelbrecht/add-exit-on-low-coverage-per-rule
Fix bug on determining exit status
2 parents 0b372dd + efacca2 commit cabfd67

File tree

6 files changed

+180
-73
lines changed

6 files changed

+180
-73
lines changed

src/ConsoleOutput.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,11 @@ public function __construct(
3939
/**
4040
* @param \RobinIngelbrecht\PHPUnitCoverageTools\MinCoverage\MinCoverageResult[] $results
4141
*/
42-
public function print(array $results, ResultStatus $finalStatus): void
42+
public function print(array $results): void
4343
{
44+
$statusWeights = array_map(fn (MinCoverageResult $result) => $result->getStatus()->getWeight(), $results);
45+
$finalStatus = ResultStatus::fromWeight(max($statusWeights));
46+
4447
$this->output->writeln('');
4548
$tableStyle = new TableStyle();
4649
$tableStyle

src/MinCoverage/MinCoverageResult.php

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -53,24 +53,6 @@ public function exitOnLowCoverage(): bool
5353
return $this->exitOnLowCoverage;
5454
}
5555

56-
public static function fromPatternAndNumbers(
57-
string $pattern,
58-
int $expectedMinCoverage,
59-
float $actualMinCoverage,
60-
int $numberOfTrackedLines,
61-
int $numberOfCoveredLines,
62-
bool $exitOnLowCoverage
63-
): self {
64-
return new self(
65-
pattern: $pattern,
66-
expectedMinCoverage: $expectedMinCoverage,
67-
actualMinCoverage: $actualMinCoverage,
68-
numberOfTrackedLines: $numberOfTrackedLines,
69-
numberOfCoveredLines: $numberOfCoveredLines,
70-
exitOnLowCoverage: $exitOnLowCoverage
71-
);
72-
}
73-
7456
/**
7557
* @param \RobinIngelbrecht\PHPUnitCoverageTools\MinCoverage\CoverageMetric[] $metrics
7658
*
@@ -86,7 +68,7 @@ public static function mapFromRulesAndMetrics(
8668
$pattern = $minCoverageRule->getPattern();
8769
$minCoverage = $minCoverageRule->getMinCoverage();
8870
if (MinCoverageRule::TOTAL === $minCoverageRule->getPattern() && $metricTotal) {
89-
$results[] = MinCoverageResult::fromPatternAndNumbers(
71+
$results[] = new MinCoverageResult(
9072
pattern: $pattern,
9173
expectedMinCoverage: $minCoverage,
9274
actualMinCoverage: $metricTotal->getTotalPercentageCoverage(),
@@ -107,7 +89,7 @@ public static function mapFromRulesAndMetrics(
10789
$coveragePercentage += ($metric->getTotalPercentageCoverage() * $weight);
10890
}
10991

110-
$results[] = MinCoverageResult::fromPatternAndNumbers(
92+
$results[] = new MinCoverageResult(
11193
pattern: $pattern,
11294
expectedMinCoverage: $minCoverage,
11395
actualMinCoverage: round($coveragePercentage, 2),

src/Subscriber/Application/ApplicationFinishedSubscriber.php

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,17 +74,13 @@ public function notify(Finished $event): void
7474
metrics: $metrics,
7575
metricTotal: $metricTotal,
7676
);
77-
$statusWeights = array_map(fn (MinCoverageResult $result) => $result->getStatus()->getWeight(), $results);
78-
$finalStatus = ResultStatus::fromWeight(max($statusWeights));
79-
80-
$this->consoleOutput->print($results, $finalStatus);
77+
$this->consoleOutput->print($results);
8178

8279
$needsExit = !empty(array_filter(
8380
$results,
84-
fn (MinCoverageResult $minCoverageResult) => $minCoverageResult->exitOnLowCoverage())
81+
fn (MinCoverageResult $minCoverageResult) => $minCoverageResult->exitOnLowCoverage() && ResultStatus::FAILED === $minCoverageResult->getStatus())
8582
);
86-
if (ResultStatus::FAILED === $finalStatus
87-
&& $needsExit) {
83+
if ($needsExit) {
8884
$this->exitter->exit();
8985
}
9086
}

tests/Subscriber/Application/ApplicationFinishedSubscriberTest.php

Lines changed: 133 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ class ApplicationFinishedSubscriberTest extends TestCase
2626
public function testNotifyWithAtLeastOneFailedRule(): void
2727
{
2828
$exitter = $this->createMock(Exitter::class);
29+
$spyOutput = new SpyOutput();
2930

3031
$exitter
3132
->expects($this->once())
3233
->method('exit');
3334

34-
$spyOutput = new SpyOutput();
3535
$subscriber = new ApplicationFinishedSubscriber(
3636
relativePathToCloverXml: 'tests/clover.xml',
3737
minCoverageRules: MinCoverageRules::fromConfigFile('tests/Subscriber/Application/min-coverage-rules-with-failed-rule.php'),
@@ -61,12 +61,18 @@ public function testNotifyWithAtLeastOneFailedRule(): void
6161

6262
public function testNotifyWithAWarning(): void
6363
{
64+
$exitter = $this->createMock(Exitter::class);
6465
$spyOutput = new SpyOutput();
66+
67+
$exitter
68+
->expects($this->never())
69+
->method('exit');
70+
6571
$subscriber = new ApplicationFinishedSubscriber(
6672
relativePathToCloverXml: 'tests/clover.xml',
6773
minCoverageRules: MinCoverageRules::fromConfigFile('tests/Subscriber/Application/min-coverage-rules-with-warning.php'),
6874
cleanUpCloverXml: false,
69-
exitter: new Exitter(),
75+
exitter: $exitter,
7076
consoleOutput: new ConsoleOutput($spyOutput),
7177
);
7278

@@ -91,12 +97,18 @@ public function testNotifyWithAWarning(): void
9197

9298
public function testNotifyWhenCoverageIsOk(): void
9399
{
100+
$exitter = $this->createMock(Exitter::class);
94101
$spyOutput = new SpyOutput();
102+
103+
$exitter
104+
->expects($this->never())
105+
->method('exit');
106+
95107
$subscriber = new ApplicationFinishedSubscriber(
96108
relativePathToCloverXml: 'tests/clover.xml',
97109
minCoverageRules: MinCoverageRules::fromConfigFile('tests/Subscriber/Application/min-coverage-rules-success.php'),
98110
cleanUpCloverXml: false,
99-
exitter: new Exitter(),
111+
exitter: $exitter,
100112
consoleOutput: new ConsoleOutput($spyOutput),
101113
);
102114

@@ -121,12 +133,18 @@ public function testNotifyWhenCoverageIsOk(): void
121133

122134
public function testNotifyWithOnlyTotal(): void
123135
{
136+
$exitter = $this->createMock(Exitter::class);
124137
$spyOutput = new SpyOutput();
138+
139+
$exitter
140+
->expects($this->never())
141+
->method('exit');
142+
125143
$subscriber = new ApplicationFinishedSubscriber(
126144
relativePathToCloverXml: 'tests/clover.xml',
127145
minCoverageRules: MinCoverageRules::fromConfigFile('tests/Subscriber/Application/min-coverage-rules-total-only.php'),
128146
cleanUpCloverXml: false,
129-
exitter: new Exitter(),
147+
exitter: $exitter,
130148
consoleOutput: new ConsoleOutput($spyOutput),
131149
);
132150

@@ -151,12 +169,18 @@ public function testNotifyWithOnlyTotal(): void
151169

152170
public function testNotifyWithoutTotal(): void
153171
{
172+
$exitter = $this->createMock(Exitter::class);
154173
$spyOutput = new SpyOutput();
174+
175+
$exitter
176+
->expects($this->never())
177+
->method('exit');
178+
155179
$subscriber = new ApplicationFinishedSubscriber(
156180
relativePathToCloverXml: 'tests/clover.xml',
157181
minCoverageRules: MinCoverageRules::fromConfigFile('tests/Subscriber/Application/min-coverage-rules-without-total.php'),
158182
cleanUpCloverXml: false,
159-
exitter: new Exitter(),
183+
exitter: $exitter,
160184
consoleOutput: new ConsoleOutput($spyOutput),
161185
);
162186

@@ -179,63 +203,51 @@ public function testNotifyWithoutTotal(): void
179203
$this->assertMatchesTextSnapshot($spyOutput);
180204
}
181205

182-
public function testNotifyWithDuplicatePatterns(): void
183-
{
184-
$spyOutput = new SpyOutput();
185-
186-
$this->expectException(\RuntimeException::class);
187-
$this->expectExceptionMessage('Make sure all coverage rule patterns are unique');
188-
189-
new ApplicationFinishedSubscriber(
190-
relativePathToCloverXml: 'tests/clover.xml',
191-
minCoverageRules: MinCoverageRules::fromConfigFile('tests/Subscriber/Application/min-coverage-rules-with-duplicates.php'),
192-
cleanUpCloverXml: false,
193-
exitter: new Exitter(),
194-
consoleOutput: new ConsoleOutput($spyOutput),
195-
);
196-
}
197-
198-
public function testNotifyWithInvalidRules(): void
206+
public function testNotifyWithRulesThatDoNotExit(): void
199207
{
208+
$exitter = $this->createMock(Exitter::class);
200209
$spyOutput = new SpyOutput();
201210

202-
$this->expectException(\RuntimeException::class);
203-
$this->expectExceptionMessage('MinCoverage has to be value between 0 and 100. 203 given');
211+
$exitter
212+
->expects($this->never())
213+
->method('exit');
204214

205-
new ApplicationFinishedSubscriber(
215+
$subscriber = new ApplicationFinishedSubscriber(
206216
relativePathToCloverXml: 'tests/clover.xml',
207-
minCoverageRules: MinCoverageRules::fromConfigFile('tests/Subscriber/Application/min-coverage-rules-invalid.php'),
217+
minCoverageRules: MinCoverageRules::fromConfigFile('tests/Subscriber/Application/min-coverage-rules-no-exit.php'),
208218
cleanUpCloverXml: false,
209-
exitter: new Exitter(),
219+
exitter: $exitter,
210220
consoleOutput: new ConsoleOutput($spyOutput),
211221
);
212-
}
213-
214-
public function testNotifyWithInvalidRuleInstances(): void
215-
{
216-
$spyOutput = new SpyOutput();
217222

218-
$this->expectException(\RuntimeException::class);
219-
$this->expectExceptionMessage('Make sure all coverage rules are of instance RobinIngelbrecht\PHPUnitCoverageTools\MinCoverage\MinCoverageRule');
223+
$subscriber->notify(event: new Finished(
224+
new Info(
225+
current: new Snapshot(
226+
time: HRTime::fromSecondsAndNanoseconds(1, 0),
227+
memoryUsage: MemoryUsage::fromBytes(100),
228+
peakMemoryUsage: MemoryUsage::fromBytes(100),
229+
garbageCollectorStatus: new GarbageCollectorStatus(0, 0, 0, 0, null, null, null, null, null, null, null, null)
230+
),
231+
durationSinceStart: Duration::fromSecondsAndNanoseconds(1, 0),
232+
memorySinceStart: MemoryUsage::fromBytes(100),
233+
durationSincePrevious: Duration::fromSecondsAndNanoseconds(1, 0),
234+
memorySincePrevious: MemoryUsage::fromBytes(100),
235+
),
236+
0
237+
));
220238

221-
new ApplicationFinishedSubscriber(
222-
relativePathToCloverXml: 'tests/clover.xml',
223-
minCoverageRules: MinCoverageRules::fromConfigFile('tests/Subscriber/Application/min-coverage-invalid-rule-instances.php'),
224-
cleanUpCloverXml: false,
225-
exitter: new Exitter(),
226-
consoleOutput: new ConsoleOutput($spyOutput),
227-
);
239+
$this->assertMatchesTextSnapshot($spyOutput);
228240
}
229241

230242
public function testDivideByZero(): void
231243
{
232244
$exitter = $this->createMock(Exitter::class);
245+
$spyOutput = new SpyOutput();
233246

234247
$exitter
235248
->expects($this->never())
236249
->method('exit');
237250

238-
$spyOutput = new SpyOutput();
239251
$subscriber = new ApplicationFinishedSubscriber(
240252
relativePathToCloverXml: 'tests/clover-test-divide-by-zero.xml',
241253
minCoverageRules: MinCoverageRules::fromInt(100, true),
@@ -265,12 +277,18 @@ public function testDivideByZero(): void
265277

266278
public function testNotifyWithNonExistingCloverFile(): void
267279
{
280+
$exitter = $this->createMock(Exitter::class);
268281
$spyOutput = new SpyOutput();
282+
283+
$exitter
284+
->expects($this->never())
285+
->method('exit');
286+
269287
$subscriber = new ApplicationFinishedSubscriber(
270288
relativePathToCloverXml: 'tests/clover-wrong.xml',
271289
minCoverageRules: MinCoverageRules::fromConfigFile('tests/Subscriber/Application/min-coverage-rules-success.php'),
272290
cleanUpCloverXml: false,
273-
exitter: new Exitter(),
291+
exitter: $exitter,
274292
consoleOutput: new ConsoleOutput($spyOutput),
275293
);
276294

@@ -295,12 +313,18 @@ public function testNotifyWithNonExistingCloverFile(): void
295313

296314
public function testNotifyWithInvalidCloverFile(): void
297315
{
316+
$exitter = $this->createMock(Exitter::class);
298317
$spyOutput = new SpyOutput();
318+
319+
$exitter
320+
->expects($this->never())
321+
->method('exit');
322+
299323
$subscriber = new ApplicationFinishedSubscriber(
300324
relativePathToCloverXml: 'tests/clover-invalid.xml',
301325
minCoverageRules: MinCoverageRules::fromConfigFile('tests/Subscriber/Application/min-coverage-rules-success.php'),
302326
cleanUpCloverXml: false,
303-
exitter: new Exitter(),
327+
exitter: $exitter,
304328
consoleOutput: new ConsoleOutput($spyOutput),
305329
);
306330

@@ -327,13 +351,14 @@ public function testNotifyWithInvalidCloverFile(): void
327351
public function testNotifyWithCleanUpCloverFile(): void
328352
{
329353
copy(dirname(__DIR__, 2).'/clover.xml', dirname(__DIR__, 2).'/clover-to-delete.xml');
354+
330355
$exitter = $this->createMock(Exitter::class);
356+
$spyOutput = new SpyOutput();
331357

332358
$exitter
333359
->expects($this->once())
334360
->method('exit');
335361

336-
$spyOutput = new SpyOutput();
337362
$subscriber = new ApplicationFinishedSubscriber(
338363
relativePathToCloverXml: 'tests/clover-to-delete.xml',
339364
minCoverageRules: MinCoverageRules::fromConfigFile('tests/Subscriber/Application/min-coverage-rules-with-failed-rule.php'),
@@ -361,6 +386,69 @@ public function testNotifyWithCleanUpCloverFile(): void
361386
$this->assertFileDoesNotExist(dirname(__DIR__, 2).'/clover-to-delete.xml');
362387
}
363388

389+
public function testNotifyWithDuplicatePatterns(): void
390+
{
391+
$exitter = $this->createMock(Exitter::class);
392+
$spyOutput = new SpyOutput();
393+
394+
$exitter
395+
->expects($this->never())
396+
->method('exit');
397+
398+
$this->expectException(\RuntimeException::class);
399+
$this->expectExceptionMessage('Make sure all coverage rule patterns are unique');
400+
401+
new ApplicationFinishedSubscriber(
402+
relativePathToCloverXml: 'tests/clover.xml',
403+
minCoverageRules: MinCoverageRules::fromConfigFile('tests/Subscriber/Application/min-coverage-rules-with-duplicates.php'),
404+
cleanUpCloverXml: false,
405+
exitter: $exitter,
406+
consoleOutput: new ConsoleOutput($spyOutput),
407+
);
408+
}
409+
410+
public function testNotifyWithInvalidRules(): void
411+
{
412+
$exitter = $this->createMock(Exitter::class);
413+
$spyOutput = new SpyOutput();
414+
415+
$exitter
416+
->expects($this->never())
417+
->method('exit');
418+
419+
$this->expectException(\RuntimeException::class);
420+
$this->expectExceptionMessage('MinCoverage has to be value between 0 and 100. 203 given');
421+
422+
new ApplicationFinishedSubscriber(
423+
relativePathToCloverXml: 'tests/clover.xml',
424+
minCoverageRules: MinCoverageRules::fromConfigFile('tests/Subscriber/Application/min-coverage-rules-invalid.php'),
425+
cleanUpCloverXml: false,
426+
exitter: $exitter,
427+
consoleOutput: new ConsoleOutput($spyOutput),
428+
);
429+
}
430+
431+
public function testNotifyWithInvalidRuleInstances(): void
432+
{
433+
$exitter = $this->createMock(Exitter::class);
434+
$spyOutput = new SpyOutput();
435+
436+
$exitter
437+
->expects($this->never())
438+
->method('exit');
439+
440+
$this->expectException(\RuntimeException::class);
441+
$this->expectExceptionMessage('Make sure all coverage rules are of instance RobinIngelbrecht\PHPUnitCoverageTools\MinCoverage\MinCoverageRule');
442+
443+
new ApplicationFinishedSubscriber(
444+
relativePathToCloverXml: 'tests/clover.xml',
445+
minCoverageRules: MinCoverageRules::fromConfigFile('tests/Subscriber/Application/min-coverage-invalid-rule-instances.php'),
446+
cleanUpCloverXml: false,
447+
exitter: $exitter,
448+
consoleOutput: new ConsoleOutput($spyOutput),
449+
);
450+
}
451+
364452
public function testFromConfigurationAndParameters(): void
365453
{
366454
$this->assertEquals(
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
+------------------------------------------------------------------<fg=black;bg=yellow;options=bold> Code coverage results </>---+------------+----------+-------------------------+------------+
3+
|<bold> Pattern </bold>|<bold> Expected </bold>|<bold> Actual </bold>|<bold> </bold>|<bold> Exit on<fg=default;bg=default></> </bold>|
4+
|<bold> </bold>|<bold> </bold>|<bold> </bold>|<bold> </bold>|<bold> fail? </bold>|
5+
+--------------------------------------------------------------------------------------------+------------+----------+-------------------------+------------+
6+
|<fg=default;bg=default> RobinIngelbrecht\PHPUnitCoverageTools\CoverageMetrics </>| 100% | <failed>0%</failed> |<fg=default;bg=default> <bold>0</bold> of 48 lines covered </>| No |
7+
|<fg=default;bg=default> RobinIngelbrecht\PHPUnitCoverageTools\Subscriber\Application\ApplicationFinishedSubscriber </>| 100% | <failed>40%</failed> |<fg=default;bg=default> <bold>18</bold> of 45 lines covered </>| No |
8+
|<fg=default;bg=default> RobinIngelbrecht\NonExistingNameSpace </>| 100% | <warning>0%</warning> |<fg=default;bg=default> No lines to track...? </>| No |
9+
|<fg=default;bg=default> Total </>| 20% | <success>28.57%</success> |<fg=default;bg=default> <bold>30</bold> of 105 lines covered </>| No |
10+
+--------------------------------------------------------------------------------------------+------------+----------+-------------------------+------------+
11+
|<failed> Not all minimum code coverage rules passed, please try again... :) </failed>|
12+
+--------------------------------------------------------------------------------------------+------------+----------+-------------------------+------------+

0 commit comments

Comments
 (0)