ILIAS  trunk Revision v13.0_alpha-62-g3b72d83f2e5
LanguageInstallationManagerMigratedDroppedKeysTest.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(['farewell' => 'Tschüss']); // 'greeting' is no longer shipped
43 $this->seedOverlay(['greeting' => ['value' => 'Hallo', 'original' => 'Hallo']]);
44 $this->db_local_changes = [self::MODULE => ['greeting' => 'Hallo']];
45
46 $this->manager()->insertLanguageForInstallation('de');
47
48 $this->assertArrayNotHasKey('greeting', $this->lngModules()[self::MODULE]);
49 // the overlay is the only source and holds no local change: nothing local to drop, lng_data is not read
50 $this->assertSame([], $this->droppedIdentifiers(self::MODULE));
51 // nothing local is left, so the overlay is removed completely
52 $this->assertFileDoesNotExist($this->overlayBase() . '.po', 'dropped from the overlay as well');
53 }
54
59 {
60 $this->shipPo(['farewell' => 'Tschüss']);
61 $this->seedOverlay(['greeting' => [
62 'value' => 'Servus',
63 'original' => 'Hallo',
64 'local_change' => '2025-05-05T05:05:05Z',
65 ]]);
66 $this->db_local_changes = [self::MODULE => ['greeting' => 'Servus']];
67
68 $this->manager()->insertLanguageForInstallation('de');
69
70 $this->assertSame('Servus', $this->lngModules()[self::MODULE]['greeting']);
71 $this->assertSame([], $this->droppedIdentifiers(self::MODULE), 'a real local change is never dropped');
72 $this->assertSame('Servus', $this->overlayPo()->find(null, 'greeting')->getTranslation());
73 }
74
79 {
80 $this->expectManagerWarning();
81 $this->shipPo(['farewell' => 'Tschüss']);
82 $this->seedOverlay(['farewell' => ['value' => 'Tschüss', 'original' => 'Tschüss']]);
83 file_put_contents($this->overlayBase() . '.po', "msgid \"kaputt\n");
84 $this->db_local_changes = [self::MODULE => ['greeting' => 'Hallo']];
85
86 $this->manager()->insertLanguageForInstallation('de');
87
88 $this->assertArrayNotHasKey(self::MODULE, $this->lngModules(), 'nothing of the module is written');
89 $this->assertSame([], $this->droppedIdentifiers(self::MODULE));
90 }
91
97 {
98 $this->shipPo(['farewell' => 'Tschüss']);
99 $this->seedOverlay(['greeting' => ['value' => 'Hallo', 'original' => 'Hallo']]);
100 $this->writeLang('lang/customizing/ilias_de.lang.local', ['pilot#:#greeting#:#Hallo']);
101
102 $this->manager()->insertLanguageForInstallation('de');
103
104 $this->assertSame('Hallo', $this->lngModules()[self::MODULE]['greeting']);
105 $this->assertSame(['value' => 'Hallo', 'local_change' => self::NOW], $this->lngData()['pilot|greeting']);
106 $this->assertSame([], $this->droppedIdentifiers(self::MODULE));
107 }
108
114 {
115 $this->shipPo(['farewell' => 'Tschüss']); // 'greeting' not shipped this run
116 $this->seedOverlay(['greeting' => [
117 'value' => 'Angepasst',
118 'original' => 'Hallo',
119 'local_change' => '2025-05-05T05:05:05Z',
120 ]]);
121 $this->db_local_changes = [self::MODULE => ['greeting' => 'Angepasst']];
122
123 $this->manager()->insertLanguageForInstallation('de');
124 $this->assertSame(
125 'Angepasst',
126 $this->lngModules()[self::MODULE]['greeting'],
127 'sanity: kept in run 1 because L != O'
128 );
129
130 $this->queries = [];
131 $this->shipPo(['greeting' => 'Neu', 'farewell' => 'Tschüss']); // the key ships again, with a new value
132 $this->db_local_changes = [self::MODULE => ['greeting' => 'Hallo']]; // reverted to the (preserved) original
133
134 $this->manager()->insertLanguageForInstallation('de');
135
136 $this->assertSame(
137 'Angepasst',
138 $this->lngModules()[self::MODULE]['greeting'],
139 'the overlay is the only source: the revert in lng_data is not read, its local change stays'
140 );
141 $this->assertSame('Angepasst', $this->lngData()['pilot|greeting']['value']);
142 }
143
149 {
150 $this->seedOverlay([
151 'A' => ['value' => 'A1', 'original' => 'A1'],
152 'B' => ['value' => 'B1', 'original' => 'B1'],
153 'C' => ['value' => 'C_custom', 'original' => 'C1', 'local_change' => '2025-01-01T00:00:00Z'],
154 ]);
155 $po_before_the_gap = file_get_contents($this->overlayBase() . '.po');
156 $mo_before_the_gap = file_get_contents(MigratedPoFixture::overlayMo($this->overlayBase()));
157 // no shipped `.po` for the whole gap: the module falls back to its plain `.lang` file
158 $this->writeLang('components/pilot/lang/ilias_de.lang', ['A#:#A1', 'B#:#B1', 'C#:#C_custom']);
159
160 $this->manager()->insertLanguageForInstallation('de');
161
162 $this->assertSame(
163 ['A' => 'A1', 'B' => 'B1', 'C' => 'C_custom'],
164 $this->lngModules()[self::MODULE],
165 'sanity: the .lang fallback is used while the .po is missing'
166 );
167 $this->assertSame(
168 ['value' => 'C_custom', 'local_change' => null],
169 $this->lngData()['pilot|C'],
170 'the DB no longer sees C as a local change while the module is not migrated'
171 );
172 $this->assertSame($po_before_the_gap, file_get_contents($this->overlayBase() . '.po'), 'the overlay is untouched during the gap');
173 $this->assertSame($mo_before_the_gap, file_get_contents(MigratedPoFixture::overlayMo($this->overlayBase())), 'the overlay is untouched during the gap');
174
175 // the module is migrated again - A's shipped value changed, B's did not, C is not shipped
176 // with the (never-shipped) customized value at all
177 $this->queries = [];
178 $this->shipPo(['A' => 'A2', 'B' => 'B1', 'C' => 'C1']);
179
180 $this->manager()->insertLanguageForInstallation('de');
181
182 $this->assertSame(
183 ['C' => 'C_custom', 'A' => 'A2', 'B' => 'B1'],
184 $this->lngModules()[self::MODULE]
185 );
186 $this->assertSame(['value' => 'A2', 'local_change' => null], $this->lngData()['pilot|A'], 'L === preserved O -> S wins');
187 $this->assertSame(['value' => 'B1', 'local_change' => null], $this->lngData()['pilot|B'], 'unchanged');
188 $this->assertSame(
189 ['value' => 'C_custom', 'local_change' => '2025-01-01 00:00:00'],
190 $this->lngData()['pilot|C'],
191 'the overlay-only local change is taken over again - the known limitation above'
192 );
193 // A and B carry the shipped values and leave the full legacy overlay, only the local change C stays
194 $overlay = $this->overlayPo();
195 $this->assertNull($overlay->find(null, 'A'));
196 $this->assertNull($overlay->find(null, 'B'));
197 $this->assertSame('A2', $this->effective()['A']['value']);
198 $this->assertSame('C_custom', $overlay->find(null, 'C')->getTranslation());
199 $this->assertNotNull(LocalChangeComments::getLocalChange($overlay->find(null, 'C')));
200 }
201}
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...
testAKeyDroppedFromTheShippedPoWithACorruptOverlayLeavesItsModuleUnchanged()
A corrupt overlay must not be treated as L === O: no three-way decision is possible,...
testAKeyThatReturnsAfterBeingUnshippedKeepsTheLocalChangeOfTheOverlay()
MigratedLanguageFileSync::sync() must preserve the local change of an entry the .po currently lacks: ...
testAKeyDroppedFromTheShippedPoWithoutALocalChangeInTheOverlayIsRemoved()
A no-longer-shipped entry that is no local change in the overlay (its only source,...
testAWholeMissingShippedPoIsBridgedByThePreservedOverlayAcrossTwoUpdates()
The whole shipped .po missing and later back: meanwhile the module is not migrated and falls back to ...
testACustomizingValueForAKeyDroppedFromTheShippedPoStaysLocal()
A customizing value equal to the previously shipped one must not be dropped as an unchanged entry onc...
testAKeyDroppedFromTheShippedPoButWithARealLocalChangeIsKept()
A real local change must survive its key disappearing from the shipped .po.
Fixtures of the LanguageInstallationManager tests for modules migrated to PO/MO ("pilot").
droppedIdentifiers(string $module)
Identifiers of $module dropped via the per-module "DELETE FROM lng_data ... AND identifier IN (....
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)
static overlayMo(string $base_path)
The compiled catalog of the revision current names; a non-existing path if there is none.
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...