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;
21
22 import static com.google.common.truth.Truth.assertWithMessage;
23
24 import java.io.IOException;
25 import java.nio.file.Files;
26 import java.nio.file.Path;
27 import java.util.List;
28 import java.util.Optional;
29 import java.util.stream.Stream;
30
31 import org.junit.jupiter.params.ParameterizedTest;
32 import org.junit.jupiter.params.provider.MethodSource;
33
34 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
35 import com.puppycrawl.tools.checkstyle.api.Configuration;
36 import com.puppycrawl.tools.checkstyle.checks.indentation.IndentationCheck;
37 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
38
39 public class IndentationTrailingCommentsVerticalAlignmentTest {
40
41 private static final String INDENTATION_TEST_FILES_PATH =
42 "com/puppycrawl/tools/checkstyle/checks/indentation/indentation";
43
44 @MethodSource("indentationTestFiles")
45 @ParameterizedTest
46 public final void testTrailingCommentsAlignment(Path testFile) throws Exception {
47 final int currentTabWidth = getTabWidth(testFile);
48 final List<String> lines = Files.readAllLines(testFile);
49 int expectedStartIndex = -1;
50
51 for (int idx = 0; idx < lines.size(); idx++) {
52 final String line = lines.get(idx);
53 if (line.trim().startsWith("import ") || line.trim().startsWith("package ")) {
54 continue;
55 }
56 final int commentStartIndex = line.indexOf("//indent:");
57 if (commentStartIndex > 0) {
58 final String codePart = line.substring(0, commentStartIndex);
59 if (!codePart.isBlank()) {
60 int actualStartIndex =
61 CommonUtil.lengthExpandedTabs(line, commentStartIndex, currentTabWidth);
62
63
64 final long extraWidth = codePart.codePoints().filter(
65 Character::isSupplementaryCodePoint).count();
66 actualStartIndex -= Math.toIntExact(extraWidth);
67
68 if (expectedStartIndex == -1) {
69 expectedStartIndex = actualStartIndex;
70 }
71 else {
72 assertWithMessage(
73 "Trailing comment alignment mismatch in file: %s on line %s",
74 testFile, idx + 1)
75 .that(actualStartIndex)
76 .isEqualTo(expectedStartIndex);
77 }
78 }
79 }
80 }
81 }
82
83 private static int getTabWidth(Path testFile) throws Exception {
84 final Optional<Path> matchingConfig = findConfigForFile(testFile);
85 int result = 4;
86
87 if (matchingConfig.isPresent()) {
88 final Configuration config = ConfigurationLoader.loadConfiguration(
89 matchingConfig.get().toString(),
90 new PropertiesExpander(System.getProperties()));
91 result = extractTabWidthFromConfig(config);
92 }
93 return result;
94 }
95
96 private static Optional<Path> findConfigForFile(Path testFile) throws IOException {
97 final String fileName = testFile.getFileName().toString();
98 final Path configDir = Path.of("src", "test", "resources",
99 "com", "puppycrawl", "tools", "checkstyle",
100 "checks", "indentation", "indentation");
101 final Optional<Path> result;
102
103 try (Stream<Path> configs = Files.walk(configDir)) {
104 result = configs
105 .filter(path -> path.toString().endsWith(".xml"))
106 .filter(path -> isConfigFileContainingFileName(path, fileName))
107 .findFirst();
108 }
109 return result;
110 }
111
112 private static boolean isConfigFileContainingFileName(Path configPath, String fileName) {
113 boolean result;
114 try {
115 result = Files.readString(configPath).contains(fileName);
116 }
117 catch (IOException exception) {
118 result = false;
119 }
120 return result;
121 }
122
123 private static int extractTabWidthFromConfig(Configuration config) throws CheckstyleException {
124 int result = 4;
125 for (Configuration child : config.getChildren()) {
126 if ("TreeWalker".equals(child.getName())) {
127 for (Configuration module : child.getChildren()) {
128 if ("IndentationCheck".equals(module.getName())
129 || "Indentation".equals(module.getName())) {
130 final IndentationCheck check = new IndentationCheck();
131 check.configure(module);
132 result = check.getIndentationTabWidth();
133 }
134 }
135 }
136 }
137 return result;
138 }
139
140 private static Stream<Path> indentationTestFiles() {
141 final Path resourcesDir = Path.of("src", "test", "resources");
142 final Path indentationDir = resourcesDir.resolve(INDENTATION_TEST_FILES_PATH);
143 Stream<Path> result;
144 try (Stream<Path> testFiles = Files.walk(indentationDir)) {
145 final List<Path> collected = testFiles
146 .filter(path -> {
147 final String fileName = path.getFileName().toString();
148 return fileName.startsWith("InputIndentation")
149 && fileName.endsWith(".java");
150 }).toList();
151 result = collected.stream();
152 }
153 catch (IOException exception) {
154 assertWithMessage("Failed to find indentation test files")
155 .that(exception)
156 .isNull();
157 result = Stream.empty();
158 }
159 return result;
160 }
161 }