From daad33548978a586c7104d3dd4fe3fc7d9bc2115 Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Fri, 31 May 2019 12:48:03 +0800 Subject: [PATCH] feat: Add @nested support for JUnit 5 (#716) --- .../plugin/searcher/JUnit4TestSearcher.java | 4 +- .../plugin/searcher/JUnit5TestSearcher.java | 4 +- .../test/plugin/util/TestSearchUtils.java | 42 +++++++++++-------- .../JUnit5RunnerResultAnalyzer.ts | 34 +++++++++++---- 4 files changed, 56 insertions(+), 28 deletions(-) diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/JUnit4TestSearcher.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/JUnit4TestSearcher.java index 8481b389..b2fef17e 100644 --- a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/JUnit4TestSearcher.java +++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/JUnit4TestSearcher.java @@ -20,10 +20,12 @@ public class JUnit4TestSearcher extends BaseFrameworkSearcher { + public static final String RUN_WITH = "org.junit.runner.RunWith"; + public JUnit4TestSearcher() { super(); this.testMethodAnnotations = new String[] { "org.junit.Test", "org.junit.experimental.theories.Theory" }; - this.testClassAnnotations = new String[] { "org.junit.runner.RunWith" }; + this.testClassAnnotations = new String[] { RUN_WITH }; } @Override diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/JUnit5TestSearcher.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/JUnit5TestSearcher.java index c9b04685..f558c16d 100644 --- a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/JUnit5TestSearcher.java +++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/JUnit5TestSearcher.java @@ -33,6 +33,8 @@ public class JUnit5TestSearcher extends BaseFrameworkSearcher { + public static final String NESTED = "org.junit.jupiter.api.Nested"; + protected static final String DISPLAY_NAME_ANNOTATION_JUNIT5 = "org.junit.jupiter.api.DisplayName"; public JUnit5TestSearcher() { @@ -40,7 +42,7 @@ public JUnit5TestSearcher() { this.testMethodAnnotations = new String[] { "org.junit.jupiter.api.Test", "org.junit.jupiter.params.ParameterizedTest", "org.junit.jupiter.api.RepeatedTest", "org.junit.jupiter.api.TestFactory" }; - this.testClassAnnotations = new String[] {}; + this.testClassAnnotations = new String[] { NESTED }; } @Override diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/util/TestSearchUtils.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/util/TestSearchUtils.java index 9a82c2fd..7e44c0b1 100644 --- a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/util/TestSearchUtils.java +++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/util/TestSearchUtils.java @@ -14,7 +14,10 @@ import com.google.gson.Gson; import com.microsoft.java.test.plugin.model.SearchTestItemParams; import com.microsoft.java.test.plugin.model.TestItem; +import com.microsoft.java.test.plugin.model.TestKind; import com.microsoft.java.test.plugin.model.TestLevel; +import com.microsoft.java.test.plugin.searcher.JUnit4TestSearcher; +import com.microsoft.java.test.plugin.searcher.JUnit5TestSearcher; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.ResourcesPlugin; @@ -24,7 +27,6 @@ import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jdt.core.Flags; -import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; @@ -32,6 +34,7 @@ import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.ITypeRoot; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.search.IJavaSearchConstants; @@ -107,11 +110,10 @@ public static List searchCodeLens(List arguments, IProgressMon resultList.add(parent); continue; } - // Check if the class can still run tests even no method is annotated. - // For example, annotated with @RunWith - final TestItem runableClass = TestFrameworkUtils.resolveTestItemForClass(type); - if (runableClass != null) { - resultList.add(runableClass); + // Class annotated by @RunWith should be considered as a Suite even it has no test method children + if (TestFrameworkUtils.hasAnnotation(type, JUnit4TestSearcher.RUN_WITH)) { + resultList.add(TestItemUtils.constructTestItem(type, TestItemUtils.getTestLevelForIType(type), + TestKind.JUnit)); } } @@ -430,21 +432,27 @@ private static List searchTestMethodsOfType(IType type) throws JavaMod } private static boolean isTestableClass(IType type) throws JavaModelException { - int flags = type.getFlags(); + final int flags = type.getFlags(); if (Flags.isInterface(flags) || Flags.isAbstract(flags)) { return false; } - IJavaElement parent = type.getParent(); - while (true) { - if (parent instanceof ICompilationUnit || parent instanceof IClassFile) { - return true; - } - if (!(parent instanceof IType) || !Flags.isStatic(flags) || !Flags.isPublic(flags)) { - return false; - } - flags = ((IType) parent).getFlags(); - parent = parent.getParent(); + + final IJavaElement parent = type.getParent(); + + if (parent instanceof ITypeRoot) { + return true; + } + + if (!(parent instanceof IType)) { + return false; } + + if (TestFrameworkUtils.hasAnnotation(type, JUnit5TestSearcher.NESTED) || + (Flags.isStatic(flags) && Flags.isPublic(flags))) { + return true; + } + + return false; } private static boolean isJavaElementExist(IJavaElement element) { diff --git a/src/runners/junit5Runner/JUnit5RunnerResultAnalyzer.ts b/src/runners/junit5Runner/JUnit5RunnerResultAnalyzer.ts index e6d24d27..eddfc3ee 100644 --- a/src/runners/junit5Runner/JUnit5RunnerResultAnalyzer.ts +++ b/src/runners/junit5Runner/JUnit5RunnerResultAnalyzer.ts @@ -47,17 +47,33 @@ export class JUnit5RunnerResultAnalyzer extends BaseRunnerResultAnalyzer { if (!id) { return id; } - const regex: RegExp = /\[class:(.*?)\]\/\[(?:method|test-template|test-factory):(.*)\]/gm; - const match: RegExpExecArray | null = regex.exec(id); - if (match && match.length === 3) { - let methodName: string = match[2]; - const index: number = methodName.indexOf('('); - if (index >= 0) { - methodName = methodName.substring(0, index); + let res: string = ''; + const regex: RegExp = /\[(.*?):(.*?)\]/g; + while (true) { + const execResult: RegExpExecArray | null = regex.exec(id); + if (!execResult || execResult.length < 3) { + break; + } + switch (execResult[1]) { + case 'class': + res += execResult[2]; + break; + case 'nested-class': + res += `$${execResult[2]}`; + break; + case 'method': + case 'test-template': + case 'test-factory': + let methodName: string = execResult[2]; + const index: number = methodName.indexOf('('); + if (index >= 0) { + methodName = methodName.substring(0, index); + } + res += `#${methodName}`; + break; } - return `${match[1]}#${methodName}`; } - return ''; + return res; } private parseTestStatus(status: JUnit5TestStatus): TestStatus {