ILIAS  trunk Revision v13.0_alpha-62-g3b72d83f2e5
LanguageInstallationManagerMigratedSourcesAndLocksTest.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
31use PHPUnit\Framework\Attributes\DataProvider;
32use PHPUnit\Framework\TestCase;
33
35{
41 {
42 $this->shipPo(['greeting' => 'Hallo', 'farewell' => 'Tschüss']);
43 $this->writeLang('lang/ilias_de.lang', ['common#:#yes#:#Ja']);
44
45 $this->manager()->insertLanguageForInstallation('de');
46
47 $this->assertSame(
48 [
49 'common|yes' => ['value' => 'Ja', 'local_change' => null],
50 'pilot|greeting' => ['value' => 'Hallo', 'local_change' => null],
51 'pilot|farewell' => ['value' => 'Tschüss', 'local_change' => null],
52 ],
53 $this->lngData()
54 );
55 $this->assertSame(['greeting' => 'Hallo', 'farewell' => 'Tschüss'], $this->lngModules()[self::MODULE]);
56 }
57
59 {
60 $this->shipPo(['greeting' => 'Hallo']);
61 $this->writeLang('lang/ilias_de.lang', ['pilot#:#greeting#:#Alt aus lang/', 'pilot#:#stale#:#Veraltet']);
62 $this->writeLang('components/pilot/lang/ilias_de.lang', ['greeting#:#Alt aus Komponente']);
63
64 $this->manager()->insertLanguageForInstallation('de');
65
66 $this->assertSame(['greeting' => 'Hallo'], $this->lngModules()[self::MODULE]);
67 $this->assertArrayNotHasKey('pilot|stale', $this->lngData());
68 }
69
74 {
75 $this->shipPo(['greeting' => 'Hallo'], 'de');
76 $this->writeLang('components/pilot/lang/ilias_en.lang', ['greeting#:#Hello']);
77
78 $this->manager()->insertLanguageForInstallation('en');
79
80 $this->assertSame(['greeting' => 'Hello'], $this->lngModules()[self::MODULE]);
81 $this->assertDirectoryDoesNotExist($this->root . '/client-data/lang');
82 }
83
88 {
89 $this->shipPo(['greeting' => ['value' => 'Hello', 'fuzzy' => true]]);
90
91 $this->manager()->insertLanguageForInstallation('de');
92
93 // only the record that the lng_data local changes were taken over (there were none)
94 $this->assertSame(['.locks', 'taken-over.de.json'], array_values(array_diff(scandir($this->root . '/client-data/lang'), ['.', '..'])));
95 $this->assertSame(
96 ['greeting' => ['value' => 'Hello', 'local_change' => false, 'local_change_date' => null, 'original' => 'Hello']],
97 $this->effective()
98 );
99 }
100
107 {
108 $this->shipPo(['greeting' => 'Hallo']);
109 $this->seedOverlay(['greeting' => ['value' => 'Servus', 'original' => 'Hallo', 'local_change' => '2020-01-01T00:00:00Z']]);
110 $lock_file = MigratedPoFixture::overlayLock($this->overlayBase());
111 $held_locks = new ReflectionProperty(MigratedLanguageFileSync::class, 'held_locks');
112
113 $observed = null;
114 $manager = $this->manager(null, function () use (&$observed, $held_locks, $lock_file): void {
115 $observed = array_key_exists($lock_file, $held_locks->getValue());
116 });
117
118 $manager->insertLanguageForInstallation('de');
119
120 $this->assertTrue($observed, 'the module\'s overlay lock must already be held at this point in the run');
121 // released again once the whole run is done
122 $this->assertSame([], $held_locks->getValue());
123 }
124
130 {
131 $first_module = MigratedPoFixture::directory('zmod', 'components/pilot/lang/');
132 $second_module = MigratedPoFixture::directory('amod', 'components/pilot/lang/');
134 $this->root . '/components/pilot/lang/zmod_de.po',
135 MigratedPoFixture::catalog('zmod', ['greeting' => 'Hallo'])
136 );
138 $this->root . '/components/pilot/lang/amod_de.po',
139 MigratedPoFixture::catalog('amod', ['greeting' => 'Hallo'])
140 );
142 $this->root . '/client-data/lang/zmod/de/zmod_de',
143 MigratedPoFixture::catalog('zmod', ['greeting' => ['value' => 'Servus', 'original' => 'Hallo']])
144 );
146 $this->root . '/client-data/lang/amod/de/amod_de',
147 MigratedPoFixture::catalog('amod', ['greeting' => ['value' => 'Servus', 'original' => 'Hallo']])
148 );
149 $held_locks = new ReflectionProperty(MigratedLanguageFileSync::class, 'held_locks');
150 $observed_order = null;
151
152 $manager = new LanguageInstallationManager(
153 $this->db(),
154 // "zmod" contributed before "amod", the opposite of sorted order
155 new LanguageFileDirectoryManager(new CustomizingLanguageFileDirectory(), $first_module, $second_module),
156 $this->root,
157 $this->repository(),
158 static fn(): \DateTimeImmutable => new \DateTimeImmutable(self::NOW, new \DateTimeZone('UTC')),
159 new CallbackTranslationCache(function () use (&$observed_order, $held_locks): void {
160 $observed_order = array_map(
161 static fn(string $lock_file): string => basename(dirname($lock_file, 2)) . '/' . basename($lock_file),
162 array_keys($held_locks->getValue())
163 );
164 }),
165 fn(): string => $this->root . '/client-data'
166 );
167
168 $manager->insertLanguageForInstallation('de');
169
170 $this->assertSame(['amod/lock', 'zmod/lock'], $observed_order);
171 }
172
177 {
178 $this->expectManagerWarning();
179 $this->shipPo(['greeting' => 'Hallo']);
180 $concurrent_edit = MigratedPoFixture::catalog(self::MODULE, [
181 'greeting' => ['value' => 'Von einem parallelen Admin-Edit', 'original' => 'Hallo', 'local_change' => '2020-01-01T00:00:00Z'],
182 ]);
183
184 $manager = $this->manager(null, function () use ($concurrent_edit): void {
185 MigratedPoFixture::writePair($this->overlayBase(), $concurrent_edit);
186 });
187
188 $unwritten = $manager->insertLanguageForInstallation('de');
189
190 $this->assertSame([self::MODULE], $unwritten);
191 $this->assertSame(
192 'Von einem parallelen Admin-Edit',
193 // untouched, so still as the fixture wrote it: with the module as msgctxt
194 $this->overlayPo()->find(self::MODULE, 'greeting')->getTranslation(),
195 'the concurrently-written overlay must survive untouched'
196 );
197 }
198}
Lets a test observe or influence the invalidation.
An in-memory gettext catalog (header block plus messages) and the only place of the component that re...
Tracks per overlay entry whether the current value still matches the value of the shipped ....
Reads the shipped .po of a migrated module and maintains its per-installation overlay: the ....
The module array just written to lng_modules could not be read back - almost always a wrong collation...
Write access to the language installation: installing, flushing and registering languages in the data...
insertLanguageForInstallation(string $lang_key)
Installs or refreshes a language from every directory the LanguageFileDirectoryManager knows (includi...
Fixtures of the LanguageInstallationManager tests for modules migrated to PO/MO ("pilot").
effective()
Shipped .po plus overlay delta, as served (MigratedLanguageFileSync::loadModuleTranslations()) - the ...
manager(?\Closure $client_data_dir_resolver=null, ?\Closure $language_cache_invalidator=null, bool $with_resolver=true, bool $with_logger=true, ?LoggerInterface $logger=null)
testAModuleWithAnExistingOverlayIsLockedForTheWholeRunNotJustItsOwnSync()
A module that already has an overlay when insertLanguage() decides what to lock is locked (MigratedLa...
testAModuleIsOnlyMigratedForLanguagesWithAShippedPo()
Only languages that ship a .po are migrated: for another language the .lang lines are used.
testAModuleWithoutAnOverlayThatGetsOneCreatedDuringTheRunIsReportedAndTheOverlaySurvives()
A module without an overlay at that point is never locked (no lock file is created).
testLocksAreAcquiredInSortedOrderRegardlessOfTheContributedDirectoryOrder()
Deadlock protection: modules with an overlay are locked in one global order (module name,...
testInstallWithoutLocalChangesWritesNoOverlayOnlyTheTakenOverRecord()
An installation without local changes writes no overlay file: the shipped state is served as is.
testValuesOfAMigratedModuleComeFromThePoEvenWithoutAnyLangLine()
The tos lines could be removed from lang/ilias_xx.lang: the database is filled from the shipped ....
static writePair(string $base_path, TranslationCatalog $catalog)
Writes "<base>.po" and, like MigratedLanguageFileSync::sync(), its compiled revision named by current...
static directory(string $prefix, string $path, bool $local=false)
static catalog(string $module, array $entries)
static writePo(string $file, TranslationCatalog $catalog)
static overlayLock(string $base_path)
A directory of language files with a path and a prefix for them.
Read-only access to the installed languages (database) and the available or valid language files (fil...