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