()
+ .put("strings", PsiType.getJavaLangString(method.manager, method.resolveScope))
+ .put("ints", PsiType.INT)
+ .put("longs", PsiType.LONG)
+ .put("doubles", PsiType.DOUBLE).build()
+
+ for (valueKey in possibleValues.keys) {
+ processArrayInAnnotationParameter(valuesSource.findDeclaredAttributeValue(valueKey),
+ { value -> checkSourceTypeAndParameterTypeAgree(method, value, possibleValues[valueKey]!!) })
+ }
+
+ val attributesNumber = valuesSource.parameterList.attributes.size
+ if (attributesNumber > 1) {
+ holder.registerProblem(valuesSource, "Exactly one type of input must be provided")
+ }
+ else if (attributesNumber == 0) {
+ holder.registerProblem(valuesSource, "No value source is defined")
+ }
+ }
+
+ private fun checkMethodSource(method: PsiMethod, methodSource: PsiAnnotation) {
+ val annotationMemberValue = methodSource.findDeclaredAttributeValue("names")
+ processArrayInAnnotationParameter(annotationMemberValue, { attributeValue ->
+ for (reference in attributeValue.references) {
+ if (reference is MethodSourceReference) {
+ val resolve = reference.resolve()
+ if (resolve !is PsiMethod) {
+ holder.registerProblem(attributeValue,
+ "Cannot resolve target method source: \'" + reference.value + "\'")
+ }
+ else {
+ val sourceProvider : PsiMethod = resolve
+ val providerName = sourceProvider.name
+
+ if (!sourceProvider.hasModifierProperty(PsiModifier.STATIC)) {
+ holder.registerProblem(attributeValue, "Method source \'$providerName\' must be static",
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
+ QuickFixFactory.getInstance().createModifierListFix(sourceProvider, PsiModifier.STATIC, true, false))
+ }
+ else if (sourceProvider.parameterList.parametersCount != 0) {
+ holder.registerProblem(attributeValue, "Method source \'$providerName\' should have no parameters")
+ }
+ else {
+ val componentType = getComponentType(sourceProvider.returnType, method)
+ if (componentType == null) {
+ holder.registerProblem(attributeValue,
+ "Method source \'$providerName\' must have one of the following return type: Stream>, Iterator>, Iterable> or Object[]")
+ }
+ else if (method.parameterList.parametersCount > 1 && !isArgumentsInheritor(componentType)) {
+ holder.registerProblem(attributeValue, "Multiple parameters have to be wrapped in Arguments")
+ }
+ }
+ }
+ }
+ }
+ })
+ }
+
+ private fun processArrayInAnnotationParameter(attributeValue: PsiAnnotationMemberValue?,
+ checker: (value : PsiAnnotationMemberValue) -> Unit) {
+ if (attributeValue is PsiLiteral || attributeValue is PsiClassObjectAccessExpression) {
+ checker.invoke(attributeValue)
+ }
+ else if (attributeValue is PsiArrayInitializerMemberValue) {
+ for (memberValue in attributeValue.initializers) {
+ processArrayInAnnotationParameter(memberValue, checker)
+ }
+ }
+ }
+
+ private fun checkSourceTypeAndParameterTypeAgree(method: PsiMethod,
+ attributeValue: PsiAnnotationMemberValue,
+ componentType: PsiType) {
+ val parameters = method.parameterList.parameters
+ if (parameters.size == 1 && !parameters[0].type.isAssignableFrom(componentType) && !isArgumentsInheritor(componentType)) {
+ holder.registerProblem(attributeValue,
+ "No implicit conversion found to convert object of type " + componentType.presentableText + " to " + parameters[0].type.presentableText)
+ }
+ }
+
+ private fun isArgumentsInheritor(componentType: PsiType): Boolean {
+ return InheritanceUtil.isInheritor(componentType, JUnitCommonClassNames.ORG_JUNIT_JUPITER_PARAMS_PROVIDER_ARGUMENTS)
+ }
+
+ private fun getComponentType(returnType: PsiType?, method: PsiMethod): PsiType? {
+ val collectionItemType = JavaGenericsUtil.getCollectionItemType(returnType, method.resolveScope)
+ if (collectionItemType != null) {
+ return collectionItemType
+ }
+
+ val streamItemType = PsiUtil.substituteTypeParameter(returnType, CommonClassNames.JAVA_UTIL_STREAM_STREAM, 1, false)
+ if (streamItemType != null) {
+ return streamItemType
+ }
+
+ return PsiUtil.substituteTypeParameter(returnType, CommonClassNames.JAVA_UTIL_ITERATOR, 1, false)
+ }
+ }
+ }
+}
diff --git a/plugins/junit/src/inspectionDescriptions/JUnit5ValidParameterizedConfiguration.html b/plugins/junit/src/inspectionDescriptions/JUnit5ValidParameterizedConfiguration.html
new file mode 100644
index 000000000000..b5b0fe35a498
--- /dev/null
+++ b/plugins/junit/src/inspectionDescriptions/JUnit5ValidParameterizedConfiguration.html
@@ -0,0 +1,19 @@
+
+
+Reports parameterized tests which have malformed sources:
+
+ -
+ MethodSource has unknown target or method is not static, no-arg
+
+ -
+ ValueSource/EnumSource types are not convertible to method parameters
+
+ -
+ No sources are defined
+
+
+
+
+ New in 2017.2
+
+
\ No newline at end of file
diff --git a/plugins/junit/test/com/intellij/execution/junit/codeInsight/JUnit5MalformedParameterizedTest.java b/plugins/junit/test/com/intellij/execution/junit/codeInsight/JUnit5MalformedParameterizedTest.java
new file mode 100644
index 000000000000..5eea8ed9d9fb
--- /dev/null
+++ b/plugins/junit/test/com/intellij/execution/junit/codeInsight/JUnit5MalformedParameterizedTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2000-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.junit.codeInsight;
+
+import com.intellij.codeInspection.InspectionProfileEntry;
+import com.intellij.testFramework.LightProjectDescriptor;
+import com.siyeh.ig.LightInspectionTestCase;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class JUnit5MalformedParameterizedTest extends LightInspectionTestCase {
+ @Nullable
+ @Override
+ protected InspectionProfileEntry getInspection() {
+ return new JUnit5MalformedParameterizedInspection();
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ addEnvironmentClass("package org.junit.jupiter.params;\n" +
+ "public @interface ParameterizedTest {}");
+ addEnvironmentClass("package org.junit.jupiter.params.provider;\n" +
+ "public @interface MethodSource {String[] names();}");
+ addEnvironmentClass("package org.junit.jupiter.params.provider;\n" +
+ "public @interface EnumSource { Class extends Enum>> value();}");
+ addEnvironmentClass("package org.junit.jupiter.params.provider;\n" +
+ "public @interface ValueSource {\n" +
+ "String[] strings() default {};\n" +
+ "int[] ints() default {};\n" +
+ "long[] longs() default {};\n" +
+ "double[] doubles() default {};\n" +
+ "}\n");
+ }
+
+ public void testMalformedSources() throws Exception {
+ doTest();
+ }
+
+ @Override
+ protected String getBasePath() {
+ return "/plugins/junit/testData/codeInsight/malformedParameterized";
+ }
+
+ @NotNull
+ @Override
+ protected LightProjectDescriptor getProjectDescriptor() {
+ return JAVA_8;
+ }
+}
diff --git a/plugins/junit/testData/codeInsight/malformedParameterized/MalformedSources.java b/plugins/junit/testData/codeInsight/malformedParameterized/MalformedSources.java
new file mode 100644
index 000000000000..a6b95922511b
--- /dev/null
+++ b/plugins/junit/testData/codeInsight/malformedParameterized/MalformedSources.java
@@ -0,0 +1,66 @@
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.*;
+
+class ParameterizedTestsDemo {
+
+ @ParameterizedTest
+ void testWithParamsNoSource(int i) { }
+
+ @ParameterizedTest
+ @MethodSource(names = {"a",
+ "b",
+ "c",
+ "d"})
+ void testWithParams(Object s) { }
+
+ String[] a() {
+ return new String[] {"a", "b"};
+ }
+
+ static String[] b(int i) {
+ return new String[] {"a", "b"};
+ }
+
+ static Object c() {
+ return new String[] {"a", "b"};
+ }
+
+ static Object[] d() {
+ return new String[] {"a", "b"};
+ }
+
+ @ParameterizedTest
+ @MethodSource(names = {"d"})
+ void testWithMultipleParams(Object s, int i) { }
+
+ @ParameterizedTest
+ @EnumSource(E.class)
+ void testWithEnumSource(int i) { }
+
+ @ParameterizedTest
+ @EnumSource(E.class)
+ void testWithEnumSourceCorrect(E e) { }
+
+ enum E {
+ A, B;
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {1})
+ void testWithValues(int i) { }
+
+ @ParameterizedTest
+ @ValueSource(ints = {1},
+ strings = "str")
+ void testWithMultipleValues(int i) { }
+
+ @ParameterizedTest
+ @ValueSource()
+ void testWithNoValues(int i) { }
+
+ @ParameterizedTest
+ @ValueSource(ints = 1)
+ void testWithValuesMultipleParams(int i, int j) { }
+
+}