Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f568271

Browse files
committed
feature #26682 Improved the lint:xliff command (javiereguiluz)
This PR was squashed before being merged into the 4.1-dev branch (closes #26682). Discussion ---------- Improved the lint:xliff command | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #26537 | License | MIT | Doc PR | - This is how it looks: ![lint-xliff-output](https://user-images.githubusercontent.com/73419/37958502-fd8732e0-31b0-11e8-8688-8644ca930daf.png) Commits ------- 6bbcc45 Improved the lint:xliff command
2 parents f62a6d7 + 6bbcc45 commit f568271

File tree

4 files changed

+350
-10
lines changed

4 files changed

+350
-10
lines changed
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\Command;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Bundle\FrameworkBundle\Command\XliffLintCommand;
16+
use Symfony\Bundle\FrameworkBundle\Console\Application;
17+
use Symfony\Component\Console\Application as BaseApplication;
18+
use Symfony\Component\Console\Output\OutputInterface;
19+
use Symfony\Component\Console\Tester\CommandTester;
20+
use Symfony\Component\Console\Helper\HelperSet;
21+
use Symfony\Component\Console\Input\InputDefinition;
22+
use Symfony\Component\HttpKernel\KernelInterface;
23+
24+
/**
25+
* Tests the part of the XliffLintCommand managed by the FrameworkBundle. The
26+
* rest of the features are tested in the Translation component.
27+
*
28+
* @author Javier Eguiluz <[email protected]>
29+
*/
30+
class XliffLintCommandTest extends TestCase
31+
{
32+
private $files;
33+
34+
public function testGetHelp()
35+
{
36+
$command = new XliffLintCommand();
37+
$expected = <<<EOF
38+
The <info>%command.name%</info> command lints a XLIFF file and outputs to STDOUT
39+
the first encountered syntax error.
40+
41+
You can validates XLIFF contents passed from STDIN:
42+
43+
<info>cat filename | php %command.full_name%</info>
44+
45+
You can also validate the syntax of a file:
46+
47+
<info>php %command.full_name% filename</info>
48+
49+
Or of a whole directory:
50+
51+
<info>php %command.full_name% dirname</info>
52+
<info>php %command.full_name% dirname --format=json</info>
53+
54+
Or find all files in a bundle:
55+
56+
<info>php %command.full_name% @AcmeDemoBundle</info>
57+
58+
EOF;
59+
60+
$this->assertEquals($expected, $command->getHelp());
61+
}
62+
63+
public function testLintFilesFromBundleDirectory()
64+
{
65+
$tester = $this->createCommandTester($this->getKernelAwareApplicationMock());
66+
$tester->execute(
67+
array('filename' => '@AppBundle/Resources'),
68+
array('verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false)
69+
);
70+
71+
$this->assertEquals(0, $tester->getStatusCode(), 'Returns 0 in case of success');
72+
$this->assertContains('[OK] All 0 XLIFF files contain valid syntax', trim($tester->getDisplay()));
73+
}
74+
75+
/**
76+
* @return CommandTester
77+
*/
78+
private function createCommandTester($application = null)
79+
{
80+
if (!$application) {
81+
$application = new BaseApplication();
82+
$application->add(new XliffLintCommand());
83+
}
84+
85+
$command = $application->find('lint:xliff');
86+
87+
if ($application) {
88+
$command->setApplication($application);
89+
}
90+
91+
return new CommandTester($command);
92+
}
93+
94+
private function getKernelAwareApplicationMock()
95+
{
96+
$kernel = $this->getMockBuilder(KernelInterface::class)
97+
->disableOriginalConstructor()
98+
->getMock();
99+
100+
$kernel
101+
->expects($this->once())
102+
->method('locateResource')
103+
->with('@AppBundle/Resources')
104+
->willReturn(sys_get_temp_dir().'/xliff-lint-test');
105+
106+
$application = $this->getMockBuilder(Application::class)
107+
->disableOriginalConstructor()
108+
->getMock();
109+
110+
$application
111+
->expects($this->once())
112+
->method('getKernel')
113+
->willReturn($kernel);
114+
115+
$application
116+
->expects($this->once())
117+
->method('getHelperSet')
118+
->willReturn(new HelperSet());
119+
120+
$application
121+
->expects($this->any())
122+
->method('getDefinition')
123+
->willReturn(new InputDefinition());
124+
125+
$application
126+
->expects($this->once())
127+
->method('find')
128+
->with('lint:xliff')
129+
->willReturn(new XliffLintCommand());
130+
131+
return $application;
132+
}
133+
134+
protected function setUp()
135+
{
136+
@mkdir(sys_get_temp_dir().'/xliff-lint-test');
137+
$this->files = array();
138+
}
139+
140+
protected function tearDown()
141+
{
142+
foreach ($this->files as $file) {
143+
if (file_exists($file)) {
144+
unlink($file);
145+
}
146+
}
147+
rmdir(sys_get_temp_dir().'/xliff-lint-test');
148+
}
149+
}

src/Symfony/Component/Translation/Command/XliffLintCommand.php

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ protected function execute(InputInterface $input, OutputInterface $output)
101101

102102
private function validate($content, $file = null)
103103
{
104+
$errors = array();
105+
104106
// Avoid: Warning DOMDocument::loadXML(): Empty string supplied as input
105107
if ('' === trim($content)) {
106108
return array('file' => $file, 'valid' => true);
@@ -110,22 +112,33 @@ private function validate($content, $file = null)
110112

111113
$document = new \DOMDocument();
112114
$document->loadXML($content);
113-
if ($document->schemaValidate(__DIR__.'/../Resources/schemas/xliff-core-1.2-strict.xsd')) {
114-
return array('file' => $file, 'valid' => true);
115+
116+
if (null !== $targetLanguage = $this->getTargetLanguageFromFile($document)) {
117+
$expectedFileExtension = sprintf('%s.xlf', str_replace('-', '_', $targetLanguage));
118+
$realFileExtension = explode('.', basename($file), 2)[1] ?? '';
119+
120+
if ($expectedFileExtension !== $realFileExtension) {
121+
$errors[] = array(
122+
'line' => -1,
123+
'column' => -1,
124+
'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),
125+
);
126+
}
115127
}
116128

117-
$errorMessages = array_map(function ($error) {
118-
return array(
119-
'line' => $error->line,
120-
'column' => $error->column,
121-
'message' => trim($error->message),
122-
);
123-
}, libxml_get_errors());
129+
$document->schemaValidate(__DIR__.'/../Resources/schemas/xliff-core-1.2-strict.xsd');
130+
foreach (libxml_get_errors() as $xmlError) {
131+
$errors[] = array(
132+
'line' => $xmlError->line,
133+
'column' => $xmlError->column,
134+
'message' => trim($xmlError->message),
135+
);
136+
}
124137

125138
libxml_clear_errors();
126139
libxml_use_internal_errors(false);
127140

128-
return array('file' => $file, 'valid' => false, 'messages' => $errorMessages);
141+
return array('file' => $file, 'valid' => 0 === count($errors), 'messages' => $errors);
129142
}
130143

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

243256
return $default($fileOrDirectory);
244257
}
258+
259+
private function getTargetLanguageFromFile(\DOMDocument $xliffContents): ?string
260+
{
261+
foreach ($xliffContents->getElementsByTagName('file')[0]->attributes ?? array() as $attribute) {
262+
if ('target-language' === $attribute->nodeName) {
263+
return $attribute->nodeValue;
264+
}
265+
}
266+
267+
return null;
268+
}
245269
}
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Translation\Tests\Command;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Console\Application;
16+
use Symfony\Component\Console\Output\OutputInterface;
17+
use Symfony\Component\Console\Tester\CommandTester;
18+
use Symfony\Component\Console\Helper\HelperSet;
19+
use Symfony\Component\Console\Input\InputDefinition;
20+
use Symfony\Component\HttpKernel\KernelInterface;
21+
use Symfony\Component\Translation\Command\XliffLintCommand;
22+
23+
/**
24+
* Tests the XliffLintCommand.
25+
*
26+
* @author Javier Eguiluz <[email protected]>
27+
*/
28+
class XliffLintCommandTest extends TestCase
29+
{
30+
private $files;
31+
32+
public function testLintCorrectFile()
33+
{
34+
$tester = $this->createCommandTester();
35+
$filename = $this->createFile();
36+
37+
$tester->execute(
38+
array('filename' => $filename),
39+
array('verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false)
40+
);
41+
42+
$this->assertEquals(0, $tester->getStatusCode(), 'Returns 0 in case of success');
43+
$this->assertContains('OK', trim($tester->getDisplay()));
44+
}
45+
46+
public function testLintIncorrectXmlSyntax()
47+
{
48+
$tester = $this->createCommandTester();
49+
$filename = $this->createFile('note <target>');
50+
51+
$tester->execute(array('filename' => $filename), array('decorated' => false));
52+
53+
$this->assertEquals(1, $tester->getStatusCode(), 'Returns 1 in case of error');
54+
$this->assertContains('Opening and ending tag mismatch: target line 6 and source', trim($tester->getDisplay()));
55+
}
56+
57+
public function testLintIncorrectTargetLanguage()
58+
{
59+
$tester = $this->createCommandTester();
60+
$filename = $this->createFile('note', 'es');
61+
62+
$tester->execute(array('filename' => $filename), array('decorated' => false));
63+
64+
$this->assertEquals(1, $tester->getStatusCode(), 'Returns 1 in case of error');
65+
$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()));
66+
}
67+
68+
/**
69+
* @expectedException \RuntimeException
70+
*/
71+
public function testLintFileNotReadable()
72+
{
73+
$tester = $this->createCommandTester();
74+
$filename = $this->createFile();
75+
unlink($filename);
76+
77+
$tester->execute(array('filename' => $filename), array('decorated' => false));
78+
}
79+
80+
public function testGetHelp()
81+
{
82+
$command = new XliffLintCommand();
83+
$expected = <<<EOF
84+
The <info>%command.name%</info> command lints a XLIFF file and outputs to STDOUT
85+
the first encountered syntax error.
86+
87+
You can validates XLIFF contents passed from STDIN:
88+
89+
<info>cat filename | php %command.full_name%</info>
90+
91+
You can also validate the syntax of a file:
92+
93+
<info>php %command.full_name% filename</info>
94+
95+
Or of a whole directory:
96+
97+
<info>php %command.full_name% dirname</info>
98+
<info>php %command.full_name% dirname --format=json</info>
99+
100+
EOF;
101+
102+
$this->assertEquals($expected, $command->getHelp());
103+
}
104+
105+
/**
106+
* @return string Path to the new file
107+
*/
108+
private function createFile($sourceContent = 'note', $targetLanguage = 'en')
109+
{
110+
$xliffContent = <<<XLIFF
111+
<?xml version="1.0"?>
112+
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
113+
<file source-language="en" target-language="$targetLanguage" datatype="plaintext" original="file.ext">
114+
<body>
115+
<trans-unit id="note">
116+
<source>$sourceContent</source>
117+
<target>NOTE</target>
118+
</trans-unit>
119+
</body>
120+
</file>
121+
</xliff>
122+
XLIFF;
123+
124+
$filename = sprintf('%s/xliff-lint-test/messages.en.xlf', sys_get_temp_dir());
125+
file_put_contents($filename, $xliffContent);
126+
127+
$this->files[] = $filename;
128+
129+
return $filename;
130+
}
131+
132+
/**
133+
* @return CommandTester
134+
*/
135+
private function createCommandTester($application = null)
136+
{
137+
if (!$application) {
138+
$application = new Application();
139+
$application->add(new XliffLintCommand());
140+
}
141+
142+
$command = $application->find('lint:xliff');
143+
144+
if ($application) {
145+
$command->setApplication($application);
146+
}
147+
148+
return new CommandTester($command);
149+
}
150+
151+
protected function setUp()
152+
{
153+
@mkdir(sys_get_temp_dir().'/xliff-lint-test');
154+
$this->files = array();
155+
}
156+
157+
protected function tearDown()
158+
{
159+
foreach ($this->files as $file) {
160+
if (file_exists($file)) {
161+
unlink($file);
162+
}
163+
}
164+
rmdir(sys_get_temp_dir().'/xliff-lint-test');
165+
}
166+
}

src/Symfony/Component/Translation/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
},
2222
"require-dev": {
2323
"symfony/config": "~3.4|~4.0",
24+
"symfony/console": "~3.4|~4.0",
2425
"symfony/dependency-injection": "~3.4|~4.0",
2526
"symfony/intl": "~3.4|~4.0",
2627
"symfony/yaml": "~3.4|~4.0",

0 commit comments

Comments
 (0)