Skip to content

Commit

Permalink
Merge pull request #69 from kasimi/validate-lang
Browse files Browse the repository at this point in the history
Test for missing language files and keys
  • Loading branch information
paul999 authored Dec 16, 2017
2 parents b05ccb0 + 5474cd3 commit 75292f9
Show file tree
Hide file tree
Showing 7 changed files with 288 additions and 0 deletions.
164 changes: 164 additions & 0 deletions src/Tests/Tests/epv_test_validate_languages.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
<?php

/**
*
* EPV :: The phpBB Forum Extension Pre Validator.
*
* @copyright (c) 2017 phpBB Limited <https://www.phpbb.com>
* @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';
}
}
15 changes: 15 additions & 0 deletions tests/testFiles/language/en/additional.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

if (!defined('IN_PHPBB'))
{
exit;
}

if (empty($lang) || !is_array($lang))
{
$lang = array();
}

$lang = array_merge($lang, array(
'C' => 'Third language string',
));
16 changes: 16 additions & 0 deletions tests/testFiles/language/en/common.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

if (!defined('IN_PHPBB'))
{
exit;
}

if (empty($lang) || !is_array($lang))
{
$lang = array();
}

$lang = array_merge($lang, array(
'A' => 'First language string',
'B' => 'Second language string',
));
15 changes: 15 additions & 0 deletions tests/testFiles/language/en_complete/additional.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

if (!defined('IN_PHPBB'))
{
exit;
}

if (empty($lang) || !is_array($lang))
{
$lang = array();
}

$lang = array_merge($lang, array(), array(
'C' => 'Third language string',
));
19 changes: 19 additions & 0 deletions tests/testFiles/language/en_complete/common.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

if (!defined('IN_PHPBB'))
{
exit;
}

if (empty($lang) || !is_array($lang))
{
$lang = array();
}

$lang = array_merge($lang, array(
'A' => 'First language string',
));

$lang = array_merge($lang, array(
'B' => 'Second language string',
));
15 changes: 15 additions & 0 deletions tests/testFiles/language/en_incomplete/common.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

if (!defined('IN_PHPBB'))
{
exit;
}

if (empty($lang) || !is_array($lang))
{
$lang = array();
}

$lang = array_merge($lang, array(
'A' => 'First language string',
));
44 changes: 44 additions & 0 deletions tests/validate_languages_test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

/**
*
* EPV :: The phpBB Forum Extension Pre Validator.
*
* @copyright (c) 2017 phpBB Limited <https://www.phpbb.com>
* @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',
]);
}
}

0 comments on commit 75292f9

Please sign in to comment.