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