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