Skip to content

Commit

Permalink
Merge pull request #6067 from jlahoda/jtreg-run-single-test-case
Browse files Browse the repository at this point in the history
Adding ability to run a single sub-test for tests under jtreg.
  • Loading branch information
mbien authored Jul 16, 2023
2 parents 3b1e00c + 404322f commit 622dbf0
Show file tree
Hide file tree
Showing 14 changed files with 482 additions and 40 deletions.
1 change: 1 addition & 0 deletions ide/gsf.testrunner.ui/nbproject/project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@
<friend>org.netbeans.modules.groovy.support</friend>
<friend>org.netbeans.modules.hudson.ui</friend>
<friend>org.netbeans.modules.java.lsp.server</friend>
<friend>org.netbeans.modules.java.openjdk.project</friend>
<friend>org.netbeans.modules.java.testrunner.ui</friend>
<friend>org.netbeans.modules.javascript.jstestdriver</friend>
<friend>org.netbeans.modules.javascript.karma</friend>
Expand Down
25 changes: 25 additions & 0 deletions java/java.openjdk.project/nbproject/project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@
<specification-version>1.64</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.netbeans.modules.gsf.testrunner.ui</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<specification-version>1.34</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.netbeans.modules.java.lexer</code-name-base>
<build-prerequisite/>
Expand Down Expand Up @@ -188,6 +196,14 @@
<specification-version>2.36</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.netbeans.modules.java.testrunner.ui</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<specification-version>1.23</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.netbeans.modules.lexer</code-name-base>
<build-prerequisite/>
Expand All @@ -197,6 +213,15 @@
<specification-version>1.66</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.netbeans.modules.parsing.api</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<release-version>1</release-version>
<specification-version>9.26</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.netbeans.modules.parsing.indexing</code-name-base>
<build-prerequisite/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
Expand All @@ -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
Expand All @@ -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);
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ClassPath> 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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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<CompilationController> {

private final int caretPosition;
private String packageName;
private String className;
private String methodName;
private FileObject fo;

/**
* <b>DO NOT USE!</b> 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<? extends TypeElement> topLevelElements = controller.getTopLevelElements();
for (Iterator<? extends TypeElement> 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<TestMethod> 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<TestMethod> 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<TreePath> methods;
if (caretPosIfAny == (-1)) {
Optional<? extends Tree> 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<TestMethod> result = new ArrayList<>();
for (TreePath tp : methods) {
if (cancel.get()) {
return null;
}
Element element = info.getTrees().getElement(tp);
if (element != null) {
List<? extends AnnotationMirror> allAnnotationMirrors = elements.getAllAnnotationMirrors(element);
for (Iterator<? extends AnnotationMirror> 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<TestMethod> 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;
}
}
}
Loading

0 comments on commit 622dbf0

Please sign in to comment.