4
0
Эх сурвалжийг харах

Add tests on i18n classes (#4756)

Alexis Degrugillier 3 жил өмнө
parent
commit
1f4e347cae

+ 7 - 1
cli/i18n/I18nCompletionValidator.php

@@ -16,6 +16,12 @@ class I18nCompletionValidator implements I18nValidatorInterface {
 	}
 
 	public function displayReport() {
+		if ($this->passEntries > $this->totalEntries) {
+			throw new \RuntimeException('The number of translated strings cannot be higher than the number of strings');
+		}
+		if ($this->totalEntries === 0) {
+			return 'There is no data.' . PHP_EOL;
+		}
 		return sprintf('Translation is %5.1f%% complete.', $this->passEntries / $this->totalEntries * 100) . PHP_EOL;
 	}
 
@@ -27,7 +33,7 @@ class I18nCompletionValidator implements I18nValidatorInterface {
 		foreach ($this->reference as $file => $data) {
 			foreach ($data as $refKey => $refValue) {
 				$this->totalEntries++;
-				if (!array_key_exists($refKey, $this->language[$file])) {
+				if (!array_key_exists($file, $this->language) || !array_key_exists($refKey, $this->language[$file])) {
 					$this->result .= "Missing key $refKey" . PHP_EOL;
 					continue;
 				}

+ 2 - 9
cli/i18n/I18nFile.php

@@ -3,16 +3,9 @@
 require_once __DIR__ . '/I18nValue.php';
 
 class I18nFile {
-
-	private $i18nPath;
-
-	public function __construct() {
-		$this->i18nPath = __DIR__ . '/../../app/i18n';
-	}
-
 	public function load() {
 		$i18n = array();
-		$dirs = new DirectoryIterator($this->i18nPath);
+		$dirs = new DirectoryIterator(I18N_PATH);
 		foreach ($dirs as $dir) {
 			if ($dir->isDot()) {
 				continue;
@@ -32,7 +25,7 @@ class I18nFile {
 
 	public function dump(array $i18n) {
 		foreach ($i18n as $language => $file) {
-			$dir = $this->i18nPath . DIRECTORY_SEPARATOR . $language;
+			$dir = I18N_PATH . DIRECTORY_SEPARATOR . $language;
 			if (!file_exists($dir)) {
 				mkdir($dir);
 			}

+ 6 - 0
cli/i18n/I18nUsageValidator.php

@@ -16,6 +16,12 @@ class I18nUsageValidator implements I18nValidatorInterface {
 	}
 
 	public function displayReport() {
+		if ($this->failedEntries > $this->totalEntries) {
+			throw new \RuntimeException('The number of unused strings cannot be higher than the number of strings');
+		}
+		if ($this->totalEntries === 0) {
+			return 'There is no data.' . PHP_EOL;
+		}
 		return sprintf('%5.1f%% of translation keys are unused.', $this->failedEntries / $this->totalEntries * 100) . PHP_EOL;
 	}
 

+ 2 - 0
cli/manipulate.translation.php

@@ -3,6 +3,8 @@
 
 require_once __DIR__ . '/i18n/I18nData.php';
 require_once __DIR__ . '/i18n/I18nFile.php';
+require_once __DIR__ . '/../constants.php';
+
 
 $options = getopt("a:hk:l:o:rv:");
 

+ 1 - 0
constants.php

@@ -16,6 +16,7 @@ define('INDEX_PATH', PUBLIC_PATH . PUBLIC_TO_INDEX_PATH);
 define('PUBLIC_RELATIVE', '..');
 define('LIB_PATH', FRESHRSS_PATH . '/lib');
 define('APP_PATH', FRESHRSS_PATH . '/app');
+define('I18N_PATH', APP_PATH . '/i18n');
 define('CORE_EXTENSIONS_PATH', LIB_PATH . '/core-extensions');
 define('TESTS_PATH', FRESHRSS_PATH . '/tests');
 //</Not customisable>

+ 143 - 0
tests/cli/i18n/I18nCompletionValidatorTest.php

@@ -0,0 +1,143 @@
+<?php
+
+require_once __DIR__ . '/../../../cli/i18n/I18nCompletionValidator.php';
+require_once __DIR__ . '/../../../cli/i18n/I18nValue.php';
+
+class I18nCompletionValidatorTest extends PHPUnit\Framework\TestCase {
+	private $value;
+
+	public function setUp(): void {
+		$this->value = $this->getMockBuilder(I18nValue::class)
+			->disableOriginalConstructor()
+			->getMock();
+	}
+
+	public function testDisplayReport() {
+		$validator = new I18nCompletionValidator([], []);
+
+		$this->assertEquals("There is no data.\n", $validator->displayReport());
+
+		$reflectionTotalEntries = new ReflectionProperty(I18nCompletionValidator::class, 'totalEntries');
+		$reflectionTotalEntries->setAccessible(true);
+		$reflectionTotalEntries->setValue($validator, 100);
+
+		$this->assertEquals("Translation is   0.0% complete.\n", $validator->displayReport());
+
+		$reflectionPassEntries = new ReflectionProperty(I18nCompletionValidator::class, 'passEntries');
+		$reflectionPassEntries->setAccessible(true);
+		$reflectionPassEntries->setValue($validator, 25);
+
+		$this->assertEquals("Translation is  25.0% complete.\n", $validator->displayReport());
+
+		$reflectionPassEntries->setValue($validator, 100);
+
+		$this->assertEquals("Translation is 100.0% complete.\n", $validator->displayReport());
+
+		$reflectionPassEntries->setValue($validator, 200);
+
+		$this->expectException(\RuntimeException::class);
+		$this->expectExceptionMessage('The number of translated strings cannot be higher than the number of strings');
+		$validator->displayReport();
+	}
+
+	public function testValidateWhenNoData() {
+		$validator = new I18nCompletionValidator([], []);
+		$this->assertTrue($validator->validate());
+		$this->assertEquals('', $validator->displayResult());
+	}
+
+	public function testValidateWhenKeyIsMissing() {
+		$validator = new I18nCompletionValidator([
+			'file1.php' => [
+				'file1.l1.l2.k1' => $this->value,
+			],
+			'file2.php' => [
+				'file2.l1.l2.k1' => $this->value,
+			],
+		], []);
+
+		$this->assertFalse($validator->validate());
+		$this->assertEquals("Missing key file1.l1.l2.k1\nMissing key file2.l1.l2.k1\n", $validator->displayResult());
+	}
+
+	public function testValidateWhenKeyIsIgnored() {
+		$this->value->expects($this->exactly(2))
+			->method('isIgnore')
+			->willReturn(true);
+
+		$validator = new I18nCompletionValidator([
+			'file1.php' => [
+				'file1.l1.l2.k1' => $this->value,
+			],
+			'file2.php' => [
+				'file2.l1.l2.k1' => $this->value,
+			],
+		], [
+			'file1.php' => [
+				'file1.l1.l2.k1' => $this->value,
+			],
+			'file2.php' => [
+				'file2.l1.l2.k1' => $this->value,
+			],
+		]);
+
+		$this->assertTrue($validator->validate());
+		$this->assertEquals('', $validator->displayResult());
+	}
+
+	public function testValidateWhenValueIsEqual() {
+		$this->value->expects($this->exactly(2))
+			->method('isIgnore')
+			->willReturn(false);
+		$this->value->expects($this->exactly(2))
+			->method('equal')
+			->willReturn(true);
+
+		$validator = new I18nCompletionValidator([
+			'file1.php' => [
+				'file1.l1.l2.k1' => $this->value,
+			],
+			'file2.php' => [
+				'file2.l1.l2.k1' => $this->value,
+			],
+		], [
+			'file1.php' => [
+				'file1.l1.l2.k1' => $this->value,
+			],
+			'file2.php' => [
+				'file2.l1.l2.k1' => $this->value,
+			],
+		]);
+
+		$this->assertFalse($validator->validate());
+		$this->assertEquals("Untranslated key file1.l1.l2.k1 - \nUntranslated key file2.l1.l2.k1 - \n", $validator->displayResult());
+	}
+
+	public function testValidateWhenValueIsDifferent() {
+		$this->value->expects($this->exactly(2))
+			->method('isIgnore')
+			->willReturn(false);
+		$this->value->expects($this->exactly(2))
+			->method('equal')
+			->willReturn(false);
+
+		$validator = new I18nCompletionValidator([
+			'file1.php' => [
+				'file1.l1.l2.k1' => $this->value,
+			],
+			'file2.php' => [
+				'file2.l1.l2.k1' => $this->value,
+			],
+		], [
+			'file1.php' => [
+				'file1.l1.l2.k1' => $this->value,
+			],
+			'file2.php' => [
+				'file2.l1.l2.k1' => $this->value,
+			],
+		]);
+
+		$this->assertTrue($validator->validate());
+		$this->assertEquals('', $validator->displayResult());
+	}
+}

+ 38 - 0
tests/cli/i18n/I18nFileTest.php

@@ -0,0 +1,38 @@
+<?php
+
+require_once __DIR__ . '/../../../cli/i18n/I18nFile.php';
+
+class I18nFileTest extends PHPUnit\Framework\TestCase {
+	public function test() {
+		$before = $this->computeFilesHash();
+
+		$file = new I18nFile();
+		$data = $file->load();
+		$file->dump($data);
+
+		$after = $this->computeFilesHash();
+
+		$this->assertEquals($before, $after);
+	}
+
+	private function computeFilesHash() {
+		$hashes = [];
+
+		$dirs = new DirectoryIterator(I18N_PATH);
+		foreach ($dirs as $dir) {
+			if ($dir->isDot()) {
+				continue;
+			}
+			$files = new DirectoryIterator($dir->getPathname());
+			foreach ($files as $file) {
+				if (!$file->isFile()) {
+					continue;
+				}
+
+				$hashes[$file->getPathName()] = sha1_file($file->getPathName());
+			}
+		}
+
+		return $hashes;
+	}
+}

+ 122 - 0
tests/cli/i18n/I18nUsageValidatorTest.php

@@ -0,0 +1,122 @@
+<?php
+
+require_once __DIR__ . '/../../../cli/i18n/I18nValue.php';
+require_once __DIR__ . '/../../../cli/i18n/I18nUsageValidator.php';
+
+class I18nUsageValidatorTest extends PHPUnit\Framework\TestCase {
+	private $value;
+
+	public function setUp(): void {
+		$this->value = $this->getMockBuilder(I18nValue::class)
+			->disableOriginalConstructor()
+			->getMock();
+	}
+
+	public function testDisplayReport() {
+		$validator = new I18nUsageValidator([], []);
+
+		$this->assertEquals("There is no data.\n", $validator->displayReport());
+
+		$reflectionTotalEntries = new ReflectionProperty(I18nUsageValidator::class, 'totalEntries');
+		$reflectionTotalEntries->setAccessible(true);
+		$reflectionTotalEntries->setValue($validator, 100);
+
+		$this->assertEquals("  0.0% of translation keys are unused.\n", $validator->displayReport());
+
+		$reflectionFailedEntries = new ReflectionProperty(I18nUsageValidator::class, 'failedEntries');
+		$reflectionFailedEntries->setAccessible(true);
+		$reflectionFailedEntries->setValue($validator, 25);
+
+		$this->assertEquals(" 25.0% of translation keys are unused.\n", $validator->displayReport());
+
+		$reflectionFailedEntries->setValue($validator, 100);
+
+		$this->assertEquals("100.0% of translation keys are unused.\n", $validator->displayReport());
+
+		$reflectionFailedEntries->setValue($validator, 200);
+
+		$this->expectException(\RuntimeException::class);
+		$this->expectExceptionMessage('The number of unused strings cannot be higher than the number of strings');
+		$validator->displayReport();
+	}
+
+	public function testValidateWhenNoData() {
+		$validator = new I18nUsageValidator([], []);
+		$this->assertTrue($validator->validate());
+		$this->assertEquals('', $validator->displayResult());
+	}
+
+	public function testValidateWhenParentKeyExistsWithoutTransformation() {
+		$validator = new I18nUsageValidator([
+			'file1' => [
+				'file1.l1.l2._' => $this->value,
+			],
+			'file2' => [
+				'file2.l1.l2._' => $this->value,
+			],
+		], [
+			'file1.l1.l2._',
+			'file2.l1.l2._',
+		]);
+		$this->assertTrue($validator->validate());
+		$this->assertEquals('', $validator->displayResult());
+	}
+
+	public function testValidateWhenParentKeyExistsWithTransformation() {
+		$validator = new I18nUsageValidator([
+			'file1' => [
+				'file1.l1.l2._' => $this->value,
+			],
+			'file2' => [
+				'file2.l1.l2._' => $this->value,
+			],
+		], [
+			'file1.l1.l2',
+			'file2.l1.l2',
+		]);
+		$this->assertTrue($validator->validate());
+		$this->assertEquals('', $validator->displayResult());
+	}
+
+	public function testValidateWhenParentKeyDoesNotExist() {
+		$validator = new I18nUsageValidator([
+			'file1' => [
+				'file1.l1.l2._' => $this->value,
+			],
+			'file2' => [
+				'file2.l1.l2._' => $this->value,
+			],
+		], []);
+		$this->assertFalse($validator->validate());
+		$this->assertEquals("Unused key file1.l1.l2._ - \nUnused key file2.l1.l2._ - \n", $validator->displayResult());
+	}
+
+	public function testValidateWhenChildKeyExists() {
+		$validator = new I18nUsageValidator([
+			'file1' => [
+				'file1.l1.l2.k1' => $this->value,
+			],
+			'file2' => [
+				'file2.l1.l2.k1' => $this->value,
+			],
+		], [
+			'file1.l1.l2.k1',
+			'file2.l1.l2.k1',
+		]);
+		$this->assertTrue($validator->validate());
+		$this->assertEquals('', $validator->displayResult());
+	}
+
+	public function testValidateWhenChildKeyDoesNotExist() {
+		$validator = new I18nUsageValidator([
+			'file1' => [
+				'file1.l1.l2.k1' => $this->value,
+			],
+			'file2' => [
+				'file2.l1.l2.k1' => $this->value,
+			],
+		], []);
+		$this->assertFalse($validator->validate());
+		$this->assertEquals("Unused key file1.l1.l2.k1 - \nUnused key file2.l1.l2.k1 - \n", $validator->displayResult());
+	}
+}

+ 83 - 0
tests/cli/i18n/I18nValueTest.php

@@ -0,0 +1,83 @@
+<?php
+
+require_once __DIR__ . '/../../../cli/i18n/I18nValue.php';
+
+class I18nValueTest extends PHPUnit\Framework\TestCase {
+	public function testConstructorWithoutState() {
+		$value = new I18nValue('some value');
+		$this->assertEquals('some value', $value->getValue());
+		$this->assertFalse($value->isIgnore());
+		$this->assertFalse($value->isTodo());
+	}
+
+	public function testConstructorWithUnknownState() {
+		$value = new I18nValue('some value -> unknown');
+		$this->assertEquals('some value', $value->getValue());
+		$this->assertFalse($value->isIgnore());
+		$this->assertFalse($value->isTodo());
+	}
+
+	public function testConstructorWithTodoState() {
+		$value = new I18nValue('some value -> todo');
+		$this->assertEquals('some value', $value->getValue());
+		$this->assertFalse($value->isIgnore());
+		$this->assertTrue($value->isTodo());
+	}
+
+	public function testConstructorWithIgnoreState() {
+		$value = new I18nValue('some value -> ignore');
+		$this->assertEquals('some value', $value->getValue());
+		$this->assertTrue($value->isIgnore());
+		$this->assertFalse($value->isTodo());
+	}
+
+	public function testClone() {
+		$value = new I18nValue('some value');
+		$clonedValue = clone $value;
+		$this->assertEquals('some value', $value->getValue());
+		$this->assertEquals('some value', $clonedValue->getValue());
+		$this->assertFalse($value->isIgnore());
+		$this->assertFalse($clonedValue->isIgnore());
+		$this->assertFalse($value->isTodo());
+		$this->assertTrue($clonedValue->isTodo());
+	}
+
+	public function testEqualWhenValueIsIdentical() {
+		$value = new I18nValue('some value');
+		$clonedValue = clone $value;
+		$this->assertTrue($value->equal($clonedValue));
+		$this->assertTrue($clonedValue->equal($value));
+	}
+
+	public function testEqualWhenValueIsDifferent() {
+		$value = new I18nValue('some value');
+		$otherValue = new I18nValue('some other value');
+		$this->assertFalse($value->equal($otherValue));
+		$this->assertFalse($otherValue->equal($value));
+	}
+
+	public function testStates() {
+		$reflectionProperty = new ReflectionProperty(I18nValue::class, 'state');
+		$reflectionProperty->setAccessible(true);
+
+		$value = new I18nValue('some value');
+		$this->assertNull($reflectionProperty->getValue($value));
+		$value->markAsDirty();
+		$this->assertEquals('dirty', $reflectionProperty->getValue($value));
+		$value->unmarkAsIgnore();
+		$this->assertEquals('dirty', $reflectionProperty->getValue($value));
+		$value->markAsIgnore();
+		$this->assertEquals('ignore', $reflectionProperty->getValue($value));
+		$value->unmarkAsIgnore();
+		$this->assertNull($reflectionProperty->getValue($value));
+		$value->markAsTodo();
+		$this->assertEquals('todo', $reflectionProperty->getValue($value));
+	}
+
+	public function testToString() {
+		$value = new I18nValue('some value');
+		$this->assertEquals('some value', $value->__toString());
+		$value->markAsTodo();
+		$this->assertEquals('some value -> todo', $value->__toString());
+	}
+}