packages/requirements/tests/Unit/Source/RegistryTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Source;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\DataProvider;
9use PHPUnit\Framework\Attributes\Small;
10use PHPUnit\Framework\Attributes\UsesClass;
11use PHPUnit\Framework\TestCase;
12use Requirements\Input\InvalidInputException;
13use Requirements\Model\Source;
14use Requirements\Source\DomSource;
15use Requirements\Source\JsonSource;
16use Requirements\Source\LocalFile;
17use Requirements\Source\Registry;
18use Requirements\Source\ResourceLoader;
19use Requirements\Source\ResourceLocation;
20use Requirements\Source\TextFragment;
21use Requirements\Source\TextSource;
22use Requirements\Source\Unit;
23use stdClass;
24use Tests\Fake\MemorySource;
25use Tests\Fake\ProjectDirectory;
26
27#[CoversClass(Registry::class)]
28#[UsesClass(DomSource::class)]
29#[UsesClass(JsonSource::class)]
30#[UsesClass(LocalFile::class)]
31#[UsesClass(ResourceLoader::class)]
32#[UsesClass(ResourceLocation::class)]
33#[UsesClass(Source::class)]
34#[UsesClass(TextFragment::class)]
35#[UsesClass(TextSource::class)]
36#[UsesClass(Unit::class)]
37#[Small]
38final class RegistryTest extends TestCase
39{
40    /**
41     * @param class-string $class
42     */
43    #[DataProvider('providerGetBuiltIn')]
44    public function testGetBuiltInExtension(string $format, string $class): void
45    {
46        self::assertInstanceOf($class, (new Registry())->get($format));
47    }
48
49    /**
50     * @return array<string, array{string, class-string}>
51     */
52    public static function providerGetBuiltIn(): array
53    {
54        return [
55            'html' => ['html', DomSource::class],
56            'xml' => ['xml', DomSource::class],
57            'ietf' => ['ietf', DomSource::class],
58            'markdown' => ['markdown', DomSource::class],
59            'json' => ['json', JsonSource::class],
60            'text' => ['text', TextSource::class],
61        ];
62    }
63
64    public function testGetSharesOneDocumentExtension(): void
65    {
66        $registry = new Registry();
67
68        self::assertSame($registry->get('html'), $registry->get('markdown'));
69    }
70
71    public function testGetSharesOneLoaderAcrossFormats(): void
72    {
73        $project = new ProjectDirectory();
74        $registry = new Registry();
75        $registry->get('html')->select(new Source('manual', 'source.html', 'html', 'main p'), 'main p', $project->directory, false);
76        $project->put('source.html', 'Changed.');
77
78        self::assertEquals([new Unit('line:1', '<!DOCTYPE html>')], $registry->get('text')->select(new Source('manual', 'source.html', 'text', 'lines:1'), 'lines:1', $project->directory, false));
79    }
80
81    public function testGetConfiguredExtension(): void
82    {
83        self::assertInstanceOf(MemorySource::class, (new Registry(['memory' => MemorySource::class]))->get('memory'));
84    }
85
86    public function testGetConfiguredExtensionReplacesABuiltIn(): void
87    {
88        $registry = new Registry(['html' => MemorySource::class]);
89
90        self::assertInstanceOf(MemorySource::class, $registry->get('html'));
91        self::assertInstanceOf(DomSource::class, $registry->get('xml'));
92    }
93
94    public function testGetRejectsUnknownFormats(): void
95    {
96        $this->expectException(InvalidInputException::class);
97        $this->expectExceptionMessage('Unknown source extension: memory');
98        (new Registry())->get('memory');
99    }
100
101    #[DataProvider('providerConfiguredNonExtension')]
102    public function testGetRejectsConfiguredNonExtensions(string $class): void
103    {
104        $this->expectException(InvalidInputException::class);
105        $this->expectExceptionMessage("$class must implement SourceExtension.");
106        (new Registry(['memory' => MemorySource::class, 'other' => $class]))->get('memory');
107    }
108
109    /**
110     * @return array<string, array{string}>
111     */
112    public static function providerConfiguredNonExtension(): array
113    {
114        return [
115            'plain class' => [stdClass::class],
116            'missing class' => ['Tests\Fake\MissingSource'],
117        ];
118    }
119}
120