Skip to content

Commit f66cd92

Browse files
committed
Issue #79: Processing UT changes to get possible property values
1 parent f92362c commit f66cd92

File tree

9 files changed

+594
-1
lines changed

9 files changed

+594
-1
lines changed

config/import-control.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@
3434
<allow pkg="org.xml"/>
3535
</subpackage>
3636

37+
<subpackage name="customcheck">
38+
<allow pkg="com.puppycrawl.tools.checkstyle"/>
39+
</subpackage>
40+
3741
<subpackage name="data">
3842
<!-- it is only allowed until https://github.com/checkstyle/checkstyle/issues/3492 -->
3943
<allow pkg="org.immutables.gson"/>

config/pmd.xml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@
3434
<!-- we use class comments as source for xdoc files, so content is big and that is by design -->
3535
<exclude name="CommentSize"/>
3636
</rule>
37-
<rule ref="rulesets/java/comments.xml/CommentRequired" />
37+
<rule ref="rulesets/java/comments.xml/CommentRequired">
38+
<properties>
39+
<property name="publicMethodCommentRequirement" value="Ignored"/>
40+
</properties>
41+
</rule>
3842
<rule ref="rulesets/java/comments.xml/CommentSize">
3943
<properties>
4044
<!-- we use class comments as source for xdoc files, so content is big and that is by design -->

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,11 @@
393393
<branchRate>0</branchRate>
394394
<lineRate>0</lineRate>
395395
</regex>
396+
<regex>
397+
<pattern>com.github.checkstyle.regression.customcheck.UnitTestProcessorCheck</pattern>
398+
<branchRate>76</branchRate>
399+
<lineRate>92</lineRate>
400+
</regex>
396401
<regex>
397402
<pattern>com.github.checkstyle.regression.report.ReportGenerator</pattern>
398403
<branchRate>0</branchRate>
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
////////////////////////////////////////////////////////////////////////////////
2+
// checkstyle: Checks Java source code for adherence to a set of rules.
3+
// Copyright (C) 2001-2017 the original author or authors.
4+
//
5+
// This library is free software; you can redistribute it and/or
6+
// modify it under the terms of the GNU Lesser General Public
7+
// License as published by the Free Software Foundation; either
8+
// version 2.1 of the License, or (at your option) any later version.
9+
//
10+
// This library is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
// Lesser General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU Lesser General Public
16+
// License along with this library; if not, write to the Free Software
17+
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18+
////////////////////////////////////////////////////////////////////////////////
19+
20+
package com.github.checkstyle.regression.customcheck;
21+
22+
import java.io.File;
23+
import java.util.Collections;
24+
import java.util.List;
25+
26+
import com.puppycrawl.tools.checkstyle.Checker;
27+
import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
28+
import com.puppycrawl.tools.checkstyle.TreeWalker;
29+
import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
30+
import com.puppycrawl.tools.checkstyle.api.Configuration;
31+
32+
/**
33+
* Processes the specific file using our custom check.
34+
*
35+
* <p>This utility class would run a custom check on the file with the given path and return
36+
* the collected property info.</p>
37+
* @author LuoLiangchen
38+
*/
39+
public final class CustomCheckProcessor {
40+
/** Prevents instantiation. */
41+
private CustomCheckProcessor() {
42+
}
43+
44+
/**
45+
* Processes the file with the given path using the given custom check.
46+
* @param path the path of the file
47+
* @param checkClass the class of the custom check
48+
* @throws CheckstyleException failure when running the check
49+
*/
50+
public static void process(String path, Class<?> checkClass) throws CheckstyleException {
51+
final DefaultConfiguration moduleConfig = createModuleConfig(checkClass);
52+
final Configuration dc = createTreeWalkerConfig(moduleConfig);
53+
final Checker checker = new Checker();
54+
checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
55+
checker.configure(dc);
56+
final List<File> processedFiles = Collections.singletonList(new File(path));
57+
checker.process(processedFiles);
58+
}
59+
60+
/**
61+
* Creates {@link DefaultConfiguration} for the {@link TreeWalker}
62+
* based on the given {@link Configuration} instance.
63+
* @param config {@link Configuration} instance.
64+
* @return {@link DefaultConfiguration} for the {@link TreeWalker}
65+
* based on the given {@link Configuration} instance.
66+
*/
67+
private static DefaultConfiguration createTreeWalkerConfig(Configuration config) {
68+
final DefaultConfiguration dc =
69+
new DefaultConfiguration("configuration");
70+
final DefaultConfiguration twConf = createModuleConfig(TreeWalker.class);
71+
// make sure that the tests always run with this charset
72+
dc.addAttribute("charset", "UTF-8");
73+
dc.addChild(twConf);
74+
twConf.addChild(config);
75+
return dc;
76+
}
77+
78+
/**
79+
* Creates {@link DefaultConfiguration} for the given class.
80+
* @param clazz the class of module
81+
* @return the {@link DefaultConfiguration} of the module class
82+
*/
83+
private static DefaultConfiguration createModuleConfig(Class<?> clazz) {
84+
return new DefaultConfiguration(clazz.getName());
85+
}
86+
}
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
////////////////////////////////////////////////////////////////////////////////
2+
// checkstyle: Checks Java source code for adherence to a set of rules.
3+
// Copyright (C) 2001-2017 the original author or authors.
4+
//
5+
// This library is free software; you can redistribute it and/or
6+
// modify it under the terms of the GNU Lesser General Public
7+
// License as published by the Free Software Foundation; either
8+
// version 2.1 of the License, or (at your option) any later version.
9+
//
10+
// This library is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
// Lesser General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU Lesser General Public
16+
// License along with this library; if not, write to the Free Software
17+
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18+
////////////////////////////////////////////////////////////////////////////////
19+
20+
package com.github.checkstyle.regression.customcheck;
21+
22+
import java.util.Collections;
23+
import java.util.LinkedHashMap;
24+
import java.util.LinkedHashSet;
25+
import java.util.LinkedList;
26+
import java.util.List;
27+
import java.util.Map;
28+
import java.util.Optional;
29+
import java.util.Set;
30+
31+
import com.github.checkstyle.regression.data.ImmutableProperty;
32+
import com.github.checkstyle.regression.data.ModuleInfo;
33+
import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
34+
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
35+
import com.puppycrawl.tools.checkstyle.api.DetailAST;
36+
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
37+
38+
/**
39+
* The custom check which processes the unit test class of a checkstyle module and grab the
40+
* possible properties that could be used for generating config.
41+
*
42+
* <p>This check would walk through the {@code @Test} annotation, find variable definition
43+
* like {@code final DefaultConfiguration checkConfig = createModuleConfig(FooCheck.class)}
44+
* and grab the property info from {@link DefaultConfiguration#addAttribute(String, String)}
45+
* method call.</p>
46+
* @author LuoLiangchen
47+
*/
48+
public class UnitTestProcessorCheck extends AbstractCheck {
49+
/** The map of unit test method name to properties. */
50+
private static final Map<String, Set<ModuleInfo.Property>> UNIT_TEST_TO_PROPERTIES =
51+
new LinkedHashMap<>();
52+
53+
@Override
54+
public int[] getDefaultTokens() {
55+
return new int[] {
56+
TokenTypes.ANNOTATION,
57+
};
58+
}
59+
60+
@Override
61+
public int[] getRequiredTokens() {
62+
return new int[] {
63+
TokenTypes.ANNOTATION,
64+
};
65+
}
66+
67+
@Override
68+
public int[] getAcceptableTokens() {
69+
return new int[] {
70+
TokenTypes.ANNOTATION,
71+
};
72+
}
73+
74+
@Override
75+
public void visitToken(DetailAST ast) {
76+
if ("Test".equals(ast.findFirstToken(TokenTypes.IDENT).getText())) {
77+
final DetailAST methodDef = ast.getParent().getParent();
78+
final DetailAST methodBlock = methodDef.findFirstToken(TokenTypes.SLIST);
79+
final Optional<String> configVariableName =
80+
getModuleConfigVariableName(methodBlock);
81+
if (configVariableName.isPresent()) {
82+
final Set<ModuleInfo.Property> properties = new LinkedHashSet<>();
83+
84+
for (DetailAST expr : getAllChildrenWithToken(methodBlock, TokenTypes.EXPR)) {
85+
if (isAddAttributeMethodCall(expr.getFirstChild(), configVariableName.get())) {
86+
final DetailAST elist =
87+
expr.getFirstChild().findFirstToken(TokenTypes.ELIST);
88+
final String key = convertExprToText(elist.getFirstChild());
89+
final String value = convertExprToText(elist.getLastChild());
90+
properties.add(ImmutableProperty.builder().name(key).value(value).build());
91+
}
92+
}
93+
94+
if (!UNIT_TEST_TO_PROPERTIES.containsValue(properties)) {
95+
final String methodName = methodDef.findFirstToken(TokenTypes.IDENT).getText();
96+
UNIT_TEST_TO_PROPERTIES.put(methodName, properties);
97+
}
98+
}
99+
}
100+
}
101+
102+
/**
103+
* Clears the map of unit test method name to properties.
104+
*/
105+
public static void clearUnitTestToPropertiesMap() {
106+
UNIT_TEST_TO_PROPERTIES.clear();
107+
}
108+
109+
/**
110+
* Gets the map of unit test method name to properties.
111+
* @return the map of unit test method name to properties
112+
*/
113+
public static Map<String, Set<ModuleInfo.Property>> getUnitTestToPropertiesMap() {
114+
return Collections.unmodifiableMap(UNIT_TEST_TO_PROPERTIES);
115+
}
116+
117+
/**
118+
* Gets the module config variable name, if it exists.
119+
* @param methodBlock the UT method block ast, which should have a type {@link TokenTypes#SLIST}
120+
* @return the optional variable name, if it exists
121+
*/
122+
private static Optional<String> getModuleConfigVariableName(DetailAST methodBlock) {
123+
Optional<String> returnValue = Optional.empty();
124+
125+
for (DetailAST ast = methodBlock.getFirstChild(); ast != null; ast = ast.getNextSibling()) {
126+
if (ast.getType() == TokenTypes.VARIABLE_DEF) {
127+
final DetailAST type = ast.findFirstToken(TokenTypes.TYPE);
128+
final DetailAST assign = ast.findFirstToken(TokenTypes.ASSIGN);
129+
if (isDefaultConfigurationType(type) && isCreateModuleConfigAssign(assign)) {
130+
returnValue = Optional.of(type.getNextSibling().getText());
131+
break;
132+
}
133+
}
134+
}
135+
136+
return returnValue;
137+
}
138+
139+
/**
140+
* Checks whether this {@link TokenTypes#TYPE} ast is {@link DefaultConfiguration}.
141+
* @param ast the {@link TokenTypes#TYPE} ast
142+
* @return true if the type is {@link DefaultConfiguration}
143+
*/
144+
private static boolean isDefaultConfigurationType(DetailAST ast) {
145+
return "DefaultConfiguration".equals(ast.getFirstChild().getText());
146+
}
147+
148+
/**
149+
* Checks whether this {@link TokenTypes#ASSIGN} ast contains
150+
* a {@code createModuleConfig} method call.
151+
* @param ast the {@link TokenTypes#ASSIGN} ast
152+
* @return true if the assignment contains a {@code createModuleConfig} method call
153+
*/
154+
private static boolean isCreateModuleConfigAssign(DetailAST ast) {
155+
final boolean result;
156+
157+
if (ast == null) {
158+
result = false;
159+
}
160+
else {
161+
final DetailAST exprChild = ast.getFirstChild().getFirstChild();
162+
result = exprChild.getType() == TokenTypes.METHOD_CALL
163+
&& exprChild.getFirstChild().getType() == TokenTypes.IDENT
164+
&& "createModuleConfig".equals(exprChild.getFirstChild().getText());
165+
}
166+
167+
return result;
168+
}
169+
170+
/**
171+
* Gets all children of a ast with the given tokens type.
172+
* @param parent the parent ast
173+
* @param type the given tokens type
174+
* @return the children with the given tokens type
175+
*/
176+
private static List<DetailAST> getAllChildrenWithToken(DetailAST parent, int type) {
177+
final List<DetailAST> returnValue = new LinkedList<>();
178+
179+
for (DetailAST ast = parent.getFirstChild(); ast != null; ast = ast.getNextSibling()) {
180+
if (ast.getType() == type) {
181+
returnValue.add(ast);
182+
}
183+
}
184+
185+
return returnValue;
186+
}
187+
188+
/**
189+
* Checks whether this expression is an {@code addAttribute} method call on an instance with
190+
* the given variable name.
191+
* @param ast the ast to check
192+
* @param variableName the given variable name of the module config instance
193+
* @return true if the expression is a valid {@code addAttribute} method call
194+
*/
195+
private static boolean isAddAttributeMethodCall(DetailAST ast, String variableName) {
196+
final boolean result;
197+
198+
if (ast.getType() == TokenTypes.METHOD_CALL
199+
&& ast.getFirstChild().getType() == TokenTypes.DOT) {
200+
final DetailAST dot = ast.getFirstChild();
201+
result = variableName.equals(dot.getFirstChild().getText())
202+
&& "addAttribute".equals(dot.getLastChild().getText());
203+
}
204+
else {
205+
result = false;
206+
}
207+
208+
return result;
209+
}
210+
211+
/**
212+
* Converts an expression to raw text.
213+
* @param ast the expression ast to convert
214+
* @return the converted raw text
215+
*/
216+
private static String convertExprToText(DetailAST ast) {
217+
final String original = ast.getFirstChild().getText();
218+
return original.substring(1, original.length() - 1);
219+
}
220+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
////////////////////////////////////////////////////////////////////////////////
2+
// checkstyle: Checks Java source code for adherence to a set of rules.
3+
// Copyright (C) 2001-2017 the original author or authors.
4+
//
5+
// This library is free software; you can redistribute it and/or
6+
// modify it under the terms of the GNU Lesser General Public
7+
// License as published by the Free Software Foundation; either
8+
// version 2.1 of the License, or (at your option) any later version.
9+
//
10+
// This library is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
// Lesser General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU Lesser General Public
16+
// License along with this library; if not, write to the Free Software
17+
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18+
////////////////////////////////////////////////////////////////////////////////
19+
20+
/**
21+
* Contains custom checks and utility classes to process files in checkstyle repository.
22+
*/
23+
package com.github.checkstyle.regression.customcheck;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
////////////////////////////////////////////////////////////////////////////////
2+
// checkstyle: Checks Java source code for adherence to a set of rules.
3+
// Copyright (C) 2001-2017 the original author or authors.
4+
//
5+
// This library is free software; you can redistribute it and/or
6+
// modify it under the terms of the GNU Lesser General Public
7+
// License as published by the Free Software Foundation; either
8+
// version 2.1 of the License, or (at your option) any later version.
9+
//
10+
// This library is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
// Lesser General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU Lesser General Public
16+
// License along with this library; if not, write to the Free Software
17+
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18+
////////////////////////////////////////////////////////////////////////////////
19+
20+
package com.github.checkstyle.regression.customcheck;
21+
22+
import static com.github.checkstyle.regression.internal.TestUtils.assertUtilsClassHasPrivateConstructor;
23+
24+
import org.junit.Test;
25+
26+
public class CustomCheckProcessorTest {
27+
@Test
28+
public void testIsProperUtilsClass() throws Exception {
29+
assertUtilsClassHasPrivateConstructor(CustomCheckProcessor.class);
30+
}
31+
}

0 commit comments

Comments
 (0)