packages/sql-faker/tests/Unit/Generation/Coverage/CoverageSnapshotStoreTest.php
1<?php
2
3declare (strict_types=1);
4
5namespace Tests\Unit\SqlFaker\Generation\Coverage;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use SqlFaker\Generation\Choice\ByteChoices;
11use SqlFaker\Generation\Coverage\CoverageException;
12use SqlFaker\Generation\Coverage\CoverageSets;
13use SqlFaker\Generation\Coverage\CoverageSnapshotStore;
14use SqlFaker\Generation\Coverage\GenerationTrace;
15use SqlFaker\Generation\Coverage\GeneratorRevision;
16use SqlFaker\Generation\Coverage\GrammarCoverage;
17use SqlFaker\Generation\Coverage\GrammarCoverageInventory;
18use SqlFaker\Generation\Coverage\LexicalObservation;
19use SqlFaker\Generation\Coverage\SnapshotValidation;
20use SqlFaker\Generation\Derivation\CompletionCosts;
21use SqlFaker\Generation\Derivation\DerivationNode;
22use SqlFaker\Grammar\Model\Grammar;
23use SqlFaker\Grammar\Model\NonTerminal;
24use SqlFaker\Grammar\Model\Production;
25use SqlFaker\Grammar\Model\ProductionRule;
26use SqlFaker\Grammar\Model\Terminal;
27use Symfony\Component\Filesystem\Filesystem;
28
29#[CoversClass(CoverageSnapshotStore::class)]
30#[UsesClass(Grammar::class)]
31#[UsesClass(Production::class)]
32#[UsesClass(ProductionRule::class)]
33#[UsesClass(Terminal::class)]
34#[UsesClass(NonTerminal::class)]
35#[UsesClass(GrammarCoverageInventory::class)]
36#[UsesClass(CoverageException::class)]
37#[UsesClass(GrammarCoverage::class)]
38#[UsesClass(GeneratorRevision::class)]
39#[UsesClass(SnapshotValidation::class)]
40#[UsesClass(GenerationTrace::class)]
41#[UsesClass(CoverageSets::class)]
42#[UsesClass(ByteChoices::class)]
43#[UsesClass(CompletionCosts::class)]
44#[UsesClass(DerivationNode::class)]
45#[UsesClass(LexicalObservation::class)]
46final class CoverageSnapshotStoreTest extends TestCase
47{
48 public function testReadReturnsNoHistoryWhenTheKeyHasNoSnapshot(): void
49 {
50 $directory = sys_get_temp_dir() . '/sql-faker-coverage-' . bin2hex(random_bytes(8));
51 (new Filesystem())->mkdir($directory);
52 $store = new CoverageSnapshotStore($directory, 'key');
53 self::assertNull($store->read());
54 unset($store);
55 (new Filesystem())->remove($directory);
56 }
57
58 public function testWriteReplacesACompleteSnapshotAndIgnoresInterruptedTemporaryFiles(): void
59 {
60 $directory = sys_get_temp_dir() . '/sql-faker-coverage-' . bin2hex(random_bytes(8));
61 (new Filesystem())->mkdir($directory);
62 $store = new CoverageSnapshotStore($directory, 'key');
63 $store->write('{"old":true}');
64 file_put_contents($directory . '/interrupted-temporary', '{"partial":');
65 self::assertSame('{"old":true}', $store->read());
66 $store->write('{"new":true}');
67 self::assertSame('{"new":true}', $store->read());
68 unset($store);
69 (new Filesystem())->remove($directory);
70 }
71
72 public function testWriteRejectsASecondWriterForTheSameKey(): void
73 {
74 $directory = sys_get_temp_dir() . '/sql-faker-coverage-' . bin2hex(random_bytes(8));
75 (new Filesystem())->mkdir($directory);
76 $store = new CoverageSnapshotStore($directory, 'key');
77 $this->expectException(CoverageException::class);
78 try {
79 new CoverageSnapshotStore($directory, 'key');
80 } finally {
81 unset($store);
82 (new Filesystem())->remove($directory);
83 }
84 }
85}
86