Skip to content
Merged
2 changes: 1 addition & 1 deletion ide/projectapi/nbproject/project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
is.autoload=true

javac.compilerargs=-Xlint -Xlint:-serial
javac.source=1.8
javac.release=17
javadoc.arch=${basedir}/arch.xml
javadoc.apichanges=${basedir}/apichanges.xml

Expand Down
42 changes: 34 additions & 8 deletions ide/projectapi/src/org/netbeans/spi/project/NestedClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,16 @@ public final class NestedClass {
* Creates a new instance holding the specified identification
* of a nested class.
*
* @param className name of a class inside the file
* @param topLevelClassName top level name of a class inside the file
* @param className name of a class inside the file. Can be an empty string
* if this NestedClass represents a top level element in the source file.
* This is relevant for Java, which allows multiple top level class
* declarations as long as they are not public. The class assumes, that the
* {@code className} follows java convention (i.e. is separated by dots).
* @param topLevelClassName top level name of a class inside the file. This
* is the simple name without package qualification.
* @param file file to be kept in the object
* @exception java.lang.IllegalArgumentException
* if the file or class name is {@code null}
* if the file, topLevelClassName or class name is {@code null}
* @since 1.99
*/
public NestedClass(String className, String topLevelClassName, FileObject file) {
Expand Down Expand Up @@ -90,7 +95,8 @@ public String getClassName() {
}

/**
* Returns name of a top level class within a file.
* Returns name of a top level class within a file. This is the simple name
* without package qualification.
*
* @return top level class name held by this object
* @since 1.99
Expand All @@ -108,9 +114,19 @@ public String getTopLevelClassName() {
* @since 1.99
*/
public String getFQN(String packageName) {
return String.join(".", packageName, topLevelClassName, className);
String classNameSuffix;
if (className.isBlank()) {
classNameSuffix = topLevelClassName;
} else {
classNameSuffix = topLevelClassName + "." + className;
}
if (packageName.isBlank()) {
return classNameSuffix;
} else {
return String.join(".", packageName, classNameSuffix);
}
}

/**
* Returns fully qualified name.
*
Expand All @@ -121,9 +137,19 @@ public String getFQN(String packageName) {
* @since 1.99
*/
public String getFQN(String packageName, String nestedClassSeparator) {
return String.join(".", packageName, String.join(nestedClassSeparator, topLevelClassName, className.replace(".", nestedClassSeparator)));
String classNameSuffix;
if (className.isBlank()) {
classNameSuffix = topLevelClassName;
} else {
classNameSuffix = topLevelClassName + nestedClassSeparator + className.replace(".", nestedClassSeparator);
}
if (packageName.isBlank()) {
return classNameSuffix;
} else {
return String.join(".", packageName, classNameSuffix);
}
}

@Override
public int hashCode() {
int hash = 3;
Expand Down
2 changes: 1 addition & 1 deletion java/gradle.test/nbproject/project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@
# under the License.

is.eager=true
javac.source=1.8
javac.release=17
javac.compilerargs=-Xlint -Xlint:-serial
nbm.module.author=Laszlo Kishalmi
36 changes: 22 additions & 14 deletions java/gradle.test/nbproject/project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,46 +26,46 @@
<code-name-base>org.netbeans.modules.gradle.test</code-name-base>
<module-dependencies>
<dependency>
<code-name-base>org.netbeans.modules.libs.gradle</code-name-base>
<code-name-base>org.netbeans.api.java.classpath</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<release-version>8</release-version>
<specification-version>8.0.1</specification-version>
<release-version>1</release-version>
<specification-version>1.41.1</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.netbeans.modules.gradle</code-name-base>
<code-name-base>org.netbeans.libs.javacapi</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<release-version>2</release-version>
<specification-version>2.0</specification-version>
<specification-version>8.53</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.netbeans.modules.gradle.java</code-name-base>
<code-name-base>org.netbeans.modules.extexecution</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<specification-version>1.17</specification-version>
<release-version>2</release-version>
<specification-version>1.45</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.netbeans.api.java.classpath</code-name-base>
<code-name-base>org.netbeans.modules.gradle</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<release-version>1</release-version>
<specification-version>1.41.1</specification-version>
<release-version>2</release-version>
<specification-version>2.0</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.netbeans.modules.extexecution</code-name-base>
<code-name-base>org.netbeans.modules.gradle.java</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<release-version>2</release-version>
<specification-version>1.45</specification-version>
<specification-version>1.17</specification-version>
</run-dependency>
</dependency>
<dependency>
Expand Down Expand Up @@ -118,6 +118,14 @@
<specification-version>1.3.1</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.netbeans.modules.libs.gradle</code-name-base>
<compile-dependency/>
<run-dependency>
<release-version>8</release-version>
<specification-version>8.0.1</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.netbeans.modules.projectapi</code-name-base>
<build-prerequisite/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,17 @@

package org.netbeans.modules.gradle.test;

import java.nio.file.Path;
import java.util.Arrays;
import org.netbeans.modules.gradle.api.NbGradleProject;
import java.util.Collection;
import org.netbeans.modules.gradle.spi.GradleProgressListenerProvider;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.lang.model.element.ElementKind;
import org.gradle.tooling.Failure;
import org.gradle.tooling.events.OperationDescriptor;
import org.gradle.tooling.events.OperationType;
Expand All @@ -46,8 +47,15 @@
import org.gradle.tooling.events.test.TestSkippedResult;
import org.gradle.tooling.events.test.TestStartEvent;
import org.gradle.tooling.events.test.TestSuccessResult;
import org.netbeans.api.java.source.ClasspathInfo;
import org.netbeans.api.java.source.ElementHandle;
import org.netbeans.api.java.source.SourceUtils;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectUtils;
import org.netbeans.modules.gradle.api.NbGradleProject;
import org.netbeans.modules.gradle.java.api.GradleJavaProject;
import org.netbeans.modules.gradle.java.api.GradleJavaSourceSet.SourceType;
import org.netbeans.modules.gradle.spi.GradleProgressListenerProvider;
import org.netbeans.modules.gsf.testrunner.api.CommonUtils;
import org.netbeans.modules.gsf.testrunner.api.CoreManager;
import org.netbeans.modules.gsf.testrunner.api.Report;
Expand All @@ -57,6 +65,8 @@
import org.netbeans.modules.gsf.testrunner.api.Testcase;
import org.netbeans.modules.gsf.testrunner.api.Trouble;
import org.netbeans.spi.project.ProjectServiceProvider;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.Lookup;

/**
Expand All @@ -68,8 +78,8 @@ public final class GradleTestProgressListener implements ProgressListener, Gradl

private final Project project;
private final Map<String, TestSession> sessions = new ConcurrentHashMap<>();

private Map<TestSession, Map<String, Testcase>> runningTests = new ConcurrentHashMap<>();
private final Map<TestSession, Map<String, TestSuite>> runningSuites = new ConcurrentHashMap<>();
private final Map<TestSession, Map<String, Testcase>> runningTests = new ConcurrentHashMap<>();

public GradleTestProgressListener(Project project) {
this.project = project;
Expand Down Expand Up @@ -190,13 +200,21 @@ private void suiteFinish(TestFinishEvent evt, JvmTestOperationDescriptor op) {
TestSession session = sessions.get(getSessionKey(evt.getDescriptor()));
assert session != null;
TestOperationResult result = evt.getResult();
TestSuite currentSuite = session.getCurrentSuite();
String suiteName = GradleTestSuite.suiteName(op);
if (suiteName.equals(currentSuite.getName())) {
// In the NetBeans wording a testsuite is the class grouping multiple
// methods (testcase). In the gradle wording a suite can be nested, for
// example the hieararchy can be:
// - Gradle Test Executor <Number> started
// - Test class <Class> started
// => We flatten the list (suites are registered base on executed
// cases (see caseStart)
TestSuite testSuite = runningSuites.get(session).remove(suiteName);
if (testSuite != null) {
Report report = session.getReport(result.getEndTime() - result.getStartTime());
session.finishSuite(currentSuite);
session.finishSuite(testSuite);
CoreManager manager = getManager();
if (manager != null) {
manager.displaySuiteRunning(session, testSuite);
manager.displayReport(session, report, true);
}
}
Expand All @@ -206,19 +224,21 @@ private void caseStart(TestStartEvent evt, JvmTestOperationDescriptor op) {
TestSession session = sessions.get(getSessionKey(evt.getDescriptor()));
assert session != null;
assert op.getParent() != null;
TestSuite currentSuite = session.getCurrentSuite();
TestSuite newSuite = new GradleTestSuite(getSuiteOpDesc((JvmTestOperationDescriptor) op.getParent(), op.getClassName()));
if ((currentSuite == null) || !currentSuite.equals(newSuite)) {
session.addSuite(newSuite);
CoreManager manager = getManager();
if (manager != null) {
manager.displaySuiteRunning(session, newSuite);
}
String suiteName = GradleTestSuite.suiteName(op.getParent());
Map<String, TestSuite> sessionSuites = runningSuites.computeIfAbsent(session, s -> new ConcurrentHashMap<>());
TestSuite ts = sessionSuites.computeIfAbsent(suiteName, s -> {
TestSuite suite = new GradleTestSuite(getSuiteOpDesc((JvmTestOperationDescriptor) op.getParent(), op.getClassName()));
session.addSuite(suite);
return suite;
});
CoreManager manager = getManager();
if (manager != null && sessionSuites.size() == 1) {
manager.displaySuiteRunning(session, ts);
}
Testcase tc = new GradleTestcase(op, session);
synchronized (this) {
synchronized (this) {
runningTests.get(session).put(getTestOpKey(op), tc);
session.addTestCase(tc);
session.addTestCase(tc);
}
}

Expand All @@ -233,7 +253,7 @@ private void caseFinish(TestFinishEvent evt, JvmTestOperationDescriptor op) {
TestOperationResult result = evt.getResult();
long time = result.getEndTime() - result.getStartTime();
tc.setTimeMillis(time);
tc.setLocation(searchLocation(op.getClassName(), op.getMethodName(), null));
tc.setLocation(searchLocation(tc, op.getClassName(), op.getMethodName(), null));
if (result instanceof TestSuccessResult) {
tc.setStatus(Status.PASSED);
}
Expand Down Expand Up @@ -261,7 +281,7 @@ private void caseFinish(TestFinishEvent evt, JvmTestOperationDescriptor op) {
stackTrace = desc.split("\\n");
trouble.setStackTrace(stackTrace);
}
tc.setLocation(searchLocation(op.getClassName(), op.getMethodName(), stackTrace));
tc.setLocation(searchLocation(tc, op.getClassName(), op.getMethodName(), stackTrace));
tc.setTrouble(trouble);
}

Expand Down Expand Up @@ -322,7 +342,39 @@ private static CoreManager getManager() {

}

private String searchLocation(String className, String methodName, String[] stackTrace) {
private String searchLocation(Testcase tc, String className, String methodName, String[] stackTrace) {
Map<ClasspathInfo, Path> classpathInfo = Map.of();
NbGradleProject nbGradleProject = tc.getSession()
.getProject()
.getLookup()
.lookup(NbGradleProject.class);
GradleJavaProject gradleJavaProject = nbGradleProject != null ? nbGradleProject.projectLookup(GradleJavaProject.class) : null;
if (gradleJavaProject != null) {
classpathInfo = gradleJavaProject
.getSourceSets()
.values()
.stream()
.flatMap(gradleJavaSourceSet -> gradleJavaSourceSet.getSourceDirs(SourceType.JAVA).stream())
.collect(
Collectors.toMap(
f -> ClasspathInfo.create(f),
f -> f.toPath()
)
);
}

String relativePath = null;
for (Map.Entry<ClasspathInfo, Path> ci : classpathInfo.entrySet()) {
FileObject fo = SourceUtils.getFile(ElementHandle.createTypeElementHandle(ElementKind.CLASS, className), ci.getKey());
if (fo != null) {
relativePath = ci.getValue().relativize(FileUtil.toFile(fo).toPath()).toString();
break;
}
}
if (relativePath != null) {
return relativePath;
}

StringBuilder ret = new StringBuilder(className.length() + methodName.length() + 10);
String fileName = null;
String line = null;
Expand Down
Loading