From 70d216dfebcbb347b742830164170b86cd8cfad8 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Mon, 25 Sep 2023 01:12:31 +0000 Subject: [PATCH] test fixes (NO_CI) --- Helper/ModuleTestTrait.php | 770 ------------------------------------- 1 file changed, 770 deletions(-) delete mode 100644 Helper/ModuleTestTrait.php diff --git a/Helper/ModuleTestTrait.php b/Helper/ModuleTestTrait.php deleted file mode 100644 index 7d316df..0000000 --- a/Helper/ModuleTestTrait.php +++ /dev/null @@ -1,770 +0,0 @@ -markTestSkipped( - 'The TestModule is not tested' - ); - } - - $this->app = new class() extends ApplicationAbstract - { - protected string $appName = 'Api'; - }; - - $this->app->dbPool = $GLOBALS['dbpool']; - $this->app->router = new WebRouter(); - $this->app->dispatcher = new Dispatcher($this->app); - $this->app->appSettings = new CoreSettings(); - $this->app->moduleManager = new ModuleManager($this->app, __DIR__ . '/../../Modules/'); - $this->app->sessionManager = new HttpSession(0); - $this->app->accountManager = new AccountManager($this->app->sessionManager); - $this->app->eventManager = new EventManager($this->app->dispatcher); - $this->app->l11nManager = new L11nManager(); - } - - /** - * @group admin - * @slowThreshold 5000 - * @group module - */ - public function testModuleIntegration() : void - { - $iResponse = new HttpResponse(); - $iRequest = new HttpRequest(new HttpUri('')); - $iRequest->header->account = 1; - $iRequest->setData('status', ModuleStatusUpdateType::INSTALL); - - $iRequest->setData('module', self::NAME); - $this->app->moduleManager->get('Admin')->apiModuleStatusUpdate($iRequest, $iResponse); - - $iRequest->setData('status', ModuleStatusUpdateType::DEACTIVATE, true); - $this->app->moduleManager->get('Admin')->apiModuleStatusUpdate($iRequest, $iResponse); - self::assertFalse($this->app->moduleManager->isActive(self::NAME), 'Module "' . self::NAME . '" is not active.'); - - $iRequest->setData('status', ModuleStatusUpdateType::ACTIVATE, true); - $this->app->moduleManager->get('Admin')->apiModuleStatusUpdate($iRequest, $iResponse); - self::assertTrue($this->app->moduleManager->isActive(self::NAME), 'Module "' . self::NAME . '" is not active.'); - } - - /** - * @group module - * @coversNothing - */ - public function testMembers() : void - { - $module = $this->app->moduleManager->get(self::NAME); - - if ($module::ID === 0) { - return; - } - - self::assertEquals(self::NAME, $module::NAME); - self::assertEquals(\realpath(__DIR__ . '/../../Modules/' . self::NAME), \realpath($module::PATH)); - self::assertGreaterThanOrEqual(0, Version::compare($module::VERSION, '1.0.0')); - } - - /** - * @group module - * @coversNothing - */ - public function testValidMapper() : void - { - $mappers = \glob(__DIR__ . '/../../Modules/' . self::NAME . '/Models/*Mapper.php'); - - foreach ($mappers as $mapper) { - $class = $this->getMapperFromPath($mapper); - $columns = $class::COLUMNS; - - foreach ($columns as $cName => $column) { - if (!\in_array($column['type'], ['int', 'string', 'compress', 'DateTime', 'DateTimeImmutable', 'Json', 'Serializable', 'bool', 'float'])) { - self::assertTrue(false, 'Mapper "' . $class . '" column "' . $cName . '" has invalid type'); - } - - if ($cName !== ($column['name'] ?? false)) { - self::assertTrue(false); - } - } - } - - self::assertTrue(true); - } - - /** - * Get mapper from file path - * - * @param string $mapper Mapper path - * - * @return string - */ - private function getMapperFromPath(string $mapper) : string - { - $name = \substr($mapper, \strlen(__DIR__ . '/../../Modules/' . self::NAME . '/Models/'), -4); - - return '\\Modules\\' . self::NAME . '\\Models\\' . $name; - } - - /** - * @group module - * @coversNothing - */ - public function testMapperAgainstModel() : void - { - $mappers = \glob(__DIR__ . '/../../Modules/' . self::NAME . '/Models/*Mapper.php'); - - foreach ($mappers as $mapper) { - $class = $this->getMapperFromPath($mapper); - if ($class === '\Modules\Admin\Models\ModuleMapper' - || !Autoloader::exists(\substr($class, 0, -6)) - ) { - continue; - } - - $columns = $class::COLUMNS; - $ownsOne = $class::OWNS_ONE; - - $classReflection = new \ReflectionClass(\substr($class, 0, -6)); - $defaultProperties = $classReflection->getDefaultProperties(); - - $invalidAcessors = []; - - foreach ($columns as $cName => $column) { - $isArray = false; - // testing existence of member variable in model - if (\stripos($column['internal'], '/') !== false) { - $column['internal'] = \explode('/', $column['internal'])[0]; - $isArray = true; - } - - if (!$classReflection->hasProperty($column['internal'])) { - self::assertTrue(false, 'Mapper "' . $class . '" column "' . $cName . '" has missing/invalid internal/member'); - } - - $property = $classReflection->getProperty($column['internal']) ?? null; - if ($property === null || (!$property->isPublic() && (!isset($column['private']) || !$column['private']))) { - $invalidAcessors[] = $column['internal']; - } - - // testing correct mapper/model variable type definition - $property = $defaultProperties[$column['internal']] ?? null; - if (!($property === null /* not every value is allowed to be null but this just has to be correctly implemented in the mapper, no additional checks for this case! */ - || (\is_string($property) && ($column['type'] === 'string' || $column['type'] === 'compress')) - || (\is_int($property) && $column['type'] === 'int') - || (\is_array($property) && ($column['type'] === 'Json' || $column['type'] === 'Serializable' || $isArray)) - || (\is_bool($property) && $column['type'] === 'bool') - || (\is_float($property) && $column['type'] === 'float') - || ($property instanceof \DateTime && $column['type'] === 'DateTime') - || ($property instanceof \DateTimeImmutable && $column['type'] === 'DateTimeImmutable') - || (isset($ownsOne[$column['internal']]) && $column['type'] === 'int') // if it is in ownsOne it can be a different type because it is a reference! - )) { - self::assertTrue(false, 'Mapper "' . $class . '" column "' . $cName . '" has invalid type compared to model definition'); - } - } - - if (!empty($invalidAcessors)) { - self::assertTrue(false, 'Mapper "' . $class . '" must define private for "' . \implode(',', $invalidAcessors) . '" or make them public (recommended) in the model'); - } - - // test hasMany variable exists in model - $rel = $class::HAS_MANY; - foreach ($rel as $pName => $def) { - $property = $classReflection->getProperty($pName) ?? null; - if (!\array_key_exists($pName, $defaultProperties) && $property === null) { - self::assertTrue(false, 'Mapper "' . $class . '" property "' . $pName . '" doesn\'t exist in model'); - } - } - } - - self::assertTrue(true); - } - - /** - * @group module - * @coversNothing - */ - public function testValidDbSchema() : void - { - $schemaPath = __DIR__ . '/../../Modules/' . self::NAME . '/Admin/Install/db.json'; - - if (!\is_file($schemaPath)) { - self::assertTrue(true); - - return; - } - - $db = \json_decode(\file_get_contents($schemaPath), true); - - foreach ($db as $name => $table) { - if ($name !== ($table['name'] ?? false)) { - self::assertTrue(false, 'Schema "' . $schemaPath . '" name "' . $name . '" is invalid'); - } - - foreach ($table['fields'] as $cName => $column) { - if ($cName !== ($column['name'] ?? false)) { - self::assertTrue(false, 'Schema "' . $schemaPath . '" name "' . $cName . '" is invalid'); - } - - if (!(\stripos($column['type'] ?? '', 'TINYINT') === 0 - || \stripos($column['type'] ?? '', 'SMALLINT') === 0 - || \stripos($column['type'] ?? '', 'INT') === 0 - || \stripos($column['type'] ?? '', 'BIGINT') === 0 - || \stripos($column['type'] ?? '', 'VARCHAR') === 0 - || \stripos($column['type'] ?? '', 'VARBINARY') === 0 - || \stripos($column['type'] ?? '', 'TEXT') === 0 - || \stripos($column['type'] ?? '', 'LONGTEXT') === 0 - || \stripos($column['type'] ?? '', 'BLOB') === 0 - || \stripos($column['type'] ?? '', 'DATETIME') === 0 - || \stripos($column['type'] ?? '', 'DECIMAL') === 0 - )) { - self::assertTrue(false, 'Schema "' . $schemaPath . '" type "' . ($column['type'] ?? '') . '" is a missing/invalid type'); - } - } - } - - $dbTemplate = \json_decode(\file_get_contents(__DIR__ . '/../../phpOMS/DataStorage/Database/tableDefinition.json'), true); - self::assertTrue(Json::validateTemplate($dbTemplate, $db), 'Invalid db template for ' . self::NAME); - } - - /** - * @group module - * @coversNothing - */ - public function testDbSchemaAgainstDb() : void - { - $builder = new SchemaBuilder($this->app->dbPool->get()); - $tables = $builder->selectTables()->execute()->fetchAll(\PDO::FETCH_COLUMN); - - $schemaPath = __DIR__ . '/../../Modules/' . self::NAME . '/Admin/Install/db.json'; - - if (!\is_file($schemaPath)) { - self::assertTrue(true); - - return; - } - - $db = \json_decode(\file_get_contents($schemaPath), true); - - foreach ($db as $name => $table) { - if (!\in_array($name, $tables)) { - self::assertTrue(false, 'Table ' . $name . ' doesn\'t exist in the database.'); - } - - $field = new SchemaBuilder($this->app->dbPool->get()); - $fields = $field->selectFields($name)->execute()->fetchAll(); - - foreach ($table['fields'] as $cName => $column) { - if (!ArrayUtils::inArrayRecursive($cName, $fields)) { - self::assertTrue(false, 'Couldn\'t find "' . $cName . '" in "' . $name . '"'); - } - } - } - - self::assertTrue(true); - } - - /** - * @group module - * @coversNothing - */ - public function testMapperAgainstDbSchema() : void - { - $schemaPath = __DIR__ . '/../../Modules/' . self::NAME . '/Admin/Install/db.json'; - $mappers = \glob(__DIR__ . '/../../Modules/' . self::NAME . '/Models/*Mapper.php'); - - if (!\is_file($schemaPath)) { - self::assertTrue(true); - - return; - } - - $db = \json_decode(\file_get_contents($schemaPath), true); - - foreach ($mappers as $mapper) { - $class = $this->getMapperFromPath($mapper); - - if (\defined('self::MAPPER_TO_IGNORE') && \in_array(\ltrim($class, '\\'), self::MAPPER_TO_IGNORE) - || empty($class::COLUMNS) - || $class === '\Modules\Admin\Models\ModuleMapper' - ) { - continue; - } - - $table = $class::TABLE; - $columns = $class::COLUMNS; - - foreach ($columns as $cName => $column) { - // testing existence of field name in schema - if (!isset($db[$table]['fields'][$cName])) { - self::assertTrue(false, 'Mapper "' . $class . '" column "' . $cName . '" doesn\'t match schema'); - } - - // testing schema/mapper same column data type - if (!(($column['type'] === 'string' - && (\stripos($db[$table]['fields'][$cName]['type'], 'VARCHAR') === 0 - || \stripos($db[$table]['fields'][$cName]['type'], 'VARBINARY') === 0 - || \stripos($db[$table]['fields'][$cName]['type'], 'BLOB') === 0 - || \stripos($db[$table]['fields'][$cName]['type'], 'TEXT') === 0 - || \stripos($db[$table]['fields'][$cName]['type'], 'LONGTEXT') === 0)) - || ($column['type'] === 'int' - && (\stripos($db[$table]['fields'][$cName]['type'], 'TINYINT') === 0 - || \stripos($db[$table]['fields'][$cName]['type'], 'SMALLINT') === 0 - || \stripos($db[$table]['fields'][$cName]['type'], 'INT') === 0 - || \stripos($db[$table]['fields'][$cName]['type'], 'BIGINT') === 0)) - || ($column['type'] === 'Json' - && (\stripos($db[$table]['fields'][$cName]['type'], 'VARCHAR') === 0 - || \stripos($db[$table]['fields'][$cName]['type'], 'LONGTEXT') === 0 - || \stripos($db[$table]['fields'][$cName]['type'], 'TEXT') === 0)) - || ($column['type'] === 'compress' - && (\stripos($db[$table]['fields'][$cName]['type'], 'BLOB') === 0)) - || ($column['type'] === 'Serializable') - || ($column['type'] === 'bool' && \stripos($db[$table]['fields'][$cName]['type'], 'TINYINT') === 0) - || ($column['type'] === 'float' && \stripos($db[$table]['fields'][$cName]['type'], 'DECIMAL') === 0) - || ($column['type'] === 'DateTime' && \stripos($db[$table]['fields'][$cName]['type'], 'DATETIME') === 0) - || ($column['type'] === 'DateTimeImmutable' && \stripos($db[$table]['fields'][$cName]['type'], 'DATETIME') === 0) - )) { - self::assertTrue(false, 'Schema "' . $schemaPath . '" type "' . ($column['type'] ?? '') . '" is incompatible with mapper "' . $class . '" definition "' . $db[$table]['fields'][$cName]['type'] . '" for field "' . $cName . '"'); - } - } - - // testing schema/mapper same primary key definition - $primary = $class::PRIMARYFIELD; - if (!($db[$table]['fields'][$primary]['primary'] ?? false)) { - self::assertTrue(false, 'Field "' . $primary . '" from mapper "' . $class . '" is not defined as primary key in table "' . $table . '"'); - } - } - - self::assertTrue(true); - } - - /** - * @group module - * @coversNothing - */ - public function testJson() : void - { - $sampleInfo = \json_decode(\file_get_contents(__DIR__ . '/../../Modules/TestModule/info.json'), true); - $infoTemplate = \json_decode(\file_get_contents(__DIR__ . '/../../phpOMS/Module/infoLayout.json'), true); - - $module = $this->app->moduleManager->get(self::NAME); - - if ($module::ID === 0) { - return; - } - - // validate info.json - $info = \json_decode(\file_get_contents($module::PATH . '/info.json'), true); - self::assertTrue($this->infoJsonTest($info, $sampleInfo), 'Info assert failed for '. self::NAME); - self::assertTrue(Json::validateTemplate($infoTemplate, $info), 'Invalid info template for ' . self::NAME); - } - - /** - * @group module - * @coversNothing - */ - public function testDependency() : void - { - $module = $this->app->moduleManager->get(self::NAME); - - if ($module::ID === 0) { - return; - } - - // validate dependency installation - $info = \json_decode(\file_get_contents($module::PATH . '/info.json'), true); - self::assertTrue($this->dependencyTest($info, $this->app->moduleManager->getInstalledModules(false)), 'Invalid dependency configuration in ' . self::NAME); - } - - /** - * @group module - * @coversNothing - */ - public function testRoutes() : void - { - $this->app->moduleManager = new ModuleManager($this->app, __DIR__ . '/../../Modules/'); - $totalBackendRoutes = \is_file(__DIR__ . '/../../Web/Backend/Routes.php') ? include __DIR__ . '/../../Web/Backend/Routes.php' : []; - $totalApiRoutes = \is_file(__DIR__ . '/../../Web/Api/Routes.php') ? include __DIR__ . '/../../Web/Api/Routes.php' : []; - - $module = $this->app->moduleManager->get(self::NAME); - - if ($module::ID === 0) { - return; - } - - // test routes - if (\is_file($module::PATH . '/Admin/Routes/Web/Backend.php')) { - $moduleRoutes = include $module::PATH . '/Admin/Routes/Web/Backend.php'; - self::assertEquals(1, $this->routesTest($moduleRoutes, $totalBackendRoutes), 'Backend route assert failed for '. self::NAME); - } - - // test routes - if (\is_file($module::PATH . '/Admin/Routes/Web/Api.php')) { - $moduleRoutes = include $module::PATH . '/Admin/Routes/Web/Api.php'; - self::assertEquals(1, $this->routesTest($moduleRoutes, $totalApiRoutes), 'Api route assert failed for '. self::NAME); - } - } - - /** - * @group module - * @coversNothing - */ - public function testHooks() : void - { - $this->app->moduleManager = new ModuleManager($this->app, __DIR__ . '/../../Modules/'); - $totalBackendHooks = \is_file(__DIR__ . '/../../Web/Backend/Hooks.php') ? include __DIR__ . '/../../Web/Backend/Hooks.php' : []; - $totalApiHooks = \is_file(__DIR__ . '/../../Web/Api/Hooks.php') ? include __DIR__ . '/../../Web/Api/Hooks.php' : []; - - $module = $this->app->moduleManager->get(self::NAME); - - if ($module::ID === 0) { - return; - } - - // test hooks - if (\is_file($module::PATH . '/Admin/Hooks/Web/Backend.php')) { - $moduleHooks = include $module::PATH . '/Admin/Hooks/Web/Backend.php'; - self::assertEquals(1, $this->hooksTest($moduleHooks, $totalBackendHooks), 'Backend hook assert failed for '. self::NAME); - } - - // test hooks - if (\is_file($module::PATH . '/Admin/Hooks/Web/Api.php')) { - $moduleHooks = include $module::PATH . '/Admin/Hooks/Web/Api.php'; - self::assertEquals(1, $this->hooksTest($moduleHooks, $totalApiHooks), 'Api hook assert failed for '. self::NAME); - } - } - - /** - * @group final - * @group module - * @coversNothing - */ - public function testNavigation() : void - { - $module = $this->app->moduleManager->get(self::NAME); - - if ($module::ID === 0 - || $this->app->moduleManager->get('Navigation')::ID === 0 - || !\is_file($module::PATH . '/Admin/Install/Navigation.install.json') - ) { - return; - } - - // test if navigation db entries match json files - self::assertTrue( - $this->navLinksTest( - $this->app->dbPool->get(), - \json_decode( - \file_get_contents($module::PATH . '/Admin/Install/Navigation.install.json'), - true - ), - self::NAME - ) - ); - } - - /** - * Test if all navigation links are in the database - * - * @param mixed $db Database connection - * @param array $links Navigation links from install file - * @param string $module Module name - * - * @return bool - */ - private function navLinksTest($db, array $links, string $module) : bool - { - $query = new Builder($db); - $query->select('nav_id')->from('nav')->where('nav_from', '=', $module); - - $result = $query->execute()->fetchAll(\PDO::FETCH_COLUMN); - $it = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($links)); - - foreach ($it as $link) { - if (\is_array($link) - && !\in_array($link['id'], $result) - ) { - return false; - } - } - - return true; - } - - /** - * Test if all dependencies got installed - * - * @param array $info Module info array/file - * @param array $modules Installed modules - * - * @return bool - */ - private function dependencyTest(array $info, array $modules) : bool - { - foreach ($info['dependencies'] as $module => $version) { - if (!isset($modules[$module])) { - return false; - } - } - - return true; - } - - /** - * Test if route destinations exist (in the *Controller and global application route file) - * - * @param array $module Routes of the module from the respective app and module route file - * @param array $total Routing file of the respective application which contains all app routes - * - * @return int - */ - private function routesTest(array $module, array $total) : int - { - foreach ($module as $route => $dests) { - // test route existence after installation - if (!isset($total[$route])) { - return -1; - } - - // test route class - foreach ($dests as $verb) { - $parts = \explode(':', $verb['dest']); - $path = __DIR__ . '/../../' . \ltrim(\strtr($parts[0], '\\', '/'), '/') . '.php'; - if (!\is_file($path)) { - return -2; - } - - // test route method - $content = \file_get_contents($path); - if (\stripos($content, 'function ' . $parts[\count($parts) - 1]) === false - && \strpos($parts[\count($parts) - 1], 'Trait') === false - ) { - return -3; - } - } - } - - return 1; - } - - /** - * Test if hook destinations exist (in the *Controller and global application hook file) - * - * @param array $module Hooks of the module from the respective app and module hook file - * @param array $total Routing file of the respective application which contains all app routes - * - * @return int - */ - private function hooksTest(array $module, array $total) : int - { - foreach ($module as $route => $dests) { - if (!isset($total[$route])) { - return -1; - } - - // test route class - foreach ($dests['callback'] as $callback) { - $parts = \explode(':', $callback); - $path = __DIR__ . '/../../' . \ltrim(\strtr($parts[0], '\\', '/'), '/') . '.php'; - if (!\is_file($path)) { - return -2; - } - - // test route method - $content = \file_get_contents($path); - if (\stripos($content, 'function ' . $parts[\count($parts) - 1]) === false) { - return -3; - } - } - } - - return 1; - } - - /** - * Test if the module info file has the correct types - * - * @param array $module Module info file - * @param array $samle Sample info file (as basis for checking the data types) - * - * @return bool - */ - private function infoJsonTest(array $module, array $sample) : bool - { - try { - if (\gettype($module['name']['id']) === \gettype($sample['name']['id']) - && \gettype($module['name']['internal']) === \gettype($sample['name']['internal']) - && \gettype($module['name']['external']) === \gettype($sample['name']['external']) - && \gettype($module['category']) === \gettype($sample['category']) - && \gettype($module['version']) === \gettype($sample['version']) - && \gettype($module['requirements']) === \gettype($sample['requirements']) - && \gettype($module['creator']) === \gettype($sample['creator']) - && \gettype($module['creator']['name']) === \gettype($sample['creator']['name']) - && \gettype($module['description']) === \gettype($sample['description']) - && \gettype($module['directory']) === \gettype($sample['directory']) - && \gettype($module['dependencies']) === \gettype($sample['dependencies']) - && \gettype($module['providing']) === \gettype($sample['providing']) - && \gettype($module['load']) === \gettype($sample['load']) - ) { - return true; - } - } catch (\Throwable $_) { - return false; - } - - return false; - } - - /** - * @group module - * @coversNothing - */ - public function testRequestLoads() : void - { - if (!\defined('self::URI_LOAD') || empty(self::URI_LOAD)) { - return; - } - - $request = new HttpRequest(new HttpUri(self::URI_LOAD)); - $request->createRequestHashs(2); - - $loaded = $this->app->moduleManager->getUriLoad($request); - - $found = false; - foreach ($loaded[4] as $module) { - if ($module['module_load_file'] === self::NAME) { - $found = true; - break; - } - } - - self::assertTrue($found, 'Module ' . self::NAME . ' is not loaded at ' . self::URI_LOAD . '. The module info.json file ("load" section for type: 4 loads) and the routing files paths should match.'); - self::assertGreaterThan(0, \count($this->app->moduleManager->getLanguageFiles($request))); - self::assertTrue(\in_array(self::NAME, $this->app->moduleManager->getRoutedModules($request))); - - $this->app->moduleManager->initRequestModules($request); - self::assertTrue($this->app->moduleManager->isRunning(self::NAME)); - } - - /** - * @group module - * @coversNothing - */ - public function testLanguage() : void - { - $module = $this->app->moduleManager->get(self::NAME); - if ($module::ID === 0) { - return; - } - - $required = ['en', 'de']; - if (!\is_dir($module::PATH . '/Theme')) { - return; - } - - $themes = \scandir($module::PATH . '/Theme'); - if ($themes === false) { - return; - } - - foreach ($themes as $theme) { - if ($theme === '.' || $theme === '..' || !\is_dir($module::PATH . '/Theme/' . $theme . '/Lang')) { - continue; - } - - $langKeys = []; - - $langFiles = \scandir($module::PATH . '/Theme/' . $theme . '/Lang'); - foreach ($langFiles as $file) { - if ($file === '.' || $file === '..' || \stripos($file, '.lang.') === false) { - continue; - } - - $parts = \explode('.', $file); - $type = ''; - - $type = \strlen($parts[0]) === 2 ? '' : $parts[0]; - - if (!isset($langKeys[$type])) { - $langKeys[$type] = [ - 'required' => null, - 'keys' => [], - ]; - } - - // check if required lanugages files exist IFF any language file of a specific type exists - if ($langKeys[$type]['required'] === null) { - $langKeys[$type]['required'] = true; - - $missingLanguages = []; - foreach ($required as $lang) { - if (!\in_array(($type !== '' ? $type . '.' : '') . $lang . '.lang.php', $langFiles)) { - $langKeys[$type]['required'] = false; - $missingLanguages[] = $lang; - } - } - - if (!empty($missingLanguages)) { - self::assertTrue(false, 'The language files "' . \implode(', ', $missingLanguages) . '" are missing with type "' . $type . '".'); - } - } - - // compare key equality - // every key in the key list must be present in all other language files of the same type and vice versa - $langArray = include $module::PATH . '/Theme/' . $theme . '/Lang/' . $file; - $langArray = \reset($langArray); - - $keys = \array_keys($langArray); - - if (empty($langKeys[$type]['keys'])) { - $langKeys[$type]['keys'] = $keys; - } elseif (!empty($diff1 = \array_diff($langKeys[$type]['keys'], $keys)) - || !empty($diff2 = \array_diff($keys, $langKeys[$type]['keys']))) { - self::assertTrue(false, $file . ': The language keys "' . \implode(', ', \array_merge($diff1, $diff2)) . '" are different.'); - } - } - } - - self::assertTrue(true); - } -}