diff --git a/ClockMock.php b/ClockMock.php index fe5cd851..962649bc 100644 --- a/ClockMock.php +++ b/ClockMock.php @@ -66,12 +66,12 @@ public static function microtime($asFloat = false) return self::$now; } - return sprintf("%0.6f %d\n", self::$now - (int) self::$now, (int) self::$now); + return sprintf('%0.6f00 %d', self::$now - (int) self::$now, (int) self::$now); } public static function register($class) { - $self = get_called_class(); + $self = \get_called_class(); $mockedNs = array(substr($class, 0, strrpos($class, '\\'))); if (strpos($class, '\\Tests\\')) { @@ -79,7 +79,7 @@ public static function register($class) $mockedNs[] = substr($ns, 0, strrpos($ns, '\\')); } foreach ($mockedNs as $ns) { - if (function_exists($ns.'\time')) { + if (\function_exists($ns.'\time')) { continue; } eval(<<setAccessible(true); - $r->setValue($e, array_slice($trace, 1, $i)); + $r->setValue($e, \array_slice($trace, 1, $i)); echo "\n".ucfirst($group).' deprecation triggered by '.$class.'::'.$method.':'; echo "\n".$msg; echo "\nStack trace:"; - echo "\n".str_replace(' '.getcwd().DIRECTORY_SEPARATOR, ' ', $e->getTraceAsString()); + echo "\n".str_replace(' '.getcwd().\DIRECTORY_SEPARATOR, ' ', $e->getTraceAsString()); echo "\n"; exit(1); @@ -161,46 +161,93 @@ public static function register($mode = 0) return $b['count'] - $a['count']; }; - foreach (array('unsilenced', 'remaining', 'legacy', 'other') as $group) { - if ($deprecations[$group.'Count']) { - echo "\n", $colorize(sprintf('%s deprecation notices (%d)', ucfirst($group), $deprecations[$group.'Count']), 'legacy' !== $group), "\n"; + $displayDeprecations = function ($deprecations) use ($colorize, $cmp) { + foreach (array('unsilenced', 'remaining', 'legacy', 'other') as $group) { + if ($deprecations[$group.'Count']) { + echo "\n", $colorize(sprintf('%s deprecation notices (%d)', ucfirst($group), $deprecations[$group.'Count']), 'legacy' !== $group), "\n"; - uasort($deprecations[$group], $cmp); + uasort($deprecations[$group], $cmp); - foreach ($deprecations[$group] as $msg => $notices) { - echo "\n", rtrim($msg, '.'), ': ', $notices['count'], "x\n"; + foreach ($deprecations[$group] as $msg => $notices) { + echo "\n ", $notices['count'], 'x: ', $msg, "\n"; - arsort($notices); + arsort($notices); - foreach ($notices as $method => $count) { - if ('count' !== $method) { - echo ' ', $count, 'x in ', preg_replace('/(.*)\\\\(.*?::.*?)$/', '$2 from $1', $method), "\n"; + foreach ($notices as $method => $count) { + if ('count' !== $method) { + echo ' ', $count, 'x in ', preg_replace('/(.*)\\\\(.*?::.*?)$/', '$2 from $1', $method), "\n"; + } } } } } - } - if (!empty($notices)) { - echo "\n"; - } + if (!empty($notices)) { + echo "\n"; + } + }; - if (DeprecationErrorHandler::MODE_WEAK !== $mode && $mode < $deprecations['unsilencedCount'] + $deprecations['remainingCount'] + $deprecations['otherCount']) { - exit(1); + $displayDeprecations($deprecations); + + // store failing status + $isFailing = DeprecationErrorHandler::MODE_WEAK !== $mode && $mode < $deprecations['unsilencedCount'] + $deprecations['remainingCount'] + $deprecations['otherCount']; + + // reset deprecations array + foreach ($deprecations as $group => $arrayOrInt) { + $deprecations[$group] = \is_int($arrayOrInt) ? 0 : array(); } + + register_shutdown_function(function () use (&$deprecations, $isFailing, $displayDeprecations, $mode) { + foreach ($deprecations as $group => $arrayOrInt) { + if (0 < (\is_int($arrayOrInt) ? $arrayOrInt : \count($arrayOrInt))) { + echo "Shutdown-time deprecations:\n"; + break; + } + } + $displayDeprecations($deprecations); + if ($isFailing || DeprecationErrorHandler::MODE_WEAK !== $mode && $mode < $deprecations['unsilencedCount'] + $deprecations['remainingCount'] + $deprecations['otherCount']) { + exit(1); + } + }); }); } } + /** + * Returns true if STDOUT is defined and supports colorization. + * + * Reference: Composer\XdebugHandler\Process::supportsColor + * https://github.com/composer/xdebug-handler + * + * @return bool + */ private static function hasColorSupport() { - if ('\\' === DIRECTORY_SEPARATOR) { - return - '10.0.10586' === PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD + if (!\defined('STDOUT')) { + return false; + } + + if ('Hyper' === getenv('TERM_PROGRAM')) { + return true; + } + + if (\DIRECTORY_SEPARATOR === '\\') { + return (\function_exists('sapi_windows_vt100_support') + && sapi_windows_vt100_support(STDOUT)) || false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); } - return defined('STDOUT') && function_exists('posix_isatty') && @posix_isatty(STDOUT); + if (\function_exists('stream_isatty')) { + return stream_isatty(STDOUT); + } + + if (\function_exists('posix_isatty')) { + return posix_isatty(STDOUT); + } + + $stat = fstat(STDOUT); + // Check if formatted mode is S_IFCHR + return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; } } diff --git a/LICENSE b/LICENSE index 207646a0..15fc1c88 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2017 Fabien Potencier +Copyright (c) 2014-2018 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/SymfonyTestsListener.php b/SymfonyTestsListener.php index e7e2d08e..98696371 100644 --- a/SymfonyTestsListener.php +++ b/SymfonyTestsListener.php @@ -61,6 +61,16 @@ public function __construct(array $mockedNamespaces = array()) } } + public function __sleep() + { + throw new \BadMethodCallException('Cannot serialize '.__CLASS__); + } + + public function __wakeup() + { + throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); + } + public function __destruct() { if (0 < $this->state) { diff --git a/Tests/ClockMockTest.php b/Tests/ClockMockTest.php new file mode 100644 index 00000000..82cfb6f5 --- /dev/null +++ b/Tests/ClockMockTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\ClockMock; + +/** + * @author Dominic Tubach + * + * @covers \Symfony\Bridge\PhpUnit\ClockMock + */ +class ClockMockTest extends TestCase +{ + public static function setUpBeforeClass() + { + ClockMock::register(__CLASS__); + } + + protected function setUp() + { + ClockMock::withClockMock(1234567890.125); + } + + public function testTime() + { + $this->assertSame(1234567890, time()); + } + + public function testSleep() + { + sleep(2); + $this->assertSame(1234567892, time()); + } + + public function testMicrotime() + { + $this->assertSame('0.12500000 1234567890', microtime()); + } + + public function testMicrotimeAsFloat() + { + $this->assertSame(1234567890.125, microtime(true)); + } + + public function testUsleep() + { + usleep(2); + $this->assertSame(1234567890.125002, microtime(true)); + } +} diff --git a/Tests/DeprecationErrorHandler/default.phpt b/Tests/DeprecationErrorHandler/default.phpt index cd733724..7a0595a7 100644 --- a/Tests/DeprecationErrorHandler/default.phpt +++ b/Tests/DeprecationErrorHandler/default.phpt @@ -59,24 +59,29 @@ $foo = new FooTestCase(); $foo->testLegacyFoo(); $foo->testNonLegacyBar(); +register_shutdown_function(function () { + exit('I get precedence over any exit statements inside the deprecation error handler.'); +}); + ?> --EXPECTF-- Unsilenced deprecation notices (3) -unsilenced foo deprecation: 2x + 2x: unsilenced foo deprecation 2x in FooTestCase::testLegacyFoo -unsilenced bar deprecation: 1x + 1x: unsilenced bar deprecation 1x in FooTestCase::testNonLegacyBar Remaining deprecation notices (1) -silenced bar deprecation: 1x + 1x: silenced bar deprecation 1x in FooTestCase::testNonLegacyBar Legacy deprecation notices (1) Other deprecation notices (1) -root deprecation: 1x + 1x: root deprecation +I get precedence over any exit statements inside the deprecation error handler. diff --git a/Tests/DeprecationErrorHandler/shutdown_deprecations.phpt b/Tests/DeprecationErrorHandler/shutdown_deprecations.phpt new file mode 100644 index 00000000..fddeed60 --- /dev/null +++ b/Tests/DeprecationErrorHandler/shutdown_deprecations.phpt @@ -0,0 +1,91 @@ +--TEST-- +Test DeprecationErrorHandler in default mode +--FILE-- +testLegacyFoo(); +$foo->testNonLegacyBar(); + +register_shutdown_function(function () { + @trigger_error('root deprecation during shutdown', E_USER_DEPRECATED); +}); + +?> +--EXPECTF-- +Unsilenced deprecation notices (3) + + 2x: unsilenced foo deprecation + 2x in FooTestCase::testLegacyFoo + + 1x: unsilenced bar deprecation + 1x in FooTestCase::testNonLegacyBar + +Remaining deprecation notices (1) + + 1x: silenced bar deprecation + 1x in FooTestCase::testNonLegacyBar + +Legacy deprecation notices (1) + +Other deprecation notices (1) + + 1x: root deprecation + +Shutdown-time deprecations: + +Other deprecation notices (1) + + 1x: root deprecation during shutdown diff --git a/TextUI/Command.php b/TextUI/Command.php index 7f218af9..82d6ab32 100644 --- a/TextUI/Command.php +++ b/TextUI/Command.php @@ -38,7 +38,7 @@ protected function handleBootstrap($filename) // By default, we want PHPUnit's autoloader before Symfony's one if (!getenv('SYMFONY_PHPUNIT_OVERLOAD')) { $filename = realpath(stream_resolve_include_path($filename)); - $symfonyLoader = realpath(dirname(PHPUNIT_COMPOSER_INSTALL).'/../../../vendor/autoload.php'); + $symfonyLoader = realpath(\dirname(PHPUNIT_COMPOSER_INSTALL).'/../../../vendor/autoload.php'); if ($filename === $symfonyLoader) { $symfonyLoader = require $symfonyLoader; diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 816cfe49..d37d2eac 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,7 +1,7 @@