ILIAS  trunk Revision v13.0_alpha-62-g3b72d83f2e5
LanguageInstallationManagerMigratedLocalChangesActionsTest.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{
37 {
38 $this->shipPo(['greeting' => 'Hallo', 'farewell' => 'Tschüss']);
39 $this->seedOverlay([
40 'greeting' => ['value' => 'Servus', 'original' => 'Hallo', 'local_change' => '2025-05-05T05:05:05Z'],
41 'farewell' => ['value' => 'Tschüss', 'original' => 'Tschüss'],
42 'added_locally' => ['value' => 'Eigene Variable', 'local_change' => '2025-06-06T06:06:06Z'],
43 ]);
44 $this->writeLang('lang/customizing/ilias_de.lang.local', ['pilot#:#greeting#:#Aus Customizing']);
45
46 $this->manager()->insertLanguageForRemovingLocalChanges('de');
47
48 $this->assertSame(
49 [
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 // "remove local changes" leaves no local change: the overlay is removed
57 $this->assertFileDoesNotExist($this->overlayBase() . '.po');
58 $this->assertFileDoesNotExist(MigratedPoFixture::overlayMo($this->overlayBase()));
59 $effective = $this->effective();
60 $this->assertSame(['greeting', 'farewell'], array_keys($effective), 'the locally added variable is gone');
61 $this->assertSame('Hallo', $effective['greeting']['value']);
62 $this->assertFalse($effective['greeting']['local_change']);
63 }
64
71 {
72 $this->shipPo(['greeting' => 'Hallo', 'farewell' => 'Tschüss']);
73 $this->seedOverlay([
74 'greeting' => ['value' => 'Hallo', 'original' => 'Hallo'],
75 'farewell' => ['value' => 'Tschüss', 'original' => 'Tschüss'],
76 ]);
77 $this->db_entries = [self::MODULE => ['greeting' => 'Hallo', 'farewell' => 'Tschüss'], 'common' => ['yes' => 'Ja']];
78 $this->writeLang('lang/customizing/ilias_de.lang.local', ['pilot#:#greeting#:#Grüß Gott']);
79
80 $this->manager()->insertLanguageForApplyingLocalChanges('de');
81
82 $this->assertSame(
83 [
84 'pilot|greeting' => ['value' => 'Grüß Gott', 'local_change' => self::NOW],
85 'pilot|farewell' => ['value' => 'Tschüss', 'local_change' => null],
86 ],
87 $this->lngData()
88 );
89 $this->assertSame(
90 [self::MODULE => ['greeting' => 'Grüß Gott', 'farewell' => 'Tschüss'], 'common' => ['yes' => 'Ja']],
91 $this->lngModules()
92 );
93 $greeting = $this->overlayPo()->find(null, 'greeting');
94 $this->assertSame('Grüß Gott', $greeting->getTranslation());
95 $this->assertSame('Hallo', LocalChangeComments::getOriginal($greeting));
96 $this->assertNotNull(LocalChangeComments::getLocalChange($greeting));
97 }
98
105 {
106 $this->shipPo(['greeting' => 'Hallo', 'farewell' => 'Tschüss', 'obsolete' => 'Alt']);
107 $this->manager()->insertLanguageForInstallation('de');
108 $this->assertSame(
109 ['value' => 'Alt', 'local_change' => null],
110 $this->lngData()['pilot|obsolete'],
111 'sanity: the row exists after the first install'
112 );
113
114 $this->shipPo(['greeting' => 'Servus', 'farewell' => 'Pfiat di']); // 'obsolete' dropped from the shipped po
115 $this->seedOverlay([
116 'greeting' => ['value' => 'Hallo', 'original' => 'Hallo'],
117 'farewell' => ['value' => 'Auf Wiedersehen', 'original' => 'Tschüss', 'local_change' => '2025-04-04T04:04:04Z'],
118 'added_locally' => ['value' => 'Eigene Var', 'local_change' => '2025-03-03T03:03:03Z'],
119 ]);
120 $this->db_local_changes = [self::MODULE => ['farewell' => 'Auf Wiedersehen']];
121 $this->db_entries = [
122 'common' => ['yes' => 'Ja', 'no' => 'Nein'],
123 self::MODULE => ['greeting' => 'Hallo', 'farewell' => 'Tschüss', 'obsolete' => 'Alt'],
124 ];
125
126 $this->manager()->insertLanguageForApplyingLocalChanges('de');
127
128 $this->assertSame(
129 'Servus',
130 $this->lngModules()[self::MODULE]['greeting'],
131 'stored "Hallo", shipped now "Servus" -> "Servus"'
132 );
133 $this->assertSame(
134 'Auf Wiedersehen',
135 $this->lngModules()[self::MODULE]['farewell'],
136 'a real local change (L != S != O) is kept'
137 );
138 $this->assertSame(
139 'Eigene Var',
140 $this->lngModules()[self::MODULE]['added_locally'],
141 'an overlay-only local change is taken over'
142 );
143 $this->assertArrayNotHasKey('obsolete', $this->lngModules()[self::MODULE]);
144 $this->assertSame(
145 ['yes' => 'Ja', 'no' => 'Nein'],
146 $this->lngModules()['common'],
147 'a non-migrated module in the seed stays complete'
148 );
149
150 $this->assertSame(['value' => 'Servus', 'local_change' => null], $this->lngData()['pilot|greeting']);
151 $this->assertSame(
152 ['value' => 'Eigene Var', 'local_change' => '2025-03-03 03:03:03'],
153 $this->lngData()['pilot|added_locally']
154 );
155 $this->assertArrayNotHasKey(
156 'pilot|obsolete',
157 $this->lngData(),
158 'the DELETE ... local_change IS NULL bulk purge took effect: the stale unchanged row is gone'
159 );
160 $this->assertNotEmpty(
161 array_filter(
162 $this->queries,
163 static fn(string $q): bool => str_starts_with($q, 'DELETE FROM lng_data')
164 && str_contains($q, 'local_change IS NULL')
165 && str_contains($q, "identifier NOT IN ('")
166 ),
167 'the bulk delete of unchanged migrated rows was actually issued'
168 );
169 }
170
176 {
177 $this->shipPo(['greeting' => 'Hallo', 'obsolete' => 'Alt']);
178 $this->manager()->insertLanguageForInstallation('de');
179 $this->shipPo(['greeting' => 'Servus']);
180 $already_recorded = count($this->queries);
181
182 $this->manager()->insertLanguageForApplyingLocalChanges('de');
183
184 $statements = [];
185 foreach (array_slice($this->queries, $already_recorded) as $query) {
186 if (str_starts_with($query, 'INSERT INTO lng_data')) {
187 $statements[] = 'write';
188 } elseif (str_starts_with($query, 'DELETE FROM lng_data') && str_contains($query, 'local_change IS NULL')) {
189 $statements[] = 'purge';
190 }
191 }
192 $this->assertSame(['write', 'purge'], array_values(array_unique($statements)));
193 }
194}
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...
testApplyingLocalChangesReconcilesEveryCaseInOneSweepAndPurgesTheStaleUnchangedRow()
The pre-INSERT "DELETE FROM lng_data WHERE ... local_change IS NULL AND module IN (....
testApplyingLocalChangesPutsTheCustomizingValueOnTopOfTheStoredEntries()
Applying local changes reconciles a migrated module with its shipped .po like an update: the not loca...
testApplyingLocalChangesWritesTheRowsBeforeItPurgesTheStaleOnes()
Written first, purged after: an aborted write must leave the former rows, not a gap.
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)
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...