This utility class would run a custom check on the file with the given path and return
+ * the collected property info.
+ * @author LuoLiangchen
+ */
+public final class CustomCheckProcessor {
+ /** Prevents instantiation. */
+ private CustomCheckProcessor() {
+ }
+
+ /**
+ * Processes the file with the given path using the given custom check.
+ *
+ * The custom check needs a public/static function to retrieve the processing result.
+ * @param path the path of the file
+ * @param checkClass the class of the custom check
+ * @throws CheckstyleException failure when running the check
+ */
+ public static void process(String path, Class> checkClass) throws CheckstyleException {
+ final DefaultConfiguration moduleConfig = createModuleConfig(checkClass);
+ final Configuration dc = createTreeWalkerConfig(moduleConfig);
+ final Checker checker = new Checker();
+ checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
+ checker.configure(dc);
+ final List processedFiles = Collections.singletonList(new File(path));
+ checker.process(processedFiles);
+ }
+
+ /**
+ * Creates {@link DefaultConfiguration} for the {@link TreeWalker}
+ * based on the given {@link Configuration} instance.
+ * @param config {@link Configuration} instance.
+ * @return {@link DefaultConfiguration} for the {@link TreeWalker}
+ * based on the given {@link Configuration} instance.
+ */
+ private static DefaultConfiguration createTreeWalkerConfig(Configuration config) {
+ final DefaultConfiguration dc =
+ new DefaultConfiguration("configuration");
+ final DefaultConfiguration twConf = createModuleConfig(TreeWalker.class);
+ // make sure that the tests always run with this charset
+ dc.addAttribute("charset", "UTF-8");
+ dc.addChild(twConf);
+ twConf.addChild(config);
+ return dc;
+ }
+
+ /**
+ * Creates {@link DefaultConfiguration} for the given class.
+ * @param clazz the class of module
+ * @return the {@link DefaultConfiguration} of the module class
+ */
+ private static DefaultConfiguration createModuleConfig(Class> clazz) {
+ return new DefaultConfiguration(clazz.getName());
+ }
+}
diff --git a/src/main/java/com/github/checkstyle/regression/customcheck/UnitTestProcessorCheck.java b/src/main/java/com/github/checkstyle/regression/customcheck/UnitTestProcessorCheck.java
new file mode 100644
index 0000000..3d636e4
--- /dev/null
+++ b/src/main/java/com/github/checkstyle/regression/customcheck/UnitTestProcessorCheck.java
@@ -0,0 +1,268 @@
+////////////////////////////////////////////////////////////////////////////////
+// checkstyle: Checks Java source code for adherence to a set of rules.
+// Copyright (C) 2001-2017 the original author or authors.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+////////////////////////////////////////////////////////////////////////////////
+
+package com.github.checkstyle.regression.customcheck;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.github.checkstyle.regression.data.ImmutableProperty;
+import com.github.checkstyle.regression.data.ModuleInfo;
+import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
+import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
+import com.puppycrawl.tools.checkstyle.api.DetailAST;
+import com.puppycrawl.tools.checkstyle.api.TokenTypes;
+
+/**
+ * The custom check which processes the unit test class of a checkstyle module and grab the
+ * possible properties that could be used for generating config.
+ *
+ * The check would walk through the {@code @Test} annotation, find variable definition
+ * like {@code final DefaultConfiguration checkConfig = createModuleConfig(FooCheck.class)}
+ * and grab the property info from {@link DefaultConfiguration#addAttribute(String, String)}
+ * method call.
+ *
+ * The check also support to detect module config which defined as a class field. This kind
+ * of config is detected by the assignment in the unit test method, like {@code checkConfig =
+ * createModuleConfig(FooCheck.class)}, where {@code checkConfig} could be a class field.
+ * @author LuoLiangchen
+ */
+public class UnitTestProcessorCheck extends AbstractCheck {
+ /** The map of unit test method name to properties. */
+ private static final Map> UNIT_TEST_TO_PROPERTIES =
+ new LinkedHashMap<>();
+
+ @Override
+ public int[] getDefaultTokens() {
+ return new int[] {
+ TokenTypes.ANNOTATION,
+ };
+ }
+
+ @Override
+ public int[] getRequiredTokens() {
+ return new int[] {
+ TokenTypes.ANNOTATION,
+ };
+ }
+
+ @Override
+ public int[] getAcceptableTokens() {
+ return new int[] {
+ TokenTypes.ANNOTATION,
+ };
+ }
+
+ @Override
+ public void visitToken(DetailAST ast) {
+ if ("Test".equals(ast.findFirstToken(TokenTypes.IDENT).getText())) {
+ final DetailAST methodDef = ast.getParent().getParent();
+ final DetailAST methodBlock = methodDef.findFirstToken(TokenTypes.SLIST);
+ final Optional configVariableName =
+ getModuleConfigVariableName(methodBlock);
+ if (configVariableName.isPresent()) {
+ final Set properties = new LinkedHashSet<>();
+
+ for (DetailAST expr : getAllChildrenWithToken(methodBlock, TokenTypes.EXPR)) {
+ if (isAddAttributeMethodCall(expr.getFirstChild(), configVariableName.get())) {
+ final DetailAST elist =
+ expr.getFirstChild().findFirstToken(TokenTypes.ELIST);
+ final String key =
+ convertExpressionToText(elist.getFirstChild().getFirstChild());
+ final String value =
+ convertExpressionToText(elist.getLastChild().getFirstChild());
+ properties.add(ImmutableProperty.builder().name(key).value(value).build());
+ }
+ }
+
+ if (!UNIT_TEST_TO_PROPERTIES.containsValue(properties)) {
+ final String methodName = methodDef.findFirstToken(TokenTypes.IDENT).getText();
+ UNIT_TEST_TO_PROPERTIES.put(methodName, properties);
+ }
+ }
+ }
+ }
+
+ /**
+ * Clears the map of unit test method name to properties.
+ */
+ public static void clearUnitTestToPropertiesMap() {
+ UNIT_TEST_TO_PROPERTIES.clear();
+ }
+
+ /**
+ * Gets the map of unit test method name to properties.
+ * @return the map of unit test method name to properties
+ */
+ public static Map> getUnitTestToPropertiesMap() {
+ return Collections.unmodifiableMap(UNIT_TEST_TO_PROPERTIES);
+ }
+
+ /**
+ * Gets the module config variable name, if it exists.
+ * @param methodBlock the UT method block ast, which should have a type {@link TokenTypes#SLIST}
+ * @return the optional variable name, if it exists
+ */
+ private static Optional getModuleConfigVariableName(DetailAST methodBlock) {
+ Optional returnValue = Optional.empty();
+
+ for (DetailAST ast = methodBlock.getFirstChild(); ast != null; ast = ast.getNextSibling()) {
+ if (ast.getType() == TokenTypes.VARIABLE_DEF) {
+ final DetailAST type = ast.findFirstToken(TokenTypes.TYPE);
+ final DetailAST assign = ast.findFirstToken(TokenTypes.ASSIGN);
+ if (isDefaultConfigurationType(type) && isCreateModuleConfigAssign(assign)) {
+ returnValue = Optional.of(type.getNextSibling().getText());
+ }
+ }
+ else if (ast.getType() == TokenTypes.EXPR
+ && ast.getFirstChild().getType() == TokenTypes.ASSIGN) {
+ final DetailAST exprChild = ast.getFirstChild();
+ if (isCreateModuleConfigAssign(exprChild)) {
+ returnValue = Optional.of(exprChild.getFirstChild().getText());
+ }
+ }
+ if (returnValue.isPresent()) {
+ break;
+ }
+ }
+
+ return returnValue;
+ }
+
+ /**
+ * Checks whether this {@link TokenTypes#TYPE} ast is {@link DefaultConfiguration}.
+ * @param ast the {@link TokenTypes#TYPE} ast
+ * @return true if the type is {@link DefaultConfiguration}
+ */
+ private static boolean isDefaultConfigurationType(DetailAST ast) {
+ return "DefaultConfiguration".equals(ast.getFirstChild().getText());
+ }
+
+ /**
+ * Checks whether this {@link TokenTypes#ASSIGN} ast contains
+ * a {@code createModuleConfig} method call.
+ * @param ast the {@link TokenTypes#ASSIGN} ast
+ * @return true if the assignment contains a {@code createModuleConfig} method call
+ */
+ private static boolean isCreateModuleConfigAssign(DetailAST ast) {
+ final boolean result;
+
+ if (ast == null) {
+ result = false;
+ }
+ else {
+ final DetailAST methodCall;
+ if (ast.findFirstToken(TokenTypes.METHOD_CALL) == null) {
+ methodCall = ast.getFirstChild().getFirstChild();
+ }
+ else {
+ methodCall = ast.findFirstToken(TokenTypes.METHOD_CALL);
+ }
+ result = methodCall.getType() == TokenTypes.METHOD_CALL
+ && methodCall.getFirstChild().getType() == TokenTypes.IDENT
+ && "createModuleConfig".equals(methodCall.getFirstChild().getText());
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets all children of a ast with the given tokens type.
+ * @param parent the parent ast
+ * @param type the given tokens type
+ * @return the children with the given tokens type
+ */
+ private static List getAllChildrenWithToken(DetailAST parent, int type) {
+ final List returnValue = new LinkedList<>();
+
+ for (DetailAST ast = parent.getFirstChild(); ast != null; ast = ast.getNextSibling()) {
+ if (ast.getType() == type) {
+ returnValue.add(ast);
+ }
+ }
+
+ return returnValue;
+ }
+
+ /**
+ * Checks whether this expression is an {@code addAttribute} method call on an instance with
+ * the given variable name.
+ * @param ast the ast to check
+ * @param variableName the given variable name of the module config instance
+ * @return true if the expression is a valid {@code addAttribute} method call
+ */
+ private static boolean isAddAttributeMethodCall(DetailAST ast, String variableName) {
+ final boolean result;
+
+ if (ast.getType() == TokenTypes.METHOD_CALL
+ && ast.getFirstChild().getType() == TokenTypes.DOT) {
+ final DetailAST dot = ast.getFirstChild();
+ result = variableName.equals(dot.getFirstChild().getText())
+ && "addAttribute".equals(dot.getLastChild().getText());
+ }
+ else {
+ result = false;
+ }
+
+ return result;
+ }
+
+ /**
+ * Converts an expression content to raw text.
+ * @param ast the first child of expression ast to convert
+ * @return the converted raw text
+ */
+ private String convertExpressionToText(DetailAST ast) {
+ String result = null;
+ if (ast.getType() == TokenTypes.STRING_LITERAL) {
+ final String original = ast.getText();
+ result = original.substring(1, original.length() - 1);
+ }
+ else if (ast.getType() == TokenTypes.PLUS) {
+ result = convertExpressionToText(ast.getFirstChild())
+ + convertExpressionToText(ast.getLastChild());
+ }
+ else if (ast.getType() == TokenTypes.LITERAL_NULL) {
+ result = ast.getText();
+ }
+ else if (ast.getType() == TokenTypes.METHOD_CALL) {
+ final String line = getFileContents().getLine(ast.getLineNo() - 1);
+ final Pattern pattern = Pattern.compile(
+ "\\.addAttribute\\(.+, (?:.+\\.)*(.+)\\.toString\\(\\)\\);");
+ final Matcher matcher = pattern.matcher(line);
+ if (matcher.find()) {
+ result = matcher.group(1);
+ }
+ }
+
+ if (result == null) {
+ throw new IllegalStateException("the processor cannot support this ast: " + ast);
+ }
+
+ return result;
+ }
+}
diff --git a/src/main/java/com/github/checkstyle/regression/customcheck/package-info.java b/src/main/java/com/github/checkstyle/regression/customcheck/package-info.java
new file mode 100644
index 0000000..1a10924
--- /dev/null
+++ b/src/main/java/com/github/checkstyle/regression/customcheck/package-info.java
@@ -0,0 +1,23 @@
+////////////////////////////////////////////////////////////////////////////////
+// checkstyle: Checks Java source code for adherence to a set of rules.
+// Copyright (C) 2001-2017 the original author or authors.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Contains custom checks and utility classes to process files in checkstyle repository.
+ */
+package com.github.checkstyle.regression.customcheck;
diff --git a/src/test/java/com/github/checkstyle/regression/customcheck/CustomCheckProcessorTest.java b/src/test/java/com/github/checkstyle/regression/customcheck/CustomCheckProcessorTest.java
new file mode 100644
index 0000000..0ae4f18
--- /dev/null
+++ b/src/test/java/com/github/checkstyle/regression/customcheck/CustomCheckProcessorTest.java
@@ -0,0 +1,31 @@
+////////////////////////////////////////////////////////////////////////////////
+// checkstyle: Checks Java source code for adherence to a set of rules.
+// Copyright (C) 2001-2017 the original author or authors.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+////////////////////////////////////////////////////////////////////////////////
+
+package com.github.checkstyle.regression.customcheck;
+
+import static com.github.checkstyle.regression.internal.TestUtils.assertUtilsClassHasPrivateConstructor;
+
+import org.junit.Test;
+
+public class CustomCheckProcessorTest {
+ @Test
+ public void testIsProperUtilsClass() throws Exception {
+ assertUtilsClassHasPrivateConstructor(CustomCheckProcessor.class);
+ }
+}
diff --git a/src/test/java/com/github/checkstyle/regression/customcheck/UnitTestProcessorCheckTest.java b/src/test/java/com/github/checkstyle/regression/customcheck/UnitTestProcessorCheckTest.java
new file mode 100644
index 0000000..86b1464
--- /dev/null
+++ b/src/test/java/com/github/checkstyle/regression/customcheck/UnitTestProcessorCheckTest.java
@@ -0,0 +1,71 @@
+////////////////////////////////////////////////////////////////////////////////
+// checkstyle: Checks Java source code for adherence to a set of rules.
+// Copyright (C) 2001-2017 the original author or authors.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+////////////////////////////////////////////////////////////////////////////////
+
+package com.github.checkstyle.regression.customcheck;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.junit.Test;
+
+import com.github.checkstyle.regression.data.ImmutableProperty;
+import com.github.checkstyle.regression.data.ModuleInfo;
+
+public class UnitTestProcessorCheckTest {
+ private static String getInputPath(String name) {
+ return "src/test/resources/com/github/checkstyle/regression/customcheck/"
+ + "unittestprocessorcheck/" + name;
+ }
+
+ @Test
+ public void testModuleConfigAsLocalVariable() throws Exception {
+ UnitTestProcessorCheck.clearUnitTestToPropertiesMap();
+ CustomCheckProcessor.process(
+ getInputPath("InputModuleConfigAsLocalVariableModuleTest.java"),
+ UnitTestProcessorCheck.class);
+ final Map> map =
+ UnitTestProcessorCheck.getUnitTestToPropertiesMap();
+
+ assertEquals("The size of UTs is wrong", 7, map.size());
+ assertPropertiesEquals(map, "testDefault");
+ assertPropertiesEquals(map, "testFormat",
+ ImmutableProperty.builder().name("format").value("^$").build());
+ assertPropertiesEquals(map, "testMethodsAndLambdas",
+ ImmutableProperty.builder().name("max").value("1").build());
+ assertPropertiesEquals(map, "testLambdasOnly",
+ ImmutableProperty.builder().name("tokens").value("LAMBDA").build());
+ assertPropertiesEquals(map, "testMethodsOnly",
+ ImmutableProperty.builder().name("tokens").value("METHOD_DEF").build());
+ assertPropertiesEquals(map, "testWithReturnOnlyAsTokens",
+ ImmutableProperty.builder().name("tokens").value("LITERAL_RETURN").build());
+ assertPropertiesEquals(map, "testMaxForVoid",
+ ImmutableProperty.builder().name("max").value("2").build(),
+ ImmutableProperty.builder().name("maxForVoid").value("0").build());
+ }
+
+ private static void assertPropertiesEquals(Map> map,
+ String key, ModuleInfo.Property... properties) {
+ assertEquals("properties is wrong from UT:" + key,
+ Arrays.stream(properties).collect(Collectors.toSet()), map.get(key));
+ }
+}
diff --git a/src/test/resources/com/github/checkstyle/regression/customcheck/unittestprocessorcheck/InputModuleConfigAsLocalVariableModuleTest.java b/src/test/resources/com/github/checkstyle/regression/customcheck/unittestprocessorcheck/InputModuleConfigAsLocalVariableModuleTest.java
new file mode 100644
index 0000000..4a9a1ed
--- /dev/null
+++ b/src/test/resources/com/github/checkstyle/regression/customcheck/unittestprocessorcheck/InputModuleConfigAsLocalVariableModuleTest.java
@@ -0,0 +1,149 @@
+////////////////////////////////////////////////////////////////////////////////
+// checkstyle: Checks Java source code for adherence to a set of rules.
+// Copyright (C) 2001-2017 the original author or authors.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+////////////////////////////////////////////////////////////////////////////////
+
+package com.github.checkstyle.regression.customcheck.unittestprocessorcheck;
+
+import static com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheck.MSG_KEY;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
+import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
+import com.puppycrawl.tools.checkstyle.api.DetailAST;
+import com.puppycrawl.tools.checkstyle.api.TokenTypes;
+import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
+
+public class InputModuleConfigAsLocalVariableModuleTest extends AbstractModuleTestSupport {
+ @Override
+ protected String getPackageLocation() {
+ return "com/puppycrawl/tools/checkstyle/checks/coding/returncount";
+ }
+
+ @Test
+ public void testDefault() throws Exception {
+ final DefaultConfiguration checkConfig =
+ createModuleConfig(ReturnCountCheck.class);
+ final String[] expected = {
+ "18:5: " + getCheckMessage(MSG_KEY, 7, 1),
+ "30:5: " + getCheckMessage(MSG_KEY, 2, 1),
+ "35:17: " + getCheckMessage(MSG_KEY, 6, 1),
+ "49:5: " + getCheckMessage(MSG_KEY, 7, 2),
+ };
+ verify(checkConfig, getPath("InputReturnCountSwitches.java"), expected);
+ }
+
+ @Test
+ public void testFormat() throws Exception {
+ final DefaultConfiguration checkConfig =
+ createModuleConfig(ReturnCountCheck.class);
+ checkConfig.addAttribute("format", "^$");
+ final String[] expected = {
+ "5:5: " + getCheckMessage(MSG_KEY, 7, 2),
+ "18:5: " + getCheckMessage(MSG_KEY, 7, 1),
+ "30:5: " + getCheckMessage(MSG_KEY, 2, 1),
+ "35:17: " + getCheckMessage(MSG_KEY, 6, 1),
+ "49:5: " + getCheckMessage(MSG_KEY, 7, 2),
+ };
+ verify(checkConfig, getPath("InputReturnCountSwitches.java"), expected);
+ }
+
+ @Test
+ public void testMethodsAndLambdas() throws Exception {
+ final DefaultConfiguration checkConfig = createModuleConfig(ReturnCountCheck.class);
+ checkConfig.addAttribute("max", "1");
+ final String[] expected = {
+ "15:55: " + getCheckMessage(MSG_KEY, 2, 1),
+ "27:49: " + getCheckMessage(MSG_KEY, 2, 1),
+ "34:42: " + getCheckMessage(MSG_KEY, 3, 1),
+ "41:5: " + getCheckMessage(MSG_KEY, 2, 1),
+ "49:57: " + getCheckMessage(MSG_KEY, 2, 1),
+ };
+ verify(checkConfig, getPath("InputReturnCountLambda.java"), expected);
+ }
+
+ @Test
+ public void testLambdasOnly() throws Exception {
+ final DefaultConfiguration checkConfig = createModuleConfig(ReturnCountCheck.class);
+ checkConfig.addAttribute("tokens", "LAMBDA");
+ final String[] expected = {
+ "34:42: " + getCheckMessage(MSG_KEY, 3, 2),
+ };
+ verify(checkConfig, getPath("InputReturnCountLambda.java"), expected);
+ }
+
+ @Test
+ public void testMethodsOnly() throws Exception {
+ final DefaultConfiguration checkConfig = createModuleConfig(ReturnCountCheck.class);
+ checkConfig.addAttribute("tokens", "METHOD_DEF");
+ final String[] expected = {
+ "26:5: " + getCheckMessage(MSG_KEY, 3, 2),
+ "33:5: " + getCheckMessage(MSG_KEY, 4, 2),
+ "41:5: " + getCheckMessage(MSG_KEY, 4, 2),
+ "56:5: " + getCheckMessage(MSG_KEY, 3, 2),
+ };
+ verify(checkConfig, getPath("InputReturnCountLambda.java"), expected);
+ }
+
+ @Test
+ public void testWithReturnOnlyAsTokens() throws Exception {
+ final DefaultConfiguration checkConfig = createModuleConfig(ReturnCountCheck.class);
+ checkConfig.addAttribute("tokens", "LITERAL_RETURN");
+ final String[] expected = CommonUtils.EMPTY_STRING_ARRAY;
+ verify(checkConfig, getPath("InputReturnCountLambda.java"), expected);
+ }
+
+ @Test
+ public void testImproperToken() {
+ final ReturnCountCheck check = new ReturnCountCheck();
+
+ final DetailAST classDefAst = new DetailAST();
+ classDefAst.setType(TokenTypes.CLASS_DEF);
+
+ try {
+ check.visitToken(classDefAst);
+ Assert.fail("IllegalStateException is expected");
+ }
+ catch (IllegalStateException ex) {
+ // it is OK
+ }
+
+ try {
+ check.leaveToken(classDefAst);
+ Assert.fail("IllegalStateException is expected");
+ }
+ catch (IllegalStateException ex) {
+ // it is OK
+ }
+ }
+
+ @Test
+ public void testMaxForVoid() throws Exception {
+ final DefaultConfiguration checkConfig = createModuleConfig(ReturnCountCheck.class);
+ checkConfig.addAttribute("max", "2");
+ checkConfig.addAttribute("maxForVoid", "0");
+ final String[] expected = {
+ "4:5: " + getCheckMessage(MSG_KEY, 1, 0),
+ "8:5: " + getCheckMessage(MSG_KEY, 1, 0),
+ "14:5: " + getCheckMessage(MSG_KEY, 2, 0),
+ "30:5: " + getCheckMessage(MSG_KEY, 3, 2),
+ };
+ verify(checkConfig, getPath("InputReturnCountVoid.java"), expected);
+ }
+}