Skip to content

Improved the lint:xliff command #26682

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bundle\FrameworkBundle\Tests\Command;

use PHPUnit\Framework\TestCase;
use Symfony\Bundle\FrameworkBundle\Command\XliffLintCommand;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Application as BaseApplication;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\HttpKernel\KernelInterface;

/**
* Tests the part of the XliffLintCommand managed by the FrameworkBundle. The
* rest of the features are tested in the Translation component.
*
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
*/
class XliffLintCommandTest extends TestCase
{
private $files;

public function testGetHelp()
{
$command = new XliffLintCommand();
$expected = <<<EOF
The <info>%command.name%</info> command lints a XLIFF file and outputs to STDOUT
the first encountered syntax error.

You can validates XLIFF contents passed from STDIN:

<info>cat filename | php %command.full_name%</info>

You can also validate the syntax of a file:

<info>php %command.full_name% filename</info>

Or of a whole directory:

<info>php %command.full_name% dirname</info>
<info>php %command.full_name% dirname --format=json</info>

Or find all files in a bundle:

<info>php %command.full_name% @AcmeDemoBundle</info>

EOF;

$this->assertEquals($expected, $command->getHelp());
}

public function testLintFilesFromBundleDirectory()
{
$tester = $this->createCommandTester($this->getKernelAwareApplicationMock());
$tester->execute(
array('filename' => '@AppBundle/Resources'),
array('verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false)
);

$this->assertEquals(0, $tester->getStatusCode(), 'Returns 0 in case of success');
$this->assertContains('[OK] All 0 XLIFF files contain valid syntax', trim($tester->getDisplay()));
}

/**
* @return CommandTester
*/
private function createCommandTester($application = null)
{
if (!$application) {
$application = new BaseApplication();
$application->add(new XliffLintCommand());
}

$command = $application->find('lint:xliff');

if ($application) {
$command->setApplication($application);
}

return new CommandTester($command);
}

private function getKernelAwareApplicationMock()
{
$kernel = $this->getMockBuilder(KernelInterface::class)
->disableOriginalConstructor()
->getMock();

$kernel
->expects($this->once())
->method('locateResource')
->with('@AppBundle/Resources')
->willReturn(sys_get_temp_dir().'/xliff-lint-test');

$application = $this->getMockBuilder(Application::class)
->disableOriginalConstructor()
->getMock();

$application
->expects($this->once())
->method('getKernel')
->willReturn($kernel);

$application
->expects($this->once())
->method('getHelperSet')
->willReturn(new HelperSet());

$application
->expects($this->any())
->method('getDefinition')
->willReturn(new InputDefinition());

$application
->expects($this->once())
->method('find')
->with('lint:xliff')
->willReturn(new XliffLintCommand());

return $application;
}

protected function setUp()
{
@mkdir(sys_get_temp_dir().'/xliff-lint-test');
$this->files = array();
}

protected function tearDown()
{
foreach ($this->files as $file) {
if (file_exists($file)) {
unlink($file);
}
}
rmdir(sys_get_temp_dir().'/xliff-lint-test');
}
}
44 changes: 34 additions & 10 deletions src/Symfony/Component/Translation/Command/XliffLintCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ protected function execute(InputInterface $input, OutputInterface $output)

private function validate($content, $file = null)
{
$errors = array();

// Avoid: Warning DOMDocument::loadXML(): Empty string supplied as input
if ('' === trim($content)) {
return array('file' => $file, 'valid' => true);
Expand All @@ -110,22 +112,33 @@ private function validate($content, $file = null)

$document = new \DOMDocument();
$document->loadXML($content);
if ($document->schemaValidate(__DIR__.'/../Resources/schemas/xliff-core-1.2-strict.xsd')) {
return array('file' => $file, 'valid' => true);

if (null !== $targetLanguage = $this->getTargetLanguageFromFile($document)) {
$expectedFileExtension = sprintf('%s.xlf', str_replace('-', '_', $targetLanguage));
$realFileExtension = explode('.', basename($file), 2)[1] ?? '';

if ($expectedFileExtension !== $realFileExtension) {
$errors[] = array(
'line' => -1,
'column' => -1,
'message' => sprintf('There is a mismatch between the file extension ("%s") and the "%s" value used in the "target-language" attribute of the file.', $realFileExtension, $targetLanguage),
);
}
}

$errorMessages = array_map(function ($error) {
return array(
'line' => $error->line,
'column' => $error->column,
'message' => trim($error->message),
);
}, libxml_get_errors());
$document->schemaValidate(__DIR__.'/../Resources/schemas/xliff-core-1.2-strict.xsd');
foreach (libxml_get_errors() as $xmlError) {
$errors[] = array(
'line' => $xmlError->line,
'column' => $xmlError->column,
'message' => trim($xmlError->message),
);
}

libxml_clear_errors();
libxml_use_internal_errors(false);

return array('file' => $file, 'valid' => false, 'messages' => $errorMessages);
return array('file' => $file, 'valid' => 0 === count($errors), 'messages' => $errors);
}

private function display(SymfonyStyle $io, array $files)
Expand Down Expand Up @@ -242,4 +255,15 @@ private function isReadable($fileOrDirectory)

return $default($fileOrDirectory);
}

private function getTargetLanguageFromFile(\DOMDocument $xliffContents): ?string
{
foreach ($xliffContents->getElementsByTagName('file')[0]->attributes ?? array() as $attribute) {
if ('target-language' === $attribute->nodeName) {
return $attribute->nodeValue;
}
}

return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Translation\Tests\Command;

use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Translation\Command\XliffLintCommand;

/**
* Tests the XliffLintCommand.
*
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
*/
class XliffLintCommandTest extends TestCase
{
private $files;

public function testLintCorrectFile()
{
$tester = $this->createCommandTester();
$filename = $this->createFile();

$tester->execute(
array('filename' => $filename),
array('verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false)
);

$this->assertEquals(0, $tester->getStatusCode(), 'Returns 0 in case of success');
$this->assertContains('OK', trim($tester->getDisplay()));
}

public function testLintIncorrectXmlSyntax()
{
$tester = $this->createCommandTester();
$filename = $this->createFile('note <target>');

$tester->execute(array('filename' => $filename), array('decorated' => false));

$this->assertEquals(1, $tester->getStatusCode(), 'Returns 1 in case of error');
$this->assertContains('Opening and ending tag mismatch: target line 6 and source', trim($tester->getDisplay()));
}

public function testLintIncorrectTargetLanguage()
{
$tester = $this->createCommandTester();
$filename = $this->createFile('note', 'es');

$tester->execute(array('filename' => $filename), array('decorated' => false));

$this->assertEquals(1, $tester->getStatusCode(), 'Returns 1 in case of error');
$this->assertContains('There is a mismatch between the file extension ("en.xlf") and the "es" value used in the "target-language" attribute of the file.', trim($tester->getDisplay()));
}

/**
* @expectedException \RuntimeException
*/
public function testLintFileNotReadable()
{
$tester = $this->createCommandTester();
$filename = $this->createFile();
unlink($filename);

$tester->execute(array('filename' => $filename), array('decorated' => false));
}

public function testGetHelp()
{
$command = new XliffLintCommand();
$expected = <<<EOF
The <info>%command.name%</info> command lints a XLIFF file and outputs to STDOUT
the first encountered syntax error.

You can validates XLIFF contents passed from STDIN:

<info>cat filename | php %command.full_name%</info>

You can also validate the syntax of a file:

<info>php %command.full_name% filename</info>

Or of a whole directory:

<info>php %command.full_name% dirname</info>
<info>php %command.full_name% dirname --format=json</info>

EOF;

$this->assertEquals($expected, $command->getHelp());
}

/**
* @return string Path to the new file
*/
private function createFile($sourceContent = 'note', $targetLanguage = 'en')
{
$xliffContent = <<<XLIFF
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" target-language="$targetLanguage" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="note">
<source>$sourceContent</source>
<target>NOTE</target>
</trans-unit>
</body>
</file>
</xliff>
XLIFF;

$filename = sprintf('%s/xliff-lint-test/messages.en.xlf', sys_get_temp_dir());
file_put_contents($filename, $xliffContent);

$this->files[] = $filename;

return $filename;
}

/**
* @return CommandTester
*/
private function createCommandTester($application = null)
{
if (!$application) {
$application = new Application();
$application->add(new XliffLintCommand());
}

$command = $application->find('lint:xliff');

if ($application) {
$command->setApplication($application);
}

return new CommandTester($command);
}

protected function setUp()
{
@mkdir(sys_get_temp_dir().'/xliff-lint-test');
$this->files = array();
}

protected function tearDown()
{
foreach ($this->files as $file) {
if (file_exists($file)) {
unlink($file);
}
}
rmdir(sys_get_temp_dir().'/xliff-lint-test');
}
}
1 change: 1 addition & 0 deletions src/Symfony/Component/Translation/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
},
"require-dev": {
"symfony/config": "~3.4|~4.0",
"symfony/console": "~3.4|~4.0",
"symfony/dependency-injection": "~3.4|~4.0",
"symfony/intl": "~3.4|~4.0",
"symfony/yaml": "~3.4|~4.0",
Expand Down