From e92a0072a45593a8e52649acca2a9d0915f1582b Mon Sep 17 00:00:00 2001 From: Luolc Date: Tue, 22 Aug 2017 02:43:51 +0800 Subject: [PATCH] Issue #79: Processing UT changes to get possible property values --- config/import-control.xml | 4 + config/pmd.xml | 6 +- pom.xml | 5 + .../customcheck/CustomCheckProcessor.java | 86 +++++++ .../customcheck/UnitTestProcessorCheck.java | 220 ++++++++++++++++++ .../regression/customcheck/package-info.java | 23 ++ .../customcheck/CustomCheckProcessorTest.java | 31 +++ .../UnitTestProcessorCheckTest.java | 103 ++++++++ ...ModuleConfigAsLocalVariableModuleTest.java | 149 ++++++++++++ 9 files changed, 626 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/github/checkstyle/regression/customcheck/CustomCheckProcessor.java create mode 100644 src/main/java/com/github/checkstyle/regression/customcheck/UnitTestProcessorCheck.java create mode 100644 src/main/java/com/github/checkstyle/regression/customcheck/package-info.java create mode 100644 src/test/java/com/github/checkstyle/regression/customcheck/CustomCheckProcessorTest.java create mode 100644 src/test/java/com/github/checkstyle/regression/customcheck/UnitTestProcessorCheckTest.java create mode 100644 src/test/resources/com/github/checkstyle/regression/customcheck/unittestprocessorcheck/InputModuleConfigAsLocalVariableModuleTest.java diff --git a/config/import-control.xml b/config/import-control.xml index b2e1110..5f59e19 100644 --- a/config/import-control.xml +++ b/config/import-control.xml @@ -34,6 +34,10 @@ + + + + diff --git a/config/pmd.xml b/config/pmd.xml index 6ab7c5e..d64bd8d 100644 --- a/config/pmd.xml +++ b/config/pmd.xml @@ -34,7 +34,11 @@ - + + + + + diff --git a/pom.xml b/pom.xml index c355d15..19033ae 100644 --- a/pom.xml +++ b/pom.xml @@ -393,6 +393,11 @@ 0 0 + + com.github.checkstyle.regression.customcheck.UnitTestProcessorCheck + 76 + 92 + com.github.checkstyle.regression.report.ReportGenerator 0 diff --git a/src/main/java/com/github/checkstyle/regression/customcheck/CustomCheckProcessor.java b/src/main/java/com/github/checkstyle/regression/customcheck/CustomCheckProcessor.java new file mode 100644 index 0000000..53757fd --- /dev/null +++ b/src/main/java/com/github/checkstyle/regression/customcheck/CustomCheckProcessor.java @@ -0,0 +1,86 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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.io.File; +import java.util.Collections; +import java.util.List; + +import com.puppycrawl.tools.checkstyle.Checker; +import com.puppycrawl.tools.checkstyle.DefaultConfiguration; +import com.puppycrawl.tools.checkstyle.TreeWalker; +import com.puppycrawl.tools.checkstyle.api.CheckstyleException; +import com.puppycrawl.tools.checkstyle.api.Configuration; + +/** + * Processes the specific file using our custom check. + * + *

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. + * @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..56e97b6 --- /dev/null +++ b/src/main/java/com/github/checkstyle/regression/customcheck/UnitTestProcessorCheck.java @@ -0,0 +1,220 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 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. + * + *

This 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.

+ * @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 = convertExprToText(elist.getFirstChild()); + final String value = convertExprToText(elist.getLastChild()); + 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()); + 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 exprChild = ast.getFirstChild().getFirstChild(); + result = exprChild.getType() == TokenTypes.METHOD_CALL + && exprChild.getFirstChild().getType() == TokenTypes.IDENT + && "createModuleConfig".equals(exprChild.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 to raw text. + * @param ast the expression ast to convert + * @return the converted raw text + */ + private static String convertExprToText(DetailAST ast) { + final String original = ast.getFirstChild().getText(); + return original.substring(1, original.length() - 1); + } +} 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..2ec1062 --- /dev/null +++ b/src/test/java/com/github/checkstyle/regression/customcheck/UnitTestProcessorCheckTest.java @@ -0,0 +1,103 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 static org.junit.Assert.assertNotNull; + +import java.io.InputStreamReader; +import java.net.URL; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; +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.ModuleExtractInfo; +import com.github.checkstyle.regression.data.ModuleInfo; +import com.github.checkstyle.regression.extract.ExtractInfoProcessor; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; + +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()); + } + + @Test + public void testAllModuleUnitTests() throws Exception { + final GsonBuilder gsonBuilder = new GsonBuilder(); + for (TypeAdapterFactory factory : ServiceLoader.load(TypeAdapterFactory.class)) { + gsonBuilder.registerTypeAdapterFactory(factory); + } + final Gson gson = gsonBuilder.create(); + final String url = "https://gist.githubusercontent.com/Luolc/783aebf09efa1c738f9d535316e97" + + "895/raw/9e02a1698adf8bc19e4188b12801d0c7ae575d8c/checkstyle_modules_e51f9458.json"; + List modules; + try (InputStreamReader reader = new InputStreamReader(new URL(url).openStream(), Charset.forName("UTF-8"))) { + modules = gson.fromJson(reader, new TypeToken>() {}.getType()); + } + assertNotNull(modules); + + final String repoPath = "D:\\personal\\develop\\java\\checkstyle\\checkstyle"; +// Map map = ExtractInfoProcessor.getModuleExtractInfos(repoPath, "master"); + int a = 1; + } + + 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); + } +}