Skip to content

Commit 9ca15be

Browse files
committed
Added ImmediateExceptionPrinter and suite of functional tests.
Added readme.
0 parents  commit 9ca15be

20 files changed

+689
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/.*/
2+
/vendor/
3+
/composer.lock

.travis.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
notifications:
2+
email: false
3+
4+
sudo: false
5+
6+
language: php
7+
8+
php:
9+
- 5.6
10+
- 7.0
11+
- 7.1
12+
13+
env:
14+
matrix:
15+
-
16+
- DEPENDENCIES=--prefer-lowest
17+
18+
matrix:
19+
fast_finish: true
20+
21+
cache:
22+
directories:
23+
- .composer/cache
24+
25+
install:
26+
- alias composer=composer\ -n && composer selfupdate
27+
- composer validate
28+
- composer update $DEPENDENCIES
29+
30+
script:
31+
- composer test -- --coverage-clover=build/logs/clover.xml
32+
33+
after_success:
34+
- composer require satooshi/php-coveralls
35+
- vendor/bin/coveralls -v

README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
PHPUnit Immediate Exception Printer
2+
===================================
3+
4+
[![Latest version][Version image]][Releases]
5+
[![Total downloads][Downloads image]][Downloads]
6+
[![Build status][Build image]][Build]
7+
[![Test coverage][Coverage image]][Coverage]
8+
[![Code style][Style image]][Style]
9+
10+
Immediate Exception Printer is a [PHPUnit][PHPUnit] plug-in that prints out exceptions and assertion failures immediately during a test run. Normally PHPUnit keeps error details secret until the end of the test run, but sometimes we don't want to wait that long. With Immediate Exception Printer, all secrets are immediately revealed, with a few extra benefits, too.
11+
12+
## Benefits
13+
14+
* Immediately prints out exceptions and assertion failures as they occur.
15+
* Displays the execution time of each test in tiered colour bands.
16+
* Displays the name of each test case as it is executed.
17+
18+
## Preview
19+
20+
The following preview is somewhat atypical but shows all tested output cases this printer supports.
21+
22+
![Preview image](https://raw.githubusercontent.com/ScriptFUSION/PHPUnit-Immediate-Exception-Printer/master/doc/images/test%20run%201.0.png)
23+
24+
## Inspiration
25+
26+
Thanks to the following open source projects that inspired this project. Keep being awesome :thumbsup:.
27+
28+
* [diablomedia/phpunit-pretty-printer](https://github.com/diablomedia/phpunit-pretty-printer)
29+
* [whatthejeff/nyancat-phpunit-resultprinter](https://github.com/whatthejeff/nyancat-phpunit-resultprinter)
30+
* [skyzyx/phpunit-result-printer](https://github.com/skyzyx/phpunit-result-printer)
31+
32+
33+
[Releases]: https://github.com/ScriptFUSION/PHPUnit-Immediate-Exception-Printer/releases
34+
[Version image]: https://poser.pugx.org/scriptfusion/phpunit-immediate-exception-printer/version "Latest version"
35+
[Downloads]: https://packagist.org/packages/scriptfusion/phpunit-immediate-exception-printer
36+
[Downloads image]: https://poser.pugx.org/scriptfusion/phpunit-immediate-exception-printer/downloads "Total downloads"
37+
[Build]: https://travis-ci.org/ScriptFUSION/PHPUnit-Immediate-Exception-Printer
38+
[Build image]: https://travis-ci.org/ScriptFUSION/PHPUnit-Immediate-Exception-Printer.svg?branch=master "Build status"
39+
[Coverage]: https://coveralls.io/github/ScriptFUSION/PHPUnit-Immediate-Exception-Printer
40+
[Coverage image]: https://coveralls.io/repos/ScriptFUSION/PHPUnit-Immediate-Exception-Printer/badge.svg "Test coverage"
41+
[Style]: https://styleci.io/repos/83920053
42+
[Style image]: https://styleci.io/repos/83920053/shield?style=flat "Code style"
43+
44+
[PHPUnit]: https://github.com/sebastianbergmann/phpunit

composer.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "scriptfusion/phpunit-immediate-exception-printer",
3+
"description": "Immediately prints any exceptions or assertion failures that occur during testing.",
4+
"authors": [
5+
{
6+
"name": "Bilge",
7+
"email": "bilge@scriptfusion.com"
8+
}
9+
],
10+
"require": {
11+
"phpunit/phpunit": "^5.5"
12+
},
13+
"autoload": {
14+
"psr-4": {
15+
"ScriptFUSION\\PHPUnitImmediateExceptionPrinter\\": "src"
16+
}
17+
},
18+
"autoload-dev": {
19+
"psr-4": {
20+
"ScriptFUSIONTest\\PHPUnitImmediateExceptionPrinter\\": "test"
21+
}
22+
},
23+
"scripts": {
24+
"test": "phpunit -c test"
25+
}
26+
}

doc/images/test run 1.0.png

38.3 KB
Loading

src/ImmediateExceptionPrinter.php

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
<?php
2+
namespace ScriptFUSION\PHPUnitImmediateExceptionPrinter;
3+
4+
class ImmediateExceptionPrinter extends \PHPUnit_TextUI_ResultPrinter
5+
{
6+
/**
7+
* The exception thrown by the last test.
8+
*
9+
* @var \Exception|null
10+
*/
11+
protected $exception;
12+
13+
/**
14+
* PHPUnit built-in progress indication character, e.g. E for error.
15+
*
16+
* @var string
17+
*/
18+
protected $progress;
19+
20+
/**
21+
* Last colour used by a progress indicator.
22+
*
23+
* @var string
24+
*/
25+
protected $lastColour;
26+
27+
private static $performanceThresholds = [
28+
'fg-red' => 1000,
29+
'fg-yellow' => 200,
30+
'fg-green' => 0,
31+
];
32+
33+
public function startTest(\PHPUnit_Framework_Test $test)
34+
{
35+
parent::startTest($test);
36+
37+
$this->exception = $this->progress = null;
38+
$this->lastColour = 'fg-green,bold';
39+
}
40+
41+
protected function writeProgress($progress)
42+
{
43+
// Record progress so it can be written later instead of at start of line.
44+
$this->progress = $progress;
45+
46+
++$this->numTestsRun;
47+
}
48+
49+
protected function writeProgressWithColor($color, $buffer)
50+
{
51+
parent::writeProgressWithColor($color, $buffer);
52+
53+
$this->lastColour = $color;
54+
}
55+
56+
public function endTest(\PHPUnit_Framework_Test $test, $time)
57+
{
58+
parent::endTest($test, $time);
59+
60+
$this->write(sprintf(
61+
'%3d%% %s ',
62+
round($this->numTestsRun / $this->numTests * 100),
63+
$this->progress
64+
));
65+
$this->writeWithColor($this->lastColour, \PHPUnit_Util_Test::describe($test), false);
66+
$this->writePerformance($time);
67+
68+
if ($this->exception) {
69+
$this->printExceptionTrace($this->exception);
70+
}
71+
}
72+
73+
/**
74+
* Writes the test performance metric formatted as the number of milliseconds elapsed.
75+
*
76+
* @param float $time Number of seconds elapsed.
77+
*/
78+
protected function writePerformance($time)
79+
{
80+
$ms = round($time * 1000);
81+
82+
foreach (self::$performanceThresholds as $colour => $threshold) {
83+
if ($ms > $threshold) {
84+
break;
85+
}
86+
}
87+
88+
$this->writeWithColor($colour, " ($ms ms)");
89+
}
90+
91+
protected function printExceptionTrace(\Exception $exception)
92+
{
93+
$this->writeNewLine();
94+
95+
// Parse nested exception trace line by line.
96+
foreach (explode("\n", $exception) as $line) {
97+
// Print exception name and message.
98+
if (!$exception instanceof \PHPUnit_Framework_AssertionFailedError
99+
&& false !== $pos = strpos($line, ': ')
100+
) {
101+
$whitespace = str_repeat(' ', $pos + 2);
102+
$this->writeWithColor('bg-red,fg-white', $whitespace);
103+
104+
// Exception name.
105+
$this->writeWithColor('bg-red,fg-white', sprintf(' %s ', substr($line, 0, $pos)), false);
106+
// Exception message.
107+
$this->writeWithColor('fg-red', substr($line, $pos + 1));
108+
109+
$this->writeWithColor('bg-red,fg-white', $whitespace);
110+
111+
continue;
112+
}
113+
114+
$this->writeWithColor('fg-red', $line);
115+
}
116+
}
117+
118+
/**
119+
* Called when an exception is thrown in the test runner.
120+
*
121+
* @param \PHPUnit_Framework_Test $test
122+
* @param \Exception $e
123+
* @param float $time
124+
*/
125+
public function addError(\PHPUnit_Framework_Test $test, \Exception $e, $time)
126+
{
127+
$this->writeProgressWithColor('fg-red,bold', 'E');
128+
129+
$this->exception = $e;
130+
$this->lastTestFailed = true;
131+
}
132+
133+
/**
134+
* Called when an assertion fails in the test runner.
135+
*
136+
* @param \PHPUnit_Framework_Test $test
137+
* @param \PHPUnit_Framework_AssertionFailedError $e
138+
* @param float $time
139+
*/
140+
public function addFailure(\PHPUnit_Framework_Test $test, \PHPUnit_Framework_AssertionFailedError $e, $time)
141+
{
142+
$this->writeProgressWithColor('fg-red,bold', 'F');
143+
144+
$this->exception = $e;
145+
$this->lastTestFailed = true;
146+
}
147+
}

test/CapabilitiesTest.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
namespace ScriptFUSIONTest\PHPUnitImmediateExceptionPrinter;
3+
4+
final class CapabilitiesTest extends \PHPUnit_Framework_TestCase
5+
{
6+
public function testSuccess()
7+
{
8+
self::assertTrue(true);
9+
}
10+
11+
public function testFailure()
12+
{
13+
self::assertTrue(false);
14+
}
15+
16+
public function testException()
17+
{
18+
throw new \LogicException('foo');
19+
}
20+
21+
public function testNestedException()
22+
{
23+
new ExceptionThrower;
24+
}
25+
26+
public function testDiffFailure()
27+
{
28+
self::assertSame('foo', 'LogicException: foo');
29+
}
30+
31+
public function testSkipped()
32+
{
33+
$this->markTestSkipped();
34+
}
35+
36+
public function testRisky()
37+
{
38+
}
39+
40+
public function testIncomplete()
41+
{
42+
$this->markTestIncomplete();
43+
}
44+
45+
/**
46+
* @dataProvider provideData
47+
*/
48+
public function testDataProvider()
49+
{
50+
self::assertTrue(true);
51+
}
52+
53+
public function provideData()
54+
{
55+
return [
56+
'foo' => ['bar'],
57+
'baz' => ['qux'],
58+
];
59+
}
60+
}

test/ExceptionThrower.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
namespace ScriptFUSIONTest\PHPUnitImmediateExceptionPrinter;
3+
4+
final class ExceptionThrower
5+
{
6+
public function __construct()
7+
{
8+
throw new \LogicException('foo', 0, new \RuntimeException('bar'));
9+
}
10+
}

test/functional/PHPUnit runner.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
require_once 'vendor/autoload.php';
3+
4+
PHPUnit_TextUI_Command::main();

test/functional/data provider.phpt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
A successful test is fed two cases by a data provider.
3+
4+
--ARGS--
5+
-c test --colors=always test/CapabilitiesTest --filter '::testDataProvider\h'
6+
7+
--FILE_EXTERNAL--
8+
PHPUnit runner.php
9+
10+
--EXPECTF--
11+
PHPUnit %s
12+
13+
50% . ScriptFUSIONTest\PHPUnitImmediateExceptionPrinter\CapabilitiesTest::testDataProvider with data set "foo" ('bar') (%i ms)
14+
100% . ScriptFUSIONTest\PHPUnitImmediateExceptionPrinter\CapabilitiesTest::testDataProvider with data set "baz" ('qux') (%i ms)
15+
16+
17+
Time: %s
18+
19+
OK (2 tests, 2 assertions)

0 commit comments

Comments
 (0)