diff --git a/src/Framework/Assert.php b/src/Framework/Assert.php index 2f4fa744..b5cb2162 100644 --- a/src/Framework/Assert.php +++ b/src/Framework/Assert.php @@ -416,6 +416,40 @@ public static function matchFile(string $file, $actual, string $description = nu } + /** + * Compares value with a previously created snapshot. + */ + public static function snapshot(string $snapshotName, $actual, string $description = null): void + { + self::$counter++; + + $snapshot = new Snapshot($snapshotName); + if (!$snapshot->exists()) { + if (!$snapshot->canUpdate()) { + self::fail("Missing snapshot '$snapshotName', use --update-snapshots option to generate it."); + } + + $snapshot->update($actual); + } + + $expected = $snapshot->read(); + if ($expected !== $actual) { + if (!$snapshot->canUpdate()) { + self::fail( + self::describe( + "%1 should be %2 in snapshot '$snapshotName'", + $description + ), + $actual, + $expected + ); + } + + $snapshot->update($actual); + } + } + + /** * Failed assertion */ diff --git a/src/Framework/Snapshot.php b/src/Framework/Snapshot.php index 5fa74ce5..d690f872 100644 --- a/src/Framework/Snapshot.php +++ b/src/Framework/Snapshot.php @@ -11,71 +11,70 @@ /** - * Snapshot testing helper. + * Snapshot of a tested value. */ class Snapshot { + /** @var string */ public static $snapshotDir = 'snapshots'; + /** @var string[] */ public static $updatedSnapshots = []; + /** @var string[] */ + private static $usedNames = []; - /** - * Compares value with a previously created snapshot. - */ - public static function match($value, string $snapshotName): void - { - $updateSnapshots = (bool) getenv(Environment::UPDATE_SNAPSHOTS); - - $testFile = $_SERVER['argv'][0]; - $snapshotFile = self::getSnapshotFile($testFile, $snapshotName); + /** @var string */ + private $name; - if (!file_exists($snapshotFile)) { - if (!$updateSnapshots) { - Assert::fail("Missing snapshot file '$snapshotFile', use --update-snapshots option to generate it."); - } - self::write($snapshotFile, $value); + public function __construct(string $name) + { + if (!preg_match('/^[a-zA-Z0-9-_]+$/', $name)) { + throw new \Exception("Invalid snapshot name '$name'. Only alphanumeric characters, dash and underscore are allowed."); } - $snapshot = self::read($snapshotFile); + if (in_array($name, self::$usedNames, true)) { + throw new \Exception("Snapshot '$name' was already asserted, please use a different name."); + } - try { - Assert::equal($snapshot, $value, "Snapshot $snapshotName"); + $this->name = self::$usedNames[] = $name; + } - } catch (AssertException $e) { - if (!$updateSnapshots) { - throw $e; - } - self::write($snapshotFile, $value); - } + public function exists(): bool + { + return file_exists($this->getSnapshotFile()); } - private static function getSnapshotFile(string $testFile, string $snapshotName): string + public function read() { - $path = self::$snapshotDir . DIRECTORY_SEPARATOR . pathinfo($testFile, PATHINFO_FILENAME) . '.' . $snapshotName . '.phps'; - if (!preg_match('#/|\w:#A', self::$snapshotDir)) { - $path = dirname($testFile) . DIRECTORY_SEPARATOR . $path; - } - return $path; + $snapshotFile = $this->getSnapshotFile(); + set_error_handler(function ($errno, $errstr) use ($snapshotFile) { + throw new \Exception("Unable to read snapshot file '$snapshotFile': $errstr"); + }); + + $snapshotContents = include $snapshotFile; + + restore_error_handler(); + return $snapshotContents; } - private static function read(string $snapshotFile) + public function canUpdate(): bool { - $snapshotContents = @file_get_contents($snapshotFile); - if ($snapshotContents === false) { - throw new \Exception("Unable to read snapshot file '$snapshotFile'."); - } - - return eval(substr($snapshotContents, strlen('canUpdate()) { + throw new \Exception('Cannot update snapshot. Please run tests again with --update-snapshots.'); + } + + $snapshotFile = $this->getSnapshotFile(); $snapshotDirectory = dirname($snapshotFile); if (!is_dir($snapshotDirectory) && !mkdir($snapshotDirectory)) { throw new \Exception("Unable to create snapshot directory '$snapshotDirectory'."); @@ -88,4 +87,15 @@ private static function write(string $snapshotFile, $value): void self::$updatedSnapshots[] = $snapshotFile; } + + + private function getSnapshotFile(): string + { + $testFile = $_SERVER['argv'][0]; + $path = self::$snapshotDir . DIRECTORY_SEPARATOR . pathinfo($testFile, PATHINFO_FILENAME) . '.' . $this->name . '.phps'; + if (!preg_match('#/|\w:#A', self::$snapshotDir)) { + $path = dirname($testFile) . DIRECTORY_SEPARATOR . $path; + } + return $path; + } } diff --git a/tests/Framework/Assert.snapshot.phpt b/tests/Framework/Assert.snapshot.phpt new file mode 100644 index 00000000..2b23044f --- /dev/null +++ b/tests/Framework/Assert.snapshot.phpt @@ -0,0 +1,29 @@ + 42]); + +Assert::exception(function () { + Assert::snapshot('invalid / name', ['answer' => 42]); +}, \Exception::class, "Invalid snapshot name 'invalid / name'. Only alphanumeric characters, dash and underscore are allowed."); + +Assert::exception(function () { + Assert::snapshot('existingSnapshot', ['answer' => 42]); +}, \Exception::class, "Snapshot 'existingSnapshot' was already asserted, please use a different name."); + +Assert::exception(function () { + Assert::snapshot('anotherSnapshot', ['answer' => 43]); +}, AssertException::class, "%a% should be %a% in snapshot 'anotherSnapshot'"); + +Assert::exception(function () { + Assert::snapshot('nonExistingSnapshot', 'value'); +}, AssertException::class, "Missing snapshot 'nonExistingSnapshot', use --update-snapshots option to generate it."); diff --git a/tests/Framework/Assert.snapshot.update.phpt b/tests/Framework/Assert.snapshot.update.phpt new file mode 100644 index 00000000..3b7078ad --- /dev/null +++ b/tests/Framework/Assert.snapshot.update.phpt @@ -0,0 +1,43 @@ + 42]); +Assert::true(file_exists(Snapshot::$snapshotDir . '/Assert.snapshot.update.newSnapshot.phps')); +Assert::contains('42', file_get_contents(Snapshot::$snapshotDir . '/Assert.snapshot.update.newSnapshot.phps')); + +// existing + +file_put_contents( + Snapshot::$snapshotDir . '/Assert.snapshot.update.updatedSnapshot.phps', + ' 43);' . PHP_EOL +); + +Assert::true(file_exists(Snapshot::$snapshotDir . '/Assert.snapshot.update.updatedSnapshot.phps')); +Assert::snapshot('updatedSnapshot', ['answer' => 42]); +Assert::true(file_exists(Snapshot::$snapshotDir . '/Assert.snapshot.update.updatedSnapshot.phps')); +Assert::contains('42', file_get_contents(Snapshot::$snapshotDir . '/Assert.snapshot.update.updatedSnapshot.phps')); + +// Snapshot::$updatedSnapshots + +Assert::same([ + Snapshot::$snapshotDir . DIRECTORY_SEPARATOR . 'Assert.snapshot.update.newSnapshot.phps', + Snapshot::$snapshotDir . DIRECTORY_SEPARATOR . 'Assert.snapshot.update.updatedSnapshot.phps', +], Snapshot::$updatedSnapshots); + +// reset the env variable so that the test does not fail due to updated snapshots +putenv(Environment::UPDATE_SNAPSHOTS . '=0'); diff --git a/tests/Framework/Snapshot.match.phpt b/tests/Framework/Snapshot.match.phpt deleted file mode 100644 index b6823cf5..00000000 --- a/tests/Framework/Snapshot.match.phpt +++ /dev/null @@ -1,21 +0,0 @@ - 42], 'existingSnapshot'); - -Assert::exception(function () { - Snapshot::match(['answer' => 43], 'existingSnapshot'); -}, AssertException::class, 'Snapshot existingSnapshot: %a% should be equal to %a%'); - -Assert::exception(function () { - Snapshot::match('value', 'nonExistingSnapshot'); -}, AssertException::class, "Missing snapshot file '%A%', use --update-snapshots option to generate it."); diff --git a/tests/Framework/Snapshot.update.phpt b/tests/Framework/Snapshot.update.phpt deleted file mode 100644 index 2e1ba1fa..00000000 --- a/tests/Framework/Snapshot.update.phpt +++ /dev/null @@ -1,43 +0,0 @@ - 42], 'newSnapshot'); -Assert::true(file_exists(Snapshot::$snapshotDir . '/Snapshot.update.newSnapshot.phps')); -Assert::contains('42', file_get_contents(Snapshot::$snapshotDir . '/Snapshot.update.newSnapshot.phps')); - -// existing - -file_put_contents( - Snapshot::$snapshotDir . '/Snapshot.update.updatedSnapshot.phps', - ' 43);' . PHP_EOL -); - -Assert::true(file_exists(Snapshot::$snapshotDir . '/Snapshot.update.updatedSnapshot.phps')); -Snapshot::match(['answer' => 42], 'updatedSnapshot'); -Assert::true(file_exists(Snapshot::$snapshotDir . '/Snapshot.update.updatedSnapshot.phps')); -Assert::contains('42', file_get_contents(Snapshot::$snapshotDir . '/Snapshot.update.updatedSnapshot.phps')); - -// Snapshot::$updatedSnapshots - -Assert::equal([ - Snapshot::$snapshotDir . DIRECTORY_SEPARATOR . 'Snapshot.update.newSnapshot.phps', - Snapshot::$snapshotDir . DIRECTORY_SEPARATOR . 'Snapshot.update.updatedSnapshot.phps', -], Snapshot::$updatedSnapshots); - -// reset the env variable so that the test does not fail due to updated snapshots -putenv(Environment::UPDATE_SNAPSHOTS . '=0'); diff --git a/tests/Framework/fixtures/Snapshot.match.existingSnapshot.phps b/tests/Framework/fixtures/Assert.snapshot.anotherSnapshot.phps similarity index 100% rename from tests/Framework/fixtures/Snapshot.match.existingSnapshot.phps rename to tests/Framework/fixtures/Assert.snapshot.anotherSnapshot.phps diff --git a/tests/Framework/fixtures/Assert.snapshot.existingSnapshot.phps b/tests/Framework/fixtures/Assert.snapshot.existingSnapshot.phps new file mode 100644 index 00000000..a7d8f5b1 --- /dev/null +++ b/tests/Framework/fixtures/Assert.snapshot.existingSnapshot.phps @@ -0,0 +1 @@ + 42]; diff --git a/tests/Runner/Runner.snapshots.phpt b/tests/Runner/Runner.snapshots.phpt index 70fc6885..2036cb69 100644 --- a/tests/Runner/Runner.snapshots.phpt +++ b/tests/Runner/Runner.snapshots.phpt @@ -52,11 +52,11 @@ $runner->run(); Assert::same(Test::FAILED, $logger->results['update-snapshots.phptx'][0]); Assert::match( - "Failed: Missing snapshot file '%a%', use --update-snapshots option to generate it.\n%A%", + "Failed: Missing snapshot '%a%', use --update-snapshots option to generate it.\n%A%", trim(Dumper::removeColors($logger->results['update-snapshots.phptx'][1])) ); -// second run, with update -> skipped +// second run, with update -> fail $runner = new Tester\Runner\Runner(createInterpreter()); $runner->paths[] = __DIR__ . '/snapshots/*.phptx'; diff --git a/tests/Runner/snapshots/update-snapshots.phptx b/tests/Runner/snapshots/update-snapshots.phptx index 4790884e..0e7a2c2d 100644 --- a/tests/Runner/snapshots/update-snapshots.phptx +++ b/tests/Runner/snapshots/update-snapshots.phptx @@ -1,7 +1,7 @@