From 404322f0484d249c3f5077bfcf9c8730c71f7a5d Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Sat, 14 Jan 2023 22:58:33 +0100 Subject: [PATCH] Adding ability to run a single sub-test for tests under jtreg. --- ide/gsf.testrunner.ui/nbproject/project.xml | 1 + .../nbproject/project.xml | 25 ++ .../openjdk/jtreg/ActionProviderImpl.java | 41 +++- .../openjdk/jtreg/ClassPathProviderImpl.java | 17 +- .../java/openjdk/jtreg/TestClassInfoTask.java | 213 ++++++++++++++++++ .../openjdk/project/ActionProviderImpl.java | 12 + .../project/ClassPathProviderImpl.java | 2 +- .../java/openjdk/project/JDKProject.java | 1 + .../project/UnitTestForSourceQueryImpl.java | 64 ++++++ java/java.testrunner.ui/nbproject/project.xml | 1 + java/testng.ui/nbproject/project.xml | 14 +- .../testng/ui/actions/TestClassInfoTask.java | 56 +++-- .../testng/ui/actions/RetoucheTestBase.java | 10 - .../ui/actions/TestClassInfoTaskTest.java | 65 ++++++ 14 files changed, 482 insertions(+), 40 deletions(-) create mode 100644 java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/jtreg/TestClassInfoTask.java create mode 100644 java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/project/UnitTestForSourceQueryImpl.java diff --git a/ide/gsf.testrunner.ui/nbproject/project.xml b/ide/gsf.testrunner.ui/nbproject/project.xml index b54d0943cfcc..1fd2cb0553f6 100644 --- a/ide/gsf.testrunner.ui/nbproject/project.xml +++ b/ide/gsf.testrunner.ui/nbproject/project.xml @@ -266,6 +266,7 @@ org.netbeans.modules.groovy.support org.netbeans.modules.hudson.ui org.netbeans.modules.java.lsp.server + org.netbeans.modules.java.openjdk.project org.netbeans.modules.java.testrunner.ui org.netbeans.modules.javascript.jstestdriver org.netbeans.modules.javascript.karma diff --git a/java/java.openjdk.project/nbproject/project.xml b/java/java.openjdk.project/nbproject/project.xml index 52f92e18abd1..1c9909c89a48 100644 --- a/java/java.openjdk.project/nbproject/project.xml +++ b/java/java.openjdk.project/nbproject/project.xml @@ -136,6 +136,14 @@ 1.64 + + org.netbeans.modules.gsf.testrunner.ui + + + + 1.34 + + org.netbeans.modules.java.lexer @@ -188,6 +196,14 @@ 2.36 + + org.netbeans.modules.java.testrunner.ui + + + + 1.23 + + org.netbeans.modules.lexer @@ -197,6 +213,15 @@ 1.66 + + org.netbeans.modules.parsing.api + + + + 1 + 9.26 + + org.netbeans.modules.parsing.indexing diff --git a/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/jtreg/ActionProviderImpl.java b/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/jtreg/ActionProviderImpl.java index 6bc5d24c4db8..6f200eec4323 100644 --- a/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/jtreg/ActionProviderImpl.java +++ b/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/jtreg/ActionProviderImpl.java @@ -64,6 +64,7 @@ import org.netbeans.spi.java.classpath.support.ClassPathSupport; import org.netbeans.spi.project.ActionProgress; import org.netbeans.spi.project.ActionProvider; +import org.netbeans.spi.project.SingleMethod; import org.netbeans.spi.project.ui.CustomizerProvider2; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; @@ -97,7 +98,7 @@ * * @author lahvac */ -@ServiceProvider(service=ActionProvider.class) +@ServiceProvider(service=ActionProvider.class, position=1_000_000) public class ActionProviderImpl implements ActionProvider { private static final Logger LOG = Logger.getLogger(ActionProviderImpl.class.getName()); @@ -107,6 +108,8 @@ public class ActionProviderImpl implements ActionProvider { COMMAND_TEST_SINGLE, COMMAND_DEBUG_TEST_SINGLE, COMMAND_PROFILE_TEST_SINGLE, + SingleMethod.COMMAND_RUN_SINGLE_METHOD, + SingleMethod.COMMAND_DEBUG_SINGLE_METHOD }; @Override @@ -129,9 +132,29 @@ public void invokeAction(String command, Lookup context) throws IllegalArgumentE "DN_Running=Running ({0})", "LBL_IncorrectVersionSelectJTReg=Location of JTReg:", "TITLE_IncorrectVersionSelectJTReg=Version of JTReg appears to be incorrect, please select a correct version"}) - public static ExecutorTask createAndRunTest(Lookup context, String command) { - final FileObject file = context.lookup(FileObject.class); + public static ExecutorTask createAndRunTest(Lookup context, String inputCommand) { + FileObject file; + String query; + String command; + + if (SingleMethod.COMMAND_RUN_SINGLE_METHOD.equals(inputCommand) || + SingleMethod.COMMAND_DEBUG_SINGLE_METHOD.equals(inputCommand)) { + SingleMethod singleMethod = context.lookup(SingleMethod.class); + + assert singleMethod != null; + + file = singleMethod.getFile(); + query = singleMethod.getMethodName(); + command = SingleMethod.COMMAND_RUN_SINGLE_METHOD.equals(inputCommand) ? COMMAND_TEST_SINGLE + : COMMAND_DEBUG_TEST_SINGLE; + } else { + file = context.lookup(FileObject.class); + query = null; + command = inputCommand; + } + ensureProjectsRegistered(file); + String ioName = COMMAND_DEBUG_TEST_SINGLE.equals(command) ? Bundle.DN_Debugging(file.getName()) : Bundle.DN_Running(file.getName()); StopAction newStop = new StopAction(); ReRunAction newReRun = new ReRunAction(COMMAND_TEST_SINGLE); @@ -305,7 +328,11 @@ public String[] getExtraMakeTargets() { } break; } - options.add(FileUtil.toFile(file).getAbsolutePath()); + String testPath = FileUtil.toFile(file).getAbsolutePath(); + if (query != null) { + testPath += "?" + query; + } + options.add(testPath); try { stop.started(); Process jtregProcess = new ProcessBuilder(options).start(); @@ -702,6 +729,12 @@ private static File findJTRegJar(String installDir) { public boolean isActionEnabled(String command, Lookup context) throws IllegalArgumentException { FileObject file = context.lookup(FileObject.class); + if (file == null) { + SingleMethod singleMethod = context.lookup(SingleMethod.class); + + file = singleMethod != null ? singleMethod.getFile() : null; + } + if (file == null) return false; diff --git a/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/jtreg/ClassPathProviderImpl.java b/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/jtreg/ClassPathProviderImpl.java index 36ce45d091f6..88f5f3b2d080 100644 --- a/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/jtreg/ClassPathProviderImpl.java +++ b/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/jtreg/ClassPathProviderImpl.java @@ -77,10 +77,21 @@ public ClassPath findClassPath(FileObject file, String type) { if (javac) { ClassPath langtoolsCP = ClassPath.getClassPath(keyRoot, ClassPath.COMPILE); Library testngLib = LibraryManager.getDefault().getLibrary("testng"); + Library junit5Lib = LibraryManager.getDefault().getLibrary("junit_5"); - if (testngLib != null) { - return ClassPathSupport.createProxyClassPath(ClassPathSupport.createClassPath(testngLib.getContent("classpath").toArray(new URL[0])), - langtoolsCP); + if (testngLib != null || junit5Lib != null) { + List parts = new ArrayList<>(); + + if (testngLib != null) { + parts.add(ClassPathSupport.createClassPath(testngLib.getContent("classpath").toArray(new URL[0]))); + } + if (junit5Lib != null) { + parts.add(ClassPathSupport.createClassPath(junit5Lib.getContent("classpath").toArray(new URL[0]))); + } + + parts.add(langtoolsCP); + + return ClassPathSupport.createProxyClassPath(parts.toArray(new ClassPath[0])); } if (langtoolsCP == null) diff --git a/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/jtreg/TestClassInfoTask.java b/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/jtreg/TestClassInfoTask.java new file mode 100644 index 000000000000..f2df2ea03a07 --- /dev/null +++ b/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/jtreg/TestClassInfoTask.java @@ -0,0 +1,213 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.netbeans.modules.java.openjdk.jtreg; + +import com.sun.source.tree.ClassTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.Tree.Kind; +import com.sun.source.util.SourcePositions; +import com.sun.source.util.TreePath; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.TypeElement; +import javax.lang.model.util.Elements; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.Position; +import org.netbeans.api.java.source.CancellableTask; +import org.netbeans.api.java.source.CompilationController; +import org.netbeans.api.java.source.CompilationInfo; +import org.netbeans.api.java.source.JavaSource.Phase; +import org.netbeans.modules.gsf.testrunner.ui.api.TestMethodController.TestMethod; +import org.netbeans.modules.java.testrunner.ui.spi.ComputeTestMethods; +import org.netbeans.modules.java.testrunner.ui.spi.ComputeTestMethods.Factory; +import org.netbeans.spi.project.SingleMethod; +import org.openide.filesystems.FileObject; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author lukas + */ +public final class TestClassInfoTask implements CancellableTask { + + private final int caretPosition; + private String packageName; + private String className; + private String methodName; + private FileObject fo; + + /** + * DO NOT USE! Package private due to use in tests + */ + static String ANNOTATION = "toolbox.TestRunner.Test"; //NOI18N + + TestClassInfoTask(int caretPosition) { + this.caretPosition = caretPosition; + } + + public void cancel() { + } + + public void run(CompilationController controller) throws Exception { + controller.toPhase(Phase.RESOLVED); + fo = controller.getFileObject(); + TypeElement typeElement = null; + List topLevelElements = controller.getTopLevelElements(); + for (Iterator it = topLevelElements.iterator(); it.hasNext();) { + typeElement = it.next(); + if (typeElement.getKind() == ElementKind.CLASS) { + className = typeElement.getSimpleName().toString(); + break; + } + } + Elements elements = controller.getElements(); + if (typeElement != null) { + packageName = elements.getPackageOf(typeElement).getQualifiedName().toString(); + } + List testMethods = computeTestMethods(controller, new AtomicBoolean(), caretPosition); + if (!testMethods.isEmpty()) { + methodName = testMethods.iterator().next().method().getMethodName(); + } + } + + String getClassName() { + return className; + } + + String getMethodName() { + return methodName; + } + + String getPackageName() { + return packageName; + } + + FileObject getFileObject() { + return fo; + } + + public static List computeTestMethods(CompilationInfo info, AtomicBoolean cancel, int caretPosIfAny) { + //TODO: first verify if this is a test class/class in a test source group? + FileObject fileObject = info.getFileObject(); + ClassTree clazz; + List methods; + if (caretPosIfAny == (-1)) { + Optional anyClass = info.getCompilationUnit().getTypeDecls().stream().filter(t -> t.getKind() == Kind.CLASS).findAny(); + if (!anyClass.isPresent()) { + return Collections.emptyList(); + } + clazz = (ClassTree) anyClass.get(); + TreePath pathToClass = new TreePath(new TreePath(info.getCompilationUnit()), clazz); + methods = clazz.getMembers().stream().filter(m -> m.getKind() == Kind.METHOD).map(m -> new TreePath(pathToClass, m)).collect(Collectors.toList()); + } else { + TreePath tp = info.getTreeUtilities().pathFor(caretPosIfAny); + while (tp != null && tp.getLeaf().getKind() != Kind.METHOD) { + tp = tp.getParentPath(); + } + if (tp != null) { + clazz = (ClassTree) tp.getParentPath().getLeaf(); + methods = Collections.singletonList(tp); + } else { + return Collections.emptyList(); + } + } + TypeElement typeElement = (TypeElement) info.getTrees().getElement(new TreePath(new TreePath(info.getCompilationUnit()), clazz)); + Elements elements = info.getElements(); + List result = new ArrayList<>(); + for (TreePath tp : methods) { + if (cancel.get()) { + return null; + } + Element element = info.getTrees().getElement(tp); + if (element != null) { + List allAnnotationMirrors = elements.getAllAnnotationMirrors(element); + for (Iterator it = allAnnotationMirrors.iterator(); it.hasNext();) { + AnnotationMirror annotationMirror = it.next(); + TypeElement annTypeElement = (TypeElement) annotationMirror.getAnnotationType().asElement(); + if (annTypeElement.getQualifiedName().contentEquals(ANNOTATION)) { + String mn = element.getSimpleName().toString(); + SourcePositions sp = info.getTrees().getSourcePositions(); + int start = (int) sp.getStartPosition(tp.getCompilationUnit(), tp.getLeaf()); + int preferred = info.getTreeUtilities().findNameSpan((MethodTree) tp.getLeaf())[0]; + int end = (int) sp.getEndPosition(tp.getCompilationUnit(), tp.getLeaf()); + Document doc = info.getSnapshot().getSource().getDocument(false); + try { + result.add(new TestMethod(typeElement.getQualifiedName().toString(), new SingleMethod(fileObject, mn), + doc != null ? doc.createPosition(start) : new SimplePosition(start), + doc != null ? doc.createPosition(preferred) : new SimplePosition(preferred), + doc != null ? doc.createPosition(end) : new SimplePosition(end))); + } catch (BadLocationException ex) { + //ignore + } + } + } + } + } + return result; + } + + @ServiceProvider(service=Factory.class) + public static final class ComputeTestMethodsImpl implements Factory { + + @Override + public ComputeTestMethods create() { + return new TaskImpl(); + } + + private static class TaskImpl implements ComputeTestMethods { + + private final AtomicBoolean cancel = new AtomicBoolean(); + + @Override + public void cancel() { + cancel.set(true); + } + + @Override + public List computeTestMethods(CompilationInfo info) { + return TestClassInfoTask.computeTestMethods(info, cancel, -1); + } + } + + } + + private static class SimplePosition implements Position { + + private final int offset; + + private SimplePosition(int offset) { + this.offset = offset; + } + + @Override + public int getOffset() { + return offset; + } + } +} diff --git a/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/project/ActionProviderImpl.java b/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/project/ActionProviderImpl.java index b39c5683b816..cba40fd7d7ae 100644 --- a/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/project/ActionProviderImpl.java +++ b/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/project/ActionProviderImpl.java @@ -38,6 +38,7 @@ import org.netbeans.spi.java.classpath.support.ClassPathSupport; import org.netbeans.spi.project.ActionProgress; import org.netbeans.spi.project.ActionProvider; +import org.netbeans.spi.project.SingleMethod; import org.netbeans.spi.project.ui.support.ProjectSensitiveActions; import org.openide.execution.ExecutorTask; import org.openide.filesystems.FileObject; @@ -151,6 +152,8 @@ private String[] readSupportedActions(FileObject from) { filteredActions.retainAll(Arrays.asList(actions)); filteredActions.add(COMMAND_BUILD_GENERIC_FAST); filteredActions.add(COMMAND_PROFILE_TEST_SINGLE); + filteredActions.add(SingleMethod.COMMAND_RUN_SINGLE_METHOD); + filteredActions.add(SingleMethod.COMMAND_DEBUG_SINGLE_METHOD); supported = filteredActions.toArray(new String[0]); break; } @@ -178,6 +181,15 @@ public void invokeAction(String command, Lookup context) throws IllegalArgumentE } } } + if (SingleMethod.COMMAND_RUN_SINGLE_METHOD.equals(command) || + SingleMethod.COMMAND_DEBUG_SINGLE_METHOD.equals(command)) { + for (ActionProvider ap : Lookup.getDefault().lookupAll(ActionProvider.class)) { + if (new HashSet<>(Arrays.asList(ap.getSupportedActions())).contains(command) && ap.isActionEnabled(command, context)) { + ap.invokeAction(command, context); + return ; + } + } + } FileObject scriptFO = script; Settings settings = project.getLookup().lookup(Settings.class); Properties props = new Properties(); diff --git a/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/project/ClassPathProviderImpl.java b/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/project/ClassPathProviderImpl.java index bca41f058dca..dbecca593677 100644 --- a/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/project/ClassPathProviderImpl.java +++ b/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/project/ClassPathProviderImpl.java @@ -194,7 +194,7 @@ public ClassPathProviderImpl(JDKProject project, ModuleRepository repository) { this.repository = repository; } - private static final String[] TEST_LIBRARIES = new String[] {"testng", "junit_4"}; + private static final String[] TEST_LIBRARIES = new String[] {"testng", "junit_4", "junit_5"}; private static URL projectDir2FakeTarget(FileObject projectDir) throws MalformedURLException { return FileUtil.getArchiveRoot(projectDir.toURI().resolve("fake-target.jar").toURL()); diff --git a/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/project/JDKProject.java b/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/project/JDKProject.java index 35d639661757..4e98d449e2e9 100644 --- a/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/project/JDKProject.java +++ b/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/project/JDKProject.java @@ -222,6 +222,7 @@ public JDKProject(FileObject projectDir, @NullAllowed ModuleRepository moduleRep new Settings(this), new BinaryForSourceQueryImpl(this, cpp.getSourceCP()), CProjectConfigurationProviderImpl.create(this), + new UnitTestForSourceQueryImpl(this), this); this.lookup = LookupProviderSupport.createCompositeLookup(base, "Projects/" + PROJECT_KEY + "/Lookup"); } catch (Throwable t) { diff --git a/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/project/UnitTestForSourceQueryImpl.java b/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/project/UnitTestForSourceQueryImpl.java new file mode 100644 index 000000000000..8bba362c3376 --- /dev/null +++ b/java/java.openjdk.project/src/org/netbeans/modules/java/openjdk/project/UnitTestForSourceQueryImpl.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.netbeans.modules.java.openjdk.project; + +import java.net.URL; +import java.util.Arrays; +import org.netbeans.api.java.project.JavaProjectConstants; +import org.netbeans.api.project.ProjectUtils; +import org.netbeans.api.project.SourceGroup; +import org.netbeans.modules.java.openjdk.project.JDKProject; +import org.netbeans.spi.java.queries.MultipleRootsUnitTestForSourceQueryImplementation; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; + +/** + * + * @author lahvac + */ +public class UnitTestForSourceQueryImpl implements MultipleRootsUnitTestForSourceQueryImplementation { + + private final JDKProject prj; + + public UnitTestForSourceQueryImpl(JDKProject prj) { + this.prj = prj; + } + + @Override + public URL[] findUnitTests(FileObject source) { + SourceGroup[] groups = ProjectUtils.getSources(prj) + .getSourceGroups(SourcesImpl.SOURCES_TYPE_JDK_PROJECT_TESTS); + return notInReturn(source, groups); + } + + @Override + public URL[] findSources(FileObject unitTest) { + SourceGroup[] groups = ProjectUtils.getSources(prj) + .getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA); + return notInReturn(unitTest, groups); + } + + private URL[] notInReturn(FileObject file, SourceGroup[] groups) { + return Arrays.stream(groups) + .map(sg -> sg.getRootFolder()) + .filter(root -> FileUtil.isParentOf(root, file) || root == file) + .map(f -> f.toURL()) + .toArray(s -> new URL[s]); + } +} diff --git a/java/java.testrunner.ui/nbproject/project.xml b/java/java.testrunner.ui/nbproject/project.xml index e9147c5546b8..30b1e91f25ce 100644 --- a/java/java.testrunner.ui/nbproject/project.xml +++ b/java/java.testrunner.ui/nbproject/project.xml @@ -197,6 +197,7 @@ org.netbeans.modules.gradle.test org.netbeans.modules.java.lsp.server + org.netbeans.modules.java.openjdk.project org.netbeans.modules.junit.ant.ui org.netbeans.modules.junit.ui org.netbeans.modules.maven.junit.ui diff --git a/java/testng.ui/nbproject/project.xml b/java/testng.ui/nbproject/project.xml index 1bbbafb61704..971beaa66b7a 100644 --- a/java/testng.ui/nbproject/project.xml +++ b/java/testng.ui/nbproject/project.xml @@ -456,14 +456,24 @@ + + org.netbeans.modules.parsing.indexing + + org.netbeans.modules.parsing.lucene + + org.netbeans.modules.parsing.nb + org.netbeans.modules.progress.ui + + org.netbeans.modules.projectapi.nb + org.netbeans.modules.projectui @@ -498,12 +508,12 @@ org.openide.text - org.openide.util.ui + org.openide.util.lookup - org.openide.util.lookup + org.openide.util.ui diff --git a/java/testng.ui/src/org/netbeans/modules/testng/ui/actions/TestClassInfoTask.java b/java/testng.ui/src/org/netbeans/modules/testng/ui/actions/TestClassInfoTask.java index eddfc91564ca..5a9bb9b78155 100644 --- a/java/testng.ui/src/org/netbeans/modules/testng/ui/actions/TestClassInfoTask.java +++ b/java/testng.ui/src/org/netbeans/modules/testng/ui/actions/TestClassInfoTask.java @@ -34,6 +34,8 @@ import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.QualifiedNameable; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; import javax.swing.text.BadLocationException; @@ -66,6 +68,7 @@ public final class TestClassInfoTask implements CancellableTaskDO NOT USE! Package private due to use in tests */ static String ANNOTATION = "org.testng.annotations.Test"; //NOI18N + static String TESTNG_ANNOTATION_PACKAGE = "org.testng.annotations"; //NOI18N TestClassInfoTask(int caretPosition) { this.caretPosition = caretPosition; @@ -139,39 +142,52 @@ public static List computeTestMethods(CompilationInfo info, AtomicBo } TypeElement typeElement = (TypeElement) info.getTrees().getElement(new TreePath(new TreePath(info.getCompilationUnit()), clazz)); Elements elements = info.getElements(); + boolean hasClassLevelAnnotation = hasTestNGTestAnnotation(elements, typeElement); List result = new ArrayList<>(); for (TreePath tp : methods) { if (cancel.get()) { return null; } Element element = info.getTrees().getElement(tp); - if (element != null) { - List allAnnotationMirrors = elements.getAllAnnotationMirrors(element); - for (Iterator it = allAnnotationMirrors.iterator(); it.hasNext();) { - AnnotationMirror annotationMirror = it.next(); - TypeElement annTypeElement = (TypeElement) annotationMirror.getAnnotationType().asElement(); - if (annTypeElement.getQualifiedName().contentEquals(ANNOTATION)) { - String mn = element.getSimpleName().toString(); - SourcePositions sp = info.getTrees().getSourcePositions(); - int start = (int) sp.getStartPosition(tp.getCompilationUnit(), tp.getLeaf()); - int preferred = info.getTreeUtilities().findNameSpan((MethodTree) tp.getLeaf())[0]; - int end = (int) sp.getEndPosition(tp.getCompilationUnit(), tp.getLeaf()); - Document doc = info.getSnapshot().getSource().getDocument(false); - try { - result.add(new TestMethod(typeElement.getQualifiedName().toString(), new SingleMethod(fileObject, mn), - doc != null ? doc.createPosition(start) : new SimplePosition(start), - doc != null ? doc.createPosition(preferred) : new SimplePosition(preferred), - doc != null ? doc.createPosition(end) : new SimplePosition(end))); - } catch (BadLocationException ex) { - //ignore + if (element != null && element.getKind() == ElementKind.METHOD) { + if (hasTestNGTestAnnotation(elements, element) || + (hasClassLevelAnnotation && element.getModifiers().contains(Modifier.PUBLIC) && + !hasTestNGAnnotation(elements, element))) { + String mn = element.getSimpleName().toString(); + SourcePositions sp = info.getTrees().getSourcePositions(); + int start = (int) sp.getStartPosition(tp.getCompilationUnit(), tp.getLeaf()); + int preferred = info.getTreeUtilities().findNameSpan((MethodTree) tp.getLeaf())[0]; + int end = (int) sp.getEndPosition(tp.getCompilationUnit(), tp.getLeaf()); + Document doc = info.getSnapshot().getSource().getDocument(false); + try { + result.add(new TestMethod(typeElement.getQualifiedName().toString(), new SingleMethod(fileObject, mn), + doc != null ? doc.createPosition(start) : new SimplePosition(start), + doc != null ? doc.createPosition(preferred) : new SimplePosition(preferred), + doc != null ? doc.createPosition(end) : new SimplePosition(end))); + } catch (BadLocationException ex) { + //ignore } } } } - } return result; } + private static boolean hasTestNGTestAnnotation(Elements elements, Element element) { + return elements.getAllAnnotationMirrors(element) + .stream() + .map(am -> (TypeElement) am.getAnnotationType().asElement()) + .anyMatch(annTypeElement -> annTypeElement.getQualifiedName().contentEquals(ANNOTATION)); + } + + private static boolean hasTestNGAnnotation(Elements elements, Element element) { + return elements.getAllAnnotationMirrors(element) + .stream() + .map(am -> (TypeElement) am.getAnnotationType().asElement()) + .map(te -> (QualifiedNameable) te.getEnclosingElement()) + .anyMatch(annTypeElement -> annTypeElement.getQualifiedName().contentEquals(TESTNG_ANNOTATION_PACKAGE)); + } + @ServiceProvider(service=Factory.class) public static final class ComputeTestMethodsImpl implements Factory { diff --git a/java/testng.ui/test/unit/src/org/netbeans/modules/testng/ui/actions/RetoucheTestBase.java b/java/testng.ui/test/unit/src/org/netbeans/modules/testng/ui/actions/RetoucheTestBase.java index e5e9268cc176..11afdedbc480 100644 --- a/java/testng.ui/test/unit/src/org/netbeans/modules/testng/ui/actions/RetoucheTestBase.java +++ b/java/testng.ui/test/unit/src/org/netbeans/modules/testng/ui/actions/RetoucheTestBase.java @@ -74,16 +74,6 @@ public ClassPath findClassPath(FileObject file, String type) { new String[]{}, new Object[]{loader, cpp}); testFO = FileUtil.createFolder(src, "sample/pkg/").createData("Test.java"); - TestUtilities.copyStringToFile(testFO, - "package sample.pkg;\n" + - "\n" + - "public class Test {\n" + - "\n" + - " @Deprecated\n" + - " void method() {\n" + - " }\n" + - "\n" + - "}\n"); } protected FileObject getTestFO() { diff --git a/java/testng.ui/test/unit/src/org/netbeans/modules/testng/ui/actions/TestClassInfoTaskTest.java b/java/testng.ui/test/unit/src/org/netbeans/modules/testng/ui/actions/TestClassInfoTaskTest.java index 4eeb3a8a32eb..060cda54176f 100644 --- a/java/testng.ui/test/unit/src/org/netbeans/modules/testng/ui/actions/TestClassInfoTaskTest.java +++ b/java/testng.ui/test/unit/src/org/netbeans/modules/testng/ui/actions/TestClassInfoTaskTest.java @@ -18,8 +18,11 @@ */ package org.netbeans.modules.testng.ui.actions; +import java.util.concurrent.atomic.AtomicBoolean; import org.netbeans.api.java.source.JavaSource; +import org.netbeans.api.java.source.JavaSource.Phase; import org.netbeans.api.java.source.TestUtilities; +import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeSuite; import org.testng.annotations.Test; @@ -32,8 +35,20 @@ public class TestClassInfoTaskTest extends RetoucheTestBase { static { TestClassInfoTask.ANNOTATION = "java.lang.Deprecated"; + TestClassInfoTask.TESTNG_ANNOTATION_PACKAGE = "java.lang"; } + private static final String DEFAULT_TEST_DATA = + "package sample.pkg;\n" + + "\n" + + "public class Test {\n" + + "\n" + + " @Deprecated\n" + + " void method() {\n" + + " }\n" + + "\n" + + "}\n"; + public TestClassInfoTaskTest(String testName) { super(testName); } @@ -44,6 +59,7 @@ public void setupClass() throws Exception { } public void testCursorInMethod() throws Exception { + TestUtilities.copyStringToFile(getTestFO(), DEFAULT_TEST_DATA); JavaSource src = JavaSource.forFileObject(getTestFO()); TestClassInfoTask task = new TestClassInfoTask(70); src.runUserActionTask(task, true); @@ -53,6 +69,7 @@ public void testCursorInMethod() throws Exception { } public void testCursorInClass() throws Exception { + TestUtilities.copyStringToFile(getTestFO(), DEFAULT_TEST_DATA); JavaSource src = JavaSource.forFileObject(getTestFO()); TestClassInfoTask task = new TestClassInfoTask(42); src.runUserActionTask(task, true); @@ -62,6 +79,7 @@ public void testCursorInClass() throws Exception { } public void testCursorInClass2() throws Exception { + TestUtilities.copyStringToFile(getTestFO(), DEFAULT_TEST_DATA); JavaSource src = JavaSource.forFileObject(getTestFO()); TestClassInfoTask task = new TestClassInfoTask(0); src.runUserActionTask(task, true); @@ -71,6 +89,7 @@ public void testCursorInClass2() throws Exception { } public void testCursorInClass3() throws Exception { + TestUtilities.copyStringToFile(getTestFO(), DEFAULT_TEST_DATA); JavaSource src = JavaSource.forFileObject(getTestFO()); TestClassInfoTask task = new TestClassInfoTask(87); src.runUserActionTask(task, true); @@ -95,4 +114,50 @@ public void testDefaultPackage() throws Exception { assertEquals("", task.getPackageName()); assertEquals("Test", task.getClassName()); } + + public void testClassAnnotated1() throws Exception { + String code = "package test;\n" + + "@Deprecated\n" + + "public class Test {\n" + + " public void method1() {\n" + + " //test1\n" + + " }\n" + + " @SuppressWarnings(\"\")\n" + + " public void method2() {\n" + + " //test2\n" + + " }\n" + + " void method3() {\n" + + " //test3\n" + + " }\n" + + " public static void method4() {\n" + + " //test4\n" + + " }\n" + + "}\n"; + TestUtilities.copyStringToFile(getTestFO(), code); + JavaSource src = JavaSource.forFileObject(getTestFO()); + TestClassInfoTask task1 = new TestClassInfoTask(code.indexOf("//test1")); + src.runUserActionTask(task1, true); + assertEquals("method1", task1.getMethodName()); + assertEquals("test", task1.getPackageName()); + assertEquals("Test", task1.getClassName()); + TestClassInfoTask task2 = new TestClassInfoTask(code.indexOf("//test2")); + src.runUserActionTask(task2, true); + assertNull(task2.getMethodName()); + assertEquals("test", task2.getPackageName()); + assertEquals("Test", task2.getClassName()); + TestClassInfoTask task3 = new TestClassInfoTask(code.indexOf("//test3")); + src.runUserActionTask(task3, true); + assertNull(task3.getMethodName()); + assertEquals("test", task3.getPackageName()); + assertEquals("Test", task3.getClassName()); + TestClassInfoTask task4 = new TestClassInfoTask(code.indexOf("//test4")); + src.runUserActionTask(task4, true); + assertEquals("method4", task4.getMethodName()); + assertEquals("test", task4.getPackageName()); + assertEquals("Test", task4.getClassName()); + src.runUserActionTask(cc -> { + cc.toPhase(Phase.ELEMENTS_RESOLVED); + assertEquals(2, TestClassInfoTask.computeTestMethods(cc, new AtomicBoolean(), -1).size()); + }, true); + } }