From 8ba0a9e6af6a651d857daa0d4f297ce1ec5fce16 Mon Sep 17 00:00:00 2001 From: saeid Date: Fri, 22 Jun 2018 02:37:25 +0430 Subject: [PATCH 1/2] use immutable date/time for testNow --- src/Chronos.php | 2 +- tests/DateTime/TestingAidsTest.php | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Chronos.php b/src/Chronos.php index bf5e762e..d2add917 100644 --- a/src/Chronos.php +++ b/src/Chronos.php @@ -168,7 +168,7 @@ public static function setTestNow($testNow = null) */ public static function getTestNow() { - return static::$testNow; + return is_object(static::$testNow) ? clone static::$testNow : static::$testNow; } /** diff --git a/tests/DateTime/TestingAidsTest.php b/tests/DateTime/TestingAidsTest.php index 70536f4b..acce780b 100644 --- a/tests/DateTime/TestingAidsTest.php +++ b/tests/DateTime/TestingAidsTest.php @@ -45,7 +45,7 @@ public function testTestingAidsWithTestNowSet($class) $class::setTestNow($notNow); $this->assertTrue($class::hasTestNow()); - $this->assertSame($notNow, $class::getTestNow()); + $this->assertEquals($notNow, $class::getTestNow()); } /** @@ -203,9 +203,9 @@ public function testSetTestNowSingular($class) $c = new $class('2016-01-03 00:00:00', 'Europe/Copenhagen'); $class::setTestNow($c); - $this->assertSame($c, MutableDate::getTestNow()); - $this->assertSame($c, Date::getTestNow()); - $this->assertSame($c, Chronos::getTestNow()); - $this->assertSame($c, MutableDateTime::getTestNow()); + $this->assertEquals($c, MutableDate::getTestNow()); + $this->assertEquals($c, Date::getTestNow()); + $this->assertEquals($c, Chronos::getTestNow()); + $this->assertEquals($c, MutableDateTime::getTestNow()); } } From c505236da22b0b6aa53badc6ea4c642d2fa9ad42 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Thu, 21 Jun 2018 21:12:09 -0400 Subject: [PATCH 2/2] Fix datetimes being used in the shared test now. When datetimes are stored in the shared test now, Date instances that use relative time modifiers e.g `-22 hours` would violate the contraints of dates by not ignoring hours. We can work around that by munging relative time strings. I've moved the clone operators around to not change the semantics of `getTestNow()`. --- src/Chronos.php | 3 +- src/Date.php | 6 +++- src/MutableDate.php | 5 ++- src/Traits/FrozenTimeTrait.php | 11 ++++++ tests/DateTime/TestingAidsTest.php | 56 ++++++++++++++++++++++++++++++ 5 files changed, 78 insertions(+), 3 deletions(-) diff --git a/src/Chronos.php b/src/Chronos.php index d2add917..2bde7da7 100644 --- a/src/Chronos.php +++ b/src/Chronos.php @@ -105,6 +105,7 @@ public function __construct($time = 'now', $tz = null) return; } + $testNow = clone $testNow; if ($relative) { $testNow = $testNow->modify($time); } @@ -168,7 +169,7 @@ public static function setTestNow($testNow = null) */ public static function getTestNow() { - return is_object(static::$testNow) ? clone static::$testNow : static::$testNow; + return static::$testNow; } /** diff --git a/src/Date.php b/src/Date.php index 43911b5d..a270b115 100644 --- a/src/Date.php +++ b/src/Date.php @@ -99,8 +99,12 @@ public function __construct($time = 'now') return; } + $testNow = clone $testNow; if ($relative) { - $testNow = $testNow->modify($time); + $time = $this->stripRelativeTime($time); + if (strlen($time) > 0) { + $testNow = $testNow->modify($time); + } } if ($tz !== $testNow->getTimezone()) { diff --git a/src/MutableDate.php b/src/MutableDate.php index 0341637b..22ee5f42 100644 --- a/src/MutableDate.php +++ b/src/MutableDate.php @@ -101,7 +101,10 @@ public function __construct($time = 'now') $testNow = clone $testNow; if ($relative) { - $testNow = $testNow->modify($time); + $time = $this->stripRelativeTime($time); + if (strlen($time) > 0) { + $testNow = $testNow->modify($time); + } } if ($tz !== $testNow->getTimezone()) { diff --git a/src/Traits/FrozenTimeTrait.php b/src/Traits/FrozenTimeTrait.php index 1f1022b2..ca731903 100644 --- a/src/Traits/FrozenTimeTrait.php +++ b/src/Traits/FrozenTimeTrait.php @@ -48,6 +48,17 @@ protected function stripTime($time) return preg_replace('/\d{1,2}:\d{1,2}:\d{1,2}(?:\.\d+)?/', '00:00:00', $time); } + /** + * Remove time components from strtotime relative strings. + * + * @param string $time The input expression + * @return string The output expression with no time modifiers. + */ + protected function stripRelativeTime($time) + { + return preg_replace('/([-+]\s*\d+\s(?:minutes|seconds|hours|microseconds))/', '', $time); + } + /** * Modify the time on the Date. * diff --git a/tests/DateTime/TestingAidsTest.php b/tests/DateTime/TestingAidsTest.php index acce780b..0353c2be 100644 --- a/tests/DateTime/TestingAidsTest.php +++ b/tests/DateTime/TestingAidsTest.php @@ -86,6 +86,62 @@ public function testNowWithTestValueSet($class) $this->assertEquals($notNow, $class::now()); } + /** + * Ensure that using test now doesn't mutate test now. + * + * @dataProvider classNameProvider + * @return void + */ + public function testNowNoMutateDateTime($class) + { + $value = '2018-06-21 10:11:12'; + $notNow = new MutableDateTime($value); + $class::setTestNow($notNow); + + $instance = new $class('-10 minutes'); + $this->assertSame('10:01:12', $instance->format('H:i:s')); + + $instance = new $class('-10 minutes'); + $this->assertSame('10:01:12', $instance->format('H:i:s')); + } + + /** + * Ensure that using test now doesn't mutate test now. + * + * @dataProvider dateClassProvider + * @return void + */ + public function testNowNoMutateDate($class) + { + $value = '2018-06-21 10:11:12'; + $notNow = new MutableDateTime($value); + $class::setTestNow($notNow); + + $instance = new $class('-1 day'); + $this->assertSame('2018-06-20 00:00:00', $instance->format('Y-m-d H:i:s')); + + $instance = new $class('-1 day'); + $this->assertSame('2018-06-20 00:00:00', $instance->format('Y-m-d H:i:s')); + } + + /** + * Ensure that setting a datetime into test now doesn't violate date semantics + * + * Modifying date instances by hours should not change the date. + * + * @dataProvider dateClassProvider + * @return void + */ + public function testNowTestDateTimeConstraints($class) + { + $value = '2018-06-21 10:11:12'; + $notNow = new MutableDateTime($value); + $class::setTestNow($notNow); + + $instance = new $class('-23 hours'); + $this->assertSame('2018-06-21 00:00:00', $instance->format('Y-m-d H:i:s')); + } + /** * @dataProvider classNameProvider * @return void