diff --git a/sevntu-checks/src/main/java/com/github/sevntu/checkstyle/checks/design/CheckstyleTestMakeupCheck.java b/sevntu-checks/src/main/java/com/github/sevntu/checkstyle/checks/design/CheckstyleTestMakeupCheck.java
new file mode 100644
index 0000000000..2cc0fc7b3e
--- /dev/null
+++ b/sevntu-checks/src/main/java/com/github/sevntu/checkstyle/checks/design/CheckstyleTestMakeupCheck.java
@@ -0,0 +1,426 @@
+////////////////////////////////////////////////////////////////////////////////
+// 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.sevntu.checkstyle.checks.design;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import com.github.sevntu.checkstyle.Utils;
+import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
+import com.puppycrawl.tools.checkstyle.api.DetailAST;
+import com.puppycrawl.tools.checkstyle.api.TokenTypes;
+import com.puppycrawl.tools.checkstyle.utils.AnnotationUtility;
+import com.puppycrawl.tools.checkstyle.utils.ScopeUtils;
+
+/**
+ *
+ * Custom check to ensure Checkstyle tests are designed correctly.
+ *
+ *
+ * Rationale: This check was made to ensure tests follow a specific design implementation
+ * so 3rd party utilities like the regression utility can parse the tests for informaiton
+ * used in creating regression reports.
+ *
+ *
+ * Check have following options:
+ *
+ *
+ * -
+ * createMethodRegexp - Regular expression for matching a create configuration method by name. This
+ * is the name of the method that starts creating a custom module configuration to be used for
+ * verifying results for regression purposes.
+ * Default value is {@code create(Root|Module)Config|getModuleConfig}.
+ *
+ * -
+ * verifyMethodRegexp - Regular expression for matching a verify method by name. This is the name
+ * of the method that verifies the execution results of the custom configuration created for
+ * regression. As such, it should accept the custom configuration as a parameter.
+ * Default value is {@code verify(Warns|Suppressed)?}.
+ *
+ *
+ *
+ * To configure the check to report incorrectly made checkstyle tests:
+ *
+ *
+ *
+ * <module name="CheckstyleTestMakeup"/>
+ *
+ *
+ * @author Richard Veach
+ */
+public class CheckstyleTestMakeupCheck extends AbstractCheck {
+ /** Violations message. */
+ public static final String MSG_KEY_CONFIG_NOT_ASSIGNED = "tester.config.not.assigned";
+ /** Violations message. */
+ public static final String MSG_KEY_CONFIG_NOT_ASSIGNED_WITH = "tester.config.not.assigned.with";
+ /** Violations message. */
+ public static final String MSG_KEY_CONFIG_NOT_ASSIGNED_PROPERLY =
+ "tester.config.not.assigned.properly";
+ /** Violations message. */
+ public static final String MSG_KEY_UNKNOWN_PROPERTY = "tester.unknown.property";
+ /** Violations message. */
+ public static final String MSG_KEY_CONFIG_NOT_FOUND = "tester.config.not.found";
+
+ /** Name of 'getPath' method. */
+ private static final String METHOD_GET_PATH = "getPath";
+
+ /** AST of method that is currently being examined. */
+ private DetailAST methodAst;
+ /** List of variable names that reference a file. */
+ private Set fileVariableNames = new HashSet<>();
+ /** List of variable names that reference a configuration. */
+ private Set checkConfigNames = new HashSet<>();
+ /** {@code true} if the 'verify' method was found in the method. */
+ private boolean foundVerify;
+
+ /** List of violations generated for a method. */
+ private final Map violations = new HashMap<>();
+
+ /** Regular expression for matching a create method by name. */
+ private Pattern createMethodRegexp = Pattern
+ .compile("create(Root|Module)Config|getModuleConfig");
+
+ /** Regular expression for matching a verify method by name. */
+ private Pattern verifyMethodRegexp = Pattern.compile("verify(Warns|Suppressed)?");
+
+ /**
+ * Setter for {@link #createMethodRegexp}.
+ * @param createMethodRegexp The value to set.
+ */
+ public void setCreateMethodRegexp(Pattern createMethodRegexp) {
+ this.createMethodRegexp = createMethodRegexp;
+ }
+
+ /**
+ * Setter for {@link #verifyMethodRegexp}.
+ * @param verifyMethodRegexp The value to set.
+ */
+ public void setVerifyMethodRegexp(Pattern verifyMethodRegexp) {
+ this.verifyMethodRegexp = verifyMethodRegexp;
+ }
+
+ @Override
+ public int[] getDefaultTokens() {
+ return new int[] {
+ TokenTypes.METHOD_DEF,
+ TokenTypes.VARIABLE_DEF,
+ TokenTypes.METHOD_CALL,
+ };
+ }
+
+ @Override
+ public int[] getAcceptableTokens() {
+ return getDefaultTokens();
+ }
+
+ @Override
+ public int[] getRequiredTokens() {
+ return getDefaultTokens();
+ }
+
+ @Override
+ public void beginTree(DetailAST rootAST) {
+ resetInternalFields();
+ }
+
+ @Override
+ public void visitToken(DetailAST ast) {
+ switch (ast.getType()) {
+ case TokenTypes.METHOD_DEF:
+ checkMethod(ast);
+ break;
+ case TokenTypes.VARIABLE_DEF:
+ checkVariable(ast);
+ break;
+ case TokenTypes.METHOD_CALL:
+ checkMethodCall(ast);
+ break;
+ default:
+ Utils.reportInvalidToken(ast.getType());
+ break;
+ }
+ }
+
+ /**
+ * Examines the method to see if it is part of a Test.
+ * @param ast The method to examine.
+ */
+ private void checkMethod(DetailAST ast) {
+ if (methodAst == null && AnnotationUtility.containsAnnotation(ast, "Test")
+ || AnnotationUtility.containsAnnotation(ast, "org.junit.Test")) {
+ methodAst = ast;
+ }
+ }
+
+ /**
+ * Examines the variable declaration to see if it is a specific variable type to track.
+ * Variables of type {@link Configuration} or {@link DefaultConfiguration} need to be assigned
+ * a {@code null}, createModuleConfig, or createRootConfig and is tracked for future purposes.
+ * Variables of type {@link File} with the modifier {@code final} are tracked for future
+ * purposes.
+ * @param ast The variable to examine.
+ */
+ private void checkVariable(DetailAST ast) {
+ if (methodAst != null && ScopeUtils.isLocalVariableDef(ast)) {
+ final DetailAST type = ast.findFirstToken(TokenTypes.TYPE).findFirstToken(
+ TokenTypes.IDENT);
+
+ if (type != null) {
+ final String typeText = type.getText();
+
+ if ("DefaultConfiguration".equals(typeText) || "Configuration".equals(typeText)) {
+ checkConfigurationVariable(ast);
+ }
+ else if ("File".equals(typeText)
+ && ast.findFirstToken(TokenTypes.MODIFIERS)
+ .findFirstToken(TokenTypes.FINAL) != null) {
+ fileVariableNames.add(ast.findFirstToken(TokenTypes.IDENT).getText());
+ }
+ }
+ }
+ }
+
+ /**
+ * Examines the configuration variable to see if it is defined as described in
+ * {@link #checkVariable(DetailAST)}.
+ * @param ast The variable to examine.
+ */
+ private void checkConfigurationVariable(DetailAST ast) {
+ checkConfigNames.add(ast.findFirstToken(TokenTypes.IDENT).getText());
+ final DetailAST assignment = ast.findFirstToken(TokenTypes.ASSIGN);
+
+ if (assignment == null) {
+ violations.put(ast, MSG_KEY_CONFIG_NOT_ASSIGNED);
+ }
+ else if (assignment.getFirstChild().getFirstChild().getType() == TokenTypes.METHOD_CALL) {
+ final DetailAST assignmentMethod = assignment.getFirstChild()
+ .getFirstChild().findFirstToken(TokenTypes.IDENT);
+
+ if (assignmentMethod != null
+ && !createMethodRegexp.matcher(assignmentMethod.getText()).matches()) {
+ violations.put(assignment, MSG_KEY_CONFIG_NOT_ASSIGNED_WITH);
+ }
+ }
+ else if (assignment.getFirstChild().getFirstChild().getType() != TokenTypes.LITERAL_NULL) {
+ violations.put(ast, MSG_KEY_CONFIG_NOT_ASSIGNED_PROPERLY);
+ }
+ }
+
+ /**
+ * Examines the method call and verify it is defined correctly.
+ * addAttribute method which is called by one of the configurations found earlier, must have
+ * all it's parameters be acceptable to {@link #isValidMethodCallExpression(DetailAST)}.
+ * Any method that matches {@link #verifyMethodRegexp} are tracked for future purposes.
+ * @param ast The method call to examine.
+ */
+ private void checkMethodCall(DetailAST ast) {
+ if (methodAst != null) {
+ final DetailAST firstChild = ast.getFirstChild();
+ final String methodCallName = getMethodCallName(firstChild);
+ final String methodCallerName = getMethodCallerName(firstChild);
+
+ if ("addAttribute".equals(methodCallName)
+ && checkConfigNames.contains(methodCallerName)) {
+ final DetailAST elist = ast.findFirstToken(TokenTypes.ELIST);
+
+ for (DetailAST expression = elist.getFirstChild(); expression != null;
+ expression = expression.getNextSibling()) {
+ if (expression.getType() == TokenTypes.EXPR
+ && !isValidMethodCallExpression(expression.getFirstChild())) {
+ violations.put(expression, MSG_KEY_UNKNOWN_PROPERTY);
+ }
+ }
+ }
+ else if (methodCallerName.equals(methodCallName)
+ && ast.getParent().getParent().getType() != TokenTypes.METHOD_CALL
+ && verifyMethodRegexp.matcher(methodCallName).matches()) {
+ foundVerify = true;
+ }
+ }
+ }
+
+ /**
+ * Retrieves the name of the method being called.
+ * @param ast The method call token to examine.
+ * @return The name of the method.
+ */
+ private String getMethodCallName(DetailAST ast) {
+ final String result;
+ if (ast.getType() == TokenTypes.DOT) {
+ result = getMethodCallName(ast.getFirstChild().getNextSibling());
+ }
+ else {
+ result = ast.getText();
+ }
+ return result;
+ }
+
+ /**
+ * Retrieves the name of the variable calling the method.
+ * @param ast The method call token to examine.
+ * @return The name of who is calling the method.
+ */
+ private String getMethodCallerName(DetailAST ast) {
+ final String result;
+ if (ast.getType() == TokenTypes.DOT) {
+ result = getMethodCallName(ast.getFirstChild());
+ }
+ else {
+ result = ast.getText();
+ }
+ return result;
+ }
+
+ /**
+ * Identifies if the parameter of the method call is valid.
+ * Plain string literal is allowed.
+ * Adding multiple string literals is allowed because of line length limits.
+ * Plain {@code null} is allowed due to backward compatibility.
+ * Method calls are allowed only if they are any form of getPath, converting an enum to a
+ * string, or retrieving the path of a final {@link File} variable.
+ * @param expression The expression to examine.
+ * @return {@code true} if the method call is defined correctly.
+ */
+ private boolean isValidMethodCallExpression(DetailAST expression) {
+ boolean result = false;
+ final DetailAST firstChild = expression.getFirstChild();
+
+ switch (expression.getType()) {
+ case TokenTypes.STRING_LITERAL:
+ result = true;
+ break;
+ case TokenTypes.METHOD_CALL:
+ result = isValidMethodCallExpressionMethodCall(firstChild);
+ break;
+ case TokenTypes.PLUS:
+ result = isValidMethodCallExpression(firstChild)
+ && isValidMethodCallExpression(firstChild.getNextSibling());
+ break;
+ case TokenTypes.LITERAL_NULL:
+ result = true;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ /**
+ * Identifies if the inner method call of a method call is valid as defined in
+ * {@link #isValidMethodCallExpression(DetailAST)}.
+ * @param firstChild The first child of the method call.
+ * @return {@code true} if the method call is defined correctly.
+ */
+ private boolean isValidMethodCallExpressionMethodCall(DetailAST firstChild) {
+ boolean result = false;
+
+ if (firstChild.getType() == TokenTypes.DOT) {
+ if (firstChild.getFirstChild().getType() == TokenTypes.DOT) {
+ result = isEnumerationCall(firstChild);
+ }
+ else if (isFileVariable(firstChild.getFirstChild())) {
+ result = true;
+ }
+ }
+ else {
+ final String methodName = firstChild.getText();
+
+ if (isMethodGetPath(methodName)) {
+ result = true;
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Checks if the method call is calling toString, getName, or name on an enumeration.
+ * @param ast The AST to examine.
+ * @return {@code true} if the method call is on a enumeration.
+ */
+ private static boolean isEnumerationCall(DetailAST ast) {
+ boolean result = false;
+ final DetailAST firstChild = ast.getFirstChild();
+ final DetailAST methodCalled = firstChild.getNextSibling();
+ final DetailAST parameters = ast.getNextSibling();
+
+ if (firstChild.getFirstChild().getType() == TokenTypes.IDENT
+ && ("toString".equals(methodCalled.getText())
+ || "getName".equals(methodCalled.getText())
+ || "name".equals(methodCalled.getText()))
+ && parameters.getChildCount() == 0) {
+ result = true;
+ }
+
+ return result;
+ }
+
+ /**
+ * Checks if the method call is 'getPath' on a {@link File} variable.
+ * @param firstChild The AST to examine.
+ * @return {@code true} if the method call is on a file variable.
+ */
+ private boolean isFileVariable(DetailAST firstChild) {
+ return METHOD_GET_PATH.equals(firstChild.getNextSibling().getText())
+ && fileVariableNames.contains(firstChild.getText());
+ }
+
+ /**
+ * Checks if the method name is a form of 'getPath'.
+ * @param methodName The name to examine.
+ * @return {@code true} if the method is of the form.
+ */
+ private static boolean isMethodGetPath(String methodName) {
+ return METHOD_GET_PATH.equals(methodName)
+ || "getNonCompilablePath".equals(methodName)
+ || "getUriString".equals(methodName)
+ || "getResourcePath".equals(methodName);
+ }
+
+ @Override
+ public void leaveToken(DetailAST ast) {
+ if (ast == methodAst) {
+ if (foundVerify) {
+ if (checkConfigNames.isEmpty()) {
+ violations.put(ast, MSG_KEY_CONFIG_NOT_FOUND);
+ }
+
+ for (Map.Entry entry : violations.entrySet()) {
+ log(entry.getKey(), entry.getValue());
+ }
+ }
+
+ resetInternalFields();
+ }
+ }
+
+ /** Resets the internal fields when a new file/method is to be processed. */
+ private void resetInternalFields() {
+ methodAst = null;
+ fileVariableNames.clear();
+ checkConfigNames.clear();
+ foundVerify = false;
+ violations.clear();
+ }
+}
diff --git a/sevntu-checks/src/main/resources/com/github/sevntu/checkstyle/checks/design/messages.properties b/sevntu-checks/src/main/resources/com/github/sevntu/checkstyle/checks/design/messages.properties
index c480b2c883..d5ecc9e63e 100644
--- a/sevntu-checks/src/main/resources/com/github/sevntu/checkstyle/checks/design/messages.properties
+++ b/sevntu-checks/src/main/resources/com/github/sevntu/checkstyle/checks/design/messages.properties
@@ -9,3 +9,8 @@ forbid.wildcard.as.return.type=Wildcard as return type should be avoided.
public.reference.to.private.type=Reference to the the instance of private type: {0}.
static.method.candidate=Method {0} should be declared as static.
constructor.without.params=Calls to constructors of ''{0}'' should use at least one parameter.
+tester.config.not.assigned=DefaultConfiguration not assigned.
+tester.config.not.assigned.with=DefaultConfiguration was not assigned by an allowed create configuration method.
+tester.config.not.assigned.properly=DefaultConfiguration was not assigned properly.
+tester.unknown.property=Unknown property name/value.
+tester.config.not.found=DefaultConfiguration was not found.
diff --git a/sevntu-checks/src/test/java/com/github/sevntu/checkstyle/checks/design/CheckstyleTestMakeupCheckTest.java b/sevntu-checks/src/test/java/com/github/sevntu/checkstyle/checks/design/CheckstyleTestMakeupCheckTest.java
new file mode 100644
index 0000000000..07eb96291d
--- /dev/null
+++ b/sevntu-checks/src/test/java/com/github/sevntu/checkstyle/checks/design/CheckstyleTestMakeupCheckTest.java
@@ -0,0 +1,122 @@
+////////////////////////////////////////////////////////////////////////////////
+// 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.sevntu.checkstyle.checks.design;
+
+import static com.github.sevntu.checkstyle.checks.design.CheckstyleTestMakeupCheck.MSG_KEY_CONFIG_NOT_ASSIGNED;
+import static com.github.sevntu.checkstyle.checks.design.CheckstyleTestMakeupCheck.MSG_KEY_CONFIG_NOT_ASSIGNED_PROPERLY;
+import static com.github.sevntu.checkstyle.checks.design.CheckstyleTestMakeupCheck.MSG_KEY_CONFIG_NOT_ASSIGNED_WITH;
+import static com.github.sevntu.checkstyle.checks.design.CheckstyleTestMakeupCheck.MSG_KEY_CONFIG_NOT_FOUND;
+import static com.github.sevntu.checkstyle.checks.design.CheckstyleTestMakeupCheck.MSG_KEY_UNKNOWN_PROPERTY;
+import static org.junit.Assert.fail;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.github.sevntu.checkstyle.BaseCheckTestSupport;
+import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
+import com.puppycrawl.tools.checkstyle.api.DetailAST;
+import com.puppycrawl.tools.checkstyle.api.TokenTypes;
+
+public class CheckstyleTestMakeupCheckTest extends BaseCheckTestSupport {
+ @Test
+ public void testMiscFile() throws Exception {
+ final DefaultConfiguration checkConfig =
+ createModuleConfig(CheckstyleTestMakeupCheck.class);
+
+ final String[] expected = {};
+
+ verify(checkConfig, getPath("InputCheckstyleTestMakeupCheck.java"), expected);
+ }
+
+ @Test
+ public void testValidFile() throws Exception {
+ final DefaultConfiguration checkConfig =
+ createModuleConfig(CheckstyleTestMakeupCheck.class);
+
+ final String[] expected = {};
+
+ verify(checkConfig, getPath("InputCheckstyleTestMakeupCheckValid.java"), expected);
+ }
+
+ @Test
+ public void testInvalidFile() throws Exception {
+ final DefaultConfiguration checkConfig =
+ createModuleConfig(CheckstyleTestMakeupCheck.class);
+
+ final String[] expected = {
+ "8:5: " + getCheckMessage(MSG_KEY_CONFIG_NOT_FOUND),
+ "15:9: " + getCheckMessage(MSG_KEY_CONFIG_NOT_ASSIGNED),
+ "21:37: " + getCheckMessage(MSG_KEY_CONFIG_NOT_ASSIGNED_WITH),
+ "27:9: " + getCheckMessage(MSG_KEY_CONFIG_NOT_ASSIGNED_PROPERLY),
+ "36:45: " + getCheckMessage(MSG_KEY_UNKNOWN_PROPERTY),
+ "37:53: " + getCheckMessage(MSG_KEY_UNKNOWN_PROPERTY),
+ "38:44: " + getCheckMessage(MSG_KEY_UNKNOWN_PROPERTY),
+ "39:50: " + getCheckMessage(MSG_KEY_UNKNOWN_PROPERTY),
+ "40:48: " + getCheckMessage(MSG_KEY_UNKNOWN_PROPERTY),
+ "41:55: " + getCheckMessage(MSG_KEY_UNKNOWN_PROPERTY),
+ "42:33: " + getCheckMessage(MSG_KEY_UNKNOWN_PROPERTY),
+ "43:36: " + getCheckMessage(MSG_KEY_UNKNOWN_PROPERTY),
+ "44:35: " + getCheckMessage(MSG_KEY_UNKNOWN_PROPERTY),
+ };
+
+ verify(checkConfig, getPath("InputCheckstyleTestMakeupCheckInvalid.java"), expected);
+ }
+
+ @Test
+ public void testInvalidFileIgnoreVerifies() throws Exception {
+ final DefaultConfiguration checkConfig =
+ createModuleConfig(CheckstyleTestMakeupCheck.class);
+ checkConfig.addAttribute("verifyMethodRegexp", "BAD");
+
+ final String[] expected = {};
+
+ verify(checkConfig, getPath("InputCheckstyleTestMakeupCheckInvalid.java"), expected);
+ }
+
+ @Test
+ public void testInvalidFileIgnoreCreates() throws Exception {
+ final DefaultConfiguration checkConfig =
+ createModuleConfig(CheckstyleTestMakeupCheck.class);
+ checkConfig.addAttribute("createMethodRegexp", "BAD");
+
+ final String[] expected = {
+ "16:36: " + getCheckMessage(MSG_KEY_CONFIG_NOT_ASSIGNED_WITH),
+ "22:43: " + getCheckMessage(MSG_KEY_CONFIG_NOT_ASSIGNED_WITH),
+ "28:36: " + getCheckMessage(MSG_KEY_CONFIG_NOT_ASSIGNED_WITH),
+ };
+
+ verify(checkConfig, getPath("InputCheckstyleTestMakeupCheckValid.java"), expected);
+ }
+
+ @Test
+ public void testInvalidToken() {
+ final DetailAST parent = new DetailAST();
+ parent.setType(TokenTypes.OBJBLOCK);
+
+ final CheckstyleTestMakeupCheck check = new CheckstyleTestMakeupCheck();
+ try {
+ check.visitToken(parent);
+ fail();
+ }
+ catch (IllegalArgumentException ex) {
+ Assert.assertEquals("Found unsupported token: OBJBLOCK", ex.getMessage());
+ }
+ }
+}
diff --git a/sevntu-checks/src/test/resources/com/github/sevntu/checkstyle/checks/design/InputCheckstyleTestMakeupCheck.java b/sevntu-checks/src/test/resources/com/github/sevntu/checkstyle/checks/design/InputCheckstyleTestMakeupCheck.java
new file mode 100644
index 0000000000..b1a0ef0f55
--- /dev/null
+++ b/sevntu-checks/src/test/resources/com/github/sevntu/checkstyle/checks/design/InputCheckstyleTestMakeupCheck.java
@@ -0,0 +1,58 @@
+package com.github.sevntu.checkstyle.checks.design;
+
+import org.junit.Test;
+
+public class InputCheckstyleTestMakeupCheck {
+ private String s;
+
+ public void method1() {
+ }
+
+ public void method2() {
+ String s = "";
+ method1();
+ }
+
+ @Test
+ public void method3() {
+ }
+
+ @org.junit.Test
+ public void method4() {
+ }
+
+ @Test
+ public void method5() {
+ new Thread(new Runnable() {
+ private String s;
+
+ @Override
+ public void run() {
+ }
+ });
+ }
+
+ @Test
+ public void method6() {
+ String s = "";
+ java.util.List t;
+ method1();
+ InputCheckstyleTestMakeupCheck.test();
+ addAttribute("", "");
+ test2().test3();
+ }
+
+ private static void test() {
+ }
+
+ private static void addAttribute(String s, String t) {
+ }
+
+ private InputCheckstyleTestMakeupCheck test2() {
+ return this;
+ }
+
+ private InputCheckstyleTestMakeupCheck test3() {
+ return this;
+ }
+}
diff --git a/sevntu-checks/src/test/resources/com/github/sevntu/checkstyle/checks/design/InputCheckstyleTestMakeupCheckInvalid.java b/sevntu-checks/src/test/resources/com/github/sevntu/checkstyle/checks/design/InputCheckstyleTestMakeupCheckInvalid.java
new file mode 100644
index 0000000000..be8f62d9a4
--- /dev/null
+++ b/sevntu-checks/src/test/resources/com/github/sevntu/checkstyle/checks/design/InputCheckstyleTestMakeupCheckInvalid.java
@@ -0,0 +1,87 @@
+package com.github.sevntu.checkstyle.checks.design;
+
+import java.io.File;
+
+import org.junit.Test;
+
+public class InputCheckstyleTestMakeupCheckInvalid {
+ @Test
+ public void method1() {
+ verify();
+ }
+
+ @Test
+ public void method2() {
+ DefaultConfiguration config;
+ verify();
+ }
+
+ @Test
+ public void method3() {
+ DefaultConfiguration config = customCreateConfig();
+ verify();
+ }
+
+ @Test
+ public void method4() {
+ DefaultConfiguration config = new DefaultConfiguration();
+ verify();
+ }
+
+ @Test
+ public void method5() {
+ final Configuration config = createModuleConfig();
+ File file = new File("");
+ file = new File("");
+ config.addAttribute("", file.getPath());
+ config.addAttribute("", file.getAbsolutePath());
+ config.addAttribute("", customValue());
+ config.addAttribute("", ENUM.TEST.getName(0));
+ config.addAttribute("", ENUM.TEST.other());
+ config.addAttribute("", ENUM.TEST.same.getName(0));
+ config.addAttribute("", 0);
+ config.addAttribute("", "" + 0);
+ config.addAttribute("", 0 + "");
+ verify();
+ }
+
+ private static String customValue() {
+ return null;
+ }
+
+ private static void verify() {
+ }
+
+ private DefaultConfiguration customCreateConfig() {
+ return new DefaultConfiguration();
+ }
+
+ private static DefaultConfiguration createModuleConfig() {
+ return new DefaultConfiguration();
+ }
+
+ private static class Configuration {
+ public void addAttribute(String s, String t) {
+ }
+
+ public void addAttribute(String s, int i) {
+ }
+ }
+
+ private static class DefaultConfiguration extends Configuration {
+ }
+
+ private enum ENUM {
+ TEST;
+
+ private static final ENUM same = TEST;
+
+ public String other() {
+ return name();
+ }
+
+ public String getName(int i) {
+ return name();
+ }
+ }
+}
diff --git a/sevntu-checks/src/test/resources/com/github/sevntu/checkstyle/checks/design/InputCheckstyleTestMakeupCheckValid.java b/sevntu-checks/src/test/resources/com/github/sevntu/checkstyle/checks/design/InputCheckstyleTestMakeupCheckValid.java
new file mode 100644
index 0000000000..f696a159ba
--- /dev/null
+++ b/sevntu-checks/src/test/resources/com/github/sevntu/checkstyle/checks/design/InputCheckstyleTestMakeupCheckValid.java
@@ -0,0 +1,99 @@
+package com.github.sevntu.checkstyle.checks.design;
+
+import java.io.File;
+
+import org.junit.Test;
+
+public class InputCheckstyleTestMakeupCheckValid {
+ @Test
+ public void method1() {
+ final Configuration config = null;
+ verify();
+ }
+
+ @Test
+ public void method2() {
+ final Configuration config = createModuleConfig();
+ verify();
+ }
+
+ @Test
+ public void method3() {
+ final DefaultConfiguration config = createRootConfig();
+ verifyWarns();
+ }
+
+ @Test
+ public void method4() {
+ final Configuration config = createModuleConfig();
+ config.addAttribute("", null);
+ config.addAttribute("", "");
+ config.addAttribute("", "" + "");
+ final File file = new File("");
+ config.addAttribute("", file.getPath());
+ config.addAttribute("", ENUM.TEST.toString());
+ config.addAttribute("", ENUM.TEST.getName());
+ config.addAttribute("", ENUM.TEST.name());
+ config.addAttribute("", getPath(""));
+ config.addAttribute("", getNonCompilablePath(""));
+ config.addAttribute("", getUriString(""));
+ config.addAttribute("", getResourcePath(""));
+ verify();
+ }
+
+ @Test
+ public void method5() {
+ // simulation of PowerMockito.mock(DefaultConfiguration.class);
+ DefaultConfiguration config = InputCheckstyleTestMakeupCheckValid.createModuleConfig();
+ verify();
+ }
+
+ private String getPath(String s) {
+ return s;
+ }
+
+ private String getNonCompilablePath(String s) {
+ return s;
+ }
+
+ private String getUriString(String s) {
+ return s;
+ }
+
+ private String getResourcePath(String s) {
+ return s;
+ }
+
+ private static void verify() {
+ }
+
+ private static void verifyWarns() {
+ }
+
+ private static void verifySuppressed() {
+ }
+
+ private static DefaultConfiguration createModuleConfig() {
+ return new DefaultConfiguration();
+ }
+
+ private static DefaultConfiguration createRootConfig() {
+ return new DefaultConfiguration();
+ }
+
+ private static class Configuration {
+ public void addAttribute(String s, String t) {
+ }
+ }
+
+ private static class DefaultConfiguration extends Configuration {
+ }
+
+ private enum ENUM {
+ TEST;
+
+ public String getName() {
+ return name();
+ }
+ }
+}
diff --git a/sevntu-checkstyle-sonar-plugin/src/main/resources/com/github/sevntu/checkstyle/sonar/checkstyle-extensions.xml b/sevntu-checkstyle-sonar-plugin/src/main/resources/com/github/sevntu/checkstyle/sonar/checkstyle-extensions.xml
index 8b13317639..978f232515 100644
--- a/sevntu-checkstyle-sonar-plugin/src/main/resources/com/github/sevntu/checkstyle/sonar/checkstyle-extensions.xml
+++ b/sevntu-checkstyle-sonar-plugin/src/main/resources/com/github/sevntu/checkstyle/sonar/checkstyle-extensions.xml
@@ -904,4 +904,20 @@
Checks if a try/catch block has a junit fail assertion inside the try for a junit method.
Checker/TreeWalker/com.github.sevntu.checkstyle.checks.coding.RequireFailForTryCatchInJunitCheck
+
+
+ com.github.sevntu.checkstyle.checks.design.CheckstyleTestMakeupCheck
+ Custom check to ensure Checkstyle tests are designed correctly.
+
+ Custom check to ensure Checkstyle tests are designed correctly.
+ Checker/TreeWalker/com.github.sevntu.checkstyle.checks.design.CheckstyleTestMakeupCheck
+
+ create(Root|Module)Config|getModuleConfig
+ Regular expression for matching a create configuration method by name.
+
+
+ verify(Warns|Suppressed)?
+ Regular expression for matching a verify method by name.
+
+