From ee19459f01c79cf099cfcfb073b0ff89e14f1589 Mon Sep 17 00:00:00 2001 From: kasimi Date: Thu, 2 Nov 2017 12:21:36 +0100 Subject: [PATCH 1/2] Test for missing language files and keys --- .../Tests/epv_test_validate_languages.php | 163 ++++++++++++++++++ tests/testFiles/language/en/additional.php | 15 ++ tests/testFiles/language/en/common.php | 16 ++ .../language/en_complete/additional.php | 15 ++ .../testFiles/language/en_complete/common.php | 19 ++ .../language/en_incomplete/common.php | 15 ++ tests/validate_languages_test.php | 44 +++++ 7 files changed, 287 insertions(+) create mode 100644 src/Tests/Tests/epv_test_validate_languages.php create mode 100644 tests/testFiles/language/en/additional.php create mode 100644 tests/testFiles/language/en/common.php create mode 100644 tests/testFiles/language/en_complete/additional.php create mode 100644 tests/testFiles/language/en_complete/common.php create mode 100644 tests/testFiles/language/en_incomplete/common.php create mode 100644 tests/validate_languages_test.php diff --git a/src/Tests/Tests/epv_test_validate_languages.php b/src/Tests/Tests/epv_test_validate_languages.php new file mode 100644 index 0000000..f1d1fdf --- /dev/null +++ b/src/Tests/Tests/epv_test_validate_languages.php @@ -0,0 +1,163 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + */ + +namespace Phpbb\Epv\Tests\Tests; + +use Phpbb\Epv\Output\OutputInterface; +use Phpbb\Epv\Tests\BaseTest; +use PhpParser\Error; +use PhpParser\Node\Expr\Array_; +use PhpParser\Node\Expr\ArrayItem; +use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\FuncCall; +use PhpParser\Node\Scalar\String_; +use PhpParser\Parser; +use PhpParser\ParserFactory; + +class epv_test_validate_languages extends BaseTest +{ + /** + * @var Parser + */ + private $parser; + + /** + * @param bool $debug if debug is enabled + * @param OutputInterface $output + * @param string $basedir + * @param string $namespace + * @param boolean $titania + * @param string $opendir + */ + public function __construct($debug, OutputInterface $output, $basedir, $namespace, $titania, $opendir) + { + parent::__construct($debug, $output, $basedir, $namespace, $titania, $opendir); + + $this->directory = true; + $this->parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); + } + + /** + * @param array $files + * + * @return void + */ + public function validateDirectory(array $files) + { + $langs = []; + $expected_keys = []; + $expected_files = []; + + foreach ($files as $file) + { + if (preg_match('#^' . $this->basedir . 'language/([a-z_]+?)/(.+\.php)$#', $file, $matches) === 1) + { + $language = $matches[1]; // language, e.g. "en" + $relative_filename = $matches[2]; // file name relative to language's base dir, e.g. "info_acp_ext.php" + $expected_files[$relative_filename] = $relative_filename; + + try + { + $keys = $this->load_language_keys($file); + $langs[$language][$relative_filename] = $keys; + + $lang_keys = isset($expected_keys[$relative_filename]) ? $expected_keys[$relative_filename] : []; + $expected_keys[$relative_filename] = array_unique(array_merge($lang_keys, $keys)); + } + catch (Error $e) + { + $this->output->addMessage(OutputInterface::FATAL, 'PHP parse error in file ' . $file->getSaveFilename() . '. Message: ' . $e->getMessage()); + } + } + } + + foreach ($langs as $lang_name => $file_contents) + { + // Check for missing language files + foreach (array_diff($expected_files, array_keys($file_contents)) as $missing_file) + { + $this->output->addMessage(OutputInterface::NOTICE, sprintf("Language %s is missing the language file %s", $lang_name, $missing_file)); + } + + // Check for missing language keys + foreach ($file_contents as $relative_filename => $present_keys) + { + foreach (array_diff($expected_keys[$relative_filename], $present_keys) as $missing_key) + { + $this->output->addMessage(OutputInterface::WARNING, sprintf("Language file %s/%s is missing the language key %s", $lang_name, $relative_filename, $missing_key)); + } + } + } + } + + /** + * This method scans through all global-scoped calls to array_merge + * and extracts all string keys of all array arguments. + * + * @param string $filename File name to a phpBB language file + * @return array + * @throws Error + */ + protected function load_language_keys($filename) + { + $contents = @file_get_contents($filename); + + $keys = []; + + $nodes = $this->parser->parse($contents); + + foreach ($nodes as $node) + { + if ($node instanceof Assign && $node->expr instanceof FuncCall) + { + /** @var FuncCall $expr */ + $expr = $node->expr; + + if ($expr->name->getFirst() === 'array_merge') + { + for ($i = 1; $i < sizeof($expr->args); $i++) + { + /** @var Array_ $array */ + $array = $expr->args[$i]->value; + + if (!($array instanceof Array_)) + { + throw new Error(sprintf('Expected argument %d of array_merge() to be %s, got %s', $i + 1, Array_::class, get_class($array)), $array->getLine()); + } + + foreach ($array->items as $item) + { + /** @var ArrayItem $item */ + if ($item->key instanceof String_) + { + $keys[] = $item->key->value; + } + else + { + $this->output->addMessage(OutputInterface::NOTICE, 'Language key is not a string value in ' . substr($filename, strlen($this->basedir)) . ' on line ' . $item->key->getLine()); + } + } + } + } + } + } + + return $keys; + } + + /** + * + * @return String + */ + public function testName() + { + return 'Test languages'; + } +} diff --git a/tests/testFiles/language/en/additional.php b/tests/testFiles/language/en/additional.php new file mode 100644 index 0000000..49b2731 --- /dev/null +++ b/tests/testFiles/language/en/additional.php @@ -0,0 +1,15 @@ + 'Third language string', +)); diff --git a/tests/testFiles/language/en/common.php b/tests/testFiles/language/en/common.php new file mode 100644 index 0000000..74cb615 --- /dev/null +++ b/tests/testFiles/language/en/common.php @@ -0,0 +1,16 @@ + 'First language string', + 'B' => 'Second language string', +)); diff --git a/tests/testFiles/language/en_complete/additional.php b/tests/testFiles/language/en_complete/additional.php new file mode 100644 index 0000000..9e7249e --- /dev/null +++ b/tests/testFiles/language/en_complete/additional.php @@ -0,0 +1,15 @@ + 'Third language string', +)); diff --git a/tests/testFiles/language/en_complete/common.php b/tests/testFiles/language/en_complete/common.php new file mode 100644 index 0000000..1b48fc2 --- /dev/null +++ b/tests/testFiles/language/en_complete/common.php @@ -0,0 +1,19 @@ + 'First language string', +)); + +$lang = array_merge($lang, array( + 'B' => 'Second language string', +)); diff --git a/tests/testFiles/language/en_incomplete/common.php b/tests/testFiles/language/en_incomplete/common.php new file mode 100644 index 0000000..173c06b --- /dev/null +++ b/tests/testFiles/language/en_incomplete/common.php @@ -0,0 +1,15 @@ + 'First language string', +)); diff --git a/tests/validate_languages_test.php b/tests/validate_languages_test.php new file mode 100644 index 0000000..c9d84e7 --- /dev/null +++ b/tests/validate_languages_test.php @@ -0,0 +1,44 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + */ + +use Phpbb\Epv\Output\OutputInterface; +use Phpbb\Epv\Tests\Tests\epv_test_validate_languages; + +class validate_languages_test extends PHPUnit_Framework_TestCase +{ + public static function setUpBeforeClass() + { + require_once('./tests/Mock/Output.php'); + } + + public function test_languages() { + /** @var OutputInterface|PHPUnit_Framework_MockObject_MockObject $output */ + $output = $this->getMock('Phpbb\Epv\Output\OutputInterface'); + + $output + ->expects($this->exactly(2)) + ->method('addMessage') + ->withConsecutive( + [OutputInterface::NOTICE, 'Language en_incomplete is missing the language file additional.php'], + [OutputInterface::WARNING, 'Language file en_incomplete/common.php is missing the language key B'] + ) + ; + + $tester = new epv_test_validate_languages(false, $output, 'tests/testFiles/', 'epv/test', false, 'tests/testFiles/'); + $tester->validateDirectory([ + 'tests/testFiles/language/en/common.php', + 'tests/testFiles/language/en/additional.php', + 'tests/testFiles/language/en_complete/common.php', + 'tests/testFiles/language/en_complete/additional.php', + 'tests/testFiles/language/en_incomplete/common.php', + ]); + } +} From 5474cd395396cbfe1b64df8acbad5335afeb61ab Mon Sep 17 00:00:00 2001 From: kasimi Date: Thu, 2 Nov 2017 12:27:07 +0100 Subject: [PATCH 2/2] Updated copyright --- src/Tests/Tests/epv_test_validate_languages.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Tests/Tests/epv_test_validate_languages.php b/src/Tests/Tests/epv_test_validate_languages.php index f1d1fdf..affc15a 100644 --- a/src/Tests/Tests/epv_test_validate_languages.php +++ b/src/Tests/Tests/epv_test_validate_languages.php @@ -1,10 +1,11 @@ - * @license GNU General Public License, version 2 (GPL-2.0) + * @copyright (c) 2017 phpBB Limited + * @license GNU General Public License, version 2 (GPL-2.0) * */