You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by db...@apache.org on 2021/09/27 08:33:38 UTC
[netbeans] branch master updated: LSP: Test results displayed using
the new TestExplorer API. (#3186)
This is an automated email from the ASF dual-hosted git repository.
dbalek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new 253b52d LSP: Test results displayed using the new TestExplorer API. (#3186)
253b52d is described below
commit 253b52dd2cebac79110b153fe0e90db72328649b
Author: Dusan Balek <du...@oracle.com>
AuthorDate: Mon Sep 27 10:33:02 2021 +0200
LSP: Test results displayed using the new TestExplorer API. (#3186)
---
.../gsf/testrunner/ui/TestMethodFinderImpl.java | 4 +-
.../gsf/testrunner/ui/api/TestMethodFinder.java | 6 +-
.../server/debugging/launch/NbLaunchDelegate.java | 2 +-
.../lsp/server/progress/TestProgressHandler.java | 6 +-
.../java/lsp/server/protocol/TestSuiteInfo.java | 152 ++++-----
.../lsp/server/protocol/WorkspaceServiceImpl.java | 18 +-
.../server/progress/TestProgressHandlerTest.java | 13 +-
java/java.lsp.server/vscode/package-lock.json | 27 +-
java/java.lsp.server/vscode/package.json | 18 +-
java/java.lsp.server/vscode/src/extension.ts | 36 +-
java/java.lsp.server/vscode/src/protocol.ts | 54 ++-
java/java.lsp.server/vscode/src/test/runTest.ts | 17 +-
java/java.lsp.server/vscode/src/testAdapter.ts | 373 +++++++++------------
13 files changed, 308 insertions(+), 418 deletions(-)
diff --git a/ide/gsf.testrunner.ui/src/org/netbeans/modules/gsf/testrunner/ui/TestMethodFinderImpl.java b/ide/gsf.testrunner.ui/src/org/netbeans/modules/gsf/testrunner/ui/TestMethodFinderImpl.java
index 6588969..1198c8d 100644
--- a/ide/gsf.testrunner.ui/src/org/netbeans/modules/gsf/testrunner/ui/TestMethodFinderImpl.java
+++ b/ide/gsf.testrunner.ui/src/org/netbeans/modules/gsf/testrunner/ui/TestMethodFinderImpl.java
@@ -107,7 +107,9 @@ public final class TestMethodFinderImpl extends EmbeddingIndexer {
pw.print("method: "); //NOI18N
pw.print(method.method().getMethodName());
pw.print(':'); //NOI18N
- pw.println(method.start().getOffset());
+ pw.print(method.start().getOffset());
+ pw.print('-'); //NOI18N
+ pw.println(method.end().getOffset());
}
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
diff --git a/ide/gsf.testrunner.ui/src/org/netbeans/modules/gsf/testrunner/ui/api/TestMethodFinder.java b/ide/gsf.testrunner.ui/src/org/netbeans/modules/gsf/testrunner/ui/api/TestMethodFinder.java
index b1dc558..22ae987 100644
--- a/ide/gsf.testrunner.ui/src/org/netbeans/modules/gsf/testrunner/ui/api/TestMethodFinder.java
+++ b/ide/gsf.testrunner.ui/src/org/netbeans/modules/gsf/testrunner/ui/api/TestMethodFinder.java
@@ -102,8 +102,10 @@ public final class TestMethodFinder {
String info = line.substring(8);
int idx = info.lastIndexOf(':');
String name = (idx < 0 ? info : info.substring(0, idx)).trim();
- Position methodPosition = idx < 0 ? null : () -> Integer.parseInt(info.substring(idx + 1));
- testMethods.add(new TestMethodController.TestMethod(className, classPosition, new SingleMethod(fo, name), methodPosition, null, null));
+ String[] range = idx < 0 ? new String[0] : info.substring(idx + 1).split("-");
+ Position methodStart = range.length > 0 ? () -> Integer.parseInt(range[0]) : null;
+ Position methodEnd = range.length > 1 ? () -> Integer.parseInt(range[1]) : null;
+ testMethods.add(new TestMethodController.TestMethod(className, classPosition, new SingleMethod(fo, name), methodStart, null, methodEnd));
}
}
} catch (IOException ex) {
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbLaunchDelegate.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbLaunchDelegate.java
index f05b675..ccfcece 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbLaunchDelegate.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbLaunchDelegate.java
@@ -508,7 +508,7 @@ public abstract class NbLaunchDelegate {
if (provider == null) {
command = debug ? mainSource ? ActionProvider.COMMAND_DEBUG
- : ActionProvider.COMMAND_DEBUG // DEBUG_TEST is missing?
+ : ActionProvider.COMMAND_TEST //TODO: COMMAND_DEBUG_TEST is missing?
: mainSource ? ActionProvider.COMMAND_RUN
: ActionProvider.COMMAND_TEST;
provider = findActionProvider(command, actionProviders, testLookup);
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/progress/TestProgressHandler.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/progress/TestProgressHandler.java
index 1a2b551..be7b83d 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/progress/TestProgressHandler.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/progress/TestProgressHandler.java
@@ -70,12 +70,12 @@ public final class TestProgressHandler implements TestResultDisplayHandler.Spi<T
@Override
public void displaySuiteRunning(TestProgressHandler token, String suiteName) {
- lspClient.notifyTestProgress(new TestProgressParams(uri, new TestSuiteInfo(suiteName, TestSuiteInfo.State.Running)));
+ lspClient.notifyTestProgress(new TestProgressParams(uri, new TestSuiteInfo(suiteName, TestSuiteInfo.State.Started)));
}
@Override
public void displaySuiteRunning(TestProgressHandler token, TestSuite suite) {
- lspClient.notifyTestProgress(new TestProgressParams(uri, new TestSuiteInfo(suite.getName(), TestSuiteInfo.State.Running)));
+ lspClient.notifyTestProgress(new TestProgressParams(uri, new TestSuiteInfo(suite.getName(), TestSuiteInfo.State.Started)));
}
@Override
@@ -121,7 +121,7 @@ public final class TestProgressHandler implements TestResultDisplayHandler.Spi<T
if (info != null) {
updateState(info, status);
} else {
- info = new TestSuiteInfo.TestCaseInfo(id, name, className + '.' + name, fo != null ? Utils.toUri(fo) : null, null, status, stackTrace);
+ info = new TestSuiteInfo.TestCaseInfo(id, name, fo != null ? Utils.toUri(fo) : null, null, status, stackTrace);
testCases.put(id, info);
}
}
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TestSuiteInfo.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TestSuiteInfo.java
index d92337d..36ea81b 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TestSuiteInfo.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TestSuiteInfo.java
@@ -20,6 +20,7 @@ package org.netbeans.modules.java.lsp.server.protocol;
import java.util.List;
import java.util.Objects;
+import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
import org.eclipse.lsp4j.util.Preconditions;
import org.eclipse.xtext.xbase.lib.Pure;
@@ -36,7 +37,7 @@ public final class TestSuiteInfo {
* The test suite name to be displayed by the Test Explorer.
*/
@NonNull
- private String suiteName;
+ private String name;
/**
* The file containing this suite (if known).
@@ -44,13 +45,13 @@ public final class TestSuiteInfo {
private String file;
/**
- * The line within the specified file where the suite definition starts (if known).
+ * The range within the specified file where the suite definition is located (if known).
*/
- private Integer line;
+ private Range range;
/**
* The state of the tests suite. Can be one of the following values:
- * "loaded" | "running" | "completed" | "errored"
+ * "loaded" | "started" | "completed" | "errored"
*/
@NonNull
private String state;
@@ -64,15 +65,15 @@ public final class TestSuiteInfo {
this("", "");
}
- public TestSuiteInfo(@NonNull final String suiteName, @NonNull final String state) {
- this.suiteName = Preconditions.checkNotNull(suiteName, "suiteName");
+ public TestSuiteInfo(@NonNull final String name, @NonNull final String state) {
+ this.name = Preconditions.checkNotNull(name, "name");
this.state = Preconditions.checkNotNull(state, "state");
}
- public TestSuiteInfo(@NonNull final String suiteName, final String file, final Integer line, @NonNull final String state, final List<TestCaseInfo> tests) {
- this(suiteName, state);
+ public TestSuiteInfo(@NonNull final String name, final String file, final Range range, @NonNull final String state, final List<TestCaseInfo> tests) {
+ this(name, state);
this.file = file;
- this.line = line;
+ this.range = range;
this.tests = tests;
}
@@ -81,15 +82,15 @@ public final class TestSuiteInfo {
*/
@Pure
@NonNull
- public String getSuiteName() {
- return suiteName;
+ public String getName() {
+ return name;
}
/**
* The test suite name to be displayed by the Test Explorer.
*/
- public void setSuiteName(@NonNull final String suiteName) {
- this.suiteName = Preconditions.checkNotNull(suiteName, "suiteName");
+ public void setSuiteName(@NonNull final String name) {
+ this.name = Preconditions.checkNotNull(name, "name");
}
/**
@@ -108,23 +109,23 @@ public final class TestSuiteInfo {
}
/**
- * The line within the specified file where the suite definition starts (if known).
+ * The range within the specified file where the suite definition is located (if known).
*/
@Pure
- public Integer getLine() {
- return line;
+ public Range getRange() {
+ return range;
}
/**
- * The line within the specified file where the suite definition starts (if known).
+ * The range within the specified file where the suite definition is located (if known).
*/
- public void setLine(final Integer line) {
- this.line = line;
+ public void setRange(final Range range) {
+ this.range = range;
}
/**
* The state of the tests suite. Can be one of the following values:
- * "loaded" | "running" | "completed" | "errored"
+ * "loaded" | "started" | "completed" | "errored"
*/
@Pure
@NonNull
@@ -134,7 +135,7 @@ public final class TestSuiteInfo {
/**
* The state of the tests suite. Can be one of the following values:
- * "loaded" | "running" | "completed" | "errored"
+ * "loaded" | "started" | "completed" | "errored"
*/
public void setState(@NonNull final String state) {
this.state = Preconditions.checkNotNull(state, "state");
@@ -159,9 +160,9 @@ public final class TestSuiteInfo {
@Pure
public String toString() {
ToStringBuilder b = new ToStringBuilder(this);
- b.add("suiteName", suiteName);
+ b.add("name", name);
b.add("file", file);
- b.add("line", line);
+ b.add("range", range);
b.add("state", state);
b.add("tests", tests);
return b.toString();
@@ -171,9 +172,9 @@ public final class TestSuiteInfo {
@Pure
public int hashCode() {
int hash = 7;
- hash = 67 * hash + Objects.hashCode(this.suiteName);
+ hash = 67 * hash + Objects.hashCode(this.name);
hash = 67 * hash + Objects.hashCode(this.file);
- hash = 67 * hash + Objects.hashCode(this.line);
+ hash = 67 * hash + Objects.hashCode(this.range);
hash = 67 * hash + Objects.hashCode(this.state);
hash = 67 * hash + Objects.hashCode(this.tests);
return hash;
@@ -192,13 +193,13 @@ public final class TestSuiteInfo {
return false;
}
final TestSuiteInfo other = (TestSuiteInfo) obj;
- if (!Objects.equals(this.suiteName, other.suiteName)) {
+ if (!Objects.equals(this.name, other.name)) {
return false;
}
if (!Objects.equals(this.file, other.file)) {
return false;
}
- if (!Objects.equals(this.line, other.line)) {
+ if (!Objects.equals(this.range, other.range)) {
return false;
}
if (!Objects.equals(this.state, other.state)) {
@@ -213,7 +214,7 @@ public final class TestSuiteInfo {
/**
* Information about a test case.
*/
- public static class TestCaseInfo {
+ public static final class TestCaseInfo {
/**
* The test case ID.
@@ -222,17 +223,10 @@ public final class TestSuiteInfo {
private String id;
/**
- * The short name to be displayed by the Test Explorer for this test case.
+ * The name to be displayed by the Test Explorer for this test case.
*/
@NonNull
- private String shortName;
-
- /**
- * The full name to be displayed by the Test Explorer when you hover over
- * this test case.
- */
- @NonNull
- private String fullName;
+ private String name;
/**
* The file containing this test case (if known).
@@ -240,13 +234,13 @@ public final class TestSuiteInfo {
private String file;
/**
- * The line within the specified file where the test case definition starts (if known).
+ * The range within the specified file where the test case definition is located (if known).
*/
- private Integer line;
+ private Range range;
/**
* The state of the test case. Can be one of the following values:
- * "loaded" | "running" | "passed" | "failed" | "skipped" | "errored"
+ * "loaded" | "started" | "passed" | "failed" | "skipped" | "errored"
*/
@NonNull
private String state;
@@ -257,20 +251,19 @@ public final class TestSuiteInfo {
private List<String> stackTrace;
public TestCaseInfo() {
- this("", "", "", "");
+ this("", "", "");
}
- public TestCaseInfo(@NonNull final String id, @NonNull final String shortName, @NonNull final String fullName, @NonNull final String state) {
+ public TestCaseInfo(@NonNull final String id, @NonNull final String name, @NonNull final String state) {
this.id = Preconditions.checkNotNull(id, "id");
- this.shortName = Preconditions.checkNotNull(shortName, "shortName");
- this.fullName = Preconditions.checkNotNull(fullName, "fullName");
+ this.name = Preconditions.checkNotNull(name, "name");
this.state = Preconditions.checkNotNull(state, "state");
}
- public TestCaseInfo(@NonNull final String id, @NonNull final String shortName, @NonNull final String fullName, final String file, final Integer line, @NonNull final String state, final List<String> stackTrace) {
- this(id, shortName, fullName, state);
+ public TestCaseInfo(@NonNull final String id, @NonNull final String name, final String file, final Range range, @NonNull final String state, final List<String> stackTrace) {
+ this(id, name, state);
this.file = file;
- this.line = line;
+ this.range = range;
this.stackTrace = stackTrace;
}
@@ -291,37 +284,19 @@ public final class TestSuiteInfo {
}
/**
- * The short name to be displayed by the Test Explorer for this test case.
- */
- @Pure
- @NonNull
- public String getShortName() {
- return shortName;
- }
-
- /**
- * The short name to be displayed by the Test Explorer for this test case.
- */
- public void setShortName(@NonNull final String shortName) {
- this.shortName = Preconditions.checkNotNull(shortName, "shortName");
- }
-
- /**
- * The full name to be displayed by the Test Explorer when you hover over
- * this test case.
+ * The name to be displayed by the Test Explorer for this test case.
*/
@Pure
@NonNull
- public String getFullName() {
- return fullName;
+ public String getName() {
+ return name;
}
/**
- * The full name to be displayed by the Test Explorer when you hover over
- * this test case.
+ * The name to be displayed by the Test Explorer for this test case.
*/
- public void setFullName(@NonNull final String fullName) {
- this.fullName = Preconditions.checkNotNull(fullName, "fullName");
+ public void setName(@NonNull final String name) {
+ this.name = Preconditions.checkNotNull(name, "name");
}
/**
@@ -340,23 +315,23 @@ public final class TestSuiteInfo {
}
/**
- * The line within the specified file where the test case definition starts (if known).
+ * The range within the specified file where the test case definition is located (if known).
*/
@Pure
- public Integer getLine() {
- return line;
+ public Range getRange() {
+ return range;
}
/**
- * The line within the specified file where the test case definition starts (if known).
+ * The range within the specified file where the test case definition is located (if known).
*/
- public void setLine(final Integer line) {
- this.line = line;
+ public void setRange(final Range range) {
+ this.range = range;
}
/**
* The state of the test case. Can be one of the following values:
- * "loaded" | "running" | "passed" | "failed" | "skipped" | "errored"
+ * "loaded" | "started" | "passed" | "failed" | "skipped" | "errored"
*/
@Pure
@NonNull
@@ -366,7 +341,7 @@ public final class TestSuiteInfo {
/**
* The state of the test case. Can be one of the following values:
- * "loaded" | "running" | "passed" | "failed" | "skipped" | "errored"
+ * "loaded" | "started" | "passed" | "failed" | "skipped" | "errored"
*/
public void setState(@NonNull final String state) {
this.state = Preconditions.checkNotNull(state, "state");
@@ -392,10 +367,9 @@ public final class TestSuiteInfo {
public String toString() {
ToStringBuilder b = new ToStringBuilder(this);
b.add("id", id);
- b.add("shortName", shortName);
- b.add("fullName", fullName);
+ b.add("name", name);
b.add("file", file);
- b.add("line", line);
+ b.add("range", range);
b.add("state", state);
b.add("stackTrace", stackTrace);
return b.toString();
@@ -406,10 +380,9 @@ public final class TestSuiteInfo {
public int hashCode() {
int hash = 5;
hash = 97 * hash + Objects.hashCode(this.id);
- hash = 97 * hash + Objects.hashCode(this.shortName);
- hash = 97 * hash + Objects.hashCode(this.fullName);
+ hash = 97 * hash + Objects.hashCode(this.name);
hash = 97 * hash + Objects.hashCode(this.file);
- hash = 97 * hash + Objects.hashCode(this.line);
+ hash = 97 * hash + Objects.hashCode(this.range);
hash = 97 * hash + Objects.hashCode(this.state);
hash = 97 * hash + Objects.hashCode(this.stackTrace);
return hash;
@@ -431,16 +404,13 @@ public final class TestSuiteInfo {
if (!Objects.equals(this.id, other.id)) {
return false;
}
- if (!Objects.equals(this.shortName, other.shortName)) {
- return false;
- }
- if (!Objects.equals(this.fullName, other.fullName)) {
+ if (!Objects.equals(this.name, other.name)) {
return false;
}
if (!Objects.equals(this.file, other.file)) {
return false;
}
- if (!Objects.equals(this.line, other.line)) {
+ if (!Objects.equals(this.range, other.range)) {
return false;
}
if (!Objects.equals(this.state, other.state)) {
@@ -462,7 +432,7 @@ public final class TestSuiteInfo {
public static final String Loaded = "loaded";
- public static final String Running = "running";
+ public static final String Started = "started";
public static final String Completed = "completed";
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java
index 0d65a1f..4ab6a64 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java
@@ -205,22 +205,22 @@ public final class WorkspaceServiceImpl implements WorkspaceService, LanguageCli
List<TestSuiteInfo.TestCaseInfo> tests = new ArrayList<>(methods.size());
String url = Utils.toUri(fo);
String testClassName = null;
- Integer testClassLine = null;
+ Range testClassRange = null;
for (TestMethodController.TestMethod testMethod : methods) {
if (testClassName == null) {
testClassName = testMethod.getTestClassName();
}
- if (testClassLine == null) {
- testClassLine = testMethod.getTestClassPosition() != null
- ? Utils.createPosition(fo, testMethod.getTestClassPosition().getOffset()).getLine()
- : null;
+ if (testClassRange == null && testMethod.getTestClassPosition() != null) {
+ Position pos = Utils.createPosition(fo, testMethod.getTestClassPosition().getOffset());
+ testClassRange = new Range(pos, pos);
}
String id = testMethod.getTestClassName() + ':' + testMethod.method().getMethodName();
- String fullName = testMethod.getTestClassName() + '.' + testMethod.method().getMethodName();
- int testLine = Utils.createPosition(fo, testMethod.start().getOffset()).getLine();
- tests.add(new TestSuiteInfo.TestCaseInfo(id, testMethod.method().getMethodName(), fullName, url, testLine, TestSuiteInfo.State.Loaded, null));
+ Position startPos = testMethod.start() != null ? Utils.createPosition(fo, testMethod.start().getOffset()) : null;
+ Position endPos = testMethod.end() != null ? Utils.createPosition(fo, testMethod.end().getOffset()) : startPos;
+ Range range = startPos != null ? new Range(startPos, endPos) : null;
+ tests.add(new TestSuiteInfo.TestCaseInfo(id, testMethod.method().getMethodName(), url, range, TestSuiteInfo.State.Loaded, null));
}
- return new TestSuiteInfo(testClassName, url, testClassLine, TestSuiteInfo.State.Loaded, tests);
+ return new TestSuiteInfo(testClassName, url, testClassRange, TestSuiteInfo.State.Loaded, tests);
};
testMethodsListener.compareAndSet(null, (fo, methods) -> {
try {
diff --git a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/progress/TestProgressHandlerTest.java b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/progress/TestProgressHandlerTest.java
index 1265b97..c325b59 100644
--- a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/progress/TestProgressHandlerTest.java
+++ b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/progress/TestProgressHandlerTest.java
@@ -27,7 +27,6 @@ import org.eclipse.lsp4j.MessageActionItem;
import org.eclipse.lsp4j.MessageParams;
import org.eclipse.lsp4j.PublishDiagnosticsParams;
import org.eclipse.lsp4j.ShowMessageRequestParams;
-import org.eclipse.lsp4j.debug.OutputEventArguments;
import org.eclipse.lsp4j.debug.services.IDebugProtocolClient;
import org.junit.Test;
import org.netbeans.api.extexecution.print.LineConvertors;
@@ -117,24 +116,22 @@ public class TestProgressHandlerTest extends NbTestCase {
assertEquals("Two messages", 2, msgs.size());
assertEquals(fo.toURI().toString(), msgs.get(0).getUri());
TestSuiteInfo suite = msgs.get(0).getSuite();
- assertEquals("TestSuiteName", suite.getSuiteName());
- assertEquals(TestSuiteInfo.State.Running, suite.getState());
+ assertEquals("TestSuiteName", suite.getName());
+ assertEquals(TestSuiteInfo.State.Started, suite.getState());
assertEquals(fo.toURI().toString(), msgs.get(1).getUri());
suite = msgs.get(1).getSuite();
- assertEquals("TestSuiteName", suite.getSuiteName());
+ assertEquals("TestSuiteName", suite.getName());
assertEquals(TestSuiteInfo.State.Completed, suite.getState());
assertEquals(2, suite.getTests().size());
TestSuiteInfo.TestCaseInfo testCase = suite.getTests().get(0);
assertEquals("TestSuiteName:test1", testCase.getId());
- assertEquals("test1", testCase.getShortName());
- assertEquals("TestSuiteName.test1", testCase.getFullName());
+ assertEquals("test1", testCase.getName());
assertEquals(fo.toURI().toString(), testCase.getFile());
assertEquals(TestSuiteInfo.State.Passed, testCase.getState());
assertNull(testCase.getStackTrace());
testCase = suite.getTests().get(1);
assertEquals("TestSuiteName:test2", testCase.getId());
- assertEquals("test2", testCase.getShortName());
- assertEquals("TestSuiteName.test2", testCase.getFullName());
+ assertEquals("test2", testCase.getName());
assertEquals(fo.toURI().toString(), testCase.getFile());
assertEquals(TestSuiteInfo.State.Failed, testCase.getState());
assertNotNull(testCase.getStackTrace());
diff --git a/java/java.lsp.server/vscode/package-lock.json b/java/java.lsp.server/vscode/package-lock.json
index 33e724a..2a231d4 100644
--- a/java/java.lsp.server/vscode/package-lock.json
+++ b/java/java.lsp.server/vscode/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "apache-netbeans-java",
- "version": "12.4.0",
+ "version": "0.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -39,9 +39,9 @@
"dev": true
},
"@types/vscode": {
- "version": "1.49.0",
- "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.49.0.tgz",
- "integrity": "sha512-wfNQmLmm1VdMBr6iuNdprWmC1YdrgZ9dQzadv+l2eSjJlElOdJw8OTm4RU4oGTBcfvG6RZI2jOcppkdSS18mZw==",
+ "version": "1.60.0",
+ "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.60.0.tgz",
+ "integrity": "sha512-wZt3VTmzYrgZ0l/3QmEbCq4KAJ71K3/hmMQ/nfpv84oH8e81KKwPEoQ5v8dNCxfHFVJ1JabHKmCvqdYOoVm1Ow==",
"dev": true
},
"agent-base": {
@@ -917,11 +917,6 @@
"is-number": "^7.0.0"
}
},
- "tslib": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
- "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
- },
"typescript": {
"version": "3.9.7",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz",
@@ -981,20 +976,6 @@
"rimraf": "^2.6.3"
}
},
- "vscode-test-adapter-api": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/vscode-test-adapter-api/-/vscode-test-adapter-api-1.9.0.tgz",
- "integrity": "sha512-lltjehUP0J9H3R/HBctjlqeUCwn2t9Lbhj2Y500ib+j5Y4H3hw+hVTzuSsfw16LtxY37knlU39QIlasa7svzOQ=="
- },
- "vscode-test-adapter-util": {
- "version": "0.7.1",
- "resolved": "https://registry.npmjs.org/vscode-test-adapter-util/-/vscode-test-adapter-util-0.7.1.tgz",
- "integrity": "sha512-OZZvLDDNhayVVISyTmgUntOhMzl6j9/wVGfNqI2zuR5bQIziTQlDs9W29dFXDTGXZOxazS6uiHkrr86BKDzYUA==",
- "requires": {
- "tslib": "^1.11.1",
- "vscode-test-adapter-api": "^1.8.0"
- }
- },
"which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
diff --git a/java/java.lsp.server/vscode/package.json b/java/java.lsp.server/vscode/package.json
index 4186922..92bb6f0 100644
--- a/java/java.lsp.server/vscode/package.json
+++ b/java/java.lsp.server/vscode/package.json
@@ -4,7 +4,7 @@
"description": "Apache NetBeans Language Server Extension for Visual Studio Code",
"author": "Apache NetBeans",
"license": "Apache 2.0",
- "version": "12.4.0",
+ "version": "0.1.0",
"preview": true,
"repository": {
"type": "git",
@@ -15,13 +15,14 @@
"categories": [
"Programming Languages",
"Debuggers",
+ "Testing",
"Other"
],
"keywords": [
"multi-root ready"
],
"engines": {
- "vscode": "^1.49.0"
+ "vscode": "^1.60.0"
},
"activationEvents": [
"onLanguage:java",
@@ -389,13 +390,13 @@
"nbcode": "node ./out/nbcode.js",
"nbjavac": "node ./out/nbcode.js -J-Dnetbeans.close=true --modules --install .*nbjavac.*",
"apisupport": "node ./out/nbcode.js -J-Dnetbeans.close=true --modules --install '(org.netbeans.libs.xerces|org.netbeans.modules.editor.structure|org.netbeans.modules.xml|org.netbeans.modules.xml.axi|org.netbeans.modules.xml.retriever|org.netbeans.modules.xml.schema.model|org.netbeans.modules.xml.tax|org.netbeans.modules.xml.text|org.netbeans.modules.ant.browsetask|.*apisupport.*|org.netbeans.modules.debugger.jpda.ant)' && node ./out/nbcode.js -J-Dnetbeans.close=true --modules --enable [...]
- },
+ },
"devDependencies": {
"@types/glob": "^7.1.1",
"@types/mocha": "^7.0.2",
"@types/node": "^13.11.0",
"@types/ps-node": "^0.1.0",
- "@types/vscode": "^1.49.0",
+ "@types/vscode": "^1.60.0",
"glob": "^7.1.6",
"mocha": "^7.1.2",
"ps-node": "^0.1.6",
@@ -405,11 +406,6 @@
"dependencies": {
"jsonc-parser": "3.0.0",
"vscode-debugadapter": "1.42.1",
- "vscode-languageclient": "6.1.3",
- "vscode-test-adapter-api": "^1.9.0",
- "vscode-test-adapter-util": "^0.7.1"
- },
- "extensionDependencies": [
- "hbenl.vscode-test-explorer"
- ]
+ "vscode-languageclient": "6.1.3"
+ }
}
diff --git a/java/java.lsp.server/vscode/src/extension.ts b/java/java.lsp.server/vscode/src/extension.ts
index 2b6723c..55dc8eb 100644
--- a/java/java.lsp.server/vscode/src/extension.ts
+++ b/java/java.lsp.server/vscode/src/extension.ts
@@ -30,8 +30,7 @@ import {
MessageType,
LogMessageNotification,
RevealOutputChannelOn,
- DocumentSelector,
- DocumentFilter
+ DocumentSelector
} from 'vscode-languageclient';
import * as net from 'net';
@@ -39,18 +38,16 @@ import * as fs from 'fs';
import * as path from 'path';
import { ChildProcess } from 'child_process';
import * as vscode from 'vscode';
-import { testExplorerExtensionId, TestHub } from 'vscode-test-adapter-api';
-import { TestAdapterRegistrar } from 'vscode-test-adapter-util';
import * as launcher from './nbcode';
import {NbTestAdapter} from './testAdapter';
-import { StatusMessageRequest, ShowStatusMessageParams, QuickPickRequest, InputBoxRequest, TestProgressNotification, DebugConnector,
+import { asRanges, StatusMessageRequest, ShowStatusMessageParams, QuickPickRequest, InputBoxRequest, TestProgressNotification, DebugConnector,
TextEditorDecorationCreateRequest, TextEditorDecorationSetNotification, TextEditorDecorationDisposeNotification,
} from './protocol';
import * as launchConfigurations from './launchConfigurations';
const API_VERSION : string = "1.0";
let client: Promise<LanguageClient>;
-let testAdapterRegistrar: TestAdapterRegistrar<NbTestAdapter>;
+let testAdapter: NbTestAdapter | null = null;
let nbProcess : ChildProcess | null = null;
let debugPort: number = -1;
let consoleLog: boolean = !!process.env['ENABLE_CONSOLE_LOG'];
@@ -333,6 +330,9 @@ export function activate(context: ExtensionContext): VSNetBeansAPI {
context.subscriptions.push(commands.registerCommand('java.run.test', async (uri, methodName?, launchConfiguration?) => {
await runDebug(true, true, uri, methodName, launchConfiguration);
}));
+ context.subscriptions.push(commands.registerCommand('java.debug.test', async (uri, methodName?, launchConfiguration?) => {
+ await runDebug(false, true, uri, methodName, launchConfiguration);
+ }));
context.subscriptions.push(commands.registerCommand('java.run.single', async (uri, methodName?, launchConfiguration?) => {
await runDebug(true, false, uri, methodName, launchConfiguration);
}));
@@ -343,16 +343,8 @@ export function activate(context: ExtensionContext): VSNetBeansAPI {
// register completions:
launchConfigurations.registerCompletion(context);
- // get the Test Explorer extension and register TestAdapter
- const testExplorerExtension = vscode.extensions.getExtension<TestHub>(testExplorerExtensionId);
- if (testExplorerExtension) {
- const testHub = testExplorerExtension.exports;
- testAdapterRegistrar = new TestAdapterRegistrar(
- testHub,
- workspaceFolder => new NbTestAdapter(workspaceFolder, client)
- );
- context.subscriptions.push(testAdapterRegistrar);
- }
+ // register TestAdapter
+ context.subscriptions.push(testAdapter = new NbTestAdapter(client));
return Object.freeze({
version : API_VERSION
@@ -606,14 +598,8 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex
return await window.showInputBox({ prompt: param.prompt, value: param.value });
});
c.onNotification(TestProgressNotification.type, param => {
- if (testAdapterRegistrar) {
- const ws = workspace.getWorkspaceFolder(vscode.Uri.parse(param.uri));
- if (ws) {
- const adapter = testAdapterRegistrar.getAdapter(ws);
- if (adapter) {
- adapter.testProgress(param.suite);
- }
- }
+ if (testAdapter) {
+ testAdapter.testProgress(param.suite);
}
});
let decorations = new Map<string, TextEditorDecorationType>();
@@ -629,7 +615,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex
editor => editor.document.uri.toString() == param.uri
);
if (editorsWithUri.length > 0) {
- editorsWithUri[0].setDecorations(decorationType, param.ranges);
+ editorsWithUri[0].setDecorations(decorationType, asRanges(param.ranges));
}
}
});
diff --git a/java/java.lsp.server/vscode/src/protocol.ts b/java/java.lsp.server/vscode/src/protocol.ts
index 3c3a254..c4bcaac 100644
--- a/java/java.lsp.server/vscode/src/protocol.ts
+++ b/java/java.lsp.server/vscode/src/protocol.ts
@@ -18,17 +18,16 @@
*/
'use strict';
-import {
- DecorationRenderOptions,
- QuickPickItem,
- Range,
- TextEditorDecorationType,
-} from 'vscode';
+import * as vscode from 'vscode';
import {
NotificationType,
RequestType,
ShowMessageParams
} from 'vscode-languageclient';
+import {
+ Position,
+ Range
+} from 'vscode-languageserver-protocol';
export interface ShowStatusMessageParams extends ShowMessageParams {
/**
@@ -53,11 +52,11 @@ export interface ShowQuickPickParams {
/**
* A list of items.
*/
- items: QuickPickItem[];
+ items: vscode.QuickPickItem[];
}
export namespace QuickPickRequest {
- export const type = new RequestType<ShowQuickPickParams, QuickPickItem[], void, void>('window/showQuickPick');
+ export const type = new RequestType<ShowQuickPickParams, vscode.QuickPickItem[], void, void>('window/showQuickPick');
}
export interface ShowInputBoxParams {
@@ -81,20 +80,19 @@ export interface TestProgressParams {
}
export interface TestSuite {
- suiteName: string;
+ name: string;
file?: string;
- line?: number;
- state: 'loaded' | 'running' | 'completed' | 'errored';
+ range?: Range;
+ state: 'loaded' | 'started' | 'completed' | 'errored';
tests?: TestCase[];
}
export interface TestCase {
id: string;
- shortName: string;
- fullName: string;
+ name: string;
file?: string;
- line?: number;
- state: 'loaded' | 'running' | 'passed' | 'failed' | 'skipped' | 'errored';
+ range?: Range;
+ state: 'loaded' | 'started' | 'passed' | 'failed' | 'skipped' | 'errored';
stackTrace?: string[];
}
@@ -118,7 +116,7 @@ export interface SetTextEditorDecorationParams {
};
export namespace TextEditorDecorationCreateRequest {
- export const type = new RequestType<DecorationRenderOptions, string, void, void>('window/createTextEditorDecoration');
+ export const type = new RequestType<vscode.DecorationRenderOptions, string, void, void>('window/createTextEditorDecoration');
};
export namespace TextEditorDecorationSetNotification {
@@ -128,3 +126,27 @@ export namespace TextEditorDecorationSetNotification {
export namespace TextEditorDecorationDisposeNotification {
export const type = new NotificationType<string, void>('window/disposeTextEditorDecoration');
};
+
+export function asPosition(value: undefined | null): undefined;
+export function asPosition(value: Position): vscode.Position;
+export function asPosition(value: Position | undefined | null): vscode.Position | undefined;
+export function asPosition(value: Position | undefined | null): vscode.Position | undefined {
+ if (!value) {
+ return undefined;
+ }
+ return new vscode.Position(value.line, value.character);
+}
+
+export function asRange(value: undefined | null): undefined;
+export function asRange(value: Range): vscode.Range;
+export function asRange(value: Range | undefined | null): vscode.Range | undefined;
+export function asRange(value: Range | undefined | null): vscode.Range | undefined {
+ if (!value) {
+ return undefined;
+ }
+ return new vscode.Range(asPosition(value.start), asPosition(value.end));
+}
+
+export function asRanges(value: Range[]): vscode.Range[] {
+ return value.map(value => asRange(value));
+}
diff --git a/java/java.lsp.server/vscode/src/test/runTest.ts b/java/java.lsp.server/vscode/src/test/runTest.ts
index d8d174c..9815e9e 100644
--- a/java/java.lsp.server/vscode/src/test/runTest.ts
+++ b/java/java.lsp.server/vscode/src/test/runTest.ts
@@ -1,8 +1,7 @@
import * as path from 'path';
-import { downloadAndUnzipVSCode, resolveCliPathFromVSCodeExecutablePath, runTests } from 'vscode-test';
+import { downloadAndUnzipVSCode, runTests } from 'vscode-test';
-import * as cp from 'child_process';
import * as fs from 'fs';
async function main() {
@@ -11,13 +10,7 @@ async function main() {
// Passed to `--extensionDevelopmentPath`
const extensionDevelopmentPath = path.resolve(__dirname, '../../');
- const vscodeExecutablePath: string = await downloadAndUnzipVSCode('1.56.2');
- const cliPath: string = resolveCliPathFromVSCodeExecutablePath(vscodeExecutablePath);
-
- cp.spawnSync(cliPath, ['--install-extension', 'hbenl.vscode-test-explorer'], {
- encoding: 'utf-8',
- stdio: 'inherit',
- });
+ const vscodeExecutablePath: string = await downloadAndUnzipVSCode('stable');
// The path to test runner
// Passed to --extensionTestsPath
@@ -37,7 +30,11 @@ async function main() {
extensionTestsEnv: {
'ENABLE_CONSOLE_LOG' : 'true'
},
- launchArgs: [workspaceDir, '--async-stack-traces']
+ launchArgs: [
+ workspaceDir,
+ '--disable-extensions',
+ '--disable-workspace-trust'
+ ]
});
} catch (err) {
console.error('Failed to run tests');
diff --git a/java/java.lsp.server/vscode/src/testAdapter.ts b/java/java.lsp.server/vscode/src/testAdapter.ts
index 95085e5..e28e5f7 100644
--- a/java/java.lsp.server/vscode/src/testAdapter.ts
+++ b/java/java.lsp.server/vscode/src/testAdapter.ts
@@ -18,125 +18,78 @@
*/
'use strict';
-import { WorkspaceFolder, Event, EventEmitter, Uri, commands, debug } from "vscode";
+import { commands, debug, tests, workspace, CancellationToken, TestController, TestItem, TestRunProfileKind, TestRunRequest, Uri, TestRun, TestMessage, Location, Position } from "vscode";
import * as path from 'path';
-import { TestAdapter, TestSuiteEvent, TestEvent, TestLoadFinishedEvent, TestLoadStartedEvent, TestRunFinishedEvent, TestRunStartedEvent, TestSuiteInfo, TestInfo, TestDecoration } from "vscode-test-adapter-api";
-import { TestSuite } from "./protocol";
+import { asRange, TestSuite } from "./protocol";
import { LanguageClient } from "vscode-languageclient";
-export class NbTestAdapter implements TestAdapter {
+export class NbTestAdapter {
+ private readonly testController: TestController;
private disposables: { dispose(): void }[] = [];
- private children: TestSuiteInfo[] = [];
- private readonly testSuite: TestSuiteInfo;
+ private currentRun: TestRun | undefined;
- private readonly testsEmitter = new EventEmitter<TestLoadStartedEvent | TestLoadFinishedEvent>();
- private readonly statesEmitter = new EventEmitter<TestRunStartedEvent | TestRunFinishedEvent | TestSuiteEvent | TestEvent>();
-
- constructor(
- public readonly workspaceFolder: WorkspaceFolder,
- private readonly client: Promise<LanguageClient>
- ) {
- this.disposables.push(this.testsEmitter);
- this.disposables.push(this.statesEmitter);
- this.testSuite = { type: 'suite', id: '*', label: 'Tests', children: this.children };
- }
-
- get tests(): Event<TestLoadStartedEvent | TestLoadFinishedEvent> {
- return this.testsEmitter.event;
- }
-
- get testStates(): Event<TestRunStartedEvent | TestRunFinishedEvent | TestSuiteEvent | TestEvent> {
- return this.statesEmitter.event;
+ constructor(client: Promise<LanguageClient>) {
+ this.testController = tests.createTestController('apacheNetBeansController', 'Apache NetBeans');
+ const runHandler = (request: TestRunRequest, cancellation: CancellationToken) => this.run(request, cancellation);
+ this.testController.createRunProfile('Run Tests', TestRunProfileKind.Run, runHandler);
+ this.testController.createRunProfile('Debug Tests', TestRunProfileKind.Debug, runHandler);
+ this.disposables.push(this.testController);
+ client.then(async () => await this.load());
}
async load(): Promise<void> {
- this.testsEmitter.fire(<TestLoadStartedEvent>{ type: 'started' });
- let clnt = await this.client;
- console.log(clnt);
- this.children.length = 0;
- const loadedTests: any = await commands.executeCommand('java.load.workspace.tests', this.workspaceFolder.uri.toString());
- if (loadedTests) {
- loadedTests.forEach((suite: TestSuite) => {
- this.updateTests(suite);
- });
- this.children.sort((a, b) => a.label.localeCompare(b.label));
- }
- if (this.children.length > 0) {
- this.testsEmitter.fire(<TestLoadFinishedEvent>{ type: 'finished', suite: this.testSuite });
- } else {
- this.testsEmitter.fire(<TestLoadFinishedEvent>{ type: 'finished' });
+ for (let workspaceFolder of workspace.workspaceFolders || []) {
+ const loadedTests: any = await commands.executeCommand('java.load.workspace.tests', workspaceFolder.uri.toString());
+ if (loadedTests) {
+ loadedTests.forEach((suite: TestSuite) => {
+ this.updateTests(suite);
+ });
+ }
}
}
- async run(tests: string[]): Promise<void> {
- this.statesEmitter.fire(<TestRunStartedEvent>{ type: 'started', tests });
- if (tests.length === 1) {
- if (tests[0] === '*') {
- await commands.executeCommand('java.run.test', this.workspaceFolder.uri.toString());
- this.statesEmitter.fire(<TestRunFinishedEvent>{ type: 'finished' });
- } else {
- const idx = tests[0].indexOf(':');
- const suiteName = idx < 0 ? tests[0] : tests[0].slice(0, idx);
- const current = this.children.find(s => s.id === suiteName);
- if (current && current.file) {
- let methodName;
- if (idx >= 0) {
- let test = current.children.find(t => t.id === tests[0]);
- if (test) {
- methodName = tests[0].slice(idx + 1);
- } else {
- let parents = current.children.filter(ti => tests[0].startsWith(ti.id));
- if (parents && parents.length === 1 && parents[0].type === 'suite') {
- methodName = parents[0].id.slice(idx + 1);
- }
- }
- }
- if (methodName) {
- await commands.executeCommand('java.run.single', Uri.file(current.file).toString(), methodName);
- } else {
- await commands.executeCommand('java.run.single', Uri.file(current.file).toString());
- }
- this.statesEmitter.fire(<TestRunFinishedEvent>{ type: 'finished' });
- } else {
- this.statesEmitter.fire(<TestLoadFinishedEvent>{ type: 'finished', errorMessage: `Cannot find suite to run: ${tests[0]}` });
+ async run(request: TestRunRequest, cancellation: CancellationToken): Promise<void> {
+ cancellation.onCancellationRequested(() => this.cancel());
+ this.currentRun = this.testController.createTestRun(request);
+ if (request.include) {
+ const include = [...new Map(request.include.map(item => !item.uri && item.parent?.uri ? [item.parent.id, item.parent] : [item.id, item])).values()];
+ for (let item of include) {
+ if (item.uri) {
+ this.set(item, 'enqueued');
+ const idx = item.id.indexOf(':');
+ await commands.executeCommand(request.profile?.kind === TestRunProfileKind.Debug ? 'java.debug.single' : 'java.run.single', item.uri.toString(), idx < 0 ? undefined : item.id.slice(idx + 1));
}
}
} else {
- this.statesEmitter.fire(<TestLoadFinishedEvent>{ type: 'finished', errorMessage: 'Failed to run mutliple tests'});
+ this.testController.items.forEach(item => this.set(item, 'enqueued'));
+ for (let workspaceFolder of workspace.workspaceFolders || []) {
+ if (!cancellation.isCancellationRequested) {
+ await commands.executeCommand(request.profile?.kind === TestRunProfileKind.Debug ? 'java.debug.test': 'java.run.test', workspaceFolder.uri.toString());
+ }
+ }
}
+ this.currentRun.end();
+ this.currentRun = undefined;
}
- async debug(tests: string[]): Promise<void> {
- this.statesEmitter.fire(<TestRunStartedEvent>{ type: 'started', tests });
- if (tests.length === 1) {
- const idx = tests[0].indexOf(':');
- const suiteName = idx < 0 ? tests[0] : tests[0].slice(0, idx);
- const current = this.children.find(s => s.id === suiteName);
- if (current && current.file) {
- let methodName;
- if (idx >= 0) {
- let test = current.children.find(t => t.id === tests[0]);
- if (test) {
- methodName = tests[0].slice(idx + 1);
- } else {
- let parents = current.children.filter(ti => tests[0].startsWith(ti.id));
- if (parents && parents.length === 1 && parents[0].type === 'suite') {
- methodName = parents[0].id.slice(idx + 1);
- }
- }
- }
- if (methodName) {
- await commands.executeCommand('java.debug.single', Uri.file(current.file).toString(), methodName);
- } else {
- await commands.executeCommand('java.debug.single', Uri.file(current.file).toString());
- }
- this.statesEmitter.fire(<TestRunFinishedEvent>{ type: 'finished' });
- } else {
- this.statesEmitter.fire(<TestLoadFinishedEvent>{ type: 'finished', errorMessage: `Cannot find suite to debug: ${tests[0]}` });
+ set(item: TestItem, state: 'enqueued' | 'started' | 'passed' | 'failed' | 'skipped' | 'errored', message?: TestMessage | readonly TestMessage[], noPassDown? : boolean): void {
+ if (this.currentRun) {
+ switch (state) {
+ case 'enqueued':
+ case 'started':
+ case 'passed':
+ case 'skipped':
+ this.currentRun[state](item);
+ break;
+ case 'failed':
+ case 'errored':
+ this.currentRun[state](item, message || new TestMessage(''));
+ break;
+ }
+ if (!noPassDown) {
+ item.children.forEach(child => this.set(child, state, message, noPassDown));
}
- } else {
- this.statesEmitter.fire(<TestLoadFinishedEvent>{ type: 'finished', errorMessage: 'Failed to debug mutliple tests'});
}
}
@@ -153,151 +106,135 @@ export class NbTestAdapter implements TestAdapter {
}
testProgress(suite: TestSuite): void {
- let cnt = this.children.length;
+ const currentSuite = this.testController.items.get(suite.name);
switch (suite.state) {
case 'loaded':
- if (this.updateTests(suite)) {
- if (this.children.length !== cnt) {
- this.children.sort((a, b) => a.label.localeCompare(b.label));
- }
- this.testsEmitter.fire(<TestLoadFinishedEvent>{ type: 'finished', suite: this.testSuite });
- }
+ this.updateTests(suite);
break;
- case 'running':
- this.statesEmitter.fire(<TestSuiteEvent>{ type: 'suite', suite: suite.suiteName, state: suite.state });
+ case 'started':
+ if (currentSuite) {
+ this.set(currentSuite, 'started');
+ }
break;
case 'completed':
case 'errored':
- let errMessage: string | undefined;
if (suite.tests) {
- if (this.updateTests(suite, true)) {
- if (this.children.length !== cnt) {
- this.children.sort((a, b) => a.label.localeCompare(b.label));
- }
- this.testsEmitter.fire(<TestLoadFinishedEvent>{ type: 'finished', suite: this.testSuite });
- }
- const currentSuite = this.children.find(s => s.id === suite.suiteName);
+ this.updateTests(suite, true);
if (currentSuite) {
- suite.tests.forEach(test => {
- let message: string | undefined;
- let decorations: TestDecoration[] | undefined;
- if (test.stackTrace) {
- message = test.stackTrace.join('\n');
- const testFile = test.file ? Uri.parse(test.file)?.path : undefined;
- if (testFile) {
- const fileName = path.basename(testFile);
- const line = test.stackTrace.map(frame => {
- const info = frame.match(/^\s*at\s*\S*\((\S*):(\d*)\)$/);
- if (info && info.length >= 3 && info[1] === fileName) {
- return parseInt(info[2]);
+ const suiteMessages: TestMessage[] = [];
+ suite.tests?.forEach(test => {
+ if (this.currentRun) {
+ let currentTest = currentSuite.children.get(test.id);
+ if (!currentTest) {
+ currentSuite.children.forEach(item => {
+ if (!currentTest && test.id.startsWith(item.id)) {
+ currentTest = item.children.get(test.id);
+ }
+ });
+ }
+ let message: TestMessage | undefined;
+ if (test.stackTrace) {
+ message = new TestMessage(test.stackTrace.join('\n'));
+ if (currentTest) {
+ const testUri = currentTest.uri || currentTest.parent?.uri;
+ if (testUri) {
+ const fileName = path.basename(testUri.path);
+ const line = test.stackTrace.map(frame => {
+ const info = frame.match(/^\s*at[^\(]*\((\S*):(\d*)\)$/);
+ if (info && info.length >= 3 && info[1] === fileName) {
+ return parseInt(info[2]);
+ }
+ return null;
+ }).find(l => l);
+ const pos = line ? new Position(line - 1, 0) : currentTest.range?.start;
+ if (pos) {
+ message.location = new Location(testUri, pos);
+ }
}
- return null;
- }).find(l => l);
- if (line) {
- decorations = [{ line: line - 1, message: test.stackTrace[0] }];
+ } else {
+ message.location = new Location(currentSuite.uri!, currentSuite.range!.start);
}
}
- }
- let currentTest = (currentSuite as TestSuiteInfo).children.find(ti => ti.id === test.id);
- if (!currentTest) {
- let parents = (currentSuite as TestSuiteInfo).children.filter(ti => test.id.startsWith(ti.id));
- if (parents && parents.length === 1 && parents[0].type === 'suite') {
- currentTest = parents[0].children.find(ti => ti.id === test.id);
+ if (currentTest && test.state !== 'loaded') {
+ this.set(currentTest, test.state, message, true);
+ } else if (test.state !== 'passed' && message) {
+ suiteMessages.push(message);
}
}
- if (currentTest) {
- this.statesEmitter.fire(<TestEvent>{ type: 'test', test: test.id, state: test.state, message, decorations });
- } else if (test.state !== 'passed' && message && !errMessage) {
- suite.state = 'errored';
- errMessage = message;
- }
});
+ if (suiteMessages.length > 0) {
+ this.set(currentSuite, 'errored', suiteMessages, true);
+ currentSuite.children.forEach(item => this.set(item, 'skipped'));
+ }
}
}
- this.statesEmitter.fire(<TestSuiteEvent>{ type: 'suite', suite: suite.suiteName, state: suite.state, message: errMessage });
break;
}
}
- updateTests(suite: TestSuite, preserveMissingTests?: boolean): boolean {
- let changed = false;
- const currentSuite = this.children.find(s => s.id === suite.suiteName);
- if (currentSuite) {
- const file = suite.file ? Uri.parse(suite.file)?.path : undefined;
- if (file && currentSuite.file !== file) {
- currentSuite.file = file;
- changed = true;
- }
- if (suite.line && currentSuite.line !== suite.line) {
- currentSuite.line = suite.line;
- changed = true
- }
- if (suite.tests) {
- const ids: Set<string> = new Set();
- const parentSuites: Map<TestSuiteInfo, string[]> = new Map();
- suite.tests.forEach(test => {
- ids.add(test.id);
- let currentTest = (currentSuite as TestSuiteInfo).children.find(ti => ti.id === test.id);
- if (currentTest) {
- const file = test.file ? Uri.parse(test.file)?.path : undefined;
- if (file && currentTest.file !== file) {
- currentTest.file = file;
- changed = true;
+ updateTests(suite: TestSuite, testExecution?: boolean): void {
+ let currentSuite = this.testController.items.get(suite.name);
+ const suiteUri = suite.file ? Uri.parse(suite.file) : undefined;
+ if (!currentSuite || suiteUri && currentSuite.uri?.toString() !== suiteUri.toString()) {
+ currentSuite = this.testController.createTestItem(suite.name, suite.name, suiteUri);
+ this.testController.items.add(currentSuite);
+ }
+ const suiteRange = asRange(suite.range);
+ if (!testExecution && suiteRange && suiteRange !== currentSuite.range) {
+ currentSuite.range = suiteRange;
+ }
+ const children: TestItem[] = []
+ const parentTests: Map<TestItem, TestItem[]> = new Map();
+ suite.tests?.forEach(test => {
+ let currentTest = currentSuite?.children.get(test.id);
+ const testUri = test.file ? Uri.parse(test.file) : undefined;
+ if (currentTest) {
+ if (currentTest.uri?.toString() !== testUri?.toString()) {
+ currentTest = this.testController.createTestItem(test.id, test.name, testUri);
+ currentSuite?.children.add(currentTest);
+ }
+ const testRange = asRange(test.range);
+ if (!testExecution && testRange && testRange !== currentTest.range) {
+ currentTest.range = testRange;
+ }
+ children.push(currentTest);
+ } else {
+ if (testExecution) {
+ const parents: TestItem[] = [];
+ currentSuite?.children.forEach(item => {
+ if (test.id.startsWith(item.id)) {
+ parents.push(item);
}
- if (test.line && currentTest.line !== test.line) {
- currentTest.line = test.line;
- changed = true;
+ });
+ if (parents.length === 1) {
+ let arr = parentTests.get(parents[0]);
+ if (!arr) {
+ parentTests.set(parents[0], arr = []);
+ children.push(parents[0]);
}
- } else {
- let parents = (currentSuite as TestSuiteInfo).children.filter(ti => test.id.startsWith(ti.id));
- if (parents && parents.length === 1) {
- let childSuite: TestSuiteInfo = parents[0].type === 'suite' ? parents[0] : { type: 'suite', id: parents[0].id, label: parents[0].label, file: parents[0].file, line: parents[0].line, children: [] };
- if (!parentSuites.has(childSuite)) {
- parentSuites.set(childSuite, childSuite.children.map(ti => ti.id));
- }
- if (parents[0].type === 'test') {
- (currentSuite as TestSuiteInfo).children[(currentSuite as TestSuiteInfo).children.indexOf(parents[0])] = childSuite;
- changed = true;
- }
- currentTest = childSuite.children.find(ti => ti.id === test.id);
- if (currentTest) {
- let arr = parentSuites.get(childSuite);
- let idx = arr ? arr.indexOf(currentTest.id) : -1;
- if (idx >= 0) {
- arr?.splice(idx, 1);
- }
- } else {
- let label = test.shortName;
- if (label.startsWith(childSuite.label)) {
- label = label.slice(childSuite.label.length).trim();
- }
- childSuite.children.push({ type: 'test', id: test.id, label, tooltip: test.fullName, file: test.file ? Uri.parse(test.file)?.path : undefined, line: test.line });
- changed = true;
- }
- } else {
- (currentSuite as TestSuiteInfo).children.push({ type: 'test', id: test.id, label: test.shortName, tooltip: test.fullName, file: test.file ? Uri.parse(test.file)?.path : undefined, line: test.line });
- changed = true;
+ let label = test.name;
+ if (label.startsWith(parents[0].label)) {
+ label = label.slice(parents[0].label.length).trim();
}
+ arr.push(this.testController.createTestItem(test.id, label));
}
- });
- parentSuites.forEach((val, key) => {
- if (val.length > 0) {
- key.children = key.children.filter(ti => val.indexOf(ti.id) < 0);
- changed = true;
- }
- });
- if (!preserveMissingTests && (currentSuite as TestSuiteInfo).children.length !== ids.size) {
- (currentSuite as TestSuiteInfo).children = (currentSuite as TestSuiteInfo).children.filter(ti => ids.has(ti.id));
- changed = true;
+ } else {
+ currentTest = this.testController.createTestItem(test.id, test.name, testUri);
+ currentTest.range = asRange(test.range);
+ children.push(currentTest);
+ currentSuite?.children.add(currentTest);
}
}
+ });
+ if (testExecution) {
+ parentTests.forEach((val, key) => {
+ const item = this.testController.createTestItem(key.id, key.label, key.uri);
+ item.range = key.range;
+ item.children.replace(val);
+ currentSuite?.children.add(item);
+ });
} else {
- const children: TestInfo[] = suite.tests ? suite.tests.map(test => {
- return { type: 'test', id: test.id, label: test.shortName, tooltip: test.fullName, file: test.file ? Uri.parse(test.file)?.path : undefined, line: test.line };
- }) : [];
- this.children.push({ type: 'suite', id: suite.suiteName, label: suite.suiteName, file: suite.file ? Uri.parse(suite.file)?.path : undefined, line: suite.line, children });
- changed = true;
+ currentSuite.children.replace(children);
}
- return changed;
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists