19declare(strict_types=1);
28use PHPUnit\Framework\Attributes\DataProvider;
29use PHPUnit\Framework\TestCase;
37 #[DataProvider('belongsInDeltaCases')]
40 $this->assertSame($expected, MigratedLanguageFileSync::belongsInDelta($shipped_value, $value));
49 'differs from shipped' => [
'Hallo',
'Servus',
true],
50 'same as shipped' => [
'Hallo',
'Hallo',
false],
51 'key not shipped at all - any non-empty value belongs' => [
null,
'Eigener Wert',
true],
52 'empty value resets to shipped, even without a shipped key' => [
null,
'',
false],
53 'empty value resets to shipped, even when shipped differs' => [
'Hallo',
'',
false],
54 'shipped value itself is empty, value matches' => [
'',
'',
false],
55 'shipped value itself is empty, value differs' => [
'',
'x',
true],
56 'the literal string "0" belongs when shipped differs' => [
'1',
'0',
true],
57 'the literal string "0" is not "empty" and does not reset' => [
null,
'0',
true],
65 #[DataProvider('resolveLocalValueCases')]
68 $this->assertSame($expected, MigratedLanguageFileSync::resolveLocalValue($shipped_value, $value));
77 'empty value of a shipped key resolves to the shipped value' => [
'Hallo',
'',
'Hallo'],
78 'empty value of a key that is not shipped stays empty' => [
null,
'',
''],
79 'a non-empty value is returned unchanged, shipped key' => [
'Hallo',
'Servus',
'Servus'],
80 'a non-empty value is returned unchanged, not a shipped key' => [
null,
'Eigener Wert',
'Eigener Wert'],
81 'the shipped value itself is empty - stays empty (nothing to reset to in practice)' => [
'',
'',
''],
91 $bad =
'<script>alert(1)</script>Hallo';
93 $result = MigratedLanguageFileSync::databaseValues(
94 [
'unmodified' => $bad,
'changed' => $bad,
'not_shipped_at_all' => $bad],
95 [
'unmodified' => $bad,
'changed' =>
'Anderer Shipped-Wert']
98 $this->assertStringNotContainsString(
'<script', $result[
'unmodified']);
99 $this->assertStringContainsString(
'Hallo', $result[
'unmodified']);
101 $this->assertSame($bad, $result[
'changed']);
103 $this->assertSame($bad, $result[
'not_shipped_at_all']);
108 $result = MigratedLanguageFileSync::databaseValues([
'greeting' =>
'Hallo'], [
'greeting' =>
'Hallo']);
110 $this->assertSame(
'Hallo', $result[
'greeting']);
115 $bad =
'<script>alert(1)</script>Hallo';
118 $result = MigratedLanguageFileSync::databaseValuesOf(
126 $this->assertStringNotContainsString(
'<script', $result[
'greeting']);
127 $this->assertStringContainsString(
'Hallo', $result[
'greeting']);
133 $bad =
'<script>alert(1)</script>Hallo';
135 $result = MigratedLanguageFileSync::databaseValuesOf(
139 'not_a_contributed_module',
143 $this->assertSame($bad, $result[
'greeting']);
152 file_put_contents($this->
shippedPo(),
"msgid \"kaputt\n");
153 $bad =
'<script>alert(1)</script>Hallo';
155 $result = MigratedLanguageFileSync::databaseValuesOf(
163 $this->assertSame($bad, $result[
'greeting']);
171 $this->assertTrue(MigratedLanguageFileSync::hasOverlay($this->manager,
'de', self::MODULE, $this->client_data_dir));
178 $this->assertFalse(MigratedLanguageFileSync::hasOverlay($this->manager,
'de', self::MODULE, $this->client_data_dir));
186 $this->assertFalse(MigratedLanguageFileSync::hasOverlay($this->manager,
'de',
'other_module', $this->client_data_dir));
187 $this->assertFalse(MigratedLanguageFileSync::hasOverlay($this->manager,
'de', self::MODULE,
null));
199 $this->assertTrue(MigratedLanguageFileSync::hasOverlay($this->manager,
'de', self::MODULE, $this->client_data_dir));
208 $entries = MigratedLanguageFileSync::moduleEntries($catalog,
'itest');
210 $this->assertArrayHasKey(
'greeting', $entries);
211 $this->assertSame(
'Hallo', $entries[
'greeting']->getTranslation());
215 #[DataProvider('foreignContexts')]
221 $entries = MigratedLanguageFileSync::moduleEntries($catalog,
'itest');
223 $this->assertArrayNotHasKey(
'greeting', $entries);
231 return [
'empty-string msgctxt' => [
''],
'context of another module' => [
'other_module']];
235 #[DataProvider('contextOrders')]
241 foreach ($module_first ? [$module_entry, $contextless] : [$contextless, $module_entry] as $entry) {
242 $catalog->add($entry);
245 $entries = MigratedLanguageFileSync::moduleEntries($catalog,
'itest');
247 $this->assertSame(
'Mit Modulkontext', $entries[
'greeting']->getTranslation());
255 return [
'module context first' => [
true],
'contextless first' => [
false]];
264 $entry = MigratedLanguageFileSync::findModuleEntry($catalog,
'itest',
'greeting');
266 $this->assertNotNull($entry);
267 $this->assertSame(
'Hallo', $entry->getTranslation());
276 $entry = MigratedLanguageFileSync::findModuleEntry($catalog,
'itest',
'greeting');
278 $this->assertSame(
'Mit Modulkontext', $entry?->getTranslation());
286 $this->assertNull(MigratedLanguageFileSync::findModuleEntry($catalog,
'itest',
'greeting'));
299 $this->assertNull(MigratedLanguageFileSync::findModuleEntry($catalog,
'itest',
'farewell'));
309 $entries = MigratedLanguageFileSync::loadShippedModuleEntries($this->
shippedPo(), self::MODULE);
311 $this->assertSame(
'Hallo', $entries[
'greeting'][
'value']);
324 $this->
sync([
'greeting' =>
'Hallo']);
326 $this->assertFileDoesNotExist($this->
overlayBase() .
'.po',
'unchanged from the contextless shipped value - no delta at all');
An in-memory gettext catalog (header block plus messages) and the only place of the component that re...
Reads the shipped .po of a migrated module and maintains its per-installation overlay: the ....
Entry rules of MigratedLanguageFileSync: delta/reset decisions, database sanitising,...
testSyncTreatsAContextlessShippedEntryAsThisModulesShippedValue()
End-to-end: sync() reads a contextless shipped entry as the module's shipped value,...
testDatabaseValuesLeavesAnAlreadyAllowedUnmodifiedValueByteIdentical()
testModuleEntriesIncludesAnEntryWithoutAnyContext()
An entry without msgctxt (null) belongs to whichever module reads the catalog.
testFindModuleEntryIgnoresAnEmptyStringContextInsteadOfTreatingItAsContextless()
BUG (documented, not fixed): unlike moduleEntries(), findModuleEntry()'s fallback $catalog->find(null...
testHasOverlayIsFalseForAnUnknownModuleOrWithoutClientDataDir()
testHasOverlayIsTrueWhenEitherOverlayFileExists()
testHasOverlayIsFalseWithoutAnyOverlayFile()
testLoadShippedModuleEntriesIncludesAContextlessEntry()
End-to-end: a contextless shipped entry is served for the module via loadShippedModuleEntries().
testDatabaseValuesSanitizesOnlyAnUnmodifiedValueEqualToTheShippedOne()
databaseValues() sanitises only an unmodified value equal to the shipped one (for the database mirror...
testDatabaseValuesOfLeavesEntriesUnchangedWhenTheShippedPoCannotBeRead()
An unreadable shipped .po must not abort the database write: $entries come back unchanged (the overla...
testBelongsInDelta(?string $shipped_value, string $value, bool $expected)
belongsInDelta() alone decides what enters the overlay; tested without sync()'s file I/O.
static resolveLocalValueCases()
testHasOverlayIsTrueEvenForAnIrregularFileAtTheOverlayPosition()
hasOverlay() reports a non-regular file (e.g.
testFindModuleEntryPrefersTheModuleContextEntry()
testModuleEntriesPrefersTheModuleContextEntryOverTheContextlessOne(bool $module_first)
A key with and without context: the module-context entry wins regardless of catalog order.
testResolveLocalValue(?string $shipped_value, string $value, string $expected)
Shared by belongsInDelta() and ilObjLanguageExt::_saveValues(): an empty value resets to the shipped ...
testFindModuleEntryReturnsNullForAForeignContext()
static belongsInDeltaCases()
testDatabaseValuesOfSanitizesAgainstTheRealShippedPo()
testModuleEntriesIgnoresAnEntryOfAForeignContext(string $context)
An entry with an empty msgctxt is a foreign context, not "no context".
testDatabaseValuesOfLeavesEntriesUnchangedWhenTheModuleIsNotMigratedForTheLanguage()
A module not migrated for the language (no shipped .po): $entries come back unchanged.
testFindModuleEntryFallsBackToTheContextlessEntry()
Fixtures of the MigratedLanguageFileSync tests.
sync(array $entries, bool $refresh=false, ?string $client_data_dir='default')
seedOverlay(array $entries, string $lang_key='de')
seedShipped(array $entries, string $lang_key='de', string $module=self::MODULE)
overlayBase(string $lang_key='de')
shippedPo(string $lang_key='de', string $module=self::MODULE)
static writePo(string $file, TranslationCatalog $catalog)
static entry(?string $context, string $id, string $value)
A directory of language files with a path and a prefix for them.