ILIAS  trunk Revision v13.0_alpha-62-g3b72d83f2e5
ShippedPoModulesConsistencyTest.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22use PHPUnit\Framework\Attributes\DataProvider;
23use PHPUnit\Framework\TestCase;
24
29class ShippedPoModulesConsistencyTest extends TestCase
30{
31 private const string CALL = '/new\s+\\\\?(?:\w+\\\\)*ComponentLanguageFileDirectory\s*\‍(/';
32 private const string CONTRIBUTION = '/new\s+\\\\?(?:\w+\\\\)*ComponentLanguageFileDirectory\s*\‍(\s*\$this\s*,\s*([\'"])([A-Za-z0-9_]+)\1'
33 . '(?:\s*,\s*([\'"])([^\'"]*)\3(?:\s*,\s*(?:null|([\'"])([^\'"]*)\5))?)?\s*,?\s*\‍)/i';
34
35 private static function root(): string
36 {
37 return (string) realpath(__DIR__ . '/../../../../../');
38 }
39
41 private static function code(string $file): string
42 {
43 $code = '';
44 foreach (token_get_all((string) file_get_contents($file)) as $token) {
45 if (!is_array($token) || !in_array($token[0], [T_COMMENT, T_DOC_COMMENT], true)) {
46 $code .= is_array($token) ? $token[1] : $token;
47 }
48 }
49
50 return $code;
51 }
52
57 private static function contributions(): array
58 {
59 static $cache = null;
60 if ($cache !== null) {
61 return $cache;
62 }
63 $modules = [];
64 $unparsed = [];
65 foreach (glob(self::root() . '/components/*/*/*.php') ?: [] as $file) {
66 if (basename($file, '.php') !== basename(dirname($file))) {
67 continue;
68 }
69 $code = self::code($file);
70 $calls = preg_match_all(self::CALL, $code);
71 $parsed = preg_match_all(self::CONTRIBUTION, $code, $matches, PREG_SET_ORDER);
72 if ($calls !== $parsed) {
73 $unparsed[] = substr($file, strlen(self::root()) + 1);
74 }
75 foreach ($matches as $match) {
76 $module = $match[2];
77 $path = ($match[4] ?? '') === '' ? 'lang/' : $match[4];
78 $modules[$module][] = [
79 'file' => $file,
80 'lang_dir' => dirname($file) . '/' . trim($path, '/'),
81 'pattern' => ($match[6] ?? '') === '' ? $module . '_%s' : $match[6],
82 ];
83 }
84 }
85 ksort($modules);
86
87 return $cache = ['modules' => $modules, 'unparsed' => $unparsed];
88 }
89
91 public static function moduleProvider(): array
92 {
93 $cases = [];
94 foreach (array_keys(self::contributions()['modules']) as $module) {
95 $cases[$module] = [$module];
96 }
97
98 return $cases;
99 }
100
101 public function testContributionsAreFound(): void
102 {
103 self::assertGreaterThan(60, count(self::contributions()['modules']));
104 self::assertSame([], self::contributions()['unparsed'], 'ComponentLanguageFileDirectory with arguments other than string literals');
105 }
106
107 public function testEveryShippedPotIsContributed(): void
108 {
109 $contributed = [];
110 foreach (self::contributions()['modules'] as $module => $contributions) {
111 foreach ($contributions as $contribution) {
112 $contributed[$contribution['lang_dir'] . '/' . MigratedLanguageFilePaths::templateFileName($contribution['pattern'], $module)] = true;
113 }
114 }
115 $uncontributed = [];
116 foreach (glob(self::root() . '/components/*/*/lang/*.pot') ?: [] as $pot) {
117 if (!isset($contributed[$pot])) {
118 $uncontributed[] = substr($pot, strlen(self::root()) + 1);
119 }
120 }
121
122 self::assertSame([], $uncontributed, '.pot without ComponentLanguageFileDirectory contribution');
123 }
124
125 #[DataProvider('moduleProvider')]
126 public function testModuleIsContributedExactlyOnce(string $module): void
127 {
128 $contributions = self::contributions()['modules'][$module];
129 self::assertCount(1, $contributions, "module '$module' contributed more than once: " . json_encode(array_column($contributions, 'file')));
130 }
131
132 #[DataProvider('moduleProvider')]
133 public function testModuleShipsPotAndEnglishPo(string $module): void
134 {
135 foreach (self::contributions()['modules'][$module] as ['lang_dir' => $dir, 'pattern' => $pattern]) {
136 self::assertFileExists("$dir/" . MigratedLanguageFilePaths::templateFileName($pattern, $module), "module '$module': .pot missing");
137 self::assertFileExists("$dir/" . sprintf($pattern, 'en') . '.po', "module '$module': English .po missing");
138 }
139 }
140
141 #[DataProvider('moduleProvider')]
143 {
144 $offending = [];
145 foreach (glob(self::root() . '/lang/ilias_*.lang') ?: [] as $file) {
146 foreach (new SplFileObject($file) as $number => $line) {
147 if (str_starts_with((string) $line, $module . '#:#')) {
148 $offending[] = basename($file) . ':' . ($number + 1);
149 }
150 }
151 }
152
153 self::assertSame([], $offending, "module '$module' still has lines in lang/ilias_*.lang");
154 }
155}
The single place that knows where the files of a module migrated to PO/MO live:
Every module a component contributes via ComponentLanguageFileDirectory ships its ....
if(!isset($arguments[0])) if(count( $arguments) > 3) if(! $remove_from_lang &&isset($arguments[2]) &&!is_dir($arguments[2])) $module
$token
Definition: xapitoken.php:69