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/04/13 17:19:48 UTC
[netbeans] branch master updated: Test Explorer fixes. (#2878)
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 d100687 Test Explorer fixes. (#2878)
d100687 is described below
commit d1006877e919e3723a73cb131a596717492caf8f
Author: Dusan Balek <du...@oracle.com>
AuthorDate: Tue Apr 13 19:19:22 2021 +0200
Test Explorer fixes. (#2878)
---
.../server/debugging/launch/NbLaunchDelegate.java | 13 ++-
.../server/protocol/TextDocumentServiceImpl.java | 65 ++++++++-------
.../lsp/server/protocol/WorkspaceServiceImpl.java | 19 +++--
java/java.lsp.server/vscode/src/testAdapter.ts | 92 +++++++++++++---------
.../maven/junit/JUnitOutputListenerProvider.java | 16 ++++
5 files changed, 128 insertions(+), 77 deletions(-)
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 a254307..cf97e0d 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
@@ -29,6 +29,8 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
@@ -93,14 +95,21 @@ public abstract class NbLaunchDelegate {
singleMethod = null;
}
ActionProgress progress = new ActionProgress() {
+ private final AtomicInteger count = new AtomicInteger(0);
+ private final AtomicBoolean finalSuccess = new AtomicBoolean(true);
@Override
protected void started() {
+ count.incrementAndGet();
}
@Override
public void finished(boolean success) {
- ioContext.stop();
- notifyFinished(context, success);
+ if (count.decrementAndGet() <= 0) {
+ ioContext.stop();
+ notifyFinished(context, success && finalSuccess.get());
+ } else if (!success) {
+ finalSuccess.set(success);
+ }
}
};
if (toRun != null) {
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java
index 0c7e8ce..15433d3 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java
@@ -52,10 +52,12 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import java.util.function.IntFunction;
import java.util.logging.Level;
@@ -235,7 +237,7 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
/**
* Documents actually opened by the client.
*/
- private final Map<String, Document> openedDocuments = new HashMap<>();
+ private final Map<String, Document> openedDocuments = new ConcurrentHashMap<>();
private final Map<String, RequestProcessor.Task> diagnosticTasks = new HashMap<>();
private final LspServerState server;
private NbCodeLanguageClient client;
@@ -1083,6 +1085,8 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
}
//end copied
+ private ConcurrentHashMap<String, Boolean> upToDateTests = new ConcurrentHashMap<>();
+
@Override
public CompletableFuture<List<? extends CodeLens>> codeLens(CodeLensParams params) {
// shortcut: if the projects are not yet initialized, return empty:
@@ -1099,32 +1103,35 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
source.runUserActionTask(cc -> {
cc.toPhase(Phase.ELEMENTS_RESOLVED);
//look for test methods:
- List<TestMethod> testMethods = new ArrayList<>();
- for (ComputeTestMethods.Factory methodsFactory : Lookup.getDefault().lookupAll(ComputeTestMethods.Factory.class)) {
- testMethods.addAll(methodsFactory.create().computeTestMethods(cc));
- }
- if (!testMethods.isEmpty()) {
- String testClassName = null;
- List<TestSuiteInfo.TestCaseInfo> tests = new ArrayList<>(testMethods.size());
- for (TestMethod testMethod : testMethods) {
- if (testClassName == null) {
- testClassName = testMethod.getTestClassName();
- }
- String id = testMethod.getTestClassName() + ':' + testMethod.method().getMethodName();
- String fullName = testMethod.getTestClassName() + '.' + testMethod.method().getMethodName();
- int line = Utils.createPosition(cc.getCompilationUnit(), testMethod.start().getOffset()).getLine();
- tests.add(new TestSuiteInfo.TestCaseInfo(id, testMethod.method().getMethodName(), fullName, uri, line, TestSuiteInfo.State.Loaded, null));
+ if (!upToDateTests.getOrDefault(uri, Boolean.FALSE)) {
+ List<TestMethod> testMethods = new ArrayList<>();
+ for (ComputeTestMethods.Factory methodsFactory : Lookup.getDefault().lookupAll(ComputeTestMethods.Factory.class)) {
+ testMethods.addAll(methodsFactory.create().computeTestMethods(cc));
}
- Integer line = null;
- Trees trees = cc.getTrees();
- for (Tree tree : cc.getCompilationUnit().getTypeDecls()) {
- Element element = trees.getElement(trees.getPath(cc.getCompilationUnit(), tree));
- if (element != null && element.getKind().isClass() && ((TypeElement)element).getQualifiedName().contentEquals(testClassName)) {
- line = Utils.createPosition(cc.getCompilationUnit(), (int)trees.getSourcePositions().getStartPosition(cc.getCompilationUnit(), tree)).getLine();
- break;
+ if (!testMethods.isEmpty()) {
+ String testClassName = null;
+ List<TestSuiteInfo.TestCaseInfo> tests = new ArrayList<>(testMethods.size());
+ for (TestMethod testMethod : testMethods) {
+ if (testClassName == null) {
+ testClassName = testMethod.getTestClassName();
+ }
+ String id = testMethod.getTestClassName() + ':' + testMethod.method().getMethodName();
+ String fullName = testMethod.getTestClassName() + '.' + testMethod.method().getMethodName();
+ int line = Utils.createPosition(cc.getCompilationUnit(), testMethod.start().getOffset()).getLine();
+ tests.add(new TestSuiteInfo.TestCaseInfo(id, testMethod.method().getMethodName(), fullName, uri, line, TestSuiteInfo.State.Loaded, null));
+ }
+ Integer line = null;
+ Trees trees = cc.getTrees();
+ for (Tree tree : cc.getCompilationUnit().getTypeDecls()) {
+ Element element = trees.getElement(trees.getPath(cc.getCompilationUnit(), tree));
+ if (element != null && element.getKind().isClass() && ((TypeElement)element).getQualifiedName().contentEquals(testClassName)) {
+ line = Utils.createPosition(cc.getCompilationUnit(), (int)trees.getSourcePositions().getStartPosition(cc.getCompilationUnit(), tree)).getLine();
+ break;
+ }
}
+ client.notifyTestProgress(new TestProgressParams(uri, new TestSuiteInfo(testClassName, uri, line, TestSuiteInfo.State.Loaded, tests)));
+ upToDateTests.put(uri, Boolean.TRUE);
}
- client.notifyTestProgress(new TestProgressParams(uri, new TestSuiteInfo(testClassName, uri, line, TestSuiteInfo.State.Loaded, tests)));
}
//look for main methods:
List<CodeLens> lens = new ArrayList<>();
@@ -1440,7 +1447,9 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
@Override
public void didChange(DidChangeTextDocumentParams params) {
- Document doc = openedDocuments.get(params.getTextDocument().getUri());
+ String uri = params.getTextDocument().getUri();
+ upToDateTests.put(uri, Boolean.FALSE);
+ Document doc = openedDocuments.get(uri);
if (doc != null) {
NbDocument.runAtomic((StyledDocument) doc, () -> {
for (TextDocumentContentChangeEvent change : params.getContentChanges()) {
@@ -1462,10 +1471,12 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
@Override
public void didClose(DidCloseTextDocumentParams params) {
try {
+ String uri = params.getTextDocument().getUri();
+ upToDateTests.remove(uri);
// the order here is important ! As the file may cease to exist, it's
// important that the doucment is already gone form the client.
- openedDocuments.remove(params.getTextDocument().getUri());
- FileObject file = fromURI(params.getTextDocument().getUri(), true);
+ openedDocuments.remove(uri);
+ FileObject file = fromURI(uri, true);
if (file == null) {
return;
}
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 28ba394..1723b1f 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
@@ -39,6 +39,7 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
@@ -179,27 +180,25 @@ public final class WorkspaceServiceImpl implements WorkspaceService, LanguageCli
private CompletableFuture<Set<URL>> getTestRootURLs(Project prj) {
final Set<URL> testRootURLs = new HashSet<>();
- List<CompletableFuture<?>> futures = new ArrayList<>();
+ List<FileObject> contained = null;
if (prj != null) {
for (SourceGroup sg : ProjectUtils.getSources(prj).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA)) {
for (URL url : UnitTestForSourceQuery.findUnitTests(sg.getRootFolder())) {
testRootURLs.add(url);
}
}
- for (Project containedPrj : ProjectUtils.getContainedProjects(prj, true)) {
- boolean testRootFound = false;
- for (SourceGroup sg : ProjectUtils.getSources(containedPrj).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA)) {
+ contained = ProjectUtils.getContainedProjects(prj, true).stream().map(p -> p.getProjectDirectory()).collect(Collectors.toList());
+ }
+ return server.asyncOpenSelectedProjects(contained).thenApply(projects -> {
+ for (Project project : projects) {
+ for (SourceGroup sg : ProjectUtils.getSources(project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA)) {
for (URL url : UnitTestForSourceQuery.findUnitTests(sg.getRootFolder())) {
testRootURLs.add(url);
- testRootFound = true;
}
}
- if (testRootFound) {
- futures.add(server.asyncOpenFileOwner(containedPrj.getProjectDirectory()));
- }
}
- }
- return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).thenApply(value -> testRootURLs);
+ return testRootURLs;
+ });
}
private void findTestMethods(Set<URL> testRootURLs, List<TestMethodController.TestMethod> testMethods) {
diff --git a/java/java.lsp.server/vscode/src/testAdapter.ts b/java/java.lsp.server/vscode/src/testAdapter.ts
index 6e9c1a6..70896b1 100644
--- a/java/java.lsp.server/vscode/src/testAdapter.ts
+++ b/java/java.lsp.server/vscode/src/testAdapter.ts
@@ -20,14 +20,14 @@
import { WorkspaceFolder, Event, EventEmitter, Uri, commands, debug } from "vscode";
import * as path from 'path';
-import { TestAdapter, TestSuiteEvent, TestEvent, TestLoadFinishedEvent, TestLoadStartedEvent, TestRunFinishedEvent, TestRunStartedEvent, TestSuiteInfo, TestInfo } from "vscode-test-adapter-api";
+import { TestAdapter, TestSuiteEvent, TestEvent, TestLoadFinishedEvent, TestLoadStartedEvent, TestRunFinishedEvent, TestRunStartedEvent, TestSuiteInfo, TestInfo, TestDecoration } from "vscode-test-adapter-api";
import { TestSuite } from "./protocol";
import { LanguageClient } from "vscode-languageclient";
export class NbTestAdapter implements TestAdapter {
private disposables: { dispose(): void }[] = [];
- private children: (TestSuiteInfo | TestInfo)[] = [];
+ private children: TestSuiteInfo[] = [];
private readonly testSuite: TestSuiteInfo;
private readonly testsEmitter = new EventEmitter<TestLoadStartedEvent | TestLoadFinishedEvent>();
@@ -58,7 +58,7 @@ export class NbTestAdapter implements TestAdapter {
const loadedTests: any = await commands.executeCommand('java.load.workspace.tests', this.workspaceFolder.uri.toString());
if (loadedTests) {
loadedTests.forEach((suite: TestSuite) => {
- this.updateTests(suite, false);
+ this.updateTests(suite);
});
}
if (this.children.length > 0) {
@@ -130,39 +130,57 @@ export class NbTestAdapter implements TestAdapter {
}
testProgress(suite: TestSuite): void {
- this.updateTests(suite, true);
- if (suite.state === 'running') {
- this.statesEmitter.fire(<TestSuiteEvent>{ type: 'suite', suite: suite.suiteName, state: suite.state });
- } else if (suite.state !== 'loaded') {
- if (suite.tests) {
- suite.tests.forEach(test => {
- let message;
- let decorations;
- 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]);
+ switch (suite.state) {
+ case 'loaded':
+ if (this.updateTests(suite)) {
+ this.testsEmitter.fire(<TestLoadFinishedEvent>{ type: 'finished', suite: this.testSuite });
+ }
+ break;
+ case 'running':
+ this.statesEmitter.fire(<TestSuiteEvent>{ type: 'suite', suite: suite.suiteName, state: suite.state });
+ break;
+ case 'completed':
+ case 'errored':
+ let errMessage: string | undefined;
+ if (suite.tests) {
+ const currentSuite = this.children.find(s => s.id === suite.suiteName);
+ 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]);
+ }
+ return null;
+ }).find(l => l);
+ if (line) {
+ decorations = [{ line: line - 1, message: test.stackTrace[0] }];
+ }
}
- return null;
- }).find(l => l);
- if (line) {
- decorations = [{ line: line - 1, message: test.stackTrace[0] }];
}
- }
+ let currentTest = (currentSuite as TestSuiteInfo).children.find(ti => ti.id === test.id);
+ 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;
+ }
+ });
}
- this.statesEmitter.fire(<TestEvent>{ type: 'test', test: test.id, state: test.state, message, decorations });
- });
- }
- this.statesEmitter.fire(<TestSuiteEvent>{ type: 'suite', suite: suite.suiteName, state: suite.state });
+ }
+ this.statesEmitter.fire(<TestSuiteEvent>{ type: 'suite', suite: suite.suiteName, state: suite.state, message: errMessage });
+ break;
}
}
- updateTests(suite: TestSuite, notifyFinish: boolean): void {
+ updateTests(suite: TestSuite): boolean {
let changed = false;
const currentSuite = this.children.find(s => s.id === suite.suiteName);
if (currentSuite) {
@@ -176,11 +194,11 @@ export class NbTestAdapter implements TestAdapter {
changed = true
}
if (suite.tests) {
- const children: (TestSuiteInfo | TestInfo)[] = [];
+ const ids = new Set();
suite.tests.forEach(test => {
+ ids.add(test.id);
let currentTest = (currentSuite as TestSuiteInfo).children.find(ti => ti.id === test.id);
if (currentTest) {
- children.push(currentTest);
const file = test.file ? Uri.parse(test.file)?.path : undefined;
if (file && currentTest.file !== file) {
currentTest.file = file;
@@ -191,14 +209,14 @@ export class NbTestAdapter implements TestAdapter {
changed = true;
}
} else {
- 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 });
+ (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;
}
});
- if ((currentSuite as TestSuiteInfo).children.length !== children.length) {
+ if ((currentSuite as TestSuiteInfo).children.length !== ids.size) {
+ (currentSuite as TestSuiteInfo).children = (currentSuite as TestSuiteInfo).children.filter(ti => ids.has(ti.id));
changed = true;
}
- (currentSuite as TestSuiteInfo).children = children;
}
} else {
const children: TestInfo[] = suite.tests ? suite.tests.map(test => {
@@ -207,8 +225,6 @@ export class NbTestAdapter implements TestAdapter {
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;
}
- if (notifyFinish && changed) {
- this.testsEmitter.fire(<TestLoadFinishedEvent>{ type: 'finished', suite: this.testSuite });
- }
+ return changed;
}
}
diff --git a/java/maven.junit/src/org/netbeans/modules/maven/junit/JUnitOutputListenerProvider.java b/java/maven.junit/src/org/netbeans/modules/maven/junit/JUnitOutputListenerProvider.java
index fca1858..e923bd6 100644
--- a/java/maven.junit/src/org/netbeans/modules/maven/junit/JUnitOutputListenerProvider.java
+++ b/java/maven.junit/src/org/netbeans/modules/maven/junit/JUnitOutputListenerProvider.java
@@ -289,6 +289,22 @@ public class JUnitOutputListenerProvider implements OutputProcessor {
} else {
if (relativeFile.exists() && relativeFile.isDirectory()) {
outputDir = relativeFile;
+ } else {
+ File parentFile = absoluteFile.getParentFile();
+ if (parentFile.exists() && parentFile.isDirectory()) {
+ absoluteFile.mkdir();
+ if (absoluteFile.exists() && absoluteFile.isDirectory()) {
+ outputDir = absoluteFile;
+ }
+ } else {
+ File parentRelativeFile = relativeFile.getParentFile();
+ if (parentRelativeFile.exists() && parentRelativeFile.isDirectory()) {
+ relativeFile.mkdir();
+ if (relativeFile.exists() && relativeFile.isDirectory()) {
+ outputDir = relativeFile;
+ }
+ }
+ }
}
}
if (null != outputDir) {
---------------------------------------------------------------------
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