1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.checks;
21
22 import static com.google.common.truth.Truth.assertWithMessage;
23 import static com.puppycrawl.tools.checkstyle.checks.TranslationCheck.MSG_KEY;
24 import static com.puppycrawl.tools.checkstyle.checks.TranslationCheck.MSG_KEY_MISSING_TRANSLATION_FILE;
25
26 import java.io.ByteArrayOutputStream;
27 import java.io.File;
28 import java.io.IOException;
29 import java.io.Writer;
30 import java.lang.reflect.Field;
31 import java.nio.charset.StandardCharsets;
32 import java.nio.file.Files;
33 import java.util.Collections;
34 import java.util.Set;
35 import java.util.SortedSet;
36 import java.util.TreeSet;
37
38 import org.junit.jupiter.api.Test;
39 import org.junit.jupiter.api.io.TempDir;
40 import org.w3c.dom.Node;
41
42 import com.google.common.collect.ImmutableMap;
43 import com.puppycrawl.tools.checkstyle.AbstractAutomaticBean.OutputStreamOptions;
44 import com.puppycrawl.tools.checkstyle.AbstractXmlTestSupport;
45 import com.puppycrawl.tools.checkstyle.Checker;
46 import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
47 import com.puppycrawl.tools.checkstyle.Definitions;
48 import com.puppycrawl.tools.checkstyle.XMLLogger;
49 import com.puppycrawl.tools.checkstyle.api.Configuration;
50 import com.puppycrawl.tools.checkstyle.api.FileText;
51 import com.puppycrawl.tools.checkstyle.api.MessageDispatcher;
52 import com.puppycrawl.tools.checkstyle.api.Violation;
53 import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
54 import com.puppycrawl.tools.checkstyle.internal.utils.XmlUtil;
55 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
56
57 public class TranslationCheckTest extends AbstractXmlTestSupport {
58
59 @TempDir
60 public File temporaryFolder;
61
62 @Override
63 protected String getPackageLocation() {
64 return "com/puppycrawl/tools/checkstyle/checks/translation";
65 }
66
67 @Test
68 public void testTranslation() throws Exception {
69 final Configuration checkConfig = createModuleConfig(TranslationCheck.class);
70 final String[] expected = {
71 "1: " + getCheckMessage(MSG_KEY, "only.english"),
72 };
73 final File[] propertyFiles = {
74 new File(getPath("messages_test_de.properties")),
75 new File(getPath("messages_test.properties")),
76 };
77 verify(
78 createChecker(checkConfig),
79 propertyFiles,
80 getPath("messages_test_de.properties"),
81 expected);
82 }
83
84 @Test
85 public void testDifferentBases() throws Exception {
86 final Configuration checkConfig = createModuleConfig(TranslationCheck.class);
87 final String[] expected = {
88 "1: " + getCheckMessage(MSG_KEY, "only.english"),
89 };
90 final File[] propertyFiles = {
91 new File(getPath("messages_test_de.properties")),
92 new File(getPath("messages_test.properties")),
93 new File(getPath("messages_translation.properties")),
94 new File(getPath("messages_translation_de.properties")),
95 };
96 verify(
97 createChecker(checkConfig),
98 propertyFiles,
99 getPath("messages_test_de.properties"),
100 expected);
101 }
102
103 @Test
104 public void testDifferentPaths() throws Exception {
105 final File file = new File(temporaryFolder, "messages_test_de.properties");
106 try (Writer writer = Files.newBufferedWriter(file.toPath(), StandardCharsets.UTF_8)) {
107 final String content = "hello=Hello\ncancel=Cancel";
108 writer.write(content);
109 }
110 final Configuration checkConfig = createModuleConfig(TranslationCheck.class);
111 final String[] expected = {
112 "1: " + getCheckMessage(MSG_KEY_MISSING_TRANSLATION_FILE,
113 "messages_test.properties"),
114 };
115 final File[] propertyFiles = {
116 file,
117 new File(getPath("messages_test.properties")),
118 };
119 verify(
120 createChecker(checkConfig),
121 propertyFiles,
122 file.getParent(),
123 expected);
124 }
125
126
127
128
129
130
131
132
133
134 @Test
135 public void testStateIsCleared() throws Exception {
136 final File fileToProcess = new File(
137 getPath("InputTranslationCheckFireErrors_de.properties")
138 );
139 final String charset = StandardCharsets.UTF_8.name();
140 final TranslationCheck check = new TranslationCheck();
141 check.beginProcessing(charset);
142 check.processFiltered(fileToProcess, new FileText(fileToProcess, charset));
143 check.beginProcessing(charset);
144 final Field field = check.getClass().getDeclaredField("filesToProcess");
145 field.setAccessible(true);
146
147 assertWithMessage("Stateful field is not cleared on beginProcessing")
148 .that((Iterable<?>) field.get(check))
149 .isEmpty();
150 }
151
152 @Test
153 public void testFileExtension() throws Exception {
154 final DefaultConfiguration checkConfig = createModuleConfig(TranslationCheck.class);
155 checkConfig.addProperty("baseName", "^InputTranslation.*$");
156 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
157 final File[] propertyFiles = {
158 new File(getPath("InputTranslation_de.txt")),
159 };
160 verify(createChecker(checkConfig),
161 propertyFiles,
162 getPath("InputTranslation_de.txt"),
163 expected);
164 }
165
166 @Test
167 public void testLogOutput() throws Exception {
168 final DefaultConfiguration checkConfig = createModuleConfig(TranslationCheck.class);
169 checkConfig.addProperty("requiredTranslations", "ja,de");
170 checkConfig.addProperty("baseName", "^InputTranslation.*$");
171 final Checker checker = createChecker(checkConfig);
172 checker.setBasedir(getPath(""));
173 final ByteArrayOutputStream out = new ByteArrayOutputStream();
174 final XMLLogger logger = new XMLLogger(out, OutputStreamOptions.NONE);
175 checker.addListener(logger);
176
177 final String defaultProps = getPath("InputTranslationCheckFireErrors.properties");
178 final String translationProps = getPath("InputTranslationCheckFireErrors_de.properties");
179
180 final File[] propertyFiles = {
181 new File(defaultProps),
182 new File(translationProps),
183 };
184
185 final String line = "1: ";
186 final String firstErrorMessage = getCheckMessage(MSG_KEY_MISSING_TRANSLATION_FILE,
187 "InputTranslationCheckFireErrors_ja.properties");
188 final String secondErrorMessage = getCheckMessage(MSG_KEY, "anotherKey");
189
190 verify(checker, propertyFiles, ImmutableMap.of(
191 ":1", Collections.singletonList(" " + firstErrorMessage),
192 "InputTranslationCheckFireErrors_de.properties",
193 Collections.singletonList(line + secondErrorMessage)));
194
195 verifyXml(getPath("ExpectedTranslationLog.xml"), out,
196 TranslationCheckTest::isFilenamesEqual,
197 firstErrorMessage, secondErrorMessage);
198 }
199
200 @Test
201 public void testOnePropertyFileSet() throws Exception {
202 final DefaultConfiguration checkConfig = createModuleConfig(TranslationCheck.class);
203 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
204 final File[] propertyFiles = {
205 new File(getPath("app-dev.properties")),
206 };
207 verify(
208 createChecker(checkConfig),
209 propertyFiles,
210 getPath("app-dev.properties"),
211 expected);
212 }
213
214 @Test
215 public void testLogIoExceptionFileNotFound() throws Exception {
216
217
218
219 final TranslationCheck check = new TranslationCheck();
220 final DefaultConfiguration checkConfig = createModuleConfig(TranslationCheck.class);
221 final TestMessageDispatcher dispatcher = new TestMessageDispatcher();
222 check.configure(checkConfig);
223 check.setMessageDispatcher(dispatcher);
224
225 final Set<String> keys = TestUtil.invokeMethod(check, "getTranslationKeys",
226 new File(".no.such.file"));
227 assertWithMessage("Translation keys should be empty when File is not found")
228 .that(keys)
229 .isEmpty();
230
231 assertWithMessage("expected number of errors to fire")
232 .that(dispatcher.savedErrors)
233 .hasSize(1);
234 final Violation violation = new Violation(0,
235 Definitions.CHECKSTYLE_BUNDLE, "general.fileNotFound",
236 null, null, TranslationCheck.class, null);
237 assertWithMessage("Invalid violation")
238 .that(dispatcher.savedErrors.iterator().next())
239 .isEqualTo(violation);
240 }
241
242 @Test
243 public void testLogIoException() throws Exception {
244
245
246
247 final TranslationCheck check = new TranslationCheck();
248 final DefaultConfiguration checkConfig = createModuleConfig(TranslationCheck.class);
249 final TestMessageDispatcher dispatcher = new TestMessageDispatcher();
250 check.configure(checkConfig);
251 check.setMessageDispatcher(dispatcher);
252 check.setId("ID1");
253
254 final Exception exception = new IOException("test exception");
255 TestUtil.invokeMethod(check, "logException", exception, new File(""));
256
257 assertWithMessage("expected number of errors to fire")
258 .that(dispatcher.savedErrors.size())
259 .isEqualTo(1);
260 final Violation violation = new Violation(0,
261 Definitions.CHECKSTYLE_BUNDLE, "general.exception",
262 new String[] {exception.getMessage()}, "ID1", TranslationCheck.class, null);
263 assertWithMessage("Invalid violation")
264 .that(dispatcher.savedErrors.iterator().next())
265 .isEqualTo(violation);
266 }
267
268 @Test
269 public void testLogIllegalArgumentException() throws Exception {
270 final DefaultConfiguration checkConfig = createModuleConfig(TranslationCheck.class);
271 checkConfig.addProperty("baseName", "^bad.*$");
272 final String[] expected = {
273 "0: " + getCheckMessage(Checker.class, "general.exception",
274 "Malformed \\uxxxx encoding."),
275 "1: " + getCheckMessage(MSG_KEY, "test"),
276 };
277 final File[] propertyFiles = {
278 new File(getPath("bad.properties")),
279 new File(getPath("bad_es.properties")),
280 };
281 verify(
282 createChecker(checkConfig),
283 propertyFiles,
284 getPath("bad.properties"),
285 expected);
286 }
287
288 @Test
289 public void testDefaultTranslationFileIsMissing() throws Exception {
290 final DefaultConfiguration checkConfig = createModuleConfig(TranslationCheck.class);
291 checkConfig.addProperty("requiredTranslations", "ja,,, de, ja");
292
293 final File[] propertyFiles = {
294 new File(getPath("messages_translation_de.properties")),
295 new File(getPath("messages_translation_ja.properties")),
296 };
297
298 final String[] expected = {
299 "1: " + getCheckMessage(MSG_KEY_MISSING_TRANSLATION_FILE,
300 "messages_translation.properties"),
301 };
302 verify(
303 createChecker(checkConfig),
304 propertyFiles,
305 getPath(""),
306 expected);
307 }
308
309 @Test
310 public void testTranslationFilesAreMissing() throws Exception {
311 final DefaultConfiguration checkConfig = createModuleConfig(TranslationCheck.class);
312 checkConfig.addProperty("requiredTranslations", "ja, de");
313
314 final File[] propertyFiles = {
315 new File(getPath("messages_translation.properties")),
316 new File(getPath("messages_translation_ja.properties")),
317 };
318
319 final String[] expected = {
320 "1: " + getCheckMessage(MSG_KEY_MISSING_TRANSLATION_FILE,
321 "messages_translation_de.properties"),
322 };
323 verify(
324 createChecker(checkConfig),
325 propertyFiles,
326 getPath(""),
327 expected);
328 }
329
330 @Test
331 public void testBaseNameWithSeparatorDefaultTranslationIsMissing() throws Exception {
332 final DefaultConfiguration checkConfig = createModuleConfig(TranslationCheck.class);
333 checkConfig.addProperty("requiredTranslations", "fr");
334
335 final File[] propertyFiles = {
336 new File(getPath("messages-translation_fr.properties")),
337 };
338
339 final String[] expected = {
340 "1: " + getCheckMessage(MSG_KEY_MISSING_TRANSLATION_FILE,
341 "messages-translation.properties"),
342 };
343 verify(
344 createChecker(checkConfig),
345 propertyFiles,
346 getPath(""),
347 expected);
348 }
349
350 @Test
351 public void testBaseNameWithSeparatorTranslationsAreMissing() throws Exception {
352 final DefaultConfiguration checkConfig = createModuleConfig(TranslationCheck.class);
353 checkConfig.addProperty("requiredTranslations", "fr, tr");
354
355 final File[] propertyFiles = {
356 new File(getPath("messages-translation.properties")),
357 new File(getPath("messages-translation_fr.properties")),
358 };
359
360 final String[] expected = {
361 "1: " + getCheckMessage(MSG_KEY_MISSING_TRANSLATION_FILE,
362 "messages-translation_tr.properties"),
363 };
364 verify(
365 createChecker(checkConfig),
366 propertyFiles,
367 getPath(""),
368 expected);
369 }
370
371 @Test
372 public void testIsNotMessagesBundle() throws Exception {
373 final DefaultConfiguration checkConfig = createModuleConfig(TranslationCheck.class);
374 checkConfig.addProperty("requiredTranslations", "de");
375
376 final File[] propertyFiles = {
377 new File(getPath("app-dev.properties")),
378 new File(getPath("app-stage.properties")),
379 };
380
381 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
382 verify(
383 createChecker(checkConfig),
384 propertyFiles,
385 getPath("app-dev.properties"),
386 expected);
387 }
388
389 @Test
390 public void testTranslationFileWithLanguageCountryVariantIsMissing() throws Exception {
391 final DefaultConfiguration checkConfig = createModuleConfig(TranslationCheck.class);
392 checkConfig.addProperty("requiredTranslations", "es, de");
393
394 final File[] propertyFiles = {
395 new File(getPath("messages_home.properties")),
396 new File(getPath("messages_home_es_US.properties")),
397 new File(getPath("messages_home_fr_CA_UNIX.properties")),
398 };
399
400 final String[] expected = {
401 "1: " + getCheckMessage(MSG_KEY_MISSING_TRANSLATION_FILE,
402 "messages_home_de.properties"),
403 };
404 verify(
405 createChecker(checkConfig),
406 propertyFiles,
407 getPath(""),
408 expected);
409 }
410
411 @Test
412 public void testTranslationFileWithLanguageCountryVariantArePresent() throws Exception {
413 final DefaultConfiguration checkConfig = createModuleConfig(TranslationCheck.class);
414 checkConfig.addProperty("requiredTranslations", "es, fr");
415
416 final File[] propertyFiles = {
417 new File(getPath("messages_home.properties")),
418 new File(getPath("messages_home_es_US.properties")),
419 new File(getPath("messages_home_fr_CA_UNIX.properties")),
420 };
421
422 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
423 verify(
424 createChecker(checkConfig),
425 propertyFiles,
426 getPath(""),
427 expected);
428 }
429
430 @Test
431 public void testBaseNameOption() throws Exception {
432 final DefaultConfiguration checkConfig = createModuleConfig(TranslationCheck.class);
433 checkConfig.addProperty("requiredTranslations", "de, es, fr, ja");
434 checkConfig.addProperty("baseName", "^.*Labels$");
435
436 final File[] propertyFiles = {
437 new File(getPath("ButtonLabels.properties")),
438 new File(getPath("ButtonLabels_de.properties")),
439 new File(getPath("ButtonLabels_es.properties")),
440 new File(getPath("ButtonLabels_fr_CA_UNIX.properties")),
441 new File(getPath("messages_home.properties")),
442 new File(getPath("messages_home_es_US.properties")),
443 new File(getPath("messages_home_fr_CA_UNIX.properties")),
444 };
445
446 final String[] expected = {
447 "1: " + getCheckMessage(MSG_KEY_MISSING_TRANSLATION_FILE,
448 "ButtonLabels_ja.properties"),
449 };
450 verify(
451 createChecker(checkConfig),
452 propertyFiles,
453 getPath(""),
454 expected);
455 }
456
457 @Test
458 public void testFileExtensions() throws Exception {
459 final DefaultConfiguration checkConfig = createModuleConfig(TranslationCheck.class);
460 checkConfig.addProperty("requiredTranslations", "de, es, fr, ja");
461 checkConfig.addProperty("fileExtensions", "properties,translation");
462 checkConfig.addProperty("baseName", "^.*(Titles|Labels)$");
463
464 final File[] propertyFiles = {
465 new File(getPath("ButtonLabels.properties")),
466 new File(getPath("ButtonLabels_de.properties")),
467 new File(getPath("ButtonLabels_es.properties")),
468 new File(getPath("ButtonLabels_fr_CA_UNIX.properties")),
469 new File(getPath("PageTitles.translation")),
470 new File(getPath("PageTitles_de.translation")),
471 new File(getPath("PageTitles_es.translation")),
472 new File(getPath("PageTitles_fr.translation")),
473 new File(getPath("PageTitles_ja.translation")),
474 };
475
476 final String[] expected = {
477 "1: " + getCheckMessage(MSG_KEY_MISSING_TRANSLATION_FILE,
478 "ButtonLabels_ja.properties"),
479 };
480
481 verify(
482 createChecker(checkConfig),
483 propertyFiles,
484 getPath(""),
485 expected);
486 }
487
488 @Test
489 public void testEqualBaseNamesButDifferentExtensions() throws Exception {
490 final DefaultConfiguration checkConfig = createModuleConfig(TranslationCheck.class);
491 checkConfig.addProperty("requiredTranslations", "de, es, fr, ja");
492 checkConfig.addProperty("fileExtensions", "properties,translations");
493 checkConfig.addProperty("baseName", "^.*Labels$");
494
495 final File[] propertyFiles = {
496 new File(getPath("ButtonLabels.properties")),
497 new File(getPath("ButtonLabels_de.properties")),
498 new File(getPath("ButtonLabels_es.properties")),
499 new File(getPath("ButtonLabels_fr_CA_UNIX.properties")),
500 new File(getPath("ButtonLabels.translations")),
501 new File(getPath("ButtonLabels_ja.translations")),
502 new File(getPath("ButtonLabels_es.translations")),
503 new File(getPath("ButtonLabels_fr_CA_UNIX.translations")),
504 new File(getPath("ButtonLabels_de.translations")),
505 };
506
507 final String[] expected = {
508 "1: " + getCheckMessage(MSG_KEY_MISSING_TRANSLATION_FILE,
509 "ButtonLabels_ja.properties"),
510 };
511
512 verify(
513 createChecker(checkConfig),
514 propertyFiles,
515 getPath(""),
516 expected);
517 }
518
519 @Test
520 public void testEqualBaseNamesButDifferentExtensions2() throws Exception {
521 final DefaultConfiguration checkConfig = createModuleConfig(TranslationCheck.class);
522 checkConfig.addProperty("requiredTranslations", "de, es");
523 checkConfig.addProperty("fileExtensions", "properties, translations");
524 checkConfig.addProperty("baseName", "^.*Labels$");
525
526 final File[] propertyFiles = {
527 new File(getPath("ButtonLabels.properties")),
528 new File(getPath("ButtonLabels_de.properties")),
529 new File(getPath("ButtonLabels_es.properties")),
530 new File(getPath("ButtonLabels_ja.translations")),
531 };
532
533 final String[] expected = {
534 "1: " + getCheckMessage(MSG_KEY_MISSING_TRANSLATION_FILE,
535 "ButtonLabels.translations"),
536 "1: " + getCheckMessage(MSG_KEY_MISSING_TRANSLATION_FILE,
537 "ButtonLabels_de.translations"),
538 "1: " + getCheckMessage(MSG_KEY_MISSING_TRANSLATION_FILE,
539 "ButtonLabels_es.translations"),
540 };
541
542 verify(
543 createChecker(checkConfig),
544 propertyFiles,
545 getPath(""),
546 expected);
547 }
548
549 @Test
550 public void testRegexpToMatchPartOfBaseName() throws Exception {
551 final DefaultConfiguration checkConfig = createModuleConfig(TranslationCheck.class);
552 checkConfig.addProperty("requiredTranslations", "de, es, fr, ja");
553 checkConfig.addProperty("fileExtensions", "properties,translations");
554 checkConfig.addProperty("baseName", "^.*Labels.*");
555
556 final File[] propertyFiles = {
557 new File(getPath("MyLabelsI18.properties")),
558 new File(getPath("MyLabelsI18_de.properties")),
559 new File(getPath("MyLabelsI18_es.properties")),
560 };
561
562 final String[] expected = {
563 "1: " + getCheckMessage(MSG_KEY_MISSING_TRANSLATION_FILE, "MyLabelsI18_fr.properties"),
564 "1: " + getCheckMessage(MSG_KEY_MISSING_TRANSLATION_FILE, "MyLabelsI18_ja.properties"),
565 };
566
567 verify(
568 createChecker(checkConfig),
569 propertyFiles,
570 getPath(""),
571 expected);
572 }
573
574 @Test
575 public void testBundlesWithSameNameButDifferentPaths() throws Exception {
576 final DefaultConfiguration checkConfig = createModuleConfig(TranslationCheck.class);
577 checkConfig.addProperty("requiredTranslations", "de");
578 checkConfig.addProperty("fileExtensions", "properties");
579 checkConfig.addProperty("baseName", "^.*Labels.*");
580
581 final File[] propertyFiles = {
582 new File(getPath("MyLabelsI18.properties")),
583 new File(getPath("MyLabelsI18_de.properties")),
584 new File(getNonCompilablePath("MyLabelsI18.properties")),
585 new File(getNonCompilablePath("MyLabelsI18_de.properties")),
586 };
587
588 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
589
590 verify(
591 createChecker(checkConfig),
592 propertyFiles,
593 getPath(""),
594 expected);
595 }
596
597 @Test
598 public void testWrongUserSpecifiedLanguageCodes() {
599 final TranslationCheck check = new TranslationCheck();
600 try {
601 check.setRequiredTranslations("11");
602 assertWithMessage(
603 "IllegalArgumentException is expected. Specified language code is incorrect.")
604 .fail();
605 }
606 catch (IllegalArgumentException ex) {
607 final String exceptionMessage = ex.getMessage();
608 assertWithMessage("Error message is unexpected")
609 .that(exceptionMessage)
610 .contains("11");
611 }
612 }
613
614
615
616
617
618
619
620
621 private static boolean isFilenamesEqual(Node expected, Node actual) {
622
623
624
625
626 return !"file".equals(expected.getNodeName())
627 || XmlUtil.getNameAttributeOfNode(expected)
628 .equals(XmlUtil.getNameAttributeOfNode(actual))
629 && XmlUtil.getChildrenElements(expected).size() == XmlUtil
630 .getChildrenElements(actual).size();
631 }
632
633 private static final class TestMessageDispatcher implements MessageDispatcher {
634
635 private Set<Violation> savedErrors;
636
637 @Override
638 public void fireFileStarted(String fileName) {
639 throw new IllegalStateException(fileName);
640 }
641
642 @Override
643 public void fireFileFinished(String fileName) {
644 throw new IllegalStateException(fileName);
645 }
646
647 @Override
648 public void fireErrors(String fileName, SortedSet<Violation> errors) {
649 savedErrors = new TreeSet<>(errors);
650 }
651
652 }
653
654 }