diff --git a/components/ILIAS/Language/tests/AdminGuiReadsValuesFromMigratedFileTest.php b/components/ILIAS/Language/tests/AdminGuiReadsValuesFromMigratedFileTest.php index 0cfc598be465..90bb201a1ff4 100644 --- a/components/ILIAS/Language/tests/AdminGuiReadsValuesFromMigratedFileTest.php +++ b/components/ILIAS/Language/tests/AdminGuiReadsValuesFromMigratedFileTest.php @@ -39,6 +39,8 @@ #[PreserveGlobalState(false)] class AdminGuiReadsValuesFromMigratedFileTest extends ilLanguageBaseTestCase { + use CapturesErrorLog; + private ?string $fixture_directory = null; /** True if this test defined CLIENT_DATA_DIR itself; tearDown() removes only such a root. */ private bool $created_client_data_dir_root = false; @@ -538,10 +540,10 @@ public function testWithoutDirectoryManagerOnlyTheRowsOfModulesOfNoComponentAreS ['module' => 'rep_robj_xtst', 'identifier' => 'hello', 'value' => 'Welt'], ]); - $this->expectOutputRegex('/Without the language file directories the values of module "poll" are not shown/'); $values = ilObjLanguageExt::_getValues('de'); $this->assertSame(['rep_robj_xtst#:#hello' => 'Welt'], $values); + $this->assertLoggedErrorMatches('/Without the language file directories the values of module "poll" are not shown/'); } /** A shipped `.po` that cannot be read: the module is missing (reported), its lng_data rows are not shown instead. */ @@ -558,8 +560,8 @@ public function testAnUnreadableShippedPoHidesTheModuleAndNeverShowsItsLngDataRo ['module' => 'rep_robj_xtst', 'identifier' => 'hello', 'value' => 'Welt'], ]); - $this->expectOutputRegex('/The language file of module "mtest" for "de" cannot be read, its values are not shown/'); $this->assertSame(['rep_robj_xtst#:#hello' => 'Welt'], ilObjLanguageExt::_getValues('de')); + $this->assertLoggedErrorMatches('/The language file of module "mtest" for "de" cannot be read, its values are not shown/'); } public function testGetModulesIncludesAMigratedModuleEvenWithoutAnyLngDataRow(): void diff --git a/components/ILIAS/Language/tests/CapturesErrorLog.php b/components/ILIAS/Language/tests/CapturesErrorLog.php new file mode 100644 index 000000000000..f481b155a239 --- /dev/null +++ b/components/ILIAS/Language/tests/CapturesErrorLog.php @@ -0,0 +1,80 @@ +assertNotFalse($file); + $this->captured_error_log_file = $file; + $this->captured_error_log_was_read = false; + $this->error_log_ini_before_test = (string) ini_get('error_log'); + ini_set('error_log', $file); + } + + /** Whatever was logged and not asserted on is a failure, as unexpected output was before. */ + #[After] + protected function restoreErrorLog(): void + { + $file = $this->captured_error_log_file; + if ($file === null) { + return; + } + $this->captured_error_log_file = null; + try { + $unexpected = $this->captured_error_log_was_read || !is_file($file) ? '' : (string) file_get_contents($file); + } finally { + ini_set('error_log', $this->error_log_ini_before_test); + @unlink($file); + } + if ($unexpected !== '') { + $this->fail('Unexpected error_log(): ' . $unexpected); + } + } + + protected function loggedErrors(): string + { + $this->captured_error_log_was_read = true; + + return $this->captured_error_log_file !== null && is_file($this->captured_error_log_file) + ? (string) file_get_contents($this->captured_error_log_file) + : ''; + } + + protected function assertLoggedErrorMatches(string $pattern): void + { + $this->assertMatchesRegularExpression($pattern, $this->loggedErrors()); + } + + protected function assertNothingLogged(): void + { + $this->assertSame('', $this->loggedErrors()); + } +} diff --git a/components/ILIAS/Language/tests/ComponentTranslation/MigratedLanguageFileSyncEditableTest.php b/components/ILIAS/Language/tests/ComponentTranslation/MigratedLanguageFileSyncEditableTest.php index cd74b4e679f0..8c9ca0048652 100644 --- a/components/ILIAS/Language/tests/ComponentTranslation/MigratedLanguageFileSyncEditableTest.php +++ b/components/ILIAS/Language/tests/ComponentTranslation/MigratedLanguageFileSyncEditableTest.php @@ -28,6 +28,8 @@ */ class MigratedLanguageFileSyncEditableTest extends MigratedLanguageFileSyncTestCase { + use CapturesErrorLog; + /** * @return array|null identifier => value */ @@ -59,8 +61,8 @@ public function testAnUnreadableOverlayYieldsTheShippedPoAloneAndIsReported(): v $this->seedOverlay(['greeting' => 'Moin']); file_put_contents($this->overlayBase() . '.po', "msgid \"kaputt\n"); - $this->expectOutputRegex('/The local changes of module "stest" for "de" cannot be read, the shipped values are used/'); $this->assertSame(['greeting' => 'Hallo'], $this->editable()); + $this->assertLoggedErrorMatches('/The local changes of module "stest" for "de" cannot be read, the shipped values are used/'); } /** For writing, the shipped values alone would drop the other local changes of the overlay: it throws instead. */ @@ -87,16 +89,19 @@ public function testWithoutClientDataDirectoryTheShippedPoAloneIsReturned(): voi $this->seedShipped(['greeting' => 'Hallo']); $this->assertSame(['greeting' => 'Hallo'], $this->editable(null)); - $this->expectOutputString(''); + $this->assertNothingLogged(); } public function testAnUnreadableShippedPoThrows(): void { file_put_contents($this->shippedPo(), "msgid \"kaputt\n"); - $this->expectOutputRegex('/cannot be read/'); - $this->expectException(\RuntimeException::class); - $this->editable(); + try { + $this->editable(); + $this->fail('A RuntimeException was expected.'); + } catch (\RuntimeException) { + $this->assertLoggedErrorMatches('/cannot be read/'); + } } /** diff --git a/components/ILIAS/Language/tests/FuzzyMarkerDefaultLanguageTest.php b/components/ILIAS/Language/tests/FuzzyMarkerDefaultLanguageTest.php index 4fbbdd3d6a18..86b3c8b1f29b 100644 --- a/components/ILIAS/Language/tests/FuzzyMarkerDefaultLanguageTest.php +++ b/components/ILIAS/Language/tests/FuzzyMarkerDefaultLanguageTest.php @@ -22,6 +22,8 @@ /** ilObjLanguageExt::withFuzzyMarkers(): the "not translated yet" marker falls back to the default language. */ class FuzzyMarkerDefaultLanguageTest extends ilLanguageBaseTestCase { + use CapturesErrorLog; + /** @var list */ private array $queries = []; @@ -62,11 +64,11 @@ private function markersWith(string $user_text, ?string $default_text): array public function testAMarkerMissingInTheUserLanguageComesFromTheDefaultLanguage(): void { // "lng" is shipped: the default language comes from the build (none here), never from lng_data - $this->expectOutputRegex('/There is no build of the language files/'); $comments = $this->markersWith('-lng_not_translated-', 'Not translated en'); $this->assertSame(['lng.#:#.a' => 'note - -lng_not_translated-', 'lng.#:#.b' => '-lng_not_translated-'], $comments); $this->assertSame([], $this->queries); + $this->assertLoggedErrorMatches('/There is no build of the language files/'); } public function testAMarkerOfTheUserLanguageIsUsedWithoutFurtherQuery(): void @@ -79,10 +81,10 @@ public function testAMarkerOfTheUserLanguageIsUsedWithoutFurtherQuery(): void public function testAMarkerMissingEverywhereStaysTheMissingKeyMarker(): void { - $this->expectOutputRegex('/There is no build of the language files/'); $comments = $this->markersWith('-lng_not_translated-', null); $this->assertSame('-lng_not_translated-', $comments['lng.#:#.b']); $this->assertSame('note - -lng_not_translated-', $comments['lng.#:#.a']); + $this->assertLoggedErrorMatches('/There is no build of the language files/'); } } diff --git a/components/ILIAS/Language/tests/PoMigrationLoadLanguageModuleTest.php b/components/ILIAS/Language/tests/PoMigrationLoadLanguageModuleTest.php index ec8cdae2cf4c..6fbb0edabdd1 100644 --- a/components/ILIAS/Language/tests/PoMigrationLoadLanguageModuleTest.php +++ b/components/ILIAS/Language/tests/PoMigrationLoadLanguageModuleTest.php @@ -50,6 +50,8 @@ public function module(): string #[PreserveGlobalState(false)] class PoMigrationLoadLanguageModuleTest extends ilLanguageBaseTestCase { + use CapturesErrorLog; + protected function setUp(): void { parent::setUp(); @@ -288,8 +290,8 @@ public function testAShippedModuleWithoutABuildHasNoIdentifiersAndIsReported(): ); // a shipped module (lang/poll.pot) without a build: no identifiers, never the database - $this->expectOutputRegex('/There is no build of the language files/'); $this->assertSame([], MigratedPoFixture::servedTexts('poll', 'de', false)); + $this->assertLoggedErrorMatches('/There is no build of the language files/'); } /** "Migrated for a language" means a shipped `.po` exists: its absence yields no identifiers, not a missing overlay `.mo`. */ @@ -305,8 +307,8 @@ public function testNoIdentifiersAndNoReportWhenNoShippedPoExistsForThisLanguage public function testAShippedModuleIsRecognisedByItsTemplateWithoutADirectoryManager(): void { // tos is recognised by its shipped template without the component graph; there is no build - $this->expectOutputRegex('/There is no build of the language files/'); $this->assertSame([], $this->callLoadFromMigratedLanguageFile('tos', 'de')); + $this->assertLoggedErrorMatches('/There is no build of the language files/'); } /** @@ -409,10 +411,10 @@ public function testACachedModuleOfAShippedModuleWithoutABuildIsNeitherServedNor ['tos' => ['tos_agreement' => 'FROM THE lng_modules CACHE']] ); - $this->expectOutputRegex('/There is no build of the language files/'); $language->loadLanguageModule('tos'); $this->assertSame('-tos_agreement-', $language->txt('tos_agreement')); + $this->assertLoggedErrorMatches('/There is no build of the language files/'); } /** Setup/CLI: no build, no component graph - a LanguageIdentifier of a shipped module has no value and never reaches lng_data/lng_modules. */ @@ -421,8 +423,8 @@ public function testAnIdentifierOfAShippedModuleWithoutBuildAndComponentGraphIsN $this->setGlobalVariable('ilDB', $this->databaseThatMustNotBeQueried()); $language = $this->buildLanguageWithoutRunningConstructor('de', 'en'); - $this->expectOutputRegex('/There is no build of the language files/'); $this->assertSame('-tos_agreement-', $language->txt(PoMigrationLoadLanguageModuleTestTosIdentifier::AGREEMENT)); + $this->assertLoggedErrorMatches('/There is no build of the language files/'); } /** The prefix index is built per manager: another manager (e.g. a test or a re-initialised container) is never answered from the old one; the first directory of a prefix wins. */ diff --git a/components/ILIAS/Language/tests/ShippedModulesNeverReadFromDatabaseTest.php b/components/ILIAS/Language/tests/ShippedModulesNeverReadFromDatabaseTest.php index 6bf1607c766e..e9c5b7c482f1 100644 --- a/components/ILIAS/Language/tests/ShippedModulesNeverReadFromDatabaseTest.php +++ b/components/ILIAS/Language/tests/ShippedModulesNeverReadFromDatabaseTest.php @@ -36,6 +36,8 @@ #[PreserveGlobalState(false)] class ShippedModulesNeverReadFromDatabaseTest extends ilLanguageBaseTestCase { + use CapturesErrorLog; + private const string LANG_DIRECTORY = 'components/Vendor/Comp/lang/'; private string $root; @@ -118,7 +120,6 @@ public function testTheOverlayOfIliasIniIsServedAfterTheSetupCreatedBuildAndClie $this->buildShippedModule(false); $this->forbidDatabase(); - $this->expectOutputRegex('/There is no build of the language files/'); $this->assertSame('-greeting-', ilLanguage::_lookupEntry('de', 'zzs', 'greeting'), 'fresh installation: no client directory, no build'); $this->buildShippedModule(); @@ -130,6 +131,7 @@ public function testTheOverlayOfIliasIniIsServedAfterTheSetupCreatedBuildAndClie MigratedTranslations::reset(); $this->assertSame('Overlay', ilLanguage::_lookupEntry('de', 'zzs', 'greeting')); + $this->assertLoggedErrorMatches('/There is no build of the language files/'); } public function testTheClientDataDirectoryIsResolvedOncePerResetAlsoWhenMissing(): void @@ -202,8 +204,8 @@ public function testWithoutABuildAShippedModuleHasNoValuesAndTheMissingBuildIsRe $this->buildShippedModule(false); $this->forbidDatabase(); - $this->expectOutputRegex('/There is no build of the language files/'); $this->assertSame('-greeting-', ilLanguage::_lookupEntry('de', 'zzs', 'greeting')); + $this->assertLoggedErrorMatches('/There is no build of the language files/'); } public function testALanguageWithAPoButNotInTheBuildIsReportedAsNotBuilt(): void @@ -211,8 +213,8 @@ public function testALanguageWithAPoButNotInTheBuildIsReportedAsNotBuilt(): void $this->buildShippedModule(true, true); $this->forbidDatabase(); - $this->expectOutputRegex('/ships a language file for "fr" that is not built/'); $this->assertSame('-greeting-', ilLanguage::_lookupEntry('fr', 'zzs', 'greeting')); + $this->assertLoggedErrorMatches('/ships a language file for "fr" that is not built/'); } public function testALanguageWithoutAPoHasNoValuesAndNoReport(): void @@ -221,7 +223,7 @@ public function testALanguageWithoutAPoHasNoValuesAndNoReport(): void $this->forbidDatabase(); $this->assertSame('-greeting-', ilLanguage::_lookupEntry('it', 'zzs', 'greeting')); - $this->expectOutputString(''); + $this->assertNothingLogged(); } /** @@ -239,7 +241,7 @@ public function testANotInstalledOrInvalidLanguageHasNoValuesNoReportAndNoDataba $this->forbidDatabase(['en']); $this->assertSame('-greeting-', ilLanguage::_lookupEntry($lang_key, 'zzs', 'greeting')); - $this->expectOutputString(''); + $this->assertNothingLogged(); } public function testAModuleOfNoComponentIsStillReadFromTheDatabase(): void