8000 feature #13398 [PhpUnit] new PhpUnit bridge (nicolas-grekas) · symfony/symfony@9a4f3e1 · GitHub
[go: up one dir, main page]

Skip to content

Commit 9a4f3e1

Browse files
committed
feature #13398 [PhpUnit] new PhpUnit bridge (nicolas-grekas)
This PR was merged into the 2.7 branch. Discussion ---------- [PhpUnit] new PhpUnit bridge | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #13644 | License | MIT | Doc PR | - From the proposed README: PHPUnit Bridge ============== Provides utilities for PHPUnit, especially user deprecation notices management. It comes with the following features: * disable the garbage collector; * auto-register `class_exists` to load Doctrine annotations; * print a user deprecation notices summary at the end of the test suite. Handling user deprecation notices is sensitive to the SYMFONY_DEPRECATIONS_HELPER environment variable. This env var configures 3 behaviors depending on its value: * when set to `strict`, all but legacy-tagged deprecation notices will make tests fail. This is the recommended mode for best forward compatibility. * `weak` on the contrary will make tests ignore all deprecation notices. This is the recommended mode for legacy projects that must use deprecated interfaces for backward compatibility reasons. * any other value will respect the current error reporting level. All three modes will display a summary of deprecation notices at the end of the test suite, split in two groups: * **Legacy** deprecation notices denote tests that explicitly test some legacy interfaces. In all 3 modes, deprecation notices triggered in a legacy-tagged test do never make a test fail. There are four ways to mark a test as legacy: - make its class start with the `Legacy` prefix; - make its method start with `testLegacy`; - make its data provider start with `provideLegacy` or `getLegacy`; - add the `@group legacy` annotation to its class or method. * **Remaining/Other** deprecation notices are all other (non-legacy) notices, grouped by message, test class and method. Usage ----- Add this bridge to the `require-dev` section of your composer.json file (not in `require`) with e.g. `composer require --dev "symfony/phpunit-bridge"`. When running `phpunit`, you will see a summary of deprecation notices at the end of the test suite. Deprecation notices in the **Remaining/Other** section need some thought. You have to decide either to: * update your code to not use deprecated interfaces anymore, thus gaining better forward compatibility; * or move them to the **Legacy** section (by using one of the above way). After reviewing them, you should silence deprecations in the **Legacy** section if you think they are triggered by tests dedicated to testing deprecated interfaces. To do so, add the following line at the beginning of your legacy test case or in the `setUp()` method of your legacy test class: `$this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);` Last but not least, you should then configure your C.I. to the reporting mode that is appropriated to your project by setting SYMFONY_DEPRECATIONS_HELPER to `strict`, `weak` or empty. It is recommended to start with `weak` mode, upgrade your code as described above, then when the *Remaining/Other* sections are empty, move to `strict` to keep forward compatibility on the long run. Commits ------- acac734 [PhpUnitBridge] new bridge for testing with PHPUnit
2 parents a630d87 + acac734 commit 9a4f3e1

File tree

100 files changed

+372
-227
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

100 files changed

+372
-227
lines changed

.travis.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ matrix:
77
- php: 5.5
88
- php: 5.6
99
- php: 5.3
10-
env: components=low
10+
env: components=low SYMFONY_DEPRECATIONS_HELPER=weak
1111
- php: 5.6
1212
env: components=high
1313
- php: hhvm-nightly
@@ -20,6 +20,7 @@ services: mongodb
2020
env:
2121
global:
2222
- components=no
23+
- SYMFONY_DEPRECATIONS_HELPER=strict
2324

2425
before_install:
2526
- travis_retry sudo apt-get install parallel
@@ -40,5 +41,5 @@ install:
4041
script:
4142
- if [ "$components" = "no" ]; then ls -d src/Symfony/*/* | parallel --gnu --keep-order 'echo -e "\\nRunning {} tests"; phpunit --exclude-group tty,benchmark,intl-data {} || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi;
4243
- if [ "$components" = "no" ]; then echo -e "\\nRunning tests requiring tty"; phpunit --group tty || (echo -e "\\e[41mKO\\e[0m tty group" && $(exit 1)); fi;
43-
- if [ "$components" = "high" ]; then find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist | sed 's#\(.*\)/.*#\1#' | parallel --gnu --keep-order -j25% 'echo -e "\\nRunning {} tests"; cd {}; composer --prefer-source update; phpunit --exclude-group tty,benchmark,intl-data || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi;
44+
- if [ "$components" = "high" ]; then find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist | sed 's#\(.*\)/.*#\1#' | parallel --gnu --keep-order -j25% 'echo -e "\\nRunning {} tests"; cd {}; composer --prefer-source update; phpunit --exclude-group tty,benchmark,intl-data,legacy || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi;
4445
- if [ "$components" = "low" ]; then find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist | sed 's#\(.*\)/.*#\1#' | parallel --gnu --keep-order -j25% 'echo -e "\\nRunning {} tests"; cd {}; composer --prefer-source --prefer-lowest --prefer-stable update; phpunit --exclude-group tty,benchmark,intl-data || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi;

composer.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"symfony/locale": "self.version",
4646
"symfony/monolog-bridge": "self.version",
4747
"symfony/options-resolver": "self.version",
48+
"symfony/phpunit-bridge": "self.version",
4849
"symfony/process": "self.version",
4950
"symfony/property-access": "self.version",
5051
"symfony/proxy-manager-bridge": "self.version",
@@ -86,6 +87,9 @@
8687
],
8788
"files": [ "src/Symfony/Component/Intl/Resources/stubs/functions.php" ]
8889
},
90+
"autoload-dev": {
91+
"files": [ "src/Symfony/Bridge/PhpUnit/bootstrap.php" ]
92+
},
8993
"minimum-stability": "dev",
9094
"extra": {
9195
"branch-alias": {

phpunit.xml.dist

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,13 @@
44
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
55
backupGlobals="false"
66
colors="true"
7-
bootstrap="autoload.php.dist"
7+
bootstrap="vendor/autoload.php"
88
>
99
<php>
10-
<!-- Disable E_USER_DEPRECATED until 3.0 -->
11-
<!-- php -r 'echo -1 & ~E_USER_DEPRECATED;' -->
12-
<ini name="error_reporting" value="-16385"/>
13-
14-
<ini name="intl.default_locale" value="en"/>
15-
<ini name="intl.error_level" value="0"/>
16-
<ini name="memory_limit" value="-1"/>
10+
<ini name="error_reporting" value="-1" />
11+
<ini name="intl.default_locale" value="en" />
12+
<ini name="intl.error_level" value="0" />
13+
<ini name="memory_limit" value="-1" />
1714
</php>
1815

1916
<testsuites>

src/Symfony/Bridge/Doctrine/Tests/bootstrap.php

Lines changed: 0 additions & 14 deletions
This file was deleted.

src/Symfony/Bridge/Doctrine/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"doctrine/common": "~2.3"
2121
},
2222
"require-dev": {
23+
"symfony/phpunit-bridge": "~2.7|~3.0.0",
2324
"symfony/stopwatch": "~2.2|~3.0.0",
2425
"symfony/dependency-injection": "~2.2|~3.0.0",
2526
"symfony/form": "~2.7|~3.0.0",

src/Symfony/Bridge/Doctrine/phpunit.xml.dist

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@
44
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
55
backupGlobals="false"
66
colors="true"
7-
bootstrap="Tests/bootstrap.php"
7+
bootstrap="vendor/autoload.php"
88
>
99
<php>
10-
<!-- Disable E_USER_DEPRECATED until 3.0 -->
11-
<!-- php -r 'echo -1 & ~E_USER_DEPRECATED;' -->
12-
<ini name="error_reporting" value="-16385"/>
10+
<ini name="error_reporting" value="-1" />
1311
</php>
1412

1513
<testsuites>

src/Symfony/Bridge/Monolog/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"monolog/monolog": "~1.11"
2121
},
2222
"require-dev": {
23+
"symfony/phpunit-bridge": "~2.7|~3.0.0",
2324
"symfony/http-kernel": "~2.4|~3.0.0",
2425
"symfony/console": "~2.4|~3.0.0",
2526
"symfony/event-dispatcher": "~2.2|~3.0.0"

src/Symfony/Bridge/Monolog/phpunit.xml.dist

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
bootstrap="vendor/autoload.php"
88
>
99
<php>
10-
<!-- Disable E_USER_DEPRECATED until 3.0 -->
11-
<!-- php -r 'echo -1 & ~E_USER_DEPRECATED;' -->
12-
<ini name="error_reporting" value="-16385"/>
10+
<ini name="error_reporting" value="-1" />
1311
</php>
1412

1513
<testsuites>

src/Symfony/Bridge/PhpUnit/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
vendor/
2+
composer.lock
3+
phpunit.xml
Lines changed: 63 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
11
<?php
22

3-
if (PHP_VERSION_ID >= 50400 && gc_enabled()) {
4-
// Disabling Zend Garbage Collection to prevent segfaults with PHP5.4+
5-
// https://bugs.php.net/bug.php?id=53976
6-
gc_disable();
7-
}
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\PhpUnit;
813

914
/**
1015
* Catch deprecation notices and print a summary report at the end of the test suite
1116
*
12-
* @internal
17+
* @author Nicolas Grekas <p@tchwork.com>
1318
*/
1419
class DeprecationErrorHandler
1520
{
1621
private static $isRegistered = false;
1722

18-
public static function register()
23+
public static function register($strict = false)
1924
{
2025
if (self::$isRegistered) {
2126
return;
@@ -28,9 +33,9 @@ public static function register()
2833
'legacy' => array(),
2934
'other' => array(),
3035
);
31-
$deprecationHandler = function ($type, $msg, $file, $line, $context) use (&$deprecations) {
36+
$deprecationHandler = function ($type, $msg, $file, $line, $context) use (&$deprecations, $strict) {
3237
if (E_USER_DEPRECATED !== $type) {
33-
return PHPUnit_Util_ErrorHandler::handleError($type, $msg, $file, $line, $context);
38+
return \PHPUnit_Util_ErrorHandler::handleError($type, $msg, $file, $line, $context);
3439
}
3540

3641
$trace = debug_backtrace(PHP_VERSION_ID >= 50400 ? DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT : true);
@@ -44,19 +49,38 @@ public static function register()
4449
$class = isset($trace[$i]['object']) ? get_class($trace[$i]['object']) : $trace[$i]['class'];
4550
$method = $trace[$i]['function'];
4651

47-
$type = 0 === strpos($method, 'testLegacy') || 0 === strpos($method, 'provideLegacy') || 0 === strpos($method, 'getLegacy') || strpos($class, '\Legacy') ? 'legacy' : 'remaining';
52+
$group = 0 === strpos($method, 'testLegacy') || 0 === strpos($method, 'provideLegacy') || 0 === strpos($method, 'getLegacy') || strpos($class, '\Legacy') || in_array('legacy', \PHPUnit_Util_Test::getGroups($class, $method), true) ? 'legacy' : 'remaining';
4853

49-
if ('legacy' === $type && 0 === (error_reporting() & E_USER_DEPRECATED)) {
50-
@++$deprecations[$type]['Silenced']['count'];
54+
if ('legacy' === $group && 0 === (error_reporting() & E_USER_DEPRECATED)) {
55+
$ref =& $deprecations[$group]['Silenced']['count'];
56+
++$ref;
5157
} else {
52-
@++$deprecations[$type][$msg]['count'];
53-
@++$deprecations[$type][$msg][$class.'::'.$method];
58+
$ref =& $deprecations[$group][$msg]['count'];
59+
++$ref;
60+
$ref =& $deprecations[$group][$msg][$class.'::'.$method];
61+
++$ref;
5462
}
5563
} else {
56-
$type = 'other';
57-
@++$deprecations[$type][$msg]['count'];
64+
$group = 'other';
65+
$ref =& $deprecations[$group][$msg]['count'];
66+
++$ref;
67+
}
68+
++$deprecations[$group.'Count'];
69+
unset($trace, $ref);
70+
71+
if ('legacy' !== $group) {
72+
try {
73+
$e = $strict ? error_reporting(-1) : error_reporting();
74+
$result = \PHPUnit_Util_ErrorHandler::handleError($type, $msg, $file, $line, $context);
75+
error_reporting($e);
76+
} catch (\Exception $x) {
77+
error_reporting($e);
78+
79+
throw $x;
80+
}
81+
82+
return $result;
5883
}
59-
++$deprecations[$type.'Count'];
6084
};
6185
$oldErrorHandler = set_error_handler($deprecationHandler);
6286

@@ -68,20 +92,16 @@ public static function register()
6892
}
6993
} else {
7094
self::$isRegistered = true;
71-
register_shutdown_function(function () use (&$deprecations, $deprecationHandler) {
72-
73-
$colorize = new \SebastianBergmann\Environment\Console();
74-
75-
if ($colorize->hasColorSupport()) {
76-
$colorize = function ($str, $red) {
77-
$color = $red ? '41;37' : '43;30';
78-
79-
return "\x1B[{$color}m{$str}\x1B[0m";
80-
};
81-
} else {
82-
$colorize = function ($str) {return $str;};
83-
}
95+
if (self::hasColorSupport()) {
96+
$colorize = function ($str, $red) {
97+
$color = $red ? '41;37' : '43;30';
8498

99+
return "\x1B[{$color}m{$str}\x1B[0m";
100+
};
101+
} else {
102+
$colorize = function ($str) {return $str;};
103+
}
104+
register_shutdown_function(function () use (&$deprecations, $deprecationHandler, $colorize) {
85105
$currErrorHandler = set_error_handler('var_dump');
86106
restore_error_handler();
87107

@@ -93,13 +113,13 @@ public static function register()
93113
return $b['count'] - $a['count'];
94114
};
95115

96-
foreach (array('remaining', 'legacy', 'other') as $type) {
97-
if ($deprecations[$type]) {
98-
echo "\n", $colorize(sprintf('%s deprecation notices (%d)', ucfirst($type), $deprecations[$type.'Count']), 'legacy' !== $type), "\n";
116+
foreach (array('remaining', 'legacy', 'other') as $group) {
117+
if ($deprecations[$group]) {
118+
echo "\n", $colorize(sprintf('%s deprecation notices (%d)', ucfirst($group), $deprecations[$group.'Count']), 'legacy' !== $group), "\n";
99119

100-
uasort($deprecations[$type], $cmp);
120+
uasort($deprecations[$group], $cmp);
101121

102-
foreach ($deprecations[$type] as $msg => $notices) {
122+
foreach ($deprecations[$group] as $msg => $notices) {
103123
echo "\n", $msg, ': ', $notices['count'], "x\n";
104124

105125
arsort($notices);
@@ -118,16 +138,13 @@ public static function register()
118138
});
119139
}
120140
}
121-
}
122-
123-
if (class_exists('PHPUnit_Util_ErrorHandler')) {
124-
DeprecationErrorHandler::register();
125-
}
126141

127-
$loader = require_once __DIR__.'/vendor/autoload.php';
128-
129-
use Doctrine\Common\Annotations\AnnotationRegistry;
130-
131-
AnnotationRegistry::registerLoader(array($loader, 'loadClass'));
142+
private static function hasColorSupport()
143+
{
144+
if ('\\' === DIRECTORY_SEPARATOR) {
145+
return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI');
146+
}
132147

133-
return $loader;
148+
return defined('STDOUT') && function_exists('posix_isatty') && @posix_isatty(STDOUT);
149+
}
150+
}

src/Symfony/Bridge/PhpUnit/LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2014-2015 Fabien Potencier
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

src/Symfony/Bridge/PhpUnit/README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
PHPUnit Bridge
2+
==============
3+
4+
Provides utilities for PHPUnit, especially user deprecation notices management.
5+
6+
It comes with the following features:
7+
8+
* disable the garbage collector;
9< 10000 /code>+
* auto-register `class_exists` to load Doctrine annotations;
10+
* print a user deprecation notices summary at the end of the test suite.
11+
12+
Handling user deprecation notices is sensitive to the SYMFONY_DEPRECATIONS_HELPER
13+
environment variable. This env var configures 3 behaviors depending on its value:
14+
15+
* when set to `strict`, all but legacy-tagged deprecation notices will make tests
16+
fail. This is the recommended mode for best forward compatibility.
17+
* `weak` on the contrary will make tests ignore all deprecation notices.
18+
This is the recommended mode for legacy projects that must use deprecated
19+
interfaces for backward compatibility reasons.
20+
* any other value will respect the current error reporting level.
21+
22+
All three modes will display a summary of deprecation notices at the end of the
23+
test suite, split in two groups:
24+
25+
* **Legacy** deprecation notices denote tests that explicitly test some legacy
26+
interfaces. In all 3 modes, deprecation notices triggered in a legacy-tagged
27+
test do never make a test fail. There are four ways to mark a test as legacy:
28+
- make its class start with the `Legacy` prefix;
29+
- make its method start with `testLegacy`;
30+
- make its data provider start with `provideLegacy` or `getLegacy`;
31+
- add the `@group legacy` annotation to its class or method.
32+
* **Remaining/Other** deprecation notices are all other (non-legacy)
33+
notices, grouped by message, test class and method.
34+
35+
Usage
36+
-----
37+
38+
Add this bridge to the `require-dev` section of your composer.json file
39+
(not in `require`) with e.g.
40+
`composer require --dev "symfony/phpunit-bridge"`.
41+
42+
When running `phpunit`, you will see a summary of deprecation notices at the end
43+
of the test suite.
44+
45+
Deprecation notices in the **Remaining/Other** section need some thought.
46+
You have to decide either to:
47+
48+
* update your code to not use deprecated interfaces anymore, thus gaining better
49+
forward compatibility;
50+
* or move them to the **Legacy** section (by using one of the above way).
51+
52+
After reviewing them, you should silence deprecations in the **Legacy** section
53+
if you think they are triggered by tests dedicated to testing deprecated
54+
interfaces. To do so, add the following line at the beginning of your legacy
55+
test case or in the `setUp()` method of your legacy test class:
56+
`$this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);`
57+
58+
Last but not least, you should then configure your C.I. to the reporting mode
59+
that is appropriated to your project by setting SYMFONY_DEPRECATIONS_HELPER to
60+
`strict`, `weak` or empty. It is recommended to start with `weak` mode, upgrade
61+
your code as described above, then when the *Remaining/Other* sections are empty,
62+
move to `strict` to keep forward compatibility on the long run.

0 commit comments

Comments
 (0)
0