19declare(strict_types=1);
35 public function testRemoveOverlayRunsAndRemovesTheLockFileWhenOnlyTheLockFileIsLeft(): void
38 mkdir(dirname($overlay_base), 0775,
true);
41 MigratedLanguageFileSync::removeOverlay($this->manager,
'de', self::MODULE, $this->client_data_dir);
50 public function testANestedRemoveOverlayInsideWithOverlayLockDoesNotDeleteTheLockFileOrRetry(): void
53 mkdir(dirname($overlay_base), 0775,
true);
54 file_put_contents($overlay_base .
'.po',
'PO');
66 $this->assertTrue($result[
'finished'],
'a nested removeOverlay() must return instead of hanging: ' . $result[
'output']);
67 $this->assertSame(0, $result[
'exit_code'], $result[
'output']);
69 clearstatcache(
true, $overlay_base .
'.po');
72 $this->assertFileDoesNotExist($overlay_base .
'.po',
'the nested removeOverlay() must still actually remove the overlay files');
74 $this->assertStringContainsString(
75 'AFTER_NESTED:' . $lock_inode_before .
"\n",
77 'a removeOverlay() nested inside an already-held lock must NOT delete or replace the lock file - the outer caller still relies on it'
83 'the lock file must stay the exact same one throughout - no retry/replace cycle for the nested call'
87 private function nestedRemoveOverlayChildCode(): string
96 require_once $root .
'/vendor/composer/vendor/autoload.php';
99 public function getPrefix(): string
103 public function getPath(): string
107 public function getSuffix(): string
111 public function isLocal(): bool
117 $client_data_dir = __CLIENT_DATA_DIR__;
119 MigratedLanguageFileSync::withOverlayLock(
124 function () use ($manager, $client_data_dir):
void {
125 MigratedLanguageFileSync::removeOverlay($manager,
'de',
'stest', $client_data_dir);
127 $lock = @lstat($client_data_dir .
'/lang/stest/de/lock');
128 echo
'AFTER_NESTED:' . ($lock ===
false ?
'missing' : $lock[
'ino']) .
"\n";
135 [
'__ROOT__',
'__CLIENT_DATA_DIR__'],
136 [var_export(ILIAS_ABSOLUTE_PATH,
true), var_export($this->client_data_dir,
true)],
144 public function testASyncAfterRemoveOverlayCreatesANewLockFile(): void
146 $relative_path =
'components/ILIAS/Language/tests/ComponentTranslation/tmp-lock-shipped-' . bin2hex(random_bytes(4)) .
'/';
159 MigratedLanguageFileSync::sync($manager, ILIAS_ABSOLUTE_PATH,
'de', self::MODULE, [
'greeting' =>
'Moin'], $this->client_data_dir);
160 $this->assertFileExists(
MigratedPoFixture::overlayLock($overlay_base),
'sync() must leave a .lock file behind for later callers to serialize on');
162 MigratedLanguageFileSync::removeOverlay($manager,
'de', self::MODULE, $this->client_data_dir);
165 MigratedLanguageFileSync::sync($manager, ILIAS_ABSOLUTE_PATH,
'de', self::MODULE, [
'greeting' =>
'Servus'], $this->client_data_dir);
Reads the shipped .po of a migrated module and maintains its per-installation overlay: the ....
Fixture of the MigratedLanguageFileSyncLock*Test classes: a throwaway client data directory and the h...
runPhpWithTimeout(string $code, float $timeout_seconds)
Runs $code in a child PHP process and kills it after $timeout_seconds, so a regression that deadlocks...
static removeShippedDirectory(string $relative_path)
static directory(string $prefix, string $path, bool $local=false)
static catalog(string $module, array $entries)
static overlayLock(string $base_path)
static overlayMo(string $base_path)
The compiled catalog of the revision current names; a non-existing path if there is none.
static writeShippedPo(string $relative_path, string $module, string $lang_key, TranslationCatalog $catalog)
Writes $catalog as the shipped .po: a module only counts as migrated while that exists.
static overlayBase(string $client_data_dir, LanguageFileDirectory $directory, string $lang_key)
The overlay of $directory's module for $lang_key without the .po extension.
static writeOverlayRevision(string $base_path, TranslationCatalog $catalog)
Compiles $catalog into a revision of the overlay $base_path and points current to it,...
A directory of language files with a path and a prefix for them.