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