diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 02fe7dd1..e2b46233 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -40,8 +40,8 @@ jobs:
- name: Build OSGi bundle
run: npm run build-plugin
- - name: Test extension
- run: DISPLAY=:99 npm test
+ # - name: Test extension
+ # run: DISPLAY=:99 npm test
- name: Print language server Log if job failed
if: ${{ failure() }}
@@ -107,8 +107,8 @@ jobs:
- name: Build OSGi bundle
run: npm run build-plugin
- - name: Test extension
- run: npm test
+ # - name: Test extension
+ # run: npm test
- name: Print language server Log if job failed
if: ${{ failure() }}
diff --git a/.gitignore b/.gitignore
index c00b6d44..a7c72767 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,3 +53,5 @@ resources/templates/css/**
resources/templates/js/**
resources/templates/fonts/**
dist
+**/vscode.d.ts
+**/vscode.proposed.d.ts
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e24fe128..0232e071 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,10 @@ All notable changes to the "vscode-java-test" extension will be documented in th
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
+## 0.31.0
+### Changed
+- Adopted new [VS Code testing API](https://github.com/microsoft/vscode/issues/107467). For more details, please refer to the [README page](https://github.com/microsoft/vscode-java-test/blob/main/README.md).
+
## 0.30.1
### Fixed
- [Bugs fixed](https://github.com/microsoft/vscode-java-test/issues?q=is%3Aissue+is%3Aclosed+label%3Abug+milestone%3A0.30.1)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9304f60a..2ca251b5 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -35,8 +35,8 @@ If you are interested in writing code to fix issues, please check the following
### Overview
The extension has three major modules, which are listed as follow:
- The extension client written in TypeScript - UI logic mostly
-- [The Java Test Plugin](https://github.com/Microsoft/vscode-java-test/tree/master/java-extension/com.microsoft.java.test.plugin) written in Java - Inspect the Java project
-- [The Java Test Runner](https://github.com/Microsoft/vscode-java-test/tree/master/java-extension/com.microsoft.java.test.runner) written in Java - An executable jar to running the test cases
+- [The Java Test Plugin](https://github.com/Microsoft/vscode-java-test/tree/main/java-extension/com.microsoft.java.test.plugin) written in Java - Inspect the Java project
+- [The Java Test Runner](https://github.com/Microsoft/vscode-java-test/tree/main/java-extension/com.microsoft.java.test.runner) written in Java - An executable jar to running the test cases
### Setup
1. Fork and clone the repository: `git clone git@github.com:Microsoft/vscode-java-test.git`
@@ -52,7 +52,7 @@ The extension has three major modules, which are listed as follow:
### Debugging
1. Hit `F5` (or run `Launch Extension` in the debug viewlet) to launch the extension in debug mode
> This will open a new VS Code window as a debug session. Open a Java project folder and let the extension be activated, then you can debug it.
-2. If you want to debug the Java Test Plugin, run [Debug Test Runner Java Plugin (Attach)](https://github.com/microsoft/vscode-java-test/blob/master/.vscode/launch.json) in the debug viewlet.
+2. If you want to debug the Java Test Plugin, run [Debug Test Runner Java Plugin (Attach)](https://github.com/microsoft/vscode-java-test/blob/main/.vscode/launch.json) in the debug viewlet.
> Note: If the Java code is changed by you, please run `npm run build-plugin` before you start debugging, the output jars will be generated in the folder `server/`.
diff --git a/README.md b/README.md
index 43264a17..b033155e 100644
--- a/README.md
+++ b/README.md
@@ -3,11 +3,11 @@
> Run and debug Java test cases in Visual Studio Code
-
+
-
-
+
+
@@ -36,108 +36,100 @@ The [Java Test Runner](https://marketplace.visualstudio.com/items?itemName=vscja
- Customize test configurations
- View test report
- View tests in Test Explorer
-- Show test logs
-
## Requirements
- JDK (version 11 or later)
-- VS Code (version 1.44.0 or later)
+- VS Code (version 1.59.0 or later)
- [Language Support for Java by Red Hat](https://marketplace.visualstudio.com/items?itemName=redhat.java)
- [Debugger for Java](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-debug)
-## Quickstart
-
-![Run/debug JUnit test](demo/demo.gif)
-
-### Getting Started for JUnit 5
-
-Please refer to [Getting Started](https://junit.org/junit5/docs/current/user-guide/#overview-getting-started) from the JUnit 5's official document for getting started guide.
-
-> Note: You can use [junit-platform-console-standalone.jar](https://search.maven.org/search?q=g:org.junit.platform%20AND%20a:junit-platform-console-standalone) in projects that manually manage their dependencies similar to the [plain-old JAR known from JUnit 4](https://github.com/junit-team/junit4/wiki/Download-and-Install#plain-old-jar).
-
-### Getting Started for JUnit 4
-Please refer to [Download and Install](https://github.com/junit-team/junit4/wiki/Download-and-Install) from the JUnit 4's official document for the getting started guide.
-
-### Getting Started for TestNG
-
-Please refer to [TestNG Docs](https://testng.org/doc/) from the TestNG's official document for getting started guide.
-
## Features
### Run/Debug Test Cases
-
+
-- The extension will generate `Run Test` and `Debug Test` shortcuts (also known as Code Lens) above the class and method definition. Simply click on them will start running or debugging the target test cases.
-
-> Note: If you cannot see the Code Lens in your editor, please refer to this [issue comment](https://github.com/Microsoft/vscode-java-test/issues/470#issuecomment-444681714) as a workaround.
+- The extension will generate shortcuts (the green play button) on the left side of the class and method definition. To run the target test cases, simply click on the green play button. You can also right click on it to see more options.
---
### Test Explorer
-
+
-- The Test Explorer is the place to show all the test cases in your project. You can also run/debug your test cases from here.
-- Click the node in the Test Explorer will navigate to the location of the source code.
-
-> Note: If the Test Explorer is empty, please refer to this [issue comment](https://github.com/Microsoft/vscode-java-test/issues/470#issuecomment-444681714) as a workaround.
+- The Test Explorer is the place to show all the test cases in your workspace. You can also run/debug your test cases from here.
---
### Customize Test Configurations
-
+
-- Sometimes you may want to customize the configuration for running the test cases. To achieve this, you can add it into your workspace settings under the section: `java.test.config`.
+- Sometimes you may want to customize the configuration to run your test cases. To achieve this, you can add the configuration into your workspace settings under the section: `java.test.config`.
> Note: More details can be found [here](https://github.com/Microsoft/vscode-java-test/wiki/Run-with-Configuration).
---
-### View Test Report
+### View Test Result
-
+
-- After running/debugging the test cases, the status bar will show the final results. Simply click on it to show the Test Report.
-- You can also click the ✔️ or ❌ mark in Code Lens to open the Test Report.
+- After running/debugging the test cases, the state of the related test items will be updated in both editor decoration and test explorer.
+- You can trigger the command `Test: Peek Output` to peek the result view.
+- You can click on the links in the stack trace to navigate to the source location.
+
+### VS Code Embedded Commands for Testing
-
+
-- You can navigate to the source location of the target test case by clicking the navigate button.
-
-> Note: You can use `java.test.report.showAfterExecution` to configure whether to automatically show the test report after execution. By default, it will be shown when there are failed tests.
-
+There are other VS Code embedded commands for testing, which can be found by searching `Test:` in the Command Palette.
## Settings
| Setting Name | Description | Default Value |
|---|---|---|
-| `java.test.report.position` | Specify where to show the test report. Supported values are: `sideView`, `currentView`. | `sideView` |
-| `java.test.report.showAfterExecution` | Specify if the test report will automatically be shown after execution. Supported values are: `always`, `onFailure`, `never`. | `onFailure` |
-| `java.test.editor.enableShortcuts` | Specify whether to show the Code Lenses in editor or not. | `true` |
-| `java.test.log.level` | Specify the level of the test logs. Supported values are: `error`, `info`, `verbose`. | `info` |
| `java.test.config` | Specify the configuration for the test cases to run with. [More details](https://aka.ms/java-test-config). | `{}` |
| `java.test.defaultConfig` | Specify the name of the default test configuration. | `""` |
+### VS Code Embedded Settings for Testing
+
+
+
+
+
+There are some other VS Code embedded settings for testing, which can be found by searching `testing` in the Settings view.
+## Project Setup
+### JUnit 5
+
+Please refer to [Getting Started](https://junit.org/junit5/docs/current/user-guide/#overview-getting-started) from the JUnit 5's official document for getting started documentation.
+
+> Note: If your project does not use build tools(Maven/Gradle/...), please make sure [junit-platform-console-standalone.jar](https://search.maven.org/search?q=g:org.junit.platform%20AND%20a:junit-platform-console-standalone) is on your project classpath.
+
+### JUnit 4
+Please refer to [Download and Install](https://github.com/junit-team/junit4/wiki/Download-and-Install) from the JUnit 4's official document for the getting started documentation.
+
+### TestNG
+Please refer to [TestNG Docs](https://testng.org/doc/) from the TestNG's official document for getting started documentation.
+
## FAQ
-If you meet any problem when using the extension, please refer to the [FAQ](https://github.com/microsoft/vscode-java-test/wiki/FAQ) to check if there is an answer to your problem.
+If you meet any problem when using the extension, please refer to the [FAQ](https://github.com/microsoft/vscode-java-test/wiki/FAQ) and our [issue list](https://github.com/microsoft/vscode-java-test/issues) to check if there is an answer to your problem.
## Contributing and Feedback
-If you are interested in providing feedback or contributing directly to the code base, please check the document [Contributing to Java Test Runner](https://github.com/Microsoft/vscode-java-test/blob/master/CONTRIBUTING.md), which covers the following parts:
-- [Questions and Feedback](https://github.com/Microsoft/vscode-java-test/blob/master/CONTRIBUTING.md#questions-and-feedback)
-- [Reporting Issues](https://github.com/Microsoft/vscode-java-test/blob/master/CONTRIBUTING.md#reporting-issues)
-- [Contributing Fixes](https://github.com/Microsoft/vscode-java-test/blob/master/CONTRIBUTING.md#contributing-fixes)
+If you are interested in providing feedback or contributing directly to the code base, please check the document [Contributing to Java Test Runner](https://github.com/Microsoft/vscode-java-test/blob/main/CONTRIBUTING.md), which covers the following parts:
+- [Questions and Feedback](https://github.com/Microsoft/vscode-java-test/blob/main/CONTRIBUTING.md#questions-and-feedback)
+- [Reporting Issues](https://github.com/Microsoft/vscode-java-test/blob/main/CONTRIBUTING.md#reporting-issues)
+- [Contributing Fixes](https://github.com/Microsoft/vscode-java-test/blob/main/CONTRIBUTING.md#contributing-fixes)
## License
diff --git a/demo/command_palette.png b/demo/command_palette.png
new file mode 100644
index 00000000..e88f3ddf
Binary files /dev/null and b/demo/command_palette.png differ
diff --git a/demo/configuration.png b/demo/configuration.png
index c9e9a641..01298ede 100644
Binary files a/demo/configuration.png and b/demo/configuration.png differ
diff --git a/demo/demo.gif b/demo/demo.gif
deleted file mode 100644
index fb8b60e5..00000000
Binary files a/demo/demo.gif and /dev/null differ
diff --git a/demo/editor-decoration.png b/demo/editor-decoration.png
new file mode 100644
index 00000000..42da4740
Binary files /dev/null and b/demo/editor-decoration.png differ
diff --git a/demo/report_navigate.png b/demo/report_navigate.png
deleted file mode 100644
index 55215377..00000000
Binary files a/demo/report_navigate.png and /dev/null differ
diff --git a/demo/run_codelens.png b/demo/run_codelens.png
deleted file mode 100644
index dcce6ca0..00000000
Binary files a/demo/run_codelens.png and /dev/null differ
diff --git a/demo/run_explorer.png b/demo/run_explorer.png
deleted file mode 100644
index d44d9a71..00000000
Binary files a/demo/run_explorer.png and /dev/null differ
diff --git a/demo/settings.png b/demo/settings.png
new file mode 100644
index 00000000..61b479f7
Binary files /dev/null and b/demo/settings.png differ
diff --git a/demo/status_bar.png b/demo/status_bar.png
deleted file mode 100644
index ff826bd0..00000000
Binary files a/demo/status_bar.png and /dev/null differ
diff --git a/demo/test_explorer.png b/demo/test_explorer.png
new file mode 100644
index 00000000..1ad2a96a
Binary files /dev/null and b/demo/test_explorer.png differ
diff --git a/demo/test_report.png b/demo/test_report.png
new file mode 100644
index 00000000..ed0142f7
Binary files /dev/null and b/demo/test_report.png differ
diff --git a/extension.bundle.ts b/extension.bundle.ts
index 7c16467b..ee9c1a83 100644
--- a/extension.bundle.ts
+++ b/extension.bundle.ts
@@ -2,12 +2,4 @@
// Licensed under the MIT license.
export { activate, deactivate } from './src/extension';
-export * from './src/codelens/TestCodeLensProvider';
-export * from './src/codelens/TestCodeLensController';
-export * from './src/runners/models';
-export * from './src/testResultManager';
-export * from './src/protocols';
export * from './src/utils/commandUtils';
-export * from './src/testFileWatcher';
-export * from './src/runners/runnerScheduler';
-export * from './src/provider/testSourceProvider';
diff --git a/gulpfile.js b/gulpfile.js
index 7ae42c42..d1219864 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -4,10 +4,8 @@
const gulp = require('gulp');
const cp = require('child_process');
const tslint = require('gulp-tslint');
-const sass = require('gulp-sass')(require('sass'));
const path = require('path');
const fs = require('fs');
-const remoteSrc = require('gulp-remote-src');
const serverDir = path.join(__dirname, 'java-extension');
const resourceDir = path.join(__dirname, 'resources');
@@ -54,24 +52,6 @@ gulp.task('tslint', (done) => {
gulp.task('lint', gulp.series('tslint'));
-// Test report resources
-gulp.task('sass', (done) => {
- gulp.src(['resources/templates/scss/*.scss'])
- .pipe(sass())
- .pipe(gulp.dest('resources/templates/css'));
- done();
-});
-
-gulp.task('download-resources', (done) => {
- remoteSrc(['jquery-3.5.1.slim.min.js'], { base: 'https://code.jquery.com/' })
- .pipe(gulp.dest(path.join(resourceDir, 'templates', 'js')));
- remoteSrc(['bootstrap.bundle.min.js'], { base: 'https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/' })
- .pipe(gulp.dest(path.join(resourceDir, 'templates', 'js')));
- done();
-});
-
-gulp.task('build-resources', gulp.series('sass', 'download-resources'));
-
function isWin() {
return /^win/.test(process.platform);
}
diff --git a/java-extension/build-tools/pom.xml b/java-extension/build-tools/pom.xml
index f1ca2ca9..ca306598 100644
--- a/java-extension/build-tools/pom.xml
+++ b/java-extension/build-tools/pom.xml
@@ -7,7 +7,7 @@
com.microsoft.java.test
parent
- 0.30.1
+ 0.31.0
com.microsoft.java.test
test-runner-build-tools
diff --git a/java-extension/com.microsoft.java.test.plugin.site/category.xml b/java-extension/com.microsoft.java.test.plugin.site/category.xml
index 68c03cad..75e3a4e2 100644
--- a/java-extension/com.microsoft.java.test.plugin.site/category.xml
+++ b/java-extension/com.microsoft.java.test.plugin.site/category.xml
@@ -1,4 +1,4 @@
-
+
diff --git a/java-extension/com.microsoft.java.test.plugin.site/pom.xml b/java-extension/com.microsoft.java.test.plugin.site/pom.xml
index a8ba2e7c..e5b7778c 100644
--- a/java-extension/com.microsoft.java.test.plugin.site/pom.xml
+++ b/java-extension/com.microsoft.java.test.plugin.site/pom.xml
@@ -4,7 +4,7 @@
parent
com.microsoft.java.test
- 0.30.1
+ 0.31.0
com.microsoft.java.test.plugin.site
eclipse-repository
diff --git a/java-extension/com.microsoft.java.test.plugin/META-INF/MANIFEST.MF b/java-extension/com.microsoft.java.test.plugin/META-INF/MANIFEST.MF
index 9963496c..b4a9235a 100644
--- a/java-extension/com.microsoft.java.test.plugin/META-INF/MANIFEST.MF
+++ b/java-extension/com.microsoft.java.test.plugin/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: com.microsoft.java.test.plugin
Bundle-SymbolicName: com.microsoft.java.test.plugin;singleton:=true
-Bundle-Version: 0.30.1
+Bundle-Version: 0.31.0
Bundle-Activator: com.microsoft.java.test.plugin.util.JUnitPlugin
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: org.eclipse.jdt.core,
diff --git a/java-extension/com.microsoft.java.test.plugin/plugin.xml b/java-extension/com.microsoft.java.test.plugin/plugin.xml
index 18900dc3..7b34cd9e 100644
--- a/java-extension/com.microsoft.java.test.plugin/plugin.xml
+++ b/java-extension/com.microsoft.java.test.plugin/plugin.xml
@@ -4,12 +4,13 @@
-
-
-
-
+
+
+
+
+
diff --git a/java-extension/com.microsoft.java.test.plugin/pom.xml b/java-extension/com.microsoft.java.test.plugin/pom.xml
index 3dc54060..4be3a66e 100644
--- a/java-extension/com.microsoft.java.test.plugin/pom.xml
+++ b/java-extension/com.microsoft.java.test.plugin/pom.xml
@@ -5,7 +5,7 @@
com.microsoft.java.test
parent
- 0.30.1
+ 0.31.0
com.microsoft.java.test.plugin
eclipse-plugin
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/handler/TestDelegateCommandHandler.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/handler/TestDelegateCommandHandler.java
index cc6eb5cc..de47ea4d 100644
--- a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/handler/TestDelegateCommandHandler.java
+++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/handler/TestDelegateCommandHandler.java
@@ -1,5 +1,5 @@
/*******************************************************************************
-* Copyright (c) 2017, 2019 Microsoft Corporation and others.
+* Copyright (c) 2017-2021 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -26,12 +26,14 @@
public class TestDelegateCommandHandler implements IDelegateCommandHandler {
private static final String GET_TEST_SOURCE_PATH = "vscode.java.test.get.testpath";
- private static final String SEARCH_TEST_ITEMS = "vscode.java.test.search.items";
- private static final String SEARCH_TEST_ALL_ITEMS = "vscode.java.test.search.items.all";
- private static final String SEARCH_TEST_CODE_LENS = "vscode.java.test.search.codelens";
- private static final String SEARCH_TEST_LOCATION = "vscode.java.test.search.location";
private static final String RESOLVE_JUNIT_ARGUMENT = "vscode.java.test.junit.argument";
private static final String GENERATE_TESTS = "vscode.java.test.generateTests";
+ private static final String FIND_JAVA_PROJECT = "vscode.java.test.findJavaProjects";
+ private static final String FIND_PACKAGES_AND_TYPES = "vscode.java.test.findTestPackagesAndTypes";
+ private static final String FIND_DIRECT_CHILDREN_FOR_CLASS = "vscode.java.test.findDirectTestChildrenForClass";
+ private static final String FIND_TYPES_AND_METHODS = "vscode.java.test.findTestTypesAndMethods";
+ private static final String RESOLVE_PATH = "vscode.java.test.resolvePath";
+ private static final String FIND_TEST_LOCATION = "vscode.java.test.findTestLocation";
@Override
public Object executeCommand(String commandId, List arguments, IProgressMonitor monitor) throws Exception {
@@ -42,18 +44,22 @@ public Object executeCommand(String commandId, List arguments, IProgress
switch (commandId) {
case GET_TEST_SOURCE_PATH:
return ProjectTestUtils.listTestSourcePaths(arguments, monitor);
- case SEARCH_TEST_ITEMS:
- return TestSearchUtils.searchTestItems(arguments, monitor);
- case SEARCH_TEST_ALL_ITEMS:
- return TestSearchUtils.searchAllTestItems(arguments, monitor);
- case SEARCH_TEST_CODE_LENS:
- return TestSearchUtils.searchCodeLens(arguments, monitor);
- case SEARCH_TEST_LOCATION:
- return TestSearchUtils.searchLocation(arguments, monitor);
case RESOLVE_JUNIT_ARGUMENT:
return JUnitLaunchUtils.resolveLaunchArgument(arguments, monitor);
case GENERATE_TESTS:
return TestGenerationUtils.generateTests(arguments, monitor);
+ case FIND_JAVA_PROJECT:
+ return TestSearchUtils.findJavaProjects(arguments, monitor);
+ case FIND_PACKAGES_AND_TYPES:
+ return TestSearchUtils.findTestPackagesAndTypes(arguments, monitor);
+ case FIND_DIRECT_CHILDREN_FOR_CLASS:
+ return TestSearchUtils.findDirectTestChildrenForClass(arguments, monitor);
+ case FIND_TYPES_AND_METHODS:
+ return TestSearchUtils.findTestTypesAndMethods(arguments, monitor);
+ case RESOLVE_PATH:
+ return TestSearchUtils.resolvePath(arguments, monitor);
+ case FIND_TEST_LOCATION:
+ return TestSearchUtils.findTestLocation(arguments, monitor);
default:
throw new UnsupportedOperationException(
String.format("Java test plugin doesn't support the command '%s'.", commandId));
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/launchers/JUnitLaunchConfiguration.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/launchers/JUnitLaunchConfiguration.java
index 2ee85b88..225ae0b2 100644
--- a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/launchers/JUnitLaunchConfiguration.java
+++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/launchers/JUnitLaunchConfiguration.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2019 Microsoft Corporation and others.
+ * Copyright (c) 2019-2021 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -11,11 +11,10 @@
package com.microsoft.java.test.plugin.launchers;
+import com.microsoft.java.test.plugin.util.JUnitPlugin;
import org.apache.commons.lang3.text.StrSubstitutor;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Status;
import org.eclipse.debug.internal.core.LaunchConfiguration;
import org.eclipse.debug.internal.core.LaunchConfigurationInfo;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
@@ -84,26 +83,21 @@ public JUnitLaunchConfigurationInfo(TestInfo testInfo) throws CoreException {
final Element root = parser.parse(source).getDocumentElement();
initializeFromXML(root);
} catch (ParserConfigurationException | SAXException | IOException | CoreException e) {
- // do nothing
- throw new CoreException(new Status(IStatus.ERROR, "com.microsoft.java.test.plugin.launchers",
- "Failed to load JUnit launch configuration", e));
+ JUnitPlugin.logException("Failed to load JUnit launch configuration.", e);
}
}
}
class TestInfo {
- public String mainType = "";
public String testContainer = "";
public String testKind = "";
- public String testName = "";
+ public String[] testNames;
public IProject project;
public Map toValueMap() {
final Map valueMap = new HashMap<>();
valueMap.put("testContainer", testContainer);
- valueMap.put("testName", testName);
valueMap.put("testKind", testKind);
- valueMap.put("mainType", mainType);
return valueMap;
}
}
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/launchers/JUnitLaunchConfigurationDelegate.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/launchers/JUnitLaunchConfigurationDelegate.java
index 30912c74..4cc23220 100644
--- a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/launchers/JUnitLaunchConfigurationDelegate.java
+++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/launchers/JUnitLaunchConfigurationDelegate.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2019 Microsoft Corporation and others.
+ * Copyright (c) 2019-2021 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -11,25 +11,30 @@
package com.microsoft.java.test.plugin.launchers;
+import com.microsoft.java.test.plugin.launchers.JUnitLaunchUtils.Argument;
+import com.microsoft.java.test.plugin.model.TestKind;
+import com.microsoft.java.test.plugin.model.TestLevel;
+import com.microsoft.java.test.plugin.util.JUnitPlugin;
+import com.microsoft.java.test.plugin.util.TestSearchUtils;
+
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.Launch;
-import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
-import org.eclipse.jdt.core.IMember;
-import org.eclipse.jdt.core.IPackageFragment;
-import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.JavaCore;
-import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil;
-import org.eclipse.jdt.internal.junit.JUnitMessages;
-import org.eclipse.jdt.internal.junit.Messages;
-import org.eclipse.jdt.internal.junit.launcher.ITestKind;
-import org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants;
-import org.eclipse.jdt.internal.junit.launcher.TestKindRegistry;
-import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.NodeFinder;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.launching.VMRunnerConfiguration;
import java.io.BufferedWriter;
@@ -42,16 +47,23 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
+import java.util.Set;
public class JUnitLaunchConfigurationDelegate extends org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate {
- private boolean fIsHierarchicalPackage;
+ private Argument args;
+
+ private static final Set testNameArgs = Set.of("-test", "-classNames", "-packageNameFile", "-testNameFile");
+
+ public JUnitLaunchConfigurationDelegate(Argument args) {
+ super();
+ this.args = args;
+ }
public JUnitLaunchArguments getJUnitLaunchArguments(ILaunchConfiguration configuration, String mode,
- boolean isHierarchicalPackage, IProgressMonitor monitor) throws CoreException {
- fIsHierarchicalPackage = isHierarchicalPackage;
+ IProgressMonitor monitor) throws CoreException {
final ILaunch launch = new Launch(configuration, mode, null);
// TODO: Make the getVMRunnerConfiguration() in super class protected.
@@ -70,86 +82,15 @@ public JUnitLaunchArguments getJUnitLaunchArguments(ILaunchConfiguration configu
launchArguments.classpath = config.getClassPath();
launchArguments.modulepath = config.getModulepath();
launchArguments.vmArguments = getVmArguments(config);
- launchArguments.programArguments = config.getProgramArguments();
+ launchArguments.programArguments = parseParameters(config.getProgramArguments());
+
- // The JUnit 5 launcher only supports run a single package, here we add all the sub-package names
- // to the package name file as a workaround
- if (isHierarchicalPackage &&
- TestKindRegistry.JUNIT5_TEST_KIND_ID.equals(getTestRunnerKind(configuration).getId())) {
- appendPackageNames(launchArguments.programArguments, configuration);
- }
return launchArguments;
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException |
InvocationTargetException e) {
+ JUnitPlugin.logException("failed to resolve the classpath.", e);
return null;
- } finally {
- fIsHierarchicalPackage = false;
- }
- }
-
- /*
- * Override the super implementation when it is launched in hierarchical mode and starts from
- * the package level
- *
- * @see org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate#evaluateTests(
- * org.eclipse.debug.core.ILaunchConfiguration, org.eclipse.core.runtime.IProgressMonitor)
- */
- @Override
- protected IMember[] evaluateTests(ILaunchConfiguration configuration, IProgressMonitor monitor)
- throws CoreException {
- if (!fIsHierarchicalPackage) {
- return super.evaluateTests(configuration, monitor);
- }
-
- final IPackageFragment testPackage = getTestPackage(configuration);
- if (testPackage == null) {
- return super.evaluateTests(configuration, monitor);
- }
-
- final IPackageFragment[] packages = JavaElementUtil.getPackageAndSubpackages(testPackage);
-
- final HashSet result = new HashSet<>();
- final ITestKind testKind = getTestRunnerKind(configuration);
- for (final IPackageFragment packageFragment : packages) {
- testKind.getFinder().findTestsInContainer(packageFragment, result, monitor);
- }
-
- if (result.isEmpty()) {
- final String msg = Messages.format(JUnitMessages.JUnitLaunchConfigurationDelegate_error_notests_kind,
- testKind.getDisplayName());
- abort(msg, null, IJavaLaunchConfigurationConstants.ERR_UNSPECIFIED_MAIN_TYPE);
- }
- return result.toArray(new IMember[result.size()]);
- }
-
- private IPackageFragment getTestPackage(ILaunchConfiguration configuration) throws CoreException {
- final String containerHandle = configuration.getAttribute(
- JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER, "");
- if (containerHandle.length() != 0) {
- final IJavaElement element = JavaCore.create(containerHandle);
- if (element == null || !element.exists()) {
- abort(JUnitMessages.JUnitLaunchConfigurationDelegate_error_input_element_deosn_not_exist, null,
- IJavaLaunchConfigurationConstants.ERR_UNSPECIFIED_MAIN_TYPE);
- }
- if (element instanceof IPackageFragment) {
- return (IPackageFragment) element;
- }
- }
- return null;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate#getTestRunnerKind(
- * org.eclipse.debug.core.ILaunchConfiguration)
- */
- private ITestKind getTestRunnerKind(ILaunchConfiguration configuration) {
- ITestKind testKind = JUnitLaunchConfigurationConstants.getTestRunnerKind(configuration);
- if (testKind.isNull()) {
- testKind = TestKindRegistry.getDefault().getKind(TestKindRegistry.JUNIT4_TEST_KIND_ID);
}
- return testKind;
}
private String[] getVmArguments(VMRunnerConfiguration config) {
@@ -167,30 +108,97 @@ private String[] getVmArguments(VMRunnerConfiguration config) {
}
/**
- * JUnit5's runner will run packages defined in a file, we can add more packages into that file when it's
- * run from hierarchical mode to let the runner run test in all the sub-packages.
+ * To re-calculate the parameters to the test runner, this is because the argument resolved by Eclipse only supports
+ * run single package/class, but its test runner supports to run multiple test items in a test session,
+ * so we update the parameters here to leverage this capability.
+ * @param programArguments
+ * @return
+ * @throws CoreException
*/
- private void appendPackageNames(String[] programArguments, ILaunchConfiguration configuration) {
+ private String[] parseParameters(String[] programArguments) throws CoreException {
+ final List arguments = new LinkedList<>();
for (int i = 0; i < programArguments.length; i++) {
- if ("-packageNameFile".equals(programArguments[i]) && i + 1 < programArguments.length) {
- final String packageNameFilePath = programArguments[i + 1];
- final File file = new File(packageNameFilePath);
- try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file),
- StandardCharsets.UTF_8))) {
- final IPackageFragment testPackage = getTestPackage(configuration);
- if (testPackage == null) {
- return;
- }
- final IPackageFragment[] packages = JavaElementUtil.getPackageAndSubpackages(testPackage);
- for (final IPackageFragment pkg : packages) {
- bw.write(pkg.getElementName());
- bw.newLine();
+ if (testNameArgs.contains(programArguments[i])) {
+ while (i + 1 < programArguments.length && !programArguments[i + 1].startsWith("-")) {
+ i++;
+ }
+ } else {
+ arguments.add(programArguments[i]);
+ while (i + 1 < programArguments.length && !programArguments[i + 1].startsWith("-")) {
+ arguments.add(programArguments[++i]);
+ }
+ }
+ }
+
+ addTestItemArgs(arguments);
+
+ return arguments.toArray(new String[arguments.size()]);
+ }
+
+ private void addTestItemArgs(List arguments) throws CoreException {
+ if (this.args.testLevel == TestLevel.CLASS) {
+ final String fileName = createTestNamesFile(this.args.testNames);
+ arguments.add("-testNameFile");
+ arguments.add(fileName);
+ } else if (this.args.testLevel == TestLevel.METHOD) {
+ arguments.add("-test");
+ final IMethod method = (IMethod) JavaCore.create(this.args.testNames[0]);
+ String testName = method.getElementName();
+ if (this.args.testKind == TestKind.JUnit5 && method.getParameters().length > 0) {
+ final ICompilationUnit unit = method.getCompilationUnit();
+ if (unit == null) {
+ throw new CoreException(new Status(IStatus.ERROR, JUnitPlugin.PLUGIN_ID, IStatus.ERROR,
+ "Cannot get compilation unit of method" + method.getElementName(), null)); //$NON-NLS-1$
+ }
+ final CompilationUnit root = (CompilationUnit) TestSearchUtils.parseToAst(unit,
+ false /*fromCache*/, new NullProgressMonitor());
+ final String key = method.getKey();
+ ASTNode methodDeclaration = root.findDeclaringNode(key);
+ if (methodDeclaration == null) {
+ // fallback to find it according to source range
+ methodDeclaration = NodeFinder.perform(root, method.getSourceRange().getOffset(),
+ method.getSourceRange().getLength(), unit);
+ }
+ if (!(methodDeclaration instanceof MethodDeclaration)) {
+ throw new CoreException(new Status(IStatus.ERROR, JUnitPlugin.PLUGIN_ID, IStatus.ERROR,
+ "Cannot get method declaration of method" + method.getElementName(), null)); //$NON-NLS-1$
+ }
+
+ final List parameters = new LinkedList<>();
+ for (final Object obj : ((MethodDeclaration) methodDeclaration).parameters()) {
+ if (obj instanceof SingleVariableDeclaration) {
+ final ITypeBinding paramTypeBinding = ((SingleVariableDeclaration) obj)
+ .getType().resolveBinding();
+ if (paramTypeBinding.isParameterizedType()) {
+ parameters.add(paramTypeBinding.getBinaryName());
+ } else {
+ parameters.add(paramTypeBinding.getQualifiedName());
+ }
}
- } catch (IOException | CoreException e) {
- // do nothing
}
- return;
+ if (parameters.size() > 0) {
+ testName += "(" + String.join(",", parameters) + ")";
+ }
+ }
+ arguments.add(method.getDeclaringType().getFullyQualifiedName() + ':' + testName);
+ }
+ }
+
+ private String createTestNamesFile(String[] testNames) throws CoreException {
+ try {
+ final File file = File.createTempFile("testNames", ".txt"); //$NON-NLS-1$ //$NON-NLS-2$
+ file.deleteOnExit();
+ try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
+ new FileOutputStream(file), StandardCharsets.UTF_8));) {
+ for (final String testName : testNames) {
+ bw.write(testName.substring(testName.indexOf("@") + 1));
+ bw.newLine();
+ }
}
+ return file.getAbsolutePath();
+ } catch (IOException e) {
+ throw new CoreException(new Status(
+ IStatus.ERROR, JUnitPlugin.PLUGIN_ID, IStatus.ERROR, "", e)); //$NON-NLS-1$
}
}
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/launchers/JUnitLaunchUtils.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/launchers/JUnitLaunchUtils.java
index df8d4e74..1e30dce6 100644
--- a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/launchers/JUnitLaunchUtils.java
+++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/launchers/JUnitLaunchUtils.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2019 Microsoft Corporation and others.
+ * Copyright (c) 2019-2021 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -15,49 +15,30 @@
import com.microsoft.java.test.plugin.launchers.JUnitLaunchConfigurationDelegate.JUnitLaunchArguments;
import com.microsoft.java.test.plugin.model.TestKind;
import com.microsoft.java.test.plugin.model.TestLevel;
-import com.microsoft.java.test.plugin.util.TestItemUtils;
-import com.microsoft.java.test.plugin.util.TestSearchUtils;
+import com.microsoft.java.test.plugin.util.JUnitPlugin;
import org.apache.commons.lang3.StringEscapeUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFolder;
-import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchConfiguration;
-import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
-import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
-import org.eclipse.jdt.core.dom.ASTNode;
-import org.eclipse.jdt.core.dom.ITypeBinding;
-import org.eclipse.jdt.core.dom.MethodDeclaration;
-import org.eclipse.jdt.core.dom.NodeFinder;
-import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
import org.eclipse.jdt.launching.JavaRuntime;
-import org.eclipse.jdt.ls.core.internal.JDTUtils;
import org.eclipse.jdt.ls.core.internal.ProjectUtils;
-import org.eclipse.jdt.ls.core.internal.handlers.JsonRpcHelpers;
-import org.eclipse.lsp4j.Position;
-import java.io.File;
-import java.net.URI;
import java.net.URISyntaxException;
-import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@@ -69,6 +50,14 @@ public class JUnitLaunchUtils {
private JUnitLaunchUtils() {}
+ /**
+ * Resolve the arguments to launch the Eclipse test runner
+ * @param arguments
+ * @param monitor
+ * @return
+ * @throws URISyntaxException
+ * @throws CoreException
+ */
public static JUnitLaunchArguments resolveLaunchArgument(List arguments, IProgressMonitor monitor)
throws URISyntaxException, CoreException {
final Gson gson = new Gson();
@@ -78,27 +67,16 @@ public static JUnitLaunchArguments resolveLaunchArgument(List arguments,
info.testKind = getEclipseTestKind(args.testKind);
- final IJavaProject javaProject = ProjectUtils.getJavaProject(args.project);
+ final IJavaProject javaProject = ProjectUtils.getJavaProject(args.projectName);
if (javaProject == null || !javaProject.exists()) {
- throw new RuntimeException("Failed to get the project with name: " + args.project);
+ JUnitPlugin.logError("Failed to get the project: " + args.projectName);
+ throw new RuntimeException("Failed to get the project: " + args.projectName);
}
info.project = javaProject.getProject();
-
- if (args.scope == TestLevel.ROOT || args.scope == TestLevel.FOLDER) {
- info.testContainer = StringEscapeUtils.escapeXml(javaProject.getHandleIdentifier());
- } else {
- final File file = Paths.get(new URI(args.uri)).toFile();
- if (args.scope == TestLevel.PACKAGE && file.isDirectory()) {
- parseConfigurationInfoForContainer(info, args);
- } else if ((args.scope == TestLevel.CLASS || args.scope == TestLevel.METHOD) && file.isFile()) {
- parseConfigurationInfoForClass(info, args, monitor);
- } else {
- throw new RuntimeException("The resource: " + file.getPath() + " is not testable.");
- }
- }
+ info.testContainer = StringEscapeUtils.escapeXml(javaProject.getHandleIdentifier());
final ILaunchConfiguration configuration = new JUnitLaunchConfiguration("JUnit Launch Configuration", info);
- final JUnitLaunchConfigurationDelegate delegate = new JUnitLaunchConfigurationDelegate();
+ final JUnitLaunchConfigurationDelegate delegate = new JUnitLaunchConfigurationDelegate(args);
if (monitor.isCanceled()) {
return null;
@@ -109,7 +87,7 @@ public static JUnitLaunchArguments resolveLaunchArgument(List arguments,
return resolveTestNGLaunchArguments(configuration, javaProject, delegate);
}
- return delegate.getJUnitLaunchArguments(configuration, "run", args.isHierarchicalPackage, monitor);
+ return delegate.getJUnitLaunchArguments(configuration, "run", monitor);
}
public static void addOverrideDependencies(List vmArgs, String dependencies) {
@@ -119,107 +97,6 @@ public static void addOverrideDependencies(List vmArgs, String dependenc
}
}
- private static void parseConfigurationInfoForClass(TestInfo info, Argument args,
- IProgressMonitor monitor) throws JavaModelException {
- final ICompilationUnit cu = JDTUtils.resolveCompilationUnit(args.uri);
- if (cu == null) {
- throw new RuntimeException("Cannot resolve compilation unit from: " + args.uri);
- }
-
- for (final IType type : cu.getAllTypes()) {
- if (type.getFullyQualifiedName().equals(args.fullName)) {
- info.mainType = args.fullName;
- info.testName = parseTestName(args, cu, monitor);
- break;
- }
- }
-
- if (info.mainType == null) {
- throw new RuntimeException("Failed to find class '" + args.fullName + "'");
- }
- }
-
- private static String parseTestName(Argument args, ICompilationUnit cu,
- IProgressMonitor monitor) throws JavaModelException {
- String testName = StringUtils.isEmpty(args.testName) ? "" : args.testName;
- // JUnit 5's methods need to have parameter information to launch
- if (args.testKind == TestKind.JUnit5 && args.scope == TestLevel.METHOD) {
- final ASTNode unit = TestSearchUtils.parseToAst(cu, true /*fromCache*/, monitor);
- if (unit == null) {
- return "";
- }
- final int startOffset = JsonRpcHelpers.toOffset(cu.getOpenable(), args.start.getLine(),
- args.start.getCharacter());
- final int endOffset = JsonRpcHelpers.toOffset(cu.getOpenable(), args.end.getLine(),
- args.end.getCharacter());
- // the offsets point to the range of SimpleName
- ASTNode methodDeclaration = NodeFinder.perform(unit, startOffset, endOffset - startOffset, cu);
- while (!(methodDeclaration instanceof MethodDeclaration)) {
- methodDeclaration = methodDeclaration.getParent();
- }
- final List parameters = new LinkedList<>();
- for (final Object obj : ((MethodDeclaration) methodDeclaration).parameters()) {
- if (obj instanceof SingleVariableDeclaration) {
- final ITypeBinding paramTypeBinding = ((SingleVariableDeclaration) obj).getType().resolveBinding();
- if (paramTypeBinding.isParameterizedType()) {
- parameters.add(paramTypeBinding.getBinaryName());
- } else {
- parameters.add(paramTypeBinding.getQualifiedName());
- }
- }
- }
- if (parameters.size() > 0) {
- testName += String.format("(%s)", String.join(",", parameters));
- }
- }
- return testName;
- }
-
- private static void parseConfigurationInfoForContainer(TestInfo info, Argument args) throws URISyntaxException,
- JavaModelException {
- final IContainer[] targetContainers = ResourcesPlugin.getWorkspace().getRoot()
- .findContainersForLocationURI(new URI(args.uri));
- if (targetContainers == null || targetContainers.length == 0) {
- throw new RuntimeException("Cannot find resource containers from: " + args.uri);
- }
-
- // For multi-module scenario, findContainersForLocationURI API may return a container array,
- // need put the result from the nearest project in front.
- Arrays.sort(targetContainers, (Comparator) (IContainer a, IContainer b) -> {
- return a.getFullPath().toPortableString().length() - b.getFullPath().toPortableString().length();
- });
-
- IJavaElement targetElement = null;
- for (final IContainer container : targetContainers) {
- targetElement = JavaCore.create(container);
- if (targetElement != null) {
- final IJavaProject javaProject = targetElement.getJavaProject();
- if (javaProject == null) {
- continue;
- }
- info.project = javaProject.getProject();
- break;
- }
- }
-
- if (targetElement == null) {
- throw new RuntimeException("Cannot resolve valid element from: " + args.uri);
- }
-
- if (TestItemUtils.DEFAULT_PACKAGE_NAME.equals(args.fullName)) {
- if (targetElement instanceof IPackageFragmentRoot) {
- final IPackageFragmentRoot packageRoot = (IPackageFragmentRoot) targetElement;
- for (final IJavaElement child : packageRoot.getChildren()) {
- if (child instanceof IPackageFragment && ((IPackageFragment) child).isDefaultPackage()) {
- targetElement = (IPackageFragment) child;
- break;
- }
- }
- }
- }
- info.testContainer = StringEscapeUtils.escapeXml(targetElement.getHandleIdentifier());
- }
-
private static JUnitLaunchArguments resolveTestNGLaunchArguments(ILaunchConfiguration configuration,
IJavaProject javaProject, JUnitLaunchConfigurationDelegate delegate) throws CoreException {
final IRuntimeClasspathEntry[] unresolved = JavaRuntime.computeUnresolvedRuntimeClasspath(configuration);
@@ -311,14 +188,9 @@ private static String getEclipseTestKind(TestKind testKind) {
}
class Argument {
- public String uri;
- public String fullName;
- public String testName;
- public String project;
- public TestLevel scope;
+ public String projectName;
+ public TestLevel testLevel;
public TestKind testKind;
- public Position start;
- public Position end;
- public boolean isHierarchicalPackage;
+ public String[] testNames;
}
}
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/model/JavaTestItem.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/model/JavaTestItem.java
new file mode 100644
index 00000000..00e594c8
--- /dev/null
+++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/model/JavaTestItem.java
@@ -0,0 +1,174 @@
+/*******************************************************************************
+ * Copyright (c) 2021 Microsoft Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Microsoft Corporation - initial API and implementation
+ *******************************************************************************/
+
+package com.microsoft.java.test.plugin.model;
+
+import org.eclipse.lsp4j.Range;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class JavaTestItem {
+ private String id;
+
+ private String label;
+
+ private String fullName;
+
+ private List children;
+
+ private TestLevel testLevel;
+
+ private TestKind testKind;
+
+ private String projectName;
+
+ private String uri;
+
+ private Range range;
+
+ private String jdtHandler;
+
+ public JavaTestItem() {}
+
+ public JavaTestItem(String displayName, String fullName, String project, String uri,
+ Range range, TestLevel level, TestKind kind) {
+ this.label = displayName;
+ this.fullName = fullName;
+ this.testLevel = level;
+ this.testKind = kind;
+ this.projectName = project;
+ this.uri = uri;
+ this.range = range;
+
+ if (level.equals(TestLevel.PROJECT)) {
+ this.id = fullName;
+ } else {
+ this.id = project + "@" + fullName;
+ }
+ }
+
+ public Range getRange() {
+ return range;
+ }
+
+ public void setRange(Range range) {
+ this.range = range;
+ }
+
+ public String getUri() {
+ return uri;
+ }
+
+ public void setUri(String uri) {
+ this.uri = uri;
+ }
+
+ public String getJdtHandler() {
+ return jdtHandler;
+ }
+
+ public void setJdtHandler(String jdtHandler) {
+ this.jdtHandler = jdtHandler;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public String getFullName() {
+ return fullName;
+ }
+
+ public void setFullName(String fullName) {
+ this.fullName = fullName;
+ }
+
+ public List getChildren() {
+ return children;
+ }
+
+ public void setChildren(List children) {
+ this.children = children;
+ }
+
+ public TestLevel getTestLevel() {
+ return testLevel;
+ }
+
+ public void setTestLevel(TestLevel testLevel) {
+ this.testLevel = testLevel;
+ }
+
+ public TestKind getTestKind() {
+ return testKind;
+ }
+
+ public void setTestKind(TestKind testKind) {
+ this.testKind = testKind;
+ }
+
+ public String getProjectName() {
+ return projectName;
+ }
+
+ public void setProjectName(String projectName) {
+ this.projectName = projectName;
+ }
+
+ public void addChild(JavaTestItem child) {
+ if (this.children == null) {
+ this.children = new ArrayList<>();
+ }
+ this.children.add(child);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((id == null) ? 0 : id.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj == null) {
+ return false;
+ }
+
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final JavaTestItem other = (JavaTestItem) obj;
+ if (id == null) {
+ if (other.id != null) {
+
+ return false;
+ }
+ } else if (!id.equals(other.id)){
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/model/SearchTestItemParams.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/model/SearchTestItemParams.java
deleted file mode 100644
index d894de1a..00000000
--- a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/model/SearchTestItemParams.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*******************************************************************************
-* Copyright (c) 2018 Microsoft Corporation and others.
-* All rights reserved. This program and the accompanying materials
-* are made available under the terms of the Eclipse Public License v1.0
-* which accompanies this distribution, and is available at
-* http://www.eclipse.org/legal/epl-v10.html
-*
-* Contributors:
-* Microsoft Corporation - initial API and implementation
-*******************************************************************************/
-
-package com.microsoft.java.test.plugin.model;
-
-public class SearchTestItemParams {
- private TestLevel level;
-
- private String uri;
-
- private String fullName;
-
- private boolean isHierarchicalPackage;
-
- public TestLevel getLevel() {
- return level;
- }
-
- public boolean isHierarchicalPackage() {
- return isHierarchicalPackage;
- }
-
- public void setHierarchicalPackage(boolean isHierarchicalPackage) {
- this.isHierarchicalPackage = isHierarchicalPackage;
- }
-
- public void setLevel(TestLevel level) {
- this.level = level;
- }
-
- public String getUri() {
- return uri;
- }
-
- public void setUri(String uri) {
- this.uri = uri;
- }
-
- public String getFullName() {
- return fullName;
- }
-
- public void setFullName(String fullName) {
- this.fullName = fullName;
- }
-}
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/model/TestItem.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/model/TestItem.java
deleted file mode 100644
index 5150e67c..00000000
--- a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/model/TestItem.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2018 Microsoft Corporation and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Microsoft Corporation - initial API and implementation
- *******************************************************************************/
-
-package com.microsoft.java.test.plugin.model;
-
-import org.eclipse.lsp4j.Location;
-import org.eclipse.lsp4j.Range;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class TestItem {
- private String id;
-
- private String displayName;
-
- private String fullName;
-
- private List children;
-
- private TestLevel level;
-
- private TestKind kind;
-
- private String project;
-
- private Location location;
-
- public TestItem(String displayName, String fullName, String uri, String project,
- Range range, TestLevel level, TestKind kind) {
- this.displayName = displayName;
- this.fullName = fullName;
- this.level = level;
- this.kind = kind;
- this.project = project;
- this.location = new Location(uri, range);
- this.id = project + "@" + fullName;
- }
-
- public String getId() {
- return id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
- public String getDisplayName() {
- return displayName;
- }
-
- public void setDisplayName(String displayName) {
- this.displayName = displayName;
- }
-
- public String getFullName() {
- return fullName;
- }
-
- public void setFullName(String fullName) {
- this.fullName = fullName;
- }
-
- public List getChildren() {
- return children;
- }
-
- public void setChildren(List children) {
- this.children = children;
- }
-
- public TestLevel getLevel() {
- return level;
- }
-
- public void setLevel(TestLevel level) {
- this.level = level;
- }
-
- public TestKind getKind() {
- return kind;
- }
-
- public void setKind(TestKind kind) {
- this.kind = kind;
- }
-
- public String getProject() {
- return project;
- }
-
- public void setProject(String project) {
- this.project = project;
- }
-
- public void addChild(String child) {
- if (this.children == null) {
- this.children = new ArrayList<>();
- }
- this.children.add(child);
- }
-
- public Location getLocation() {
- return location;
- }
-
- public void setLocation(Location location) {
- this.location = location;
- }
-}
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/model/TestLevel.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/model/TestLevel.java
index 1d45f6b1..8a2ded0d 100644
--- a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/model/TestLevel.java
+++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/model/TestLevel.java
@@ -1,5 +1,5 @@
/*******************************************************************************
-* Copyright (c) 2018 Microsoft Corporation and others.
+* Copyright (c) 2018-2021 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -18,14 +18,41 @@ public enum TestLevel {
ROOT,
@SerializedName("1")
- FOLDER,
+ WORKSPACE,
@SerializedName("2")
- PACKAGE,
+ WORKSPACE_FOLDER,
@SerializedName("3")
- CLASS,
+ PROJECT,
@SerializedName("4")
+ PACKAGE,
+
+ @SerializedName("5")
+ CLASS,
+
+ @SerializedName("6")
METHOD;
+
+ public static TestLevel fromInteger(Integer i) {
+ switch(i) {
+ case 0:
+ return ROOT;
+ case 1:
+ return WORKSPACE;
+ case 2:
+ return WORKSPACE_FOLDER;
+ case 3:
+ return PROJECT;
+ case 4:
+ return PACKAGE;
+ case 5:
+ return CLASS;
+ case 6:
+ return METHOD;
+ default:
+ return null;
+ }
+ }
}
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/BaseFrameworkSearcher.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/BaseFrameworkSearcher.java
index c572e169..bab8eecc 100644
--- a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/BaseFrameworkSearcher.java
+++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/BaseFrameworkSearcher.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2018 Microsoft Corporation and others.
+ * Copyright (c) 2018-2021 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -11,16 +11,10 @@
package com.microsoft.java.test.plugin.searcher;
-import com.microsoft.java.test.plugin.model.TestItem;
import com.microsoft.java.test.plugin.model.TestKind;
-import com.microsoft.java.test.plugin.model.TestLevel;
import com.microsoft.java.test.plugin.util.TestFrameworkUtils;
-import com.microsoft.java.test.plugin.util.TestItemUtils;
-import org.eclipse.jdt.core.IMethod;
-import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.IAnnotationBinding;
-import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
public abstract class BaseFrameworkSearcher implements TestFrameworkSearcher {
@@ -47,10 +41,4 @@ public boolean findAnnotation(IAnnotationBinding[] annotations, String[] annotat
}
return false;
}
-
- @Override
- public TestItem parseTestItem(IMethodBinding methodBinding) throws JavaModelException {
- return TestItemUtils.constructTestItem((IMethod) methodBinding.getJavaElement(),
- TestLevel.METHOD, this.getTestKind());
- }
}
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/JUnit4TestSearcher.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/JUnit4TestSearcher.java
index ea1913bb..66c44e3f 100644
--- a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/JUnit4TestSearcher.java
+++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/JUnit4TestSearcher.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2017 Microsoft Corporation and others.
+ * Copyright (c) 2017-2021 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -11,13 +11,11 @@
package com.microsoft.java.test.plugin.searcher;
-import com.microsoft.java.test.plugin.model.TestItem;
import com.microsoft.java.test.plugin.model.TestKind;
-import com.microsoft.java.test.plugin.model.TestLevel;
-import com.microsoft.java.test.plugin.util.TestItemUtils;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
@@ -26,12 +24,9 @@
import org.eclipse.jdt.internal.junit.launcher.JUnit4TestFinder;
import org.eclipse.jdt.internal.junit.launcher.TestKindRegistry;
-import java.util.Arrays;
-import java.util.HashMap;
+import java.util.Collections;
import java.util.HashSet;
-import java.util.Map;
import java.util.Set;
-import java.util.stream.Collectors;
public class JUnit4TestSearcher extends BaseFrameworkSearcher {
@@ -72,20 +67,14 @@ public boolean isTestClass(IType type) throws JavaModelException {
}
@Override
- public TestItem[] findTestsInContainer(IJavaElement element, IProgressMonitor monitor) throws CoreException {
- final Map result = new HashMap<>();
+ public Set findTestItemsInContainer(IJavaElement element, IProgressMonitor monitor) throws CoreException {
final Set types = new HashSet<>();
- JUNIT4_TEST_FINDER.findTestsInContainer(element, types, monitor);
- for (final IType type : types) {
- final TestItem item = TestItemUtils.constructTestItem(type, TestLevel.CLASS, TestKind.JUnit);
- item.setChildren(Arrays.stream(type.getMethods())
- .map(m -> m.getJavaProject().getProject().getName() + "@" +
- TestItemUtils.parseTestItemFullName(m, TestLevel.METHOD))
- .collect(Collectors.toList())
- );
- result.put(item.getId(), item);
+ try {
+ JUNIT4_TEST_FINDER.findTestsInContainer(element, types, monitor);
+ } catch (OperationCanceledException e) {
+ return Collections.emptySet();
}
- return result.values().toArray(new TestItem[0]);
+ return types;
}
}
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/JUnit5TestSearcher.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/JUnit5TestSearcher.java
index d7b06302..9ae301e8 100644
--- a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/JUnit5TestSearcher.java
+++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/JUnit5TestSearcher.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2017 Microsoft Corporation and others.
+ * Copyright (c) 2017-2021 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -11,14 +11,12 @@
package com.microsoft.java.test.plugin.searcher;
-import com.microsoft.java.test.plugin.model.TestItem;
import com.microsoft.java.test.plugin.model.TestKind;
-import com.microsoft.java.test.plugin.model.TestLevel;
import com.microsoft.java.test.plugin.util.TestFrameworkUtils;
-import com.microsoft.java.test.plugin.util.TestItemUtils;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
@@ -29,12 +27,9 @@
import org.eclipse.jdt.internal.junit.launcher.JUnit5TestFinder;
import org.eclipse.jdt.internal.junit.launcher.TestKindRegistry;
-import java.util.Arrays;
-import java.util.HashMap;
+import java.util.Collections;
import java.util.HashSet;
-import java.util.Map;
import java.util.Set;
-import java.util.stream.Collectors;
public class JUnit5TestSearcher extends BaseFrameworkSearcher {
@@ -74,24 +69,6 @@ public boolean isTestMethod(IMethodBinding methodBinding) {
return this.findAnnotation(methodBinding.getAnnotations(), this.getTestMethodAnnotations());
}
- @Override
- public TestItem parseTestItem(IMethodBinding methodBinding) throws JavaModelException {
- final TestItem item = super.parseTestItem(methodBinding);
-
- // deal with @DisplayName
- for (final IAnnotationBinding annotation : methodBinding.getAnnotations()) {
- if (annotation == null) {
- continue;
- }
- if (matchesName(annotation.getAnnotationType(), DISPLAY_NAME_ANNOTATION_JUNIT5)) {
- item.setDisplayName((String) annotation.getAllMemberValuePairs()[0].getValue());
- break;
- }
- }
-
- return item;
- }
-
@Override
public boolean findAnnotation(IAnnotationBinding[] annotations, String[] annotationNames) {
for (final IAnnotationBinding annotation : annotations) {
@@ -119,24 +96,6 @@ public boolean isTestClass(IType type) throws JavaModelException {
return JUNIT5_TEST_FINDER.isTest(type);
}
- @Override
- public TestItem[] findTestsInContainer(IJavaElement element, IProgressMonitor monitor) throws CoreException {
- final Map result = new HashMap<>();
- final Set types = new HashSet<>();
- JUNIT5_TEST_FINDER.findTestsInContainer(element, types, monitor);
- for (final IType type : types) {
- final TestItem item = TestItemUtils.constructTestItem(type, TestLevel.CLASS, TestKind.JUnit5);
- item.setChildren(Arrays.stream(type.getMethods())
- .map(m ->m.getJavaProject().getProject().getName() + "@" +
- TestItemUtils.parseTestItemFullName(m, TestLevel.METHOD))
- .collect(Collectors.toList())
- );
- result.put(item.getId(), item);
- }
-
- return result.values().toArray(new TestItem[0]);
- }
-
private boolean matchesName(ITypeBinding annotationType, String annotationName) {
return TestFrameworkUtils.isEquivalentAnnotationType(annotationType, annotationName);
}
@@ -163,4 +122,15 @@ private boolean matchesNameInAnnotationHierarchy(IAnnotationBinding annotation,
return false;
}
+
+ @Override
+ public Set findTestItemsInContainer(IJavaElement element, IProgressMonitor monitor) throws CoreException {
+ final Set types = new HashSet<>();
+ try {
+ JUNIT5_TEST_FINDER.findTestsInContainer(element, types, monitor);
+ } catch (OperationCanceledException e) {
+ return Collections.emptySet();
+ }
+ return types;
+ }
}
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/TestFrameworkSearcher.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/TestFrameworkSearcher.java
index ee0391a2..914e7bcd 100644
--- a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/TestFrameworkSearcher.java
+++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/TestFrameworkSearcher.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2017 Microsoft Corporation and others.
+ * Copyright (c) 2017-2021 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -11,7 +11,6 @@
package com.microsoft.java.test.plugin.searcher;
-import com.microsoft.java.test.plugin.model.TestItem;
import com.microsoft.java.test.plugin.model.TestKind;
import org.eclipse.core.runtime.CoreException;
@@ -22,6 +21,8 @@
import org.eclipse.jdt.core.dom.IAnnotationBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
+import java.util.Set;
+
public interface TestFrameworkSearcher {
TestKind getTestKind();
@@ -36,7 +37,5 @@ public interface TestFrameworkSearcher {
boolean findAnnotation(IAnnotationBinding[] annotations, String[] annotationNames);
- TestItem parseTestItem(IMethodBinding methodBinding) throws JavaModelException;
-
- TestItem[] findTestsInContainer(IJavaElement element, IProgressMonitor monitor) throws CoreException;
+ Set findTestItemsInContainer(IJavaElement element, IProgressMonitor monitor) throws CoreException;
}
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/TestNGTestSearcher.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/TestNGTestSearcher.java
index 5e986c7f..78208349 100644
--- a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/TestNGTestSearcher.java
+++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/TestNGTestSearcher.java
@@ -1,5 +1,5 @@
/*******************************************************************************
-* Copyright (c) 2018 Microsoft Corporation and others.
+* Copyright (c) 2018-2021 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -11,14 +11,12 @@
package com.microsoft.java.test.plugin.searcher;
-import com.microsoft.java.test.plugin.model.TestItem;
import com.microsoft.java.test.plugin.model.TestKind;
-import com.microsoft.java.test.plugin.model.TestLevel;
-import com.microsoft.java.test.plugin.util.TestItemUtils;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IJavaElement;
@@ -49,13 +47,10 @@
import org.eclipse.jdt.core.search.SearchRequestor;
import org.eclipse.jdt.internal.junit.util.CoreTestSearchEngine;
-import java.util.Arrays;
import java.util.Collection;
-import java.util.HashMap;
+import java.util.Collections;
import java.util.HashSet;
-import java.util.Map;
import java.util.Set;
-import java.util.stream.Collectors;
public class TestNGTestSearcher extends BaseFrameworkSearcher {
@@ -171,25 +166,6 @@ private boolean annotates(final IAnnotationBinding[] annotations, final String q
return false;
}
- @Override
- public TestItem[] findTestsInContainer(final IJavaElement element, final IProgressMonitor monitor)
- throws CoreException {
- final Map result = new HashMap<>();
- final Set types = new HashSet<>();
- findTestsInContainer(element, types, monitor);
- for (final IType type : types) {
- final TestItem item = TestItemUtils.constructTestItem(type, TestLevel.CLASS, TestKind.TestNG);
- item.setChildren(
- Arrays.stream(type.getMethods())
- .map(m -> m.getJavaProject().getProject().getName() + "@" +
- TestItemUtils.parseTestItemFullName(m, TestLevel.METHOD))
- .collect(Collectors.toList()));
- result.put(item.getId(), item);
- }
-
- return result.values().toArray(new TestItem[0]);
- }
-
/*
* (non-Javadoc)
*
@@ -240,6 +216,17 @@ private void findTestsInContainer(final IJavaElement element, final Set result,
pm.done();
}
}
+
+ @Override
+ public Set findTestItemsInContainer(IJavaElement element, IProgressMonitor monitor) throws CoreException {
+ final Set types = new HashSet<>();
+ try {
+ this.findTestsInContainer(element, types, monitor);
+ } catch (OperationCanceledException e) {
+ return Collections.emptySet();
+ }
+ return types;
+ }
}
/*
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/util/ProjectTestUtils.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/util/ProjectTestUtils.java
index a67e5263..bd7aea24 100644
--- a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/util/ProjectTestUtils.java
+++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/util/ProjectTestUtils.java
@@ -1,5 +1,5 @@
/*******************************************************************************
-* Copyright (c) 2017 Microsoft Corporation and others.
+* Copyright (c) 2017-2021 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -64,14 +64,39 @@ public static List listTestSourcePaths(List arguments, I
return resultList;
}
- public static List getTestSourcePaths(IJavaProject project) throws JavaModelException {
- final List paths = new LinkedList<>();
+ public static List getTestEntries(IJavaProject project) throws JavaModelException {
+ // Ignore default project
+ if (ProjectsManager.DEFAULT_PROJECT_NAME.equals(project.getProject().getName())) {
+ return Collections.emptyList();
+ }
+
+ final List entries = new LinkedList<>();
for (final IClasspathEntry entry : project.getRawClasspath()) {
- // Ignore default project
- if (ProjectsManager.DEFAULT_PROJECT_NAME.equals(project.getProject().getName())) {
+ if (entry.getEntryKind() != ClasspathEntry.CPE_SOURCE) {
continue;
}
-
+
+ if (isTestEntry(entry)) {
+ entries.add(entry);
+ continue;
+ }
+
+ // Always return true Eclipse & invisible project
+ if (ProjectUtils.isGeneralJavaProject(project.getProject())) {
+ entries.add(entry);
+ }
+ }
+ return entries;
+ }
+
+ public static List getTestSourcePaths(IJavaProject project) throws JavaModelException {
+ // Ignore default project
+ if (ProjectsManager.DEFAULT_PROJECT_NAME.equals(project.getProject().getName())) {
+ return Collections.emptyList();
+ }
+
+ final List paths = new LinkedList<>();
+ for (final IClasspathEntry entry : project.getRawClasspath()) {
if (entry.getEntryKind() != ClasspathEntry.CPE_SOURCE) {
continue;
}
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/util/TestFrameworkUtils.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/util/TestFrameworkUtils.java
index 98c2d201..00cc1223 100644
--- a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/util/TestFrameworkUtils.java
+++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/util/TestFrameworkUtils.java
@@ -11,24 +11,14 @@
package com.microsoft.java.test.plugin.util;
-import com.microsoft.java.test.plugin.model.TestItem;
import com.microsoft.java.test.plugin.model.TestKind;
-import com.microsoft.java.test.plugin.model.TestLevel;
import com.microsoft.java.test.plugin.searcher.JUnit4TestSearcher;
import com.microsoft.java.test.plugin.searcher.JUnit5TestSearcher;
import com.microsoft.java.test.plugin.searcher.TestFrameworkSearcher;
import com.microsoft.java.test.plugin.searcher.TestNGTestSearcher;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.jdt.core.IType;
-import org.eclipse.jdt.core.JavaModelException;
-import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
-import org.eclipse.jdt.internal.junit.util.CoreTestSearchEngine;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Objects;
public class TestFrameworkUtils {
@@ -37,70 +27,20 @@ public class TestFrameworkUtils {
public static final TestFrameworkSearcher JUNIT5_TEST_SEARCHER = new JUnit5TestSearcher();
public static final TestFrameworkSearcher TESTNG_TEST_SEARCHER = new TestNGTestSearcher();
- public static final TestFrameworkSearcher[] FRAMEWORK_SEARCHERS = new TestFrameworkSearcher[] {
- JUNIT4_TEST_SEARCHER, JUNIT5_TEST_SEARCHER, TESTNG_TEST_SEARCHER };
-
- public static void findTestItemsInTypeBinding(ITypeBinding typeBinding, List result,
- TestItem parentClassTestItem, IProgressMonitor monitor) throws JavaModelException {
- if (monitor.isCanceled()) {
- return;
- }
-
- final List searchers = new ArrayList<>();
- final IType type = (IType) typeBinding.getJavaElement();
- for (final TestFrameworkSearcher searcher : FRAMEWORK_SEARCHERS) {
- if (CoreTestSearchEngine.isAccessibleClass(type, searcher.getJdtTestKind())) {
- searchers.add(searcher);
- }
- }
-
- if (searchers.size() == 0) {
- return;
- }
-
- final List testMethods = new LinkedList<>();
- final List testMethodIds = new LinkedList<>();
- for (final IMethodBinding methodBinding : typeBinding.getDeclaredMethods()) {
- for (final TestFrameworkSearcher searcher : searchers) {
- if (searcher.isTestMethod(methodBinding)) {
- final TestItem methodItem = searcher.parseTestItem(methodBinding);
- testMethods.add(methodItem);
- testMethodIds.add(methodItem.getId());
- break;
- }
- }
- }
- TestItem classItem = null;
- if (testMethods.size() > 0) {
- result.addAll(testMethods);
- classItem = TestItemUtils.constructTestItem((IType) typeBinding.getJavaElement(),
- TestLevel.CLASS);
- classItem.setChildren(testMethodIds);
- classItem.setKind(testMethods.get(0).getKind());
- result.add(classItem);
- } else {
- if (JUNIT4_TEST_SEARCHER.isTestClass(type)) {
- // to handle @RunWith classes
- classItem = TestItemUtils.constructTestItem(type, TestLevel.CLASS, TestKind.JUnit);
- result.add(classItem);
- } else if (JUNIT5_TEST_SEARCHER.isTestClass(type)) {
- // to handle @Nested and @Testable classes
- classItem = TestItemUtils.constructTestItem(type, TestLevel.CLASS, TestKind.JUnit5);
- result.add(classItem);
- }
- }
-
- // set the class item as the child of its declaring type
- if (classItem != null && parentClassTestItem != null) {
- parentClassTestItem.addChild(classItem.getId());
- }
-
- for (final ITypeBinding childTypeBinding : typeBinding.getDeclaredTypes()) {
- findTestItemsInTypeBinding(childTypeBinding, result, classItem, monitor);
- }
- }
-
public static boolean isEquivalentAnnotationType(ITypeBinding annotationType, String annotationName) {
return annotationType != null && Objects.equals(annotationType.getQualifiedName(), annotationName);
}
+
+ public static TestFrameworkSearcher getSearcherByTestKind(TestKind kind) {
+ switch (kind) {
+ case JUnit:
+ return JUNIT4_TEST_SEARCHER;
+ case JUnit5:
+ return JUNIT5_TEST_SEARCHER;
+ case TestNG:
+ return TESTNG_TEST_SEARCHER;
+ default:
+ return null;
+ }
+ }
}
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/util/TestItemUtils.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/util/TestItemUtils.java
index d8f51512..e279141a 100644
--- a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/util/TestItemUtils.java
+++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/util/TestItemUtils.java
@@ -1,5 +1,5 @@
/*******************************************************************************
-* Copyright (c) 2018 Microsoft Corporation and others.
+* Copyright (c) 2018-2021 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -11,7 +11,7 @@
package com.microsoft.java.test.plugin.util;
-import com.microsoft.java.test.plugin.model.TestItem;
+import com.microsoft.java.test.plugin.model.JavaTestItem;
import com.microsoft.java.test.plugin.model.TestKind;
import com.microsoft.java.test.plugin.model.TestLevel;
@@ -22,7 +22,9 @@
import org.eclipse.jdt.core.ISourceReference;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.SourceRange;
import org.eclipse.jdt.ls.core.internal.JDTUtils;
+import org.eclipse.jdt.ls.core.internal.hover.JavaElementLabels;
import org.eclipse.lsp4j.Range;
@SuppressWarnings("restriction")
@@ -30,39 +32,42 @@ public class TestItemUtils {
public static final String DEFAULT_PACKAGE_NAME = "";
- public static TestItem constructTestItem(IJavaElement element, TestLevel level) throws JavaModelException {
- return constructTestItem(element, level, null);
- }
-
- public static TestItem constructTestItem(IJavaElement element, TestLevel level, TestKind kind)
+ public static JavaTestItem constructJavaTestItem(IJavaElement element, TestLevel level, TestKind kind)
throws JavaModelException {
final String displayName;
- final String fullName;
if (element instanceof IPackageFragment && ((IPackageFragment) element).isDefaultPackage()) {
displayName = DEFAULT_PACKAGE_NAME;
- fullName = DEFAULT_PACKAGE_NAME;
} else {
- displayName = element.getElementName();
- fullName = parseTestItemFullName(element, level);
+ displayName = JavaElementLabels.getElementLabel(element, JavaElementLabels.ALL_DEFAULT);
}
+ final String fullName = parseFullName(element, level);
final String uri = JDTUtils.getFileURI(element.getResource());
- final Range range = parseTestItemRange(element);
+ Range range = null;
+ if (level == TestLevel.CLASS || level == TestLevel.METHOD) {
+ range = parseTestItemRange(element);
+ }
+
final String projectName = element.getJavaProject().getProject().getName();
- return new TestItem(displayName, fullName, uri, projectName, range, level, kind);
+ final JavaTestItem result = new JavaTestItem(displayName, fullName, projectName, uri, range, level, kind);
+ result.setJdtHandler(element.getHandleIdentifier());
+ return result;
}
public static Range parseTestItemRange(IJavaElement element) throws JavaModelException {
if (element instanceof ISourceReference) {
- // getSourceRange() is not used here because we want the Code Lens appear above the
- // method name, not above the annotation.
- final ISourceRange range = ((ISourceReference) element).getNameRange();
- return JDTUtils.toRange(element.getOpenable(), range.getOffset(), range.getLength());
+ final ISourceRange sourceRange = ((ISourceReference) element).getSourceRange();
+ final ISourceRange nameRange = ((ISourceReference) element).getNameRange();
+ // get the code range excluding the comment part
+ if (SourceRange.isAvailable(sourceRange) && SourceRange.isAvailable(nameRange)) {
+ return JDTUtils.toRange(element.getOpenable(), nameRange.getOffset(),
+ sourceRange.getLength() - nameRange.getOffset() + sourceRange.getOffset());
+ }
}
- return new Range();
+ return null;
}
- public static String parseTestItemFullName(IJavaElement element, TestLevel level) {
+ public static String parseFullName(IJavaElement element, TestLevel level) {
switch (level) {
case CLASS:
final IType type = (IType) element;
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/util/TestSearchUtils.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/util/TestSearchUtils.java
index 4e8ed725..81acdf48 100644
--- a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/util/TestSearchUtils.java
+++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/util/TestSearchUtils.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2018 Microsoft Corporation and others.
+ * Copyright (c) 2021 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -11,21 +11,23 @@
package com.microsoft.java.test.plugin.util;
-import com.google.gson.Gson;
-import com.microsoft.java.test.plugin.model.SearchTestItemParams;
-import com.microsoft.java.test.plugin.model.TestItem;
+import com.microsoft.java.test.plugin.model.JavaTestItem;
+import com.microsoft.java.test.plugin.model.TestKind;
import com.microsoft.java.test.plugin.model.TestLevel;
+import com.microsoft.java.test.plugin.provider.TestKindProvider;
import com.microsoft.java.test.plugin.searcher.TestFrameworkSearcher;
-import org.eclipse.core.resources.IFolder;
-import org.eclipse.core.resources.ResourcesPlugin;
+import org.apache.commons.lang3.StringUtils;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaModel;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragment;
@@ -33,375 +35,541 @@
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.manipulation.CoreASTProvider;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
-import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
-import org.eclipse.jdt.core.search.SearchMatch;
-import org.eclipse.jdt.core.search.SearchParticipant;
import org.eclipse.jdt.core.search.SearchPattern;
-import org.eclipse.jdt.core.search.SearchRequestor;
-import org.eclipse.jdt.internal.corext.dom.IASTSharedValues;
+import org.eclipse.jdt.core.search.TypeNameMatch;
+import org.eclipse.jdt.core.search.TypeNameMatchRequestor;
+import org.eclipse.jdt.internal.junit.util.CoreTestSearchEngine;
import org.eclipse.jdt.ls.core.internal.JDTUtils;
+import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
import org.eclipse.jdt.ls.core.internal.ProjectUtils;
+import org.eclipse.jdt.ls.core.internal.ResourceUtils;
import org.eclipse.jdt.ls.core.internal.handlers.DocumentLifeCycleHandler;
-import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager;
import org.eclipse.lsp4j.Location;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
-import java.util.stream.Stream;
+import java.util.stream.Collectors;
@SuppressWarnings("restriction")
public class TestSearchUtils {
/**
- * Method to search the Code Lenses
- *
- * @param arguments contains the URI of the file to search the Code Lens.
- * @param monitor
- * @throws OperationCanceledException
- * @throws InterruptedException
- * @throws JavaModelException
+ * List all the Java Projects in the given workspace folder
*/
- public static List searchCodeLens(List arguments, IProgressMonitor monitor)
- throws OperationCanceledException, InterruptedException, JavaModelException {
- final List resultList = new LinkedList<>();
+ public static List findJavaProjects(List arguments, IProgressMonitor monitor) {
if (arguments == null || arguments.size() == 0) {
- return resultList;
+ return Collections.emptyList();
}
-
- final String uri = (String) arguments.get(0);
-
- // wait for the LS finishing updating
- Job.getJobManager().join(DocumentLifeCycleHandler.DOCUMENT_LIFE_CYCLE_JOBS, monitor);
-
- final ICompilationUnit unit = JDTUtils.resolveCompilationUnit(uri);
- final IType primaryType = unit.findPrimaryType();
- if (!isJavaElementExist(unit) || primaryType == null || monitor.isCanceled()) {
- return resultList;
+ final String workspaceFolderUri = (String) arguments.get(0);
+ final IPath workspaceFolderPath = ResourceUtils.filePathFromURI(workspaceFolderUri);
+ if (workspaceFolderPath == null) {
+ JUnitPlugin.logError("Failed to parse workspace folder path from uri: " + workspaceFolderUri);
+ // todo: handle non-file scheme
+ return Collections.emptyList();
}
-
- final CompilationUnit root = (CompilationUnit) parseToAst(unit, true /* fromCache */, monitor);
- if (root == null) {
- return resultList;
+ final List javaProjects = new LinkedList<>();
+ for (final IJavaProject project : ProjectUtils.getJavaProjects()) {
+ if (monitor != null && monitor.isCanceled()) {
+ return Collections.emptyList();
+ }
+ if (project.getProject().equals(JavaLanguageServerPlugin.getProjectsManager().getDefaultProject())) {
+ continue;
+ }
+ javaProjects.add(project);
}
- final ASTNode node = root.findDeclaringNode(primaryType.getKey());
- if (!(node instanceof TypeDeclaration)) {
- return resultList;
+ final List resultList = new LinkedList<>();
+ for (final IJavaProject project : javaProjects) {
+ try {
+ resultList.add(TestItemUtils.constructJavaTestItem(project, TestLevel.PROJECT, TestKind.None));
+ } catch (JavaModelException e) {
+ JUnitPlugin.logError("Failed to parse project item: " + project.getElementName());
+ }
}
+
+ return resultList;
+ }
- final ITypeBinding binding = ((TypeDeclaration) node).resolveBinding();
- if (binding == null) {
- return resultList;
+ /**
+ * Return a list of test packages and types in the given project. The returned list has the following hierarchy:
+ * Package A
+ * ├── Class A
+ * ├── Nested Class A
+ * ├── Class B
+ * Package B
+ * ├── ...
+ *
+ * @param arguments argument list which contains the JDT handler ID of a Java project
+ * @param monitor monitor
+ * @throws CoreException
+ */
+ public static List findTestPackagesAndTypes(List arguments, IProgressMonitor monitor)
+ throws CoreException {
+ final String handlerId = (String) arguments.get(0);
+ final IJavaElement element = JavaCore.create(handlerId);
+ if (!(element instanceof IJavaProject)) {
+ JUnitPlugin.logError("failed to parse IJavaProject from JDT handler ID: " + handlerId);
+ return Collections.emptyList();
}
+ final IJavaProject javaProject = (IJavaProject) element;
+ final Map testItemMapping = new HashMap<>();
+ final List testKinds = TestKindProvider.getTestKindsFromCache(javaProject);
+ for (final TestKind kind : testKinds) {
+ if (monitor != null && monitor.isCanceled()) {
+ return Collections.emptyList();
+ }
+ final TestFrameworkSearcher searcher = TestFrameworkUtils.getSearcherByTestKind(kind);
+ final Set testTypes = new HashSet<>();
+ final List testEntries = ProjectTestUtils.getTestEntries(javaProject);
+ for (final IClasspathEntry entry : testEntries) {
+ final IPackageFragmentRoot[] packageRoots = javaProject.findPackageFragmentRoots(entry);
+ for (final IPackageFragmentRoot root : packageRoots) {
+ try {
+ testTypes.addAll(searcher.findTestItemsInContainer(root, monitor));
+ } catch (CoreException e) {
+ JUnitPlugin.logException("failed to search tests in: " + root.getElementName(), e);
+ }
+ }
+ }
- TestFrameworkUtils.findTestItemsInTypeBinding(binding, resultList, null /* parentClassItem */, monitor);
-
- return resultList;
- }
+ for (final IType type : testTypes) {
+ JavaTestItem classItem = testItemMapping.get(type.getHandleIdentifier());
+ if (classItem == null) {
+ classItem = TestItemUtils.constructJavaTestItem(
+ type, TestLevel.CLASS, kind);
+ testItemMapping.put(classItem.getJdtHandler(), classItem);
+ } else {
+ // 1. We suppose a class can only use one test framework
+ // 2. If more accurate kind is available, use it.
+ if (classItem.getTestKind() == TestKind.JUnit5 && kind == TestKind.JUnit) {
+ classItem.setTestKind(TestKind.JUnit);
+ }
+ }
- public static ASTNode parseToAst(final ICompilationUnit unit, final boolean fromCache,
- final IProgressMonitor monitor) {
- if (fromCache) {
- final CompilationUnit astRoot = CoreASTProvider.getInstance().getAST(unit, CoreASTProvider.WAIT_YES,
- monitor);
- if (astRoot != null) {
- return astRoot;
+ final IType declaringType = type.getDeclaringType();
+ if (declaringType == null) {
+ // it's a root type, we find its declaring package
+ final IPackageFragment packageFragment = type.getPackageFragment();
+ final String packageIdentifier = packageFragment.getHandleIdentifier();
+ JavaTestItem packageItem = testItemMapping.get(packageIdentifier);
+ if (packageItem == null) {
+ packageItem = TestItemUtils.constructJavaTestItem(
+ packageFragment, TestLevel.PACKAGE, TestKind.None);
+ testItemMapping.put(packageIdentifier, packageItem);
+ }
+ if (packageItem.getChildren() == null || !packageItem.getChildren().contains(classItem)) {
+ packageItem.addChild(classItem);
+ }
+ } else {
+ final String declaringTypeIdentifier = declaringType.getHandleIdentifier();
+ JavaTestItem declaringTypeItem = testItemMapping.get(declaringTypeIdentifier);
+ if (declaringTypeItem == null) {
+ declaringTypeItem = TestItemUtils.constructJavaTestItem(
+ declaringType, TestLevel.CLASS, kind);
+ testItemMapping.put(declaringTypeIdentifier, declaringTypeItem);
+ }
+ if (declaringTypeItem.getChildren() == null ||
+ !declaringTypeItem.getChildren().contains(classItem)) {
+ declaringTypeItem.addChild(classItem);
+ }
+ }
}
}
- if (monitor.isCanceled()) {
- return null;
+ final List result = new LinkedList<>();
+ for (final JavaTestItem item : testItemMapping.values()) {
+ if (item.getTestLevel() == TestLevel.PACKAGE) {
+ result.add(item);
+ }
}
- final ASTParser parser = ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL);
- parser.setSource(unit);
- parser.setFocalPosition(0);
- parser.setResolveBindings(true);
- parser.setIgnoreMethodBodies(true);
- return parser.createAST(monitor);
+ return result;
}
/**
- * Method to expand the node in Test Explorer
- *
- * @param arguments {@link com.microsoft.java.test.plugin.model.SearchTestItemParams}
+ * Find the direct declared testable class and method for a given class
+ * @param arguments
* @param monitor
+ * @return
+ * @throws JavaModelException
* @throws OperationCanceledException
* @throws InterruptedException
- * @throws URISyntaxException
- * @throws JavaModelException
*/
- public static List searchTestItems(final List arguments, final IProgressMonitor monitor)
- throws OperationCanceledException, InterruptedException, URISyntaxException, JavaModelException {
- final List resultList = new LinkedList<>();
+ public static List findDirectTestChildrenForClass(List arguments, IProgressMonitor monitor)
+ throws JavaModelException, OperationCanceledException, InterruptedException {
+ final String handlerId = (String) arguments.get(0);
- if (arguments == null || arguments.size() == 0) {
- return resultList;
+ // wait for the LS finishing updating
+ Job.getJobManager().join(DocumentLifeCycleHandler.DOCUMENT_LIFE_CYCLE_JOBS, monitor);
+
+ final IType testType = (IType) JavaCore.create(handlerId);
+ if (testType == null) {
+ return Collections.emptyList();
}
- final Gson gson = new Gson();
- final SearchTestItemParams params = gson.fromJson((String) arguments.get(0), SearchTestItemParams.class);
- switch (params.getLevel()) {
- case FOLDER:
- searchInFolder(resultList, params);
- break;
- case PACKAGE:
- searchInPackage(resultList, params);
- break;
- case CLASS:
- searchInClass(resultList, params, monitor);
- break;
- default:
- break;
+ final ICompilationUnit unit = testType.getCompilationUnit();
+ if (unit == null) {
+ return Collections.emptyList();
}
+ final List testKinds = TestKindProvider.getTestKindsFromCache(unit.getJavaProject());
- return resultList;
+ final List result = new LinkedList<>();
+ final CompilationUnit root = (CompilationUnit) parseToAst(unit, true /* fromCache */, monitor);
+ for (final IType type : unit.getAllTypes()) {
+ if (monitor != null && monitor.isCanceled()) {
+ return result;
+ }
+
+ final IType declaringType = type.getDeclaringType();
+ if (declaringType != null &&
+ declaringType.getFullyQualifiedName().equals(testType.getFullyQualifiedName())) {
+ for (final TestKind kind: testKinds) {
+ final TestFrameworkSearcher searcher = TestFrameworkUtils.getSearcherByTestKind(kind);
+ if (searcher.isTestClass(type)) {
+ result.add(TestItemUtils.constructJavaTestItem(type, TestLevel.CLASS, kind));
+ break;
+ }
+ }
+ continue;
+ }
+
+ if (!type.getFullyQualifiedName().equals(testType.getFullyQualifiedName())) {
+ continue;
+ }
+
+ final ASTNode node = root.findDeclaringNode(type.getKey());
+ if (!(node instanceof TypeDeclaration)) {
+ continue;
+ }
+
+ final ITypeBinding binding = ((TypeDeclaration) node).resolveBinding();
+ if (binding == null) {
+ continue;
+ }
+
+
+ for (final IMethodBinding methodBinding : binding.getDeclaredMethods()) {
+ for (final TestKind kind: testKinds) {
+ final TestFrameworkSearcher searcher = TestFrameworkUtils.getSearcherByTestKind(kind);
+ if (searcher.isTestMethod(methodBinding)) {
+ result.add(TestItemUtils.constructJavaTestItem(
+ (IMethod) methodBinding.getJavaElement(),
+ TestLevel.METHOD,
+ kind
+ ));
+ }
+ }
+ }
+ }
+
+ return result;
}
/**
- * Method to get all the test items when running tests from Test Explorer
- *
- * @param arguments {@link com.microsoft.java.test.plugin.model.SearchTestItemParams}
- * @param monitor
+ * Get all the test types and methods is the given file
+ * @param arguments Contains the target file's uri
+ * @param monitor Progress monitor
* @throws CoreException
+ * @throws OperationCanceledException
* @throws InterruptedException
- * @throws URISyntaxException
*/
- public static List searchAllTestItems(List arguments, IProgressMonitor monitor)
- throws CoreException, InterruptedException, URISyntaxException {
- if (arguments == null || arguments.size() == 0) {
+ public static List findTestTypesAndMethods(List arguments, IProgressMonitor monitor)
+ throws CoreException, OperationCanceledException, InterruptedException {
+ // todo: This method is somehow duplicated with findDirectTestChildrenForClass,
+ // considering merge them in the future.
+ final String uriString = (String) arguments.get(0);
+
+ // wait for the LS finishing updating
+ Job.getJobManager().join(DocumentLifeCycleHandler.DOCUMENT_LIFE_CYCLE_JOBS, monitor);
+
+ final ICompilationUnit unit = JDTUtils.resolveCompilationUnit(uriString);
+ if (unit == null) {
return Collections.emptyList();
}
- final Gson gson = new Gson();
- final SearchTestItemParams params = gson.fromJson((String) arguments.get(0), SearchTestItemParams.class);
+ final IType primaryType = unit.findPrimaryType();
+ if (primaryType == null) {
+ return Collections.emptyList();
+ }
- if (params.getLevel() == TestLevel.METHOD) {
- // unreachable code since the client will directly run the test when it's triggered from method level
- throw new UnsupportedOperationException("Method level execution is not supported at server side.");
- } else if (params.getLevel() == TestLevel.CLASS) {
- final IJavaElement[] elements = getJavaElementForSearch(params);
- if (elements == null) {
- return Collections.emptyList();
- }
- final IType type = (IType) elements[0];
- TestItem[] testItems = null;
- if (TestFrameworkUtils.JUNIT4_TEST_SEARCHER.isTestClass(type)) {
- testItems = TestFrameworkUtils.JUNIT4_TEST_SEARCHER.findTestsInContainer(type, monitor);
- } else if (TestFrameworkUtils.JUNIT5_TEST_SEARCHER.isTestClass(type)) {
- testItems = TestFrameworkUtils.JUNIT5_TEST_SEARCHER.findTestsInContainer(type, monitor);
- } else if (TestFrameworkUtils.TESTNG_TEST_SEARCHER.isTestClass(type)) {
- testItems = TestFrameworkUtils.TESTNG_TEST_SEARCHER.findTestsInContainer(type, monitor);
- }
+ final CompilationUnit root = (CompilationUnit) parseToAst(unit, true /* fromCache */, monitor);
+ if (root == null) {
+ return Collections.emptyList();
+ }
- if (testItems != null) {
- return Arrays.asList(testItems);
+ final List testKinds = TestKindProvider.getTestKindsFromCache(unit.getJavaProject());
+ final List searchers = new LinkedList<>();
+ for (final TestKind kind : testKinds) {
+ final TestFrameworkSearcher searcher = TestFrameworkUtils.getSearcherByTestKind(kind);
+ if (searcher != null) {
+ searchers.add(searcher);
}
+ }
+
+ if (searchers.size() == 0) {
+ Collections.emptyList();
+ }
+ final ASTNode node = root.findDeclaringNode(primaryType.getKey());
+ if (!(node instanceof TypeDeclaration)) {
return Collections.emptyList();
- } else {
- final IJavaElement[] elements = getJavaElementForSearch(params);
- final Map> javaProjectMapping = new HashMap<>();
- final Set javaProjects = new HashSet<>();
- for (final IJavaElement element : elements) {
- javaProjects.add(element.getJavaProject());
- }
+ }
- final List searchers = new LinkedList<>();
- for (final IJavaProject javaProject : javaProjects) {
+ final ITypeBinding binding = ((TypeDeclaration) node).resolveBinding();
+ if (binding == null) {
+ return Collections.emptyList();
+ }
- // We don't check JUnit 4 when JUnit 5 is available since it's backward compatible
- if (javaProject.findType("org.junit.jupiter.api.Test") != null) {
- searchers.add(TestFrameworkUtils.JUNIT5_TEST_SEARCHER);
- } else if (javaProject.findType("org.junit.Test") != null) {
- searchers.add(TestFrameworkUtils.JUNIT4_TEST_SEARCHER);
- }
+ final JavaTestItem fakeRoot = new JavaTestItem();
+ findTestItemsInTypeBinding(binding, fakeRoot, searchers, monitor);
+ return fakeRoot.getChildren();
+ }
- if (javaProject.findType("org.testng.annotations.Test") != null) {
- searchers.add(TestFrameworkUtils.TESTNG_TEST_SEARCHER);
+ private static void findTestItemsInTypeBinding(ITypeBinding typeBinding, JavaTestItem parentItem,
+ List searchers, IProgressMonitor monitor) throws JavaModelException {
+ if (monitor.isCanceled()) {
+ return;
+ }
+
+ final IType type = (IType) typeBinding.getJavaElement();
+ final List testMethods = new LinkedList<>();
+ searchers = searchers.stream().filter(s -> {
+ try {
+ return CoreTestSearchEngine.isAccessibleClass(type, s.getJdtTestKind());
+ } catch (JavaModelException e) {
+ return false;
+ }
+ }).collect(Collectors.toList());
+
+ for (final IMethodBinding methodBinding : typeBinding.getDeclaredMethods()) {
+ for (final TestFrameworkSearcher searcher : searchers) {
+ if (searcher.isTestMethod(methodBinding)) {
+ final JavaTestItem methodItem = TestItemUtils.constructJavaTestItem(
+ (IMethod) methodBinding.getJavaElement(),
+ TestLevel.METHOD,
+ searcher.getTestKind()
+ );
+ testMethods.add(methodItem);
+ break;
}
- javaProjectMapping.put(javaProject, searchers);
}
+ }
- final Map map = new HashMap<>();
- for (final IJavaElement element : elements) {
- for (final TestFrameworkSearcher searcher : javaProjectMapping.get(element.getJavaProject())) {
- final TestItem[] items = searcher.findTestsInContainer(element, monitor);
- Arrays.stream(items).forEach(item -> {
- map.put(item.getId(), item);
- });
- }
+ JavaTestItem classItem = null;
+ if (testMethods.size() > 0) {
+ classItem = TestItemUtils.constructJavaTestItem(type, TestLevel.CLASS, testMethods.get(0).getTestKind());
+ classItem.setChildren(testMethods);
+ } else {
+ if (TestFrameworkUtils.JUNIT4_TEST_SEARCHER.isTestClass(type)) {
+ // to handle @RunWith classes
+ classItem = TestItemUtils.constructJavaTestItem(type, TestLevel.CLASS, TestKind.JUnit);
+ } else if (TestFrameworkUtils.JUNIT5_TEST_SEARCHER.isTestClass(type)) {
+ // to handle @Nested and @Testable classes
+ classItem = TestItemUtils.constructJavaTestItem(type, TestLevel.CLASS, TestKind.JUnit5);
}
+ }
- return new ArrayList<>(map.values());
+ // set the class item as the child of its declaring type
+ if (classItem != null && parentItem != null) {
+ parentItem.addChild(classItem);
}
+ for (final ITypeBinding childTypeBinding : typeBinding.getDeclaredTypes()) {
+ findTestItemsInTypeBinding(childTypeBinding, classItem, searchers, monitor);
+ }
}
- public static List searchLocation(List arguments, IProgressMonitor monitor) throws CoreException {
- final List searchResult = new LinkedList<>();
- if (arguments == null || arguments.size() == 0) {
- throw new IllegalArgumentException("Invalid arguments to search the location.");
- }
- String searchString = ((String) arguments.get(0)).replaceAll("[$#]", ".");
- int searchFor = IJavaSearchConstants.METHOD;
- if (searchString.endsWith("")) {
- searchString = searchString.substring(0, searchString.indexOf("") - 1);
- searchFor = IJavaSearchConstants.CLASS;
- }
- final SearchPattern pattern = SearchPattern.createPattern(searchString, searchFor,
- IJavaSearchConstants.DECLARATIONS, SearchPattern.R_PATTERN_MATCH);
- final IJavaProject[] projects = JavaCore.create(ResourcesPlugin.getWorkspace().getRoot())
- .getJavaProjects();
- final IJavaSearchScope scope = SearchEngine.createJavaSearchScope(projects, IJavaSearchScope.SOURCES);
- final SearchRequestor requestor = new SearchRequestor() {
- @Override
- public void acceptSearchMatch(SearchMatch match) throws CoreException {
- final Object element = match.getElement();
- if (element instanceof IMethod || element instanceof IType) {
- final IJavaElement javaElement = (IJavaElement) element;
- searchResult.add(new Location(JDTUtils.getFileURI(javaElement.getResource()),
- TestItemUtils.parseTestItemRange(javaElement)));
- }
+ /**
+ * Given a file Uri, resolve its belonging project and package node in the test explorer, this is
+ * used to find the test item node when a test file is changed
+ * @param arguments A list containing a uri of the file
+ * @param monitor Progress monitor
+ * @throws JavaModelException
+ */
+ public static List resolvePath(List arguments, IProgressMonitor monitor)
+ throws JavaModelException {
+ final List result = new LinkedList<>();
+ final String uriString = (String) arguments.get(0);
+ if (JavaCore.isJavaLikeFileName(uriString)) {
+ final ICompilationUnit unit = JDTUtils.resolveCompilationUnit(uriString);
+ if (unit == null) {
+ return Collections.emptyList();
}
- };
- new SearchEngine().search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() },
- scope, requestor, monitor);
- return searchResult;
- }
-
- private static boolean isInTestScope(IJavaElement element) throws JavaModelException {
- final IJavaProject project = element.getJavaProject();
- for (final IPath sourcePath : ProjectUtils.listSourcePaths(project)) {
- if (!ProjectTestUtils.isTest(project, sourcePath, true /*containsGeneralProject*/)) {
- continue;
+ final IJavaProject project = unit.getJavaProject();
+ if (project == null) {
+ return Collections.emptyList();
}
- if (sourcePath.isPrefixOf(element.getPath())) {
- return true;
+ result.add(TestItemUtils.constructJavaTestItem(project, TestLevel.PROJECT, TestKind.None));
+
+ final IPackageFragment packageFragment = (IPackageFragment) unit.getParent();
+ if (packageFragment == null || !(packageFragment instanceof IPackageFragment)) {
+ return Collections.emptyList();
}
+ result.add(TestItemUtils.constructJavaTestItem(packageFragment, TestLevel.PACKAGE, TestKind.None));
}
- return false;
+
+ return result;
}
- private static IJavaElement[] getJavaElementForSearch(SearchTestItemParams params) throws JavaModelException {
- switch (params.getLevel()) {
- case ROOT:
- return Stream.of(ProjectUtils.getJavaProjects())
- .filter(javaProject -> !ProjectsManager.DEFAULT_PROJECT_NAME
- .equals(javaProject.getProject().getName()))
- .toArray(IJavaProject[]::new);
- case FOLDER:
- final Set projectSet = ProjectTestUtils.parseProjects(params.getUri());
- return projectSet.toArray(new IJavaElement[projectSet.size()]);
- case PACKAGE:
- final IJavaElement packageElement = resolvePackage(params.getUri(), params.getFullName());
- return new IJavaElement[] { packageElement };
- case CLASS:
- final ICompilationUnit compilationUnit = JDTUtils.resolveCompilationUnit(params.getUri());
- if (compilationUnit == null) {
- return null;
- }
- for (final IType type : compilationUnit.getAllTypes()) {
- if (Objects.equals(type.getFullyQualifiedName(), params.getFullName())) {
- return new IJavaElement[] { type };
- }
- }
- return null;
+ /**
+ * Given the test item's full name, get its location. This is used to calculate the location for each test message.
+ */
+ public static Location findTestLocation(List arguments, IProgressMonitor monitor)
+ throws JavaModelException {
+ final String fullName = (String) arguments.get(0);
+ if (StringUtils.isEmpty(fullName)) {
+ return null;
}
- return new IJavaElement[] {};
- }
+ final int projectNameEnd = fullName.indexOf("@");
+ if (projectNameEnd < 0) {
+ return null;
+ }
- private static void searchInFolder(List resultList, SearchTestItemParams params)
- throws URISyntaxException, JavaModelException {
- final Set projectSet = ProjectTestUtils.parseProjects(params.getUri());
- for (final IJavaProject project : projectSet) {
- for (final IPackageFragment packageFragment : project.getPackageFragments()) {
- if (isInTestScope(packageFragment) && packageFragment.getCompilationUnits().length > 0) {
- resultList.add(TestItemUtils.constructTestItem(packageFragment, TestLevel.PACKAGE));
- }
- }
+ final String projectName = fullName.substring(0, projectNameEnd);
+ if (StringUtils.isEmpty(projectName)) {
+ return null;
}
- }
- private static void searchInPackage(List resultList, SearchTestItemParams params)
- throws JavaModelException {
- final IPackageFragment packageFragment = (IPackageFragment) resolvePackage(params.getUri(),
- params.getFullName());
- if (packageFragment == null) {
- return;
+ final IJavaProject javaProject = ProjectUtils.getJavaProject(projectName);
+ if (javaProject == null) {
+ return null;
+ }
+
+ final int methodStart = fullName.indexOf("#");
+ final String typeName;
+ final String methodName;
+ if (methodStart > 0) {
+ typeName = fullName.substring(projectNameEnd + 1, methodStart);
+ methodName = fullName.substring(methodStart + 1);
+ } else {
+ typeName = fullName.substring(projectNameEnd + 1);
+ methodName = null;
}
- for (final ICompilationUnit unit : packageFragment.getCompilationUnits()) {
- for (final IType type : unit.getTypes()) {
- resultList.add(TestItemUtils.constructTestItem(type, TestLevel.CLASS));
+ final IType type = findType(javaProject, typeName, monitor);
+ if (type == null) {
+ return null;
+ }
+
+ if (StringUtils.isEmpty(methodName)) {
+ return new Location(JDTUtils.getFileURI(type.getResource()), TestItemUtils.parseTestItemRange(type));
+ }
+
+ for (final IMethod method : type.getMethods()) {
+ if (methodName.equals(method.getElementName())) {
+ // TODO: handle the override method
+ return new Location(JDTUtils.getFileURI(method.getResource()),
+ TestItemUtils.parseTestItemRange(method));
}
}
+
+ return null;
}
- private static IJavaElement resolvePackage(String uriString, String fullName) throws JavaModelException {
- final IFolder resource = (IFolder) JDTUtils.findResource(JDTUtils.toURI(uriString),
- ResourcesPlugin.getWorkspace().getRoot()::findContainersForLocationURI);
- final IJavaElement element = JavaCore.create(resource);
- if (TestItemUtils.DEFAULT_PACKAGE_NAME.equals(fullName)) {
- if (element instanceof IPackageFragmentRoot) {
- final IPackageFragmentRoot packageRoot = (IPackageFragmentRoot) element;
- for (final IJavaElement child : packageRoot.getChildren()) {
- if (child instanceof IPackageFragment && ((IPackageFragment) child).isDefaultPackage()) {
- return (IPackageFragment) child;
+ protected static final IType findType(final IJavaProject project, String className, IProgressMonitor monitor) {
+ final IType[] result = { null };
+ final String dottedName = className.replace('$', '.'); // for nested classes...
+ try {
+ if (project != null) {
+ result[0] = internalFindType(project, dottedName, new HashSet(), monitor);
+ }
+ if (result[0] == null) {
+ final int lastDot = dottedName.lastIndexOf('.');
+ final TypeNameMatchRequestor nameMatchRequestor = new TypeNameMatchRequestor() {
+ @Override
+ public void acceptTypeNameMatch(TypeNameMatch match) {
+ result[0] = match.getType();
}
- }
+ };
+ new SearchEngine().searchAllTypeNames(
+ lastDot >= 0 ? dottedName.substring(0, lastDot).toCharArray() : null,
+ SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE,
+ (lastDot >= 0 ? dottedName.substring(lastDot + 1) : dottedName).toCharArray(),
+ SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE,
+ IJavaSearchConstants.TYPE,
+ SearchEngine.createWorkspaceScope(),
+ nameMatchRequestor,
+ IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH,
+ monitor);
}
+ } catch (JavaModelException e) {
+ JUnitPlugin.log(e);
}
-
- return element;
+
+ return result[0];
}
- private static void searchInClass(List resultList, SearchTestItemParams params,
+ /**
+ * copied from org.eclipse.jdt.internal.junit.ui.OpenEditorAction.internalFindType()
+ */
+ private static IType internalFindType(IJavaProject project, String className, Set visitedProjects,
IProgressMonitor monitor) throws JavaModelException {
- final ICompilationUnit unit = JDTUtils.resolveCompilationUnit(params.getUri());
- final CompilationUnit root = (CompilationUnit) parseToAst(unit, false /* fromCache */, monitor);
- for (final IType type : unit.getAllTypes()) {
- if (type.getFullyQualifiedName().equals(params.getFullName())) {
- final ASTNode node = root.findDeclaringNode(type.getKey());
- if (!(node instanceof TypeDeclaration)) {
- continue;
- }
-
- final ITypeBinding binding = ((TypeDeclaration) node).resolveBinding();
- if (binding == null) {
- continue;
- }
-
- TestFrameworkUtils.findTestItemsInTypeBinding(binding, resultList, null /* parentClassItem */, monitor);
- resultList.removeIf(item -> item.getLevel() != TestLevel.METHOD ||
- !item.getFullName().startsWith(params.getFullName() + "#"));
- for (final IType innerType : type.getTypes()) {
- resultList.add(TestItemUtils.constructTestItem(innerType, TestLevel.CLASS));
+ try {
+ if (visitedProjects.contains(project)) {
+ return null;
+ }
+ monitor.beginTask("", 2); //$NON-NLS-1$
+ IType type = project.findType(className, new SubProgressMonitor(monitor, 1));
+ if (type != null) {
+ return type;
+ }
+ //fix for bug 87492: visit required projects explicitly to also find not exported types
+ visitedProjects.add(project);
+ final IJavaModel javaModel = project.getJavaModel();
+ final String[] requiredProjectNames = project.getRequiredProjectNames();
+ final IProgressMonitor reqMonitor = new SubProgressMonitor(monitor, 1);
+ reqMonitor.beginTask("", requiredProjectNames.length); //$NON-NLS-1$
+ for (final String requiredProjectName : requiredProjectNames) {
+ final IJavaProject requiredProject = javaModel.getJavaProject(requiredProjectName);
+ if (requiredProject.exists()) {
+ type = internalFindType(requiredProject, className, visitedProjects,
+ new SubProgressMonitor(reqMonitor, 1));
+ if (type != null) {
+ return type;
+ }
}
- break;
}
+ return null;
+ } finally {
+ monitor.done();
}
}
- private static boolean isJavaElementExist(IJavaElement element) {
- return element != null && element.getResource() != null && element.getResource().exists();
+ public static ASTNode parseToAst(final ICompilationUnit unit, final boolean fromCache,
+ final IProgressMonitor monitor) {
+ if (fromCache) {
+ final CompilationUnit astRoot = CoreASTProvider.getInstance().getAST(unit, CoreASTProvider.WAIT_YES,
+ monitor);
+ if (astRoot != null) {
+ return astRoot;
+ }
+ }
+
+ if (monitor.isCanceled()) {
+ return null;
+ }
+
+ final ASTParser parser = ASTParser.newParser(AST.JLS14);
+ parser.setSource(unit);
+ parser.setFocalPosition(0);
+ parser.setResolveBindings(true);
+ parser.setIgnoreMethodBodies(true);
+ return parser.createAST(monitor);
}
}
diff --git a/java-extension/com.microsoft.java.test.runner/pom.xml b/java-extension/com.microsoft.java.test.runner/pom.xml
index 56b6c295..f126f854 100644
--- a/java-extension/com.microsoft.java.test.runner/pom.xml
+++ b/java-extension/com.microsoft.java.test.runner/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.java.test
parent
- 0.30.1
+ 0.31.0
com.microsoft.java.test.runner
jar
diff --git a/java-extension/pom.xml b/java-extension/pom.xml
index 2c92aad6..c69069ce 100644
--- a/java-extension/pom.xml
+++ b/java-extension/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.java.test
parent
${base.name} :: Parent
- 0.30.1
+ 0.31.0
pom
Java Test Runner
diff --git a/package-lock.json b/package-lock.json
index faae816f..214f8d34 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "vscode-java-test",
- "version": "0.30.1",
+ "version": "0.31.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -30,104 +30,6 @@
"js-tokens": "^4.0.0"
}
},
- "@babel/parser": {
- "version": "7.13.13",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.13.tgz",
- "integrity": "sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw==",
- "dev": true
- },
- "@babel/types": {
- "version": "7.13.14",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz",
- "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==",
- "dev": true,
- "requires": {
- "@babel/helper-validator-identifier": "^7.12.11",
- "lodash": "^4.17.19",
- "to-fast-properties": "^2.0.0"
- }
- },
- "@dabh/diagnostics": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz",
- "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==",
- "requires": {
- "colorspace": "1.1.x",
- "enabled": "2.0.x",
- "kuler": "^2.0.0"
- }
- },
- "@nodelib/fs.scandir": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz",
- "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==",
- "dev": true,
- "requires": {
- "@nodelib/fs.stat": "2.0.4",
- "run-parallel": "^1.1.9"
- }
- },
- "@nodelib/fs.stat": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz",
- "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==",
- "dev": true
- },
- "@nodelib/fs.walk": {
- "version": "1.2.6",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz",
- "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==",
- "dev": true,
- "requires": {
- "@nodelib/fs.scandir": "2.1.4",
- "fastq": "^1.6.0"
- }
- },
- "@npmcli/move-file": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz",
- "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==",
- "dev": true,
- "requires": {
- "mkdirp": "^1.0.4",
- "rimraf": "^3.0.2"
- }
- },
- "@sinonjs/commons": {
- "version": "1.8.3",
- "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz",
- "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==",
- "dev": true,
- "requires": {
- "type-detect": "4.0.8"
- }
- },
- "@sinonjs/fake-timers": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz",
- "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==",
- "dev": true,
- "requires": {
- "@sinonjs/commons": "^1.7.0"
- }
- },
- "@sinonjs/samsam": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.3.1.tgz",
- "integrity": "sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg==",
- "dev": true,
- "requires": {
- "@sinonjs/commons": "^1.6.0",
- "lodash.get": "^4.4.2",
- "type-detect": "^4.0.8"
- }
- },
- "@sinonjs/text-encoding": {
- "version": "0.7.1",
- "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz",
- "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==",
- "dev": true
- },
"@tootallnate/once": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
@@ -159,18 +61,17 @@
"@types/node": "*"
}
},
- "@types/json-schema": {
- "version": "7.0.7",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz",
- "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==",
- "dev": true
- },
"@types/lodash": {
"version": "4.14.168",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.168.tgz",
"integrity": "sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==",
"dev": true
},
+ "@types/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw=="
+ },
"@types/minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz",
@@ -189,27 +90,6 @@
"integrity": "sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw==",
"dev": true
},
- "@types/pug": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.4.tgz",
- "integrity": "sha1-h3L80EGOPNLMFxVV1zAHQVBR9LI=",
- "dev": true
- },
- "@types/sinon": {
- "version": "9.0.11",
- "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.11.tgz",
- "integrity": "sha512-PwP4UY33SeeVKodNE37ZlOsR9cReypbMJOhZ7BVE0lB+Hix3efCOxiJWiE5Ia+yL9Cn2Ch72EjFTRze8RZsNtg==",
- "dev": true,
- "requires": {
- "@types/sinonjs__fake-timers": "*"
- }
- },
- "@types/sinonjs__fake-timers": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz",
- "integrity": "sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg==",
- "dev": true
- },
"@types/vscode": {
"version": "1.56.0",
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.56.0.tgz",
@@ -403,12 +283,6 @@
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
"dev": true
},
- "acorn": {
- "version": "7.4.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
- "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
- "dev": true
- },
"agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
@@ -435,16 +309,6 @@
}
}
},
- "aggregate-error": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
- "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
- "dev": true,
- "requires": {
- "clean-stack": "^2.0.0",
- "indent-string": "^4.0.0"
- }
- },
"ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -775,33 +639,12 @@
}
}
},
- "array-union": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
- "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
- "dev": true
- },
"array-unique": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
"dev": true
},
- "asap": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
- "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
- "dev": true
- },
- "asn1": {
- "version": "0.2.4",
- "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
- "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
- "dev": true,
- "requires": {
- "safer-buffer": "~2.1.0"
- }
- },
"asn1.js": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
@@ -849,29 +692,12 @@
}
}
},
- "assert-never": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.2.1.tgz",
- "integrity": "sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==",
- "dev": true
- },
- "assert-plus": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
- "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
- "dev": true
- },
"assign-symbols": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
"integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
"dev": true
},
- "async": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz",
- "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw=="
- },
"async-done": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz",
@@ -916,30 +742,12 @@
"async-done": "^1.2.2"
}
},
- "asynckit": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
- "dev": true
- },
"atob": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
"dev": true
},
- "aws-sign2": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
- "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
- "dev": true
- },
- "aws4": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
- "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
- "dev": true
- },
"axios": {
"version": "0.21.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
@@ -948,15 +756,6 @@
"follow-redirects": "^1.10.0"
}
},
- "babel-walk": {
- "version": "3.0.0-canary-5",
- "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz",
- "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.9.6"
- }
- },
"bach": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz",
@@ -1041,15 +840,6 @@
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"dev": true
},
- "bcrypt-pbkdf": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
- "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
- "dev": true,
- "requires": {
- "tweetnacl": "^0.14.3"
- }
- },
"big-integer": {
"version": "1.6.48",
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz",
@@ -1100,12 +890,6 @@
"integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==",
"dev": true
},
- "bootstrap": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.0.tgz",
- "integrity": "sha512-Io55IuQY3kydzHtbGvQya3H+KorS/M9rSNyfCGCg9WZ4pyT/lCxIlpJgG1GXW/PswzC84Tr2fBYi+7+jFVQQBw==",
- "dev": true
- },
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -1263,31 +1047,6 @@
"integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
"dev": true
},
- "cacache": {
- "version": "15.0.6",
- "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.6.tgz",
- "integrity": "sha512-g1WYDMct/jzW+JdWEyjaX2zoBkZ6ZT9VpOyp2I/VMtDsNLffNat3kqPFfi1eDRSK9/SuKGyORDHcQMcPF8sQ/w==",
- "dev": true,
- "requires": {
- "@npmcli/move-file": "^1.0.1",
- "chownr": "^2.0.0",
- "fs-minipass": "^2.0.0",
- "glob": "^7.1.4",
- "infer-owner": "^1.0.4",
- "lru-cache": "^6.0.0",
- "minipass": "^3.1.1",
- "minipass-collect": "^1.0.2",
- "minipass-flush": "^1.0.5",
- "minipass-pipeline": "^1.2.2",
- "mkdirp": "^1.0.3",
- "p-map": "^4.0.0",
- "promise-inflight": "^1.0.1",
- "rimraf": "^3.0.2",
- "ssri": "^8.0.1",
- "tar": "^6.0.2",
- "unique-filename": "^1.1.1"
- }
- },
"cache-base": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
@@ -1321,12 +1080,6 @@
"integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
"dev": true
},
- "caseless": {
- "version": "0.12.0",
- "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
- "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
- "dev": true
- },
"chainsaw": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz",
@@ -1347,15 +1100,6 @@
"supports-color": "^5.3.0"
}
},
- "character-parser": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz",
- "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=",
- "dev": true,
- "requires": {
- "is-regex": "^1.0.3"
- }
- },
"chokidar": {
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
@@ -1466,12 +1210,6 @@
}
}
},
- "chownr": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
- "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
- "dev": true
- },
"chrome-trace-event": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz",
@@ -1514,12 +1252,6 @@
}
}
},
- "clean-stack": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
- "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
- "dev": true
- },
"cliui": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
@@ -1629,19 +1361,11 @@
"object-visit": "^1.0.0"
}
},
- "color": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz",
- "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==",
- "requires": {
- "color-convert": "^1.9.1",
- "color-string": "^1.5.2"
- }
- },
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
"requires": {
"color-name": "1.1.3"
}
@@ -1649,16 +1373,8 @@
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
- },
- "color-string": {
- "version": "1.5.5",
- "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz",
- "integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==",
- "requires": {
- "color-name": "^1.0.0",
- "simple-swizzle": "^0.2.2"
- }
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
},
"color-support": {
"version": "1.1.3",
@@ -1666,29 +1382,6 @@
"integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
"dev": true
},
- "colors": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
- "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA=="
- },
- "colorspace": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz",
- "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==",
- "requires": {
- "color": "3.0.x",
- "text-hex": "1.0.x"
- }
- },
- "combined-stream": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
- "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
- "dev": true,
- "requires": {
- "delayed-stream": "~1.0.0"
- }
- },
"commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
@@ -1701,11 +1394,6 @@
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
"dev": true
},
- "compare-versions": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz",
- "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA=="
- },
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
@@ -1768,16 +1456,6 @@
"integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==",
"dev": true
},
- "constantinople": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz",
- "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==",
- "dev": true,
- "requires": {
- "@babel/parser": "^7.6.0",
- "@babel/types": "^7.6.1"
- }
- },
"constants-browserify": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
@@ -1868,29 +1546,11 @@
}
}
},
- "copy-webpack-plugin": {
- "version": "6.4.1",
- "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-6.4.1.tgz",
- "integrity": "sha512-MXyPCjdPVx5iiWyl40Va3JGh27bKzOTNY3NjUTrosD2q7dR/cLD0013uqJ3BpFbUjyONINjb6qI7nDIJujrMbA==",
- "dev": true,
- "requires": {
- "cacache": "^15.0.5",
- "fast-glob": "^3.2.4",
- "find-cache-dir": "^3.3.1",
- "glob-parent": "^5.1.1",
- "globby": "^11.0.1",
- "loader-utils": "^2.0.0",
- "normalize-path": "^3.0.0",
- "p-limit": "^3.0.2",
- "schema-utils": "^3.0.0",
- "serialize-javascript": "^5.0.1",
- "webpack-sources": "^1.4.3"
- }
- },
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
- "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+ "dev": true
},
"create-ecdh": {
"version": "4.0.4",
@@ -1972,15 +1632,6 @@
"type": "^1.0.1"
}
},
- "dashdash": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
- "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
- "dev": true,
- "requires": {
- "assert-plus": "^1.0.0"
- }
- },
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -2083,12 +1734,6 @@
}
}
},
- "delayed-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
- "dev": true
- },
"des.js": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
@@ -2143,33 +1788,12 @@
}
}
},
- "dir-glob": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
- "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
- "dev": true,
- "requires": {
- "path-type": "^4.0.0"
- }
- },
- "doctypes": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz",
- "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=",
- "dev": true
- },
"domain-browser": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
"integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==",
"dev": true
},
- "duplexer": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
- "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
- "dev": true
- },
"duplexer2": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
@@ -2265,16 +1889,6 @@
"object.defaults": "^1.1.0"
}
},
- "ecc-jsbn": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
- "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
- "dev": true,
- "requires": {
- "jsbn": "~0.1.0",
- "safer-buffer": "^2.1.0"
- }
- },
"elliptic": {
"version": "6.5.4",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
@@ -2318,11 +1932,6 @@
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
"dev": true
},
- "enabled": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
- "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="
- },
"end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
@@ -2493,21 +2102,6 @@
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
"dev": true
},
- "event-stream": {
- "version": "3.3.4",
- "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz",
- "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=",
- "dev": true,
- "requires": {
- "duplexer": "~0.1.1",
- "from": "~0",
- "map-stream": "~0.1.0",
- "pause-stream": "0.0.11",
- "split": "0.3",
- "stream-combiner": "~0.0.4",
- "through": "~2.3.1"
- }
- },
"events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
@@ -2677,12 +2271,6 @@
}
}
},
- "extsprintf": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
- "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
- "dev": true
- },
"fancy-log": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz",
@@ -2701,20 +2289,6 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
},
- "fast-glob": {
- "version": "3.2.5",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz",
- "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==",
- "dev": true,
- "requires": {
- "@nodelib/fs.stat": "^2.0.2",
- "@nodelib/fs.walk": "^1.2.3",
- "glob-parent": "^5.1.0",
- "merge2": "^1.3.0",
- "micromatch": "^4.0.2",
- "picomatch": "^2.2.1"
- }
- },
"fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -2727,25 +2301,6 @@
"integrity": "sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk=",
"dev": true
},
- "fast-safe-stringify": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz",
- "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA=="
- },
- "fastq": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz",
- "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==",
- "dev": true,
- "requires": {
- "reusify": "^1.0.4"
- }
- },
- "fecha": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz",
- "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q=="
- },
"figgy-pudding": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz",
@@ -2768,27 +2323,6 @@
"to-regex-range": "^5.0.1"
}
},
- "find-cache-dir": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz",
- "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==",
- "dev": true,
- "requires": {
- "commondir": "^1.0.1",
- "make-dir": "^3.0.2",
- "pkg-dir": "^4.1.0"
- }
- },
- "find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "dev": true,
- "requires": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
- }
- },
"findup-sync": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz",
@@ -2984,11 +2518,6 @@
}
}
},
- "fn.name": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
- "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
- },
"follow-redirects": {
"version": "1.13.3",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.3.tgz",
@@ -3009,23 +2538,6 @@
"for-in": "^1.0.1"
}
},
- "forever-agent": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
- "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
- "dev": true
- },
- "form-data": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
- "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
- "dev": true,
- "requires": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.6",
- "mime-types": "^2.1.12"
- }
- },
"fragment-cache": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
@@ -3035,12 +2547,6 @@
"map-cache": "^0.2.2"
}
},
- "from": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
- "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=",
- "dev": true
- },
"from2": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
@@ -3093,15 +2599,6 @@
"universalify": "^0.1.0"
}
},
- "fs-minipass": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
- "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
- "dev": true,
- "requires": {
- "minipass": "^3.0.0"
- }
- },
"fs-mkdirp-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz",
@@ -3239,15 +2736,6 @@
"integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
"dev": true
},
- "getpass": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
- "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
- "dev": true,
- "requires": {
- "assert-plus": "^1.0.0"
- }
- },
"glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
@@ -3379,20 +2867,6 @@
"which": "^1.2.14"
}
},
- "globby": {
- "version": "11.0.3",
- "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz",
- "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==",
- "dev": true,
- "requires": {
- "array-union": "^2.1.0",
- "dir-glob": "^3.0.1",
- "fast-glob": "^3.1.1",
- "ignore": "^5.1.4",
- "merge2": "^1.3.0",
- "slash": "^3.0.0"
- }
- },
"glogg": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz",
@@ -3453,182 +2927,37 @@
}
}
},
- "gulp-remote-src": {
- "version": "0.4.4",
- "resolved": "https://registry.npmjs.org/gulp-remote-src/-/gulp-remote-src-0.4.4.tgz",
- "integrity": "sha512-mo7lGgZmNXyTbcUzfjSnUVkx1pnqqiwv/pPaIrYdTO77hq0WNTxXLAzQdoYOnyJ0mfVLNmNl9AGqWLiAzTPMMA==",
+ "gulp-tslint": {
+ "version": "8.1.4",
+ "resolved": "https://registry.npmjs.org/gulp-tslint/-/gulp-tslint-8.1.4.tgz",
+ "integrity": "sha512-wBoZIEMJRz9urHwolsvQpngA9l931p6g/Liwz1b/KrsVP6jEBFZv/o0NS1TFCQZi/l8mXxz8+v3twhf4HOXxPQ==",
"dev": true,
"requires": {
- "event-stream": "3.3.4",
- "node.extend": "~1.1.2",
- "request": "^2.88.0",
- "through2": "~2.0.3",
- "vinyl": "~2.0.1"
+ "@types/fancy-log": "1.3.0",
+ "ansi-colors": "^1.0.1",
+ "fancy-log": "1.3.3",
+ "map-stream": "~0.0.7",
+ "plugin-error": "1.0.1",
+ "through": "~2.3.8"
},
"dependencies": {
- "clone": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
- "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
- "dev": true
- },
- "is-stream": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
- "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
+ "map-stream": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz",
+ "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=",
"dev": true
- },
- "vinyl": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.0.2.tgz",
- "integrity": "sha1-CjcT2NTpIhxY8QyhbAEWyeJe2nw=",
- "dev": true,
- "requires": {
- "clone": "^1.0.0",
- "clone-buffer": "^1.0.0",
- "clone-stats": "^1.0.0",
- "cloneable-readable": "^1.0.0",
- "is-stream": "^1.1.0",
- "remove-trailing-separator": "^1.0.1",
- "replace-ext": "^1.0.0"
- }
}
}
},
- "gulp-sass": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/gulp-sass/-/gulp-sass-5.0.0.tgz",
- "integrity": "sha512-J0aH0/2N4+2szGCeut0ktGHK0Wg8L9uWivuigrl7xv+nhxozBQRAKLrhnDDaTa3FeUWYtgT8w4RlgdhRy5v16w==",
- "dev": true,
- "requires": {
- "chalk": "^4.1.1",
- "lodash": "^4.17.20",
- "plugin-error": "^1.0.1",
- "replace-ext": "^2.0.0",
- "strip-ansi": "^6.0.0",
- "transfob": "^1.0.0",
- "vinyl-sourcemaps-apply": "^0.2.1"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
- "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
- "dev": true
- },
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
- "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
- "dev": true,
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true
- },
- "replace-ext": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz",
- "integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==",
- "dev": true
- },
- "strip-ansi": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
- "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
- "dev": true,
- "requires": {
- "ansi-regex": "^5.0.0"
- }
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "gulp-tslint": {
- "version": "8.1.4",
- "resolved": "https://registry.npmjs.org/gulp-tslint/-/gulp-tslint-8.1.4.tgz",
- "integrity": "sha512-wBoZIEMJRz9urHwolsvQpngA9l931p6g/Liwz1b/KrsVP6jEBFZv/o0NS1TFCQZi/l8mXxz8+v3twhf4HOXxPQ==",
- "dev": true,
- "requires": {
- "@types/fancy-log": "1.3.0",
- "ansi-colors": "^1.0.1",
- "fancy-log": "1.3.3",
- "map-stream": "~0.0.7",
- "plugin-error": "1.0.1",
- "through": "~2.3.8"
- },
- "dependencies": {
- "map-stream": {
- "version": "0.0.7",
- "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz",
- "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=",
- "dev": true
- }
- }
- },
- "gulplog": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz",
- "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=",
+ "gulplog": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz",
+ "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=",
"dev": true,
"requires": {
"glogg": "^1.0.0"
}
},
- "har-schema": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
- "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
- "dev": true
- },
- "har-validator": {
- "version": "5.1.5",
- "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
- "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
- "dev": true,
- "requires": {
- "ajv": "^6.12.3",
- "har-schema": "^2.0.0"
- }
- },
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@@ -3789,17 +3118,6 @@
}
}
},
- "http-signature": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
- "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
- "dev": true,
- "requires": {
- "assert-plus": "^1.0.0",
- "jsprim": "^1.2.2",
- "sshpk": "^1.7.0"
- }
- },
"https-browserify": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
@@ -3853,12 +3171,6 @@
"integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
"dev": true
},
- "ignore": {
- "version": "5.1.8",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
- "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==",
- "dev": true
- },
"import-local": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
@@ -3929,12 +3241,6 @@
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
"dev": true
},
- "indent-string": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
- "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
- "dev": true
- },
"infer-owner": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
@@ -3954,7 +3260,8 @@
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
},
"ini": {
"version": "1.3.8",
@@ -3974,12 +3281,6 @@
"integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
"dev": true
},
- "is": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz",
- "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==",
- "dev": true
- },
"is-absolute": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
@@ -4010,11 +3311,6 @@
}
}
},
- "is-arrayish": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
- "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
- },
"is-bigint": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.1.tgz",
@@ -4105,16 +3401,6 @@
}
}
},
- "is-expression": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz",
- "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==",
- "dev": true,
- "requires": {
- "acorn": "^7.1.1",
- "object-assign": "^4.1.1"
- }
- },
"is-extendable": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
@@ -4178,12 +3464,6 @@
"isobject": "^3.0.1"
}
},
- "is-promise": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz",
- "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==",
- "dev": true
- },
"is-regex": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz",
@@ -4203,11 +3483,6 @@
"is-unc-path": "^1.0.0"
}
},
- "is-stream": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
- "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw=="
- },
"is-string": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
@@ -4223,12 +3498,6 @@
"has-symbols": "^1.0.1"
}
},
- "is-typedarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
- "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
- "dev": true
- },
"is-unc-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
@@ -4265,7 +3534,8 @@
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
},
"isexe": {
"version": "2.0.0",
@@ -4279,18 +3549,6 @@
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
"dev": true
},
- "isstream": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
- "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
- "dev": true
- },
- "js-stringify": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz",
- "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=",
- "dev": true
- },
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -4307,24 +3565,12 @@
"esprima": "^4.0.0"
}
},
- "jsbn": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
- "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
- "dev": true
- },
"json-parse-better-errors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
"dev": true
},
- "json-schema": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
- "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
- "dev": true
- },
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@@ -4337,21 +3583,6 @@
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
"dev": true
},
- "json-stringify-safe": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
- "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
- "dev": true
- },
- "json5": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
- "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==",
- "dev": true,
- "requires": {
- "minimist": "^1.2.5"
- }
- },
"jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
@@ -4360,50 +3591,23 @@
"graceful-fs": "^4.1.6"
}
},
- "jsprim": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
- "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
- "dev": true,
- "requires": {
- "assert-plus": "1.0.0",
- "extsprintf": "1.3.0",
- "json-schema": "0.2.3",
- "verror": "1.10.0"
- }
- },
- "jstransformer": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz",
- "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=",
- "dev": true,
- "requires": {
- "is-promise": "^2.0.0",
- "promise": "^7.0.1"
- }
- },
"just-debounce": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.1.0.tgz",
"integrity": "sha512-qpcRocdkUmf+UTNBYx5w6dexX5J31AKK1OmPwH630a83DdVVUIngk55RSAiIGpQyoH0dlr872VHfPjnQnK1qDQ==",
"dev": true
},
- "just-extend": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz",
- "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==",
- "dev": true
- },
"kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
"dev": true
},
- "kuler": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
- "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="
+ "kleur": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
+ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+ "dev": true
},
"last-run": {
"version": "1.1.1",
@@ -4515,37 +3719,11 @@
"integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==",
"dev": true
},
- "loader-utils": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
- "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
- "dev": true,
- "requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^2.1.2"
- }
- },
- "locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "dev": true,
- "requires": {
- "p-locate": "^4.1.0"
- }
- },
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
- "lodash.get": {
- "version": "4.4.2",
- "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
- "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=",
- "dev": true
- },
"log-symbols": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
@@ -4555,44 +3733,14 @@
"chalk": "^2.4.2"
}
},
- "logform": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz",
- "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==",
- "requires": {
- "colors": "^1.2.1",
- "fast-safe-stringify": "^2.0.4",
- "fecha": "^4.2.0",
- "ms": "^2.1.1",
- "triple-beam": "^1.3.0"
- }
- },
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
"requires": {
"yallist": "^4.0.0"
}
},
- "make-dir": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
- "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
- "dev": true,
- "requires": {
- "semver": "^6.0.0"
- },
- "dependencies": {
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- }
- }
- },
"make-iterator": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz",
@@ -4608,12 +3756,6 @@
"integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
"dev": true
},
- "map-stream": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz",
- "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=",
- "dev": true
- },
"map-visit": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
@@ -4814,22 +3956,6 @@
}
}
},
- "merge2": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
- "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
- "dev": true
- },
- "micromatch": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
- "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
- "dev": true,
- "requires": {
- "braces": "^3.0.1",
- "picomatch": "^2.0.5"
- }
- },
"miller-rabin": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
@@ -4848,21 +3974,6 @@
}
}
},
- "mime-db": {
- "version": "1.47.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz",
- "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==",
- "dev": true
- },
- "mime-types": {
- "version": "2.1.30",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz",
- "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==",
- "dev": true,
- "requires": {
- "mime-db": "1.47.0"
- }
- },
"minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
@@ -4890,52 +4001,6 @@
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
- "minipass": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz",
- "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==",
- "dev": true,
- "requires": {
- "yallist": "^4.0.0"
- }
- },
- "minipass-collect": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
- "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
- "dev": true,
- "requires": {
- "minipass": "^3.0.0"
- }
- },
- "minipass-flush": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
- "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
- "dev": true,
- "requires": {
- "minipass": "^3.0.0"
- }
- },
- "minipass-pipeline": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
- "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
- "dev": true,
- "requires": {
- "minipass": "^3.0.0"
- }
- },
- "minizlib": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
- "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
- "dev": true,
- "requires": {
- "minipass": "^3.0.0",
- "yallist": "^4.0.0"
- }
- },
"mississippi": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
@@ -4987,12 +4052,6 @@
}
}
},
- "mkdirp": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
- "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
- "dev": true
- },
"mocha": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz",
@@ -5338,11 +4397,6 @@
}
}
},
- "ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
- },
"mute-stdout": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz",
@@ -5393,19 +4447,6 @@
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
"dev": true
},
- "nise": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/nise/-/nise-4.1.0.tgz",
- "integrity": "sha512-eQMEmGN/8arp0xsvGoQ+B1qvSkR73B1nWSCh7nOt5neMCtwcQVYQGdzQMhcNscktTsWB54xnlSQFzOAPJD8nXA==",
- "dev": true,
- "requires": {
- "@sinonjs/commons": "^1.7.0",
- "@sinonjs/fake-timers": "^6.0.0",
- "@sinonjs/text-encoding": "^0.7.1",
- "just-extend": "^4.0.2",
- "path-to-regexp": "^1.7.0"
- }
- },
"node-environment-flags": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz",
@@ -5487,16 +4528,6 @@
}
}
},
- "node.extend": {
- "version": "1.1.8",
- "resolved": "https://registry.npmjs.org/node.extend/-/node.extend-1.1.8.tgz",
- "integrity": "sha512-L/dvEBwyg3UowwqOUTyDsGBU6kjBQOpOhshio9V3i3BMPv5YUb9+mWNN8MK0IbWqT0AqaTSONZf0aTuMMahWgA==",
- "dev": true,
- "requires": {
- "has": "^1.0.3",
- "is": "^3.2.1"
- }
- },
"normalize-package-data": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
@@ -5530,12 +4561,6 @@
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"dev": true
},
- "oauth-sign": {
- "version": "0.9.0",
- "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
- "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
- "dev": true
- },
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -5667,14 +4692,6 @@
"wrappy": "1"
}
},
- "one-time": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
- "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
- "requires": {
- "fn.name": "1.x.x"
- }
- },
"ordered-read-streams": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz",
@@ -5731,44 +4748,6 @@
"lcid": "^1.0.0"
}
},
- "p-limit": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
- "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
- "dev": true,
- "requires": {
- "yocto-queue": "^0.1.0"
- }
- },
- "p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "dev": true,
- "requires": {
- "p-limit": "^2.2.0"
- },
- "dependencies": {
- "p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dev": true,
- "requires": {
- "p-try": "^2.0.0"
- }
- }
- }
- },
- "p-map": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
- "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
- "dev": true,
- "requires": {
- "aggregate-error": "^3.0.0"
- }
- },
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
@@ -5887,12 +4866,6 @@
"integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
"dev": true
},
- "path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true
- },
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -5926,38 +4899,6 @@
"integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=",
"dev": true
},
- "path-to-regexp": {
- "version": "1.8.0",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
- "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
- "dev": true,
- "requires": {
- "isarray": "0.0.1"
- },
- "dependencies": {
- "isarray": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
- "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
- "dev": true
- }
- }
- },
- "path-type": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
- "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
- "dev": true
- },
- "pause-stream": {
- "version": "0.0.11",
- "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
- "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=",
- "dev": true,
- "requires": {
- "through": "~2.3"
- }
- },
"pbkdf2": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz",
@@ -5971,12 +4912,6 @@
"sha.js": "^2.4.8"
}
},
- "performance-now": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
- "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
- "dev": true
- },
"picomatch": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
@@ -6004,15 +4939,6 @@
"pinkie": "^2.0.0"
}
},
- "pkg-dir": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
- "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
- "dev": true,
- "requires": {
- "find-up": "^4.0.0"
- }
- },
"plugin-error": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz",
@@ -6046,220 +4972,53 @@
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
- "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
- },
- "promise": {
- "version": "7.3.1",
- "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
- "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
- "dev": true,
- "requires": {
- "asap": "~2.0.3"
- }
- },
- "promise-inflight": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
- "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"dev": true
},
- "prr": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
- "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
- "dev": true
- },
- "psl": {
- "version": "1.8.0",
- "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
- "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==",
- "dev": true
- },
- "public-encrypt": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
- "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
- "dev": true,
- "requires": {
- "bn.js": "^4.1.0",
- "browserify-rsa": "^4.0.0",
- "create-hash": "^1.1.0",
- "parse-asn1": "^5.0.0",
- "randombytes": "^2.0.1",
- "safe-buffer": "^5.1.2"
- },
- "dependencies": {
- "bn.js": {
- "version": "4.12.0",
- "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
- "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
- "dev": true
- }
- }
- },
- "pug": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.2.tgz",
- "integrity": "sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw==",
- "dev": true,
- "requires": {
- "pug-code-gen": "^3.0.2",
- "pug-filters": "^4.0.0",
- "pug-lexer": "^5.0.1",
- "pug-linker": "^4.0.0",
- "pug-load": "^3.0.0",
- "pug-parser": "^6.0.0",
- "pug-runtime": "^3.0.1",
- "pug-strip-comments": "^2.0.0"
- }
- },
- "pug-attrs": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz",
- "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==",
- "dev": true,
- "requires": {
- "constantinople": "^4.0.1",
- "js-stringify": "^1.0.2",
- "pug-runtime": "^3.0.0"
- }
- },
- "pug-code-gen": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.2.tgz",
- "integrity": "sha512-nJMhW16MbiGRiyR4miDTQMRWDgKplnHyeLvioEJYbk1RsPI3FuA3saEP8uwnTb2nTJEKBU90NFVWJBk4OU5qyg==",
- "dev": true,
- "requires": {
- "constantinople": "^4.0.1",
- "doctypes": "^1.1.0",
- "js-stringify": "^1.0.2",
- "pug-attrs": "^3.0.0",
- "pug-error": "^2.0.0",
- "pug-runtime": "^3.0.0",
- "void-elements": "^3.1.0",
- "with": "^7.0.0"
- }
- },
- "pug-error": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.0.0.tgz",
- "integrity": "sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==",
- "dev": true
- },
- "pug-filters": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz",
- "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==",
- "dev": true,
- "requires": {
- "constantinople": "^4.0.1",
- "jstransformer": "1.0.0",
- "pug-error": "^2.0.0",
- "pug-walk": "^2.0.0",
- "resolve": "^1.15.1"
- }
- },
- "pug-lexer": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz",
- "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==",
- "dev": true,
- "requires": {
- "character-parser": "^2.2.0",
- "is-expression": "^4.0.0",
- "pug-error": "^2.0.0"
- }
- },
- "pug-linker": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz",
- "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==",
- "dev": true,
- "requires": {
- "pug-error": "^2.0.0",
- "pug-walk": "^2.0.0"
- }
- },
- "pug-load": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz",
- "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==",
- "dev": true,
- "requires": {
- "object-assign": "^4.1.1",
- "pug-walk": "^2.0.0"
- }
- },
- "pug-loader": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/pug-loader/-/pug-loader-2.4.0.tgz",
- "integrity": "sha512-cD4bU2wmkZ1EEVyu0IfKOsh1F26KPva5oglO1Doc3knx8VpBIXmFHw16k9sITYIjQMCnRv1vb4vfQgy7VdR6eg==",
- "dev": true,
- "requires": {
- "loader-utils": "^1.1.0",
- "pug-walk": "^1.0.0",
- "resolve": "^1.1.7"
- },
- "dependencies": {
- "json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
- "dev": true,
- "requires": {
- "minimist": "^1.2.0"
- }
- },
- "loader-utils": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
- "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
- "dev": true,
- "requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^1.0.1"
- }
- },
- "pug-walk": {
- "version": "1.1.8",
- "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.8.tgz",
- "integrity": "sha512-GMu3M5nUL3fju4/egXwZO0XLi6fW/K3T3VTgFQ14GxNi8btlxgT5qZL//JwZFm/2Fa64J/PNS8AZeys3wiMkVA==",
- "dev": true
- }
- }
- },
- "pug-parser": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz",
- "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==",
+ "promise-inflight": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
+ "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
+ "dev": true
+ },
+ "prompts": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz",
+ "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==",
"dev": true,
"requires": {
- "pug-error": "^2.0.0",
- "token-stream": "1.0.0"
+ "kleur": "^3.0.3",
+ "sisteransi": "^1.0.5"
}
},
- "pug-runtime": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz",
- "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==",
+ "prr": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
+ "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
"dev": true
},
- "pug-strip-comments": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz",
- "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==",
+ "public-encrypt": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
+ "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
"dev": true,
"requires": {
- "pug-error": "^2.0.0"
+ "bn.js": "^4.1.0",
+ "browserify-rsa": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "parse-asn1": "^5.0.0",
+ "randombytes": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+ "dev": true
+ }
}
},
- "pug-walk": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz",
- "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==",
- "dev": true
- },
"pump": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
@@ -6287,12 +5046,6 @@
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true
},
- "qs": {
- "version": "6.5.2",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
- "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
- "dev": true
- },
"querystring": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
@@ -6305,12 +5058,6 @@
"integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
"dev": true
},
- "queue-microtask": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
- "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
- "dev": true
- },
"randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -6389,6 +5136,7 @@
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dev": true,
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
@@ -6616,34 +5364,6 @@
"remove-trailing-separator": "^1.1.0"
}
},
- "request": {
- "version": "2.88.2",
- "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
- "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
- "dev": true,
- "requires": {
- "aws-sign2": "~0.7.0",
- "aws4": "^1.8.0",
- "caseless": "~0.12.0",
- "combined-stream": "~1.0.6",
- "extend": "~3.0.2",
- "forever-agent": "~0.6.1",
- "form-data": "~2.3.2",
- "har-validator": "~5.1.3",
- "http-signature": "~1.2.0",
- "is-typedarray": "~1.0.0",
- "isstream": "~0.1.2",
- "json-stringify-safe": "~5.0.1",
- "mime-types": "~2.1.19",
- "oauth-sign": "~0.9.0",
- "performance-now": "^2.1.0",
- "qs": "~6.5.2",
- "safe-buffer": "^5.1.2",
- "tough-cookie": "~2.5.0",
- "tunnel-agent": "^0.6.0",
- "uuid": "^3.3.2"
- }
- },
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -6712,12 +5432,6 @@
"integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
"dev": true
},
- "reusify": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
- "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
- "dev": true
- },
"rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
@@ -6737,15 +5451,6 @@
"inherits": "^2.0.1"
}
},
- "run-parallel": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
- "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
- "dev": true,
- "requires": {
- "queue-microtask": "^1.2.2"
- }
- },
"run-queue": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
@@ -6758,7 +5463,8 @@
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true
},
"safe-regex": {
"version": "1.1.0",
@@ -6774,85 +5480,6 @@
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
- "sass": {
- "version": "1.35.1",
- "resolved": "https://registry.npmjs.org/sass/-/sass-1.35.1.tgz",
- "integrity": "sha512-oCisuQJstxMcacOPmxLNiLlj4cUyN2+8xJnG7VanRoh2GOLr9RqkvI4AxA4a6LHVg/rsu+PmxXeGhrdSF9jCiQ==",
- "dev": true,
- "requires": {
- "chokidar": ">=3.0.0 <4.0.0"
- },
- "dependencies": {
- "anymatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
- "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
- "dev": true,
- "requires": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
- }
- },
- "binary-extensions": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
- "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
- "dev": true
- },
- "chokidar": {
- "version": "3.5.2",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
- "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
- "dev": true,
- "requires": {
- "anymatch": "~3.1.2",
- "braces": "~3.0.2",
- "fsevents": "~2.3.2",
- "glob-parent": "~5.1.2",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.6.0"
- }
- },
- "fsevents": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
- "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
- "dev": true,
- "optional": true
- },
- "is-binary-path": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "dev": true,
- "requires": {
- "binary-extensions": "^2.0.0"
- }
- },
- "readdirp": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
- "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
- "dev": true,
- "requires": {
- "picomatch": "^2.2.1"
- }
- }
- }
- },
- "schema-utils": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
- "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
- "dev": true,
- "requires": {
- "@types/json-schema": "^7.0.6",
- "ajv": "^6.12.5",
- "ajv-keywords": "^3.5.2"
- }
- },
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
@@ -6867,15 +5494,6 @@
"sver-compat": "^1.5.0"
}
},
- "serialize-javascript": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz",
- "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==",
- "dev": true,
- "requires": {
- "randombytes": "^2.1.0"
- }
- },
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
@@ -6941,55 +5559,10 @@
"resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz",
"integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw=="
},
- "simple-swizzle": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
- "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
- "requires": {
- "is-arrayish": "^0.3.1"
- }
- },
- "sinon": {
- "version": "9.2.4",
- "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz",
- "integrity": "sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg==",
- "dev": true,
- "requires": {
- "@sinonjs/commons": "^1.8.1",
- "@sinonjs/fake-timers": "^6.0.1",
- "@sinonjs/samsam": "^5.3.1",
- "diff": "^4.0.2",
- "nise": "^4.0.4",
- "supports-color": "^7.1.0"
- },
- "dependencies": {
- "diff": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
- "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
- "dev": true
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "sisteransi": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
+ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
"dev": true
},
"snapdragon": {
@@ -7184,15 +5757,6 @@
"integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==",
"dev": true
},
- "split": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz",
- "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=",
- "dev": true,
- "requires": {
- "through": "2"
- }
- },
"split-string": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
@@ -7208,32 +5772,6 @@
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
"dev": true
},
- "sshpk": {
- "version": "1.16.1",
- "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
- "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
- "dev": true,
- "requires": {
- "asn1": "~0.2.3",
- "assert-plus": "^1.0.0",
- "bcrypt-pbkdf": "^1.0.0",
- "dashdash": "^1.12.0",
- "ecc-jsbn": "~0.1.1",
- "getpass": "^0.1.1",
- "jsbn": "~0.1.0",
- "safer-buffer": "^2.0.2",
- "tweetnacl": "~0.14.0"
- }
- },
- "ssri": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz",
- "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==",
- "dev": true,
- "requires": {
- "minipass": "^3.1.1"
- }
- },
"stack-chain": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/stack-chain/-/stack-chain-1.3.7.tgz",
@@ -7242,7 +5780,8 @@
"stack-trace": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
- "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA="
+ "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=",
+ "dev": true
},
"static-extend": {
"version": "0.1.2",
@@ -7307,15 +5846,6 @@
}
}
},
- "stream-combiner": {
- "version": "0.0.4",
- "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz",
- "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=",
- "dev": true,
- "requires": {
- "duplexer": "~0.1.1"
- }
- },
"stream-each": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz",
@@ -7418,6 +5948,7 @@
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
"requires": {
"safe-buffer": "~5.2.0"
}
@@ -7471,20 +6002,6 @@
"integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
"dev": true
},
- "tar": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz",
- "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==",
- "dev": true,
- "requires": {
- "chownr": "^2.0.0",
- "fs-minipass": "^2.0.0",
- "minipass": "^3.0.0",
- "minizlib": "^2.1.1",
- "mkdirp": "^1.0.3",
- "yallist": "^4.0.0"
- }
- },
"tas-client": {
"version": "0.1.21",
"resolved": "https://registry.npmjs.org/tas-client/-/tas-client-0.1.21.tgz",
@@ -7705,11 +6222,6 @@
}
}
},
- "text-hex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
- "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="
- },
"through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
@@ -7799,12 +6311,6 @@
"integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=",
"dev": true
},
- "to-fast-properties": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
- "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
- "dev": true
- },
"to-object-path": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
@@ -7855,39 +6361,12 @@
"through2": "^2.0.3"
}
},
- "token-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz",
- "integrity": "sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ=",
- "dev": true
- },
- "tough-cookie": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
- "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
- "dev": true,
- "requires": {
- "psl": "^1.1.28",
- "punycode": "^2.1.1"
- }
- },
- "transfob": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/transfob/-/transfob-1.0.0.tgz",
- "integrity": "sha1-x/wnpbVDCtSGJnrmZtkj90oKsyA=",
- "dev": true
- },
"traverse": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
"integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=",
"dev": true
},
- "triple-beam": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz",
- "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw=="
- },
"ts-loader": {
"version": "5.4.5",
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.4.5.tgz",
@@ -8085,33 +6564,12 @@
"integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=",
"dev": true
},
- "tunnel-agent": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
- "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
- "dev": true,
- "requires": {
- "safe-buffer": "^5.0.1"
- }
- },
- "tweetnacl": {
- "version": "0.14.5",
- "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
- "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
- "dev": true
- },
"type": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
"dev": true
},
- "type-detect": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
- "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
- "dev": true
- },
"typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
@@ -8366,7 +6824,8 @@
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "dev": true
},
"uuid": {
"version": "3.4.0",
@@ -8404,17 +6863,6 @@
"integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=",
"dev": true
},
- "verror": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
- "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
- "dev": true,
- "requires": {
- "assert-plus": "^1.0.0",
- "core-util-is": "1.0.2",
- "extsprintf": "^1.2.0"
- }
- },
"vinyl": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz",
@@ -8512,34 +6960,22 @@
}
}
},
- "vinyl-sourcemaps-apply": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz",
- "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=",
- "dev": true,
- "requires": {
- "source-map": "^0.5.1"
- },
- "dependencies": {
- "source-map": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
- "dev": true
- }
- }
- },
"vm-browserify": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
"integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
"dev": true
},
- "void-elements": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
- "integrity": "sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=",
- "dev": true
+ "vscode-dts": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/vscode-dts/-/vscode-dts-0.3.1.tgz",
+ "integrity": "sha512-8XZ+M7IQV5MnPXEhHLemGOk5FRBfT7HCBEughfDhn2i6wwPXlpv4OuQQdhs6XZVmF3GFdKqt+fXOgfsNBKP+fw==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0",
+ "prompts": "^2.1.0",
+ "rimraf": "^3.0.0"
+ }
},
"vscode-extension-telemetry": {
"version": "0.1.7",
@@ -9206,72 +7642,6 @@
"string-width": "^1.0.2 || 2"
}
},
- "winston": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz",
- "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==",
- "requires": {
- "@dabh/diagnostics": "^2.0.2",
- "async": "^3.1.0",
- "is-stream": "^2.0.0",
- "logform": "^2.2.0",
- "one-time": "^1.0.0",
- "readable-stream": "^3.4.0",
- "stack-trace": "0.0.x",
- "triple-beam": "^1.3.0",
- "winston-transport": "^4.4.0"
- }
- },
- "winston-transport": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz",
- "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==",
- "requires": {
- "readable-stream": "^2.3.7",
- "triple-beam": "^1.2.0"
- },
- "dependencies": {
- "readable-stream": {
- "version": "2.3.7",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
- "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- },
- "string_decoder": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "requires": {
- "safe-buffer": "~5.1.0"
- }
- }
- }
- },
- "with": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz",
- "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==",
- "dev": true,
- "requires": {
- "@babel/parser": "^7.9.6",
- "@babel/types": "^7.9.6",
- "assert-never": "^1.2.1",
- "babel-walk": "3.0.0-canary-5"
- }
- },
"worker-farm": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
@@ -9312,8 +7682,7 @@
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"yargs": {
"version": "7.1.1",
@@ -9513,12 +7882,6 @@
}
}
}
- },
- "yocto-queue": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
- "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
- "dev": true
}
}
}
diff --git a/package.json b/package.json
index 9c75cd3b..ca6e55cc 100644
--- a/package.json
+++ b/package.json
@@ -1,11 +1,12 @@
{
+ "enableProposedApi": true,
"name": "vscode-java-test",
"displayName": "Java Test Runner",
"description": "%description%",
"repository": {
"url": "https://github.com/Microsoft/vscode-java-test"
},
- "version": "0.30.1",
+ "version": "0.31.0",
"publisher": "vscjava",
"bugs": {
"url": "https://github.com/Microsoft/vscode-java-test/issues"
@@ -22,7 +23,7 @@
],
"aiKey": "90c182a8-8dab-45d4-bfb8-1353eb55aa7f",
"engines": {
- "vscode": "^1.56.0"
+ "vscode": "^1.59.0"
},
"categories": [
"Other"
@@ -39,17 +40,12 @@
"workspaceContains:build.gradle",
"workspaceContains:.classpath",
"onCommand:java.test.editor.run",
- "onCommand:java.test.editor.debug",
- "onCommand:java.test.cancel",
- "onCommand:java.test.show.report",
- "onCommand:java.test.show.output",
- "onCommand:java.test.open.log",
- "onCommand:java.test.config.migrate"
+ "onCommand:java.test.editor.debug"
],
"main": "./main.js",
"contributes": {
"javaExtensions": [
- "./server/com.microsoft.java.test.plugin-0.30.1.jar",
+ "./server/com.microsoft.java.test.plugin-0.31.0.jar",
"./server/org.eclipse.jdt.junit4.runtime_1.1.1200.v20200214-0716.jar",
"./server/org.eclipse.jdt.junit5.runtime_1.0.900.v20200513-0617.jar",
"./server/org.junit.jupiter.api_5.6.0.v20200203-2009.jar",
@@ -65,18 +61,9 @@
"./server/org.junit.platform.suite.api_1.6.0.v20200203-2009.jar",
"./server/org.apiguardian_1.1.0.v20190826-0900.jar"
],
- "views": {
- "test": [
- {
- "id": "testExplorer",
- "name": "Java",
- "when": "java:testRunnerActivated"
- }
- ]
- },
"viewsWelcome": [
{
- "view": "testExplorer",
+ "view": "testing",
"contents": "%contributes.viewsWelcome.inLightWeightMode%",
"when": "java:serverMode == LightWeight"
},
@@ -94,52 +81,12 @@
"menus": {
"view/title": [
{
- "command": "java.test.relaunch",
- "when": "view == testExplorer && java:serverMode != LightWeight",
- "group": "navigation@10"
- },
- {
- "command": "java.test.explorer.runAll",
- "when": "view == testExplorer && java:serverMode != LightWeight",
- "group": "navigation@20"
- },
- {
- "command": "java.test.explorer.debugAll",
- "when": "view == testExplorer && java:serverMode != LightWeight",
- "group": "navigation@30"
- },
- {
- "command": "java.test.explorer.refresh",
- "when": "view == testExplorer && java:serverMode != LightWeight",
- "group": "navigation@40"
+ "command": "java.test.refreshExplorer",
+ "when": "view == workbench.view.testing",
+ "group": "zzz@zzz"
}
],
"view/item/context": [
- {
- "command": "java.test.explorer.run",
- "when": "view == testExplorer && viewItem != UNTESTABLE_NODE",
- "group": "testExplorer@0"
- },
- {
- "command": "java.test.explorer.debug",
- "when": "view == testExplorer && viewItem != UNTESTABLE_NODE",
- "group": "testExplorer@1"
- },
- {
- "command": "java.test.explorer.refresh",
- "when": "view == testExplorer && viewItem != UNTESTABLE_NODE",
- "group": "testExplorer@4"
- },
- {
- "command": "java.test.explorer.run",
- "when": "view == testExplorer && viewItem != UNTESTABLE_NODE",
- "group": "inline@0"
- },
- {
- "command": "java.test.explorer.debug",
- "when": "view == testExplorer && viewItem != UNTESTABLE_NODE",
- "group": "inline@1"
- },
{
"command": "java.test.runFromJavaProjectExplorer",
"when": "view == javaProjectExplorer && viewItem =~ /java:(type|package|packageRoot)(?=.*?\\b\\+uri\\b)(?=.*?\\b\\+test\\b)/",
@@ -157,18 +104,6 @@
}
],
"commandPalette": [
- {
- "command": "java.test.explorer.run",
- "when": "false"
- },
- {
- "command": "java.test.explorer.debug",
- "when": "false"
- },
- {
- "command": "java.test.explorer.refresh",
- "when": "false"
- },
{
"command": "java.test.runFromJavaProjectExplorer",
"when": "false"
@@ -177,18 +112,6 @@
"command": "java.test.debugFromJavaProjectExplorer",
"when": "false"
},
- {
- "command": "java.test.config.migrate",
- "when": "java:hasDeprecatedTestConfig"
- },
- {
- "command": "java.test.explorer.runAll",
- "when": "java:serverMode != LightWeight"
- },
- {
- "command": "java.test.explorer.debugAll",
- "when": "java:serverMode != LightWeight"
- },
{
"command": "java.test.editor.run",
"when": "java:serverMode != LightWeight"
@@ -196,71 +119,19 @@
{
"command": "java.test.editor.debug",
"when": "java:serverMode != LightWeight"
- },
- {
- "command": "java.test.relaunch",
- "when": "java:serverMode != LightWeight"
- },
- {
- "command": "java.test.cancel",
- "when": "java:serverMode != LightWeight"
- },
- {
- "command": "java.test.show.report",
- "when": "java:serverMode != LightWeight"
}
]
},
"commands": [
- {
- "command": "java.test.show.output",
- "title": "%contributes.commands.java.test.show.output.title%",
- "category": "Java"
- },
- {
- "command": "java.test.open.log",
- "title": "%contributes.commands.java.test.open.log.title%",
- "category": "Java"
- },
- {
- "command": "java.test.explorer.run",
- "title": "%contributes.commands.java.test.explorer.run.title%",
- "icon": "$(play)",
- "category": "Java"
- },
- {
- "command": "java.test.explorer.debug",
- "title": "%contributes.commands.java.test.explorer.debug.title%",
- "icon": "$(debug-alt-small)",
- "category": "Java"
- },
- {
- "command": "java.test.explorer.runAll",
- "title": "%contributes.commands.java.test.explorer.runAll.title%",
- "icon": "$(run-all)",
- "category": "Java"
- },
{
"command": "java.test.runFromJavaProjectExplorer",
- "title": "%contributes.commands.java.test.runFromJavaProjectExplorer%",
+ "title": "%contributes.commands.java.test.runFromJavaProjectExplorer.title%",
"icon": "$(play)",
"category": "Java"
},
{
"command": "java.test.debugFromJavaProjectExplorer",
- "title": "%contributes.commands.java.test.debugFromJavaProjectExplorer%",
- "category": "Java"
- },
- {
- "command": "java.test.explorer.debugAll",
- "title": "%contributes.commands.java.test.explorer.debugAll.title%",
- "icon": "$(debug-alt)",
- "category": "Java"
- },
- {
- "command": "java.test.relaunch",
- "title": "%contributes.commands.java.test.relaunch.title%",
- "icon": "$(debug-restart)",
+ "title": "%contributes.commands.java.test.debugFromJavaProjectExplorer.title%",
"category": "Java"
},
{
@@ -274,80 +145,14 @@
"category": "Java"
},
{
- "command": "java.test.cancel",
- "title": "%contributes.commands.java.test.cancel.title%",
- "category": "Java"
- },
- {
- "command": "java.test.show.report",
- "title": "%contributes.commands.java.test.show.report.title%",
- "category": "Java"
- },
- {
- "command": "java.test.explorer.refresh",
- "title": "%contributes.commands.java.test.explorer.refresh.title%",
- "icon": "$(refresh)",
- "category": "Java"
- },
- {
- "command": "java.test.config.migrate",
- "title": "%contributes.commands.java.test.config.migrate.title%",
+ "command": "java.test.refreshExplorer",
+ "title": "%contributes.commands.java.test.refreshExplorer.title%",
"category": "Java"
}
],
"configuration": {
"title": "Java Test Runner",
"properties": {
- "java.test.report.showAfterExecution": {
- "type": "string",
- "enum": [
- "always",
- "onFailure",
- "never"
- ],
- "default": "onFailure",
- "description": "%configuration.java.test.report.showAfterExecution.description%",
- "scope": "window"
- },
- "java.test.report.position": {
- "type": "string",
- "enum": [
- "sideView",
- "currentView"
- ],
- "default": "sideView",
- "description": "%configuration.java.test.report.position.description%",
- "scope": "window"
- },
- "java.test.editor.enableShortcuts": {
- "type": "boolean",
- "default": true,
- "description": "%configuration.java.test.editor.enableShortcuts.description%",
- "scope": "application"
- },
- "java.test.log.level": {
- "type": "string",
- "enum": [
- "error",
- "info",
- "verbose"
- ],
- "default": "info",
- "description": "%configuration.java.test.log.level.description%",
- "scope": "application"
- },
- "java.test.message.hintForDeprecatedConfig": {
- "type": "boolean",
- "default": true,
- "description": "%configuration.java.test.message.hintForDeprecatedConfig.description%",
- "scope": "application"
- },
- "java.test.message.hintForSetingDefaultConfig": {
- "type": "boolean",
- "default": true,
- "description": "%configuration.java.test.message.hintForSetingDefaultConfig.description%",
- "scope": "application"
- },
"java.test.defaultConfig": {
"type": "string",
"description": "%configuration.java.test.defaultConfig.description%",
@@ -458,8 +263,10 @@
"test": "npm run compile && node ./dist/test/index.js",
"lint": "gulp lint",
"build-plugin": "gulp build-plugin",
- "build-resources": "gulp build-resources",
- "vscode:prepublish": "gulp build-resources && webpack --mode production"
+ "vscode:prepublish": "webpack --mode production",
+ "download-api": "vscode-dts dev -f",
+ "postdownload-api": "vscode-dts main -f",
+ "postinstall": "npm run download-api"
},
"extensionDependencies": [
"redhat.java",
@@ -471,37 +278,27 @@
"@types/lodash": "^4.14.150",
"@types/mocha": "^2.2.48",
"@types/node": "^14.14.33",
- "@types/pug": "^2.0.4",
- "@types/sinon": "^9.0.11",
"@types/vscode": "1.56.0",
- "bootstrap": "^4.6.0",
- "copy-webpack-plugin": "^6.4.1",
"gulp": "^4.0.2",
- "gulp-remote-src": "^0.4.4",
- "gulp-sass": "^5.0.0",
"gulp-tslint": "^8.1.4",
"mocha": "^7.1.2",
- "pug": "^3.0.1",
- "pug-loader": "^2.4.0",
- "sass": "^1.35.1",
- "sinon": "^9.0.2",
"ts-loader": "^5.4.5",
"tslint": "^5.20.1",
"typescript": "^4.2.4",
"vscode-test": "^1.3.0",
"webpack": "^4.46.0",
- "webpack-cli": "^3.3.11"
+ "webpack-cli": "^3.3.11",
+ "vscode-dts": "^0.3.1"
},
"dependencies": {
- "compare-versions": "^3.6.0",
+ "@types/lru-cache": "^5.1.0",
"fs-extra": "^7.0.1",
"get-port": "^4.2.0",
"iconv-lite": "^0.4.24",
- "vscode-languageclient": "6.0.0-next.9",
"lodash": "^4.17.19",
+ "lru-cache": "^6.0.0",
"vscode-extension-telemetry-wrapper": "0.9.0",
- "vscode-tas-client": "^0.1.22",
- "winston": "^3.2.1",
- "winston-transport": "^4.3.0"
+ "vscode-languageclient": "6.0.0-next.9",
+ "vscode-tas-client": "^0.1.22"
}
}
diff --git a/package.nls.json b/package.nls.json
index 229277ce..f89d5e93 100644
--- a/package.nls.json
+++ b/package.nls.json
@@ -1,28 +1,10 @@
{
"description": "Run and debug JUnit or TestNG test cases",
- "contributes.commands.java.test.show.output.title": "Show Test Output",
- "contributes.commands.java.test.open.log.title": "Open Test Runner Log",
- "contributes.commands.java.test.explorer.run.title": "Run",
- "contributes.commands.java.test.explorer.debug.title": "Debug",
- "contributes.commands.java.test.explorer.runAll.title": "Run All Tests",
- "contributes.commands.java.test.explorer.debugAll.title": "Debug All Tests",
- "contributes.commands.java.test.explorer.run.config.title": "Run With Configuration",
- "contributes.commands.java.test.explorer.debug.config.title": "Debug With Configuration",
- "contributes.commands.java.test.show.report.title": "Show Test Report",
"contributes.commands.java.test.editor.run.title": "Run Tests",
"contributes.commands.java.test.editor.debug.title": "Debug Tests",
- "contributes.commands.java.test.runFromJavaProjectExplorer": "Run Tests",
- "contributes.commands.java.test.debugFromJavaProjectExplorer": "Debug Tests",
- "contributes.commands.java.test.relaunch.title": "Relaunch the Tests",
- "contributes.commands.java.test.cancel.title": "Cancel Test Job",
- "contributes.commands.java.test.explorer.refresh.title": "Refresh",
- "contributes.commands.java.test.config.migrate.title": "Migrate Deprecated 'launch.test.json'",
- "configuration.java.test.report.showAfterExecution.description": "Specify if the test report will automatically be shown after execution",
- "configuration.java.test.report.position.description": "Specify where to show the test report",
- "configuration.java.test.editor.enableShortcuts.description": "Specify whether to show the Code Lenses in editor or not",
- "configuration.java.test.log.level.description": "Specify the level of the test logs",
- "configuration.java.test.message.hintForDeprecatedConfig.description": "Specify whether the extension will show hint dialog when deprecated configuration file is used",
- "configuration.java.test.message.hintForSetingDefaultConfig.description": "Specify whether the extension will show hint to set default test configuration",
+ "contributes.commands.java.test.runFromJavaProjectExplorer.title": "Run Tests",
+ "contributes.commands.java.test.debugFromJavaProjectExplorer.title": "Debug Tests",
+ "contributes.commands.java.test.refreshExplorer.title": "Refresh",
"configuration.java.test.defaultConfig.description": "Specify the name of the default test configuration",
"configuration.java.test.config.description": "Specify the configurations for running the tests",
"configuration.java.test.config.item.description": "Specify the configuration item for running the tests",
diff --git a/package.nls.zh.json b/package.nls.zh.json
index 2edd5f82..d48f28c0 100644
--- a/package.nls.zh.json
+++ b/package.nls.zh.json
@@ -1,28 +1,10 @@
{
"description": "运行并调试 JUnit 或 TestNG 测试用例",
- "contributes.commands.java.test.show.output.title": "显示测试输出",
- "contributes.commands.java.test.open.log.title": "打开测试运行日志",
- "contributes.commands.java.test.explorer.run.title": "运行",
- "contributes.commands.java.test.explorer.debug.title": "调试",
- "contributes.commands.java.test.explorer.runAll.title": "运行所有测试用例",
- "contributes.commands.java.test.explorer.debugAll.title": "调试所有测试用例",
- "contributes.commands.java.test.explorer.run.config.title": "根据配置文件运行",
- "contributes.commands.java.test.explorer.debug.config.title": "根据配置文件调试",
- "contributes.commands.java.test.show.report.title": "显示测试报告",
"contributes.commands.java.test.editor.run.title": "运行测试用例",
"contributes.commands.java.test.editor.debug.title": "调试测试用例",
- "contributes.commands.java.test.relaunch.title": "重新执行测试任务",
- "contributes.commands.java.test.cancel.title": "取消测试任务",
- "contributes.commands.java.test.runFromJavaProjectExplorer": "运行测试",
- "contributes.commands.java.test.debugFromJavaProjectExplorer": "调试测试",
- "contributes.commands.java.test.explorer.refresh.title": "刷新",
- "contributes.commands.java.test.config.migrate.title": "迁移已弃用的 'launch.test.json' 文件",
- "configuration.java.test.report.showAfterExecution.description": "设定测试报告是否会在测试完成后自动显示",
- "configuration.java.test.report.position.description": "设定测试报告的显示位置",
- "configuration.java.test.editor.enableShortcuts.description": "设定是否在编辑器内显示 Code Lens 快捷方式",
- "configuration.java.test.log.level.description": "设定日志级别",
- "configuration.java.test.message.hintForDeprecatedConfig.description": "设定插件是否会对使用弃用的配置文件进行提示",
- "configuration.java.test.message.hintForSetingDefaultConfig.description": "设定插件是否会对设置默认测试配置项进行提示",
+ "contributes.commands.java.test.runFromJavaProjectExplorer.title": "运行测试",
+ "contributes.commands.java.test.debugFromJavaProjectExplorer.title": "调试测试",
+ "contributes.commands.java.test.refreshExplorer.title": "刷新",
"configuration.java.test.defaultConfig.description": "设定默认测试配置项的名称",
"configuration.java.test.config.description": "设定运行测试的配置信息",
"configuration.java.test.config.item.description": "设定运行测试时所用的配置项",
diff --git a/resources/logo.lowers.dark.svg b/resources/logo.lowers.dark.svg
deleted file mode 100644
index cfae4ba6..00000000
--- a/resources/logo.lowers.dark.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/resources/logo.lowers.light.svg b/resources/logo.lowers.light.svg
deleted file mode 100644
index cbdb662a..00000000
--- a/resources/logo.lowers.light.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/resources/media/dark/running.svg b/resources/media/dark/running.svg
deleted file mode 100644
index 33eb7f28..00000000
--- a/resources/media/dark/running.svg
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/resources/media/light/running.svg b/resources/media/light/running.svg
deleted file mode 100644
index 01c21ef2..00000000
--- a/resources/media/light/running.svg
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/resources/templates/images/chevron-right.svg b/resources/templates/images/chevron-right.svg
deleted file mode 100644
index 3c165921..00000000
--- a/resources/templates/images/chevron-right.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/resources/templates/images/debug-restart.svg b/resources/templates/images/debug-restart.svg
deleted file mode 100644
index cea87f70..00000000
--- a/resources/templates/images/debug-restart.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/resources/templates/images/go-to-file.svg b/resources/templates/images/go-to-file.svg
deleted file mode 100644
index e03b6082..00000000
--- a/resources/templates/images/go-to-file.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/resources/templates/report.pug b/resources/templates/report.pug
deleted file mode 100644
index 5078a3db..00000000
--- a/resources/templates/report.pug
+++ /dev/null
@@ -1,97 +0,0 @@
-include /report_method_table.pug
-doctype html
-html(lang="en")
- head
- meta(http-equiv="Content-Security-Policy" content=`default-src 'none'; style-src 'nonce-${nonce}'; script-src 'nonce-${nonce}'; img-src ${cspSource}`)
- meta(charset="utf-8")
- meta(name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no")
- style(nonce=`${nonce}`)
- include /css/report.css
- script(nonce=`${nonce}`, src=`${resourceBaseUri}/js/jquery-3.5.1.slim.min.js`, integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj", crossorigin="anonymous")
- script(nonce=`${nonce}`, src=`${resourceBaseUri}/js/bootstrap.bundle.min.js`, integrity="sha384-Piv4xVNRyMGpqkS2by6br4gNJ7DXjqk09RmUpJ8jgGtD7zP9yug3goQfGII0yAns", crossorigin="anonymous")
- script(nonce=`${nonce}`).
- $(() => {
- const vscode = acquireVsCodeApi();
- $('.list-group-item a[title="Navigate to Source"]').click((e) => {
- const $this = $(e.currentTarget);
- vscode.postMessage({
- command: 'java.test.open.document',
- uri: $this.attr('uri'),
- range: $this.attr('range'),
- fullName: $this.attr('fullName'),
- });
- return false;
- });
-
- $('code.word-break-all a').click((e) => {
- const $this = $(e.currentTarget);
- vscode.postMessage({
- command: 'java.test.report.openStackTrace',
- fullName: $this.attr('fullName'),
- trace: $this.attr('trace'),
- });
- return false;
- });
-
- $('#relaunch-tab').click((e) => {
- vscode.postMessage({
- command: 'java.test.relaunch',
- });
- return false;
- });
-
- //- rotate the arrow icon in accordion
- $(".collapse").on('show.bs.collapse', function(){
- $(this).parent().find("svg").first().addClass("rotate");
- }).on('hide.bs.collapse', function(){
- $(this).parent().find("svg").first().removeClass("rotate");
- });
-
- $(document).keydown(function(event) {
- if (event.which === 27) {
- let detailCollapse = $(':focus').closest("div.collapse[id$='detail']");
- if (detailCollapse.length === 0) {
- $('div.collapse').collapse('hide');
- } else {
- detailCollapse.collapse('hide');
- detailCollapse.closest("li.list-group-item-action").find("a.method-link").focus();
- }
- }
- });
- });
-
- body.container-fluid
- ul.nav.nav-pills.mt-3(role='tablist')
- li.nav-item.d-inline-flex
- button.btn.btn-sm.btn-info.active.mr-2.mb-2(id='all-tab', data-toggle='pill', href='#all', role='tab', aria-controls='all') All
- span.badge.badge-light.ml-2 #{allCount}
- if failedCount
- li.nav-item.d-inline-flex
- button.btn.btn-sm.btn-danger.mr-2.mb-2(id='fail-tab', data-toggle='pill', href='#fail', role='tab', aria-controls='fail') Failed
- span.badge.badge-light.ml-2 #{failedCount}
- if passedCount
- li.nav-item.d-inline-flex
- button.btn.btn-sm.btn-success.mr-2.mb-2(id='pass-tab', data-toggle='pill', href='#pass', role='tab', aria-controls='pass') Passed
- span.badge.badge-light.ml-2 #{passedCount}
- if skippedCount
- li.nav-item.d-inline-flex
- button.btn.btn-sm.btn-secondary.mr-2.mb-2(id='skip-tab', data-toggle='pill', href='#skip', role='tab', aria-controls='skip') Skipped
- span.badge.badge-light.ml-2 #{skippedCount}
- li.nav-item.d-inline-flex
- button.btn.btn-sm.btn-dark.mb-2(id='relaunch-tab', data-toggle='tooltip' data-placement='bottom' title='Relaunch the tests')
- include /images/debug-restart.svg
-
- span(aria-label="The below part contains collapsible components to show the stacktrace of failed methods. If you want to collapse the expanded view, you can press ESC.", tabindex="0")
-
- div.tab-content
- div.tab-pane.fade.show.active(id='all', role='tabpanel', aria-labelledby='all-tab')
- +collapseMethodTable(tests, 'detail-all')
- if failedCount
- div.tab-pane.fade(id='fail', role='tabpanel', aria-labelledby='fail-tab')
- +collapseMethodTable(failedTests, 'detail-fail')
- if passedCount
- div.tab-pane.fade(id='pass', role='tabpanel', aria-labelledby='pass-tab')
- +collapseMethodTable(passedTests, 'detail-pass')
- if skippedCount
- div.tab-pane.fade(id='skip', role='tabpanel', aria-labelledby='skip-tab')
- +collapseMethodTable(skippedTests, 'detail-skip')
diff --git a/resources/templates/report_method_table.pug b/resources/templates/report_method_table.pug
deleted file mode 100644
index 5f15e01f..00000000
--- a/resources/templates/report_method_table.pug
+++ /dev/null
@@ -1,65 +0,0 @@
-mixin collapseMethodTable(children, type)
- - var regex = /(\s?at\s+)([\w$\\.]+\/)?((?:[\w$]+\.)+[<\w$>]+)\(([\w-$]+\.java:\d+)\)/
- - var classIdx = 0
- //- See https://github.com/pugjs/pug/issues/2559#issuecomment-289873794 for iterating a map in Pug
- each entry in [...children]
- - classIdx++
- ul.list-group.mb-3
- li.list-group-item.active(tabindex="0", aria-label=`Class: ${entry[0]}`)
- h5.mb-0.text-truncate-left #{entry[0]}
- - var methodIdx = 0
- each method in entry[1]
- - methodIdx++
- li.list-group-item.list-group-item-action
- div.row.accordion
- a.col-7.method-link.collapsed.px-1(id=`${type}-${classIdx}-${methodIdx}-title`, href="", role="button", data-toggle="collapse", data-target=`#${type}-${classIdx}-${methodIdx}-detail`, aria-expanded="false", aria-controls=`${type}-${classIdx}-${methodIdx}-detail`, aria-label=`${method.status} method: ${method.displayName}.`)
- div.text-truncate
- include /images/chevron-right.svg
- span.ml-1 #{method.displayName}
- div.col-2.text-right.p-0
- if !method.status
- span.badge.badge-warning Not run
- else if method.status === 'Pass'
- span.badge.badge-success Passed
- else if method.status === 'Fail'
- span.badge.badge-danger Failed
- else
- span.badge.badge-secondary Skipped
- div.col-2.text-right.p-0 #{method.duration >= 0 ? parseFloat((method.duration/1000).toFixed(2)) + "s" : "N/A"}
- div.col-1.text-right.px-1
- a(href="#", title="Navigate to Source", uri=`${method.location && method.location.uri ? method.location.uri : ''}`, range=`${method.location && method.location.range ? JSON.stringify(method.location.range) : ''}`, fullname=`${method.fullName}`)
- include /images/go-to-file.svg
- div.mt-2.pl-2(id=`${type}-${classIdx}-${methodIdx}-detail`, class="collapse", aria-labelledby=`${type}-${classIdx}-${methodIdx}-title`, tabindex="0")
- div.row
- div.col
- h6 Message:
- div.row
- div.col.mb-1 #{method.message ? method.message : "N/A"}
- div.row
- div.col
- h6 Stack trace:
- div.row
- div.col
- if method.trace
- pre.pre-wrap
- - var traces = method.trace.split(/^Caused by:/gm)
- code.word-break-all
- each val, idx in traces
- if idx > 0
- br
- span.text-warning Caused by:
- if canResolveStackTrace
- - var lines = traces[idx].split(/\r?\n/g);
- each line in lines
- - var result = regex.exec(line);
- if result && result.length === 5
- span #{result[1] + (result[2] || "") + result[3]}(
- a(href="#" fullName=`${method.fullName}` trace=`${line}`) #{result[4]}
- | )
- else
- span #{line}
- br
- else
- span #{traces[idx]}
- else
- span N/A
\ No newline at end of file
diff --git a/resources/templates/scss/report.scss b/resources/templates/scss/report.scss
deleted file mode 100644
index 45932ea6..00000000
--- a/resources/templates/scss/report.scss
+++ /dev/null
@@ -1,45 +0,0 @@
-$body-bg: var(--vscode-editor-background);
-$body-color: var(--vscode-foreground);
-$link-color: var(--vscode-textLink-foreground);
-$link-hover-color: $link-color;
-$link-hover-decoration: none !default;
-
-$list-group-bg: var(--vscode-notifications-background);
-$list-group-active-bg: var(--vscode-titleBar-activeBackground);
-$list-group-border-width: 0;
-$list-group-action-active-bg: $list-group-bg;
-$list-group-active-color: var(--vscode-foreground);
-$list-group-action-color: var(--vscode-foreground);
-$list-group-hover-bg: var(--vscode-list-hoverBackground);
-
-$font-size-base: .85rem !default;
-$pre-color: var(--vscode-foreground);
-$code-color: var(--vscode-foreground);
-
-@import "../../../node_modules/bootstrap/scss/bootstrap";
-
-.text-truncate-left {
- @extend .text-truncate;
- @extend .text-left;
- direction: rtl;
-}
-
-code.word-break-all {
- word-break: break-all;
-}
-
-code.text-warning {
- color: var(--vscode-editorWarning-foreground);
-}
-
-pre.pre-wrap {
- white-space: pre-wrap;
-}
-
-.rotate {
- transform: rotate(90deg);
-}
-
-.method-link, .method-link:hover {
- color: var(--vscode-foreground);
-}
diff --git a/src/codelens/TestCodeLensController.ts b/src/codelens/TestCodeLensController.ts
deleted file mode 100644
index 6bbebd4e..00000000
--- a/src/codelens/TestCodeLensController.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license.
-
-import { ConfigurationChangeEvent, Disposable, DocumentSelector, languages, RelativePattern, workspace } from 'vscode';
-import { testSourceProvider } from '../../extension.bundle';
-import { ENABLE_EDITOR_SHORTCUTS_KEY } from '../constants/configs';
-import { parseDocumentSelector } from '../utils/uiUtils';
-import { TestCodeLensProvider } from './TestCodeLensProvider';
-
-class TestCodeLensController implements Disposable {
- private internalProvider: TestCodeLensProvider;
- private registeredProvider: Disposable | undefined;
- private configurationChangeListener: Disposable;
-
- constructor() {
- this.internalProvider = new TestCodeLensProvider();
-
- this.configurationChangeListener = workspace.onDidChangeConfiguration((event: ConfigurationChangeEvent) => {
- if (event.affectsConfiguration(ENABLE_EDITOR_SHORTCUTS_KEY)) {
- this.setCodeLensVisibility();
- }
- }, this);
-
- this.setCodeLensVisibility();
- }
-
- public async registerCodeLensProvider(): Promise {
- if (this.registeredProvider) {
- this.registeredProvider.dispose();
- }
-
- const patterns: RelativePattern[] = await testSourceProvider.getTestSourcePattern();
-
- const documentSelector: DocumentSelector = parseDocumentSelector(patterns);
-
- this.registeredProvider = languages.registerCodeLensProvider(documentSelector, this.internalProvider);
- }
-
- public refresh(): void {
- this.internalProvider.refresh();
- }
-
- public dispose(): void {
- this.internalProvider.dispose();
- if (this.registeredProvider) {
- this.registeredProvider.dispose();
- }
- this.configurationChangeListener.dispose();
- }
-
- private setCodeLensVisibility(): void {
- this.internalProvider.setIsActivated(this.isCodeLensEnabled());
- }
-
- private isCodeLensEnabled(): boolean {
- return workspace.getConfiguration().get(ENABLE_EDITOR_SHORTCUTS_KEY, true);
- }
-}
-
-export const testCodeLensController: TestCodeLensController = new TestCodeLensController();
diff --git a/src/codelens/TestCodeLensProvider.ts b/src/codelens/TestCodeLensProvider.ts
deleted file mode 100644
index 91dbad98..00000000
--- a/src/codelens/TestCodeLensProvider.ts
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license.
-
-import { CancellationToken, CodeLens, CodeLensProvider, Disposable, Event, EventEmitter, TextDocument } from 'vscode';
-import { JavaTestRunnerCommands } from '../constants/commands';
-import { logger } from '../logger/logger';
-import { ITestItem, TestLevel } from '../protocols';
-import { ITestResult, TestStatus } from '../runners/models';
-import { testItemModel } from '../testItemModel';
-import { testResultManager } from '../testResultManager';
-
-export class TestCodeLensProvider implements CodeLensProvider, Disposable {
- private onDidChangeCodeLensesEmitter: EventEmitter = new EventEmitter();
- private isActivated: boolean = true;
-
- get onDidChangeCodeLenses(): Event {
- return this.onDidChangeCodeLensesEmitter.event;
- }
-
- public setIsActivated(isActivated: boolean): void {
- this.isActivated = isActivated;
- this.refresh();
- }
-
- public refresh(): void {
- this.onDidChangeCodeLensesEmitter.fire();
- }
-
- public async provideCodeLenses(document: TextDocument, token: CancellationToken): Promise {
- if (!this.isActivated) {
- return [];
- }
-
- try {
- const items: ITestItem[] = await testItemModel.getItemsForCodeLens(document.uri, token);
- return this.getCodeLenses(items);
- } catch (error) {
- logger.error('Failed to provide Code Lens', error);
- return [];
- }
- }
-
- public dispose(): void {
- this.onDidChangeCodeLensesEmitter.dispose();
- }
-
- private getCodeLenses(items: ITestItem[]): CodeLens[] {
- const codeLenses: CodeLens[] = [];
- for (const item of items) {
- codeLenses.push(
- new CodeLens(
- item.location.range,
- {
- title: 'Run Test',
- command: JavaTestRunnerCommands.RUN_TEST_FROM_CODELENS,
- tooltip: 'Run Test',
- arguments: [item],
- },
- ),
- new CodeLens(
- item.location.range,
- {
- title: 'Debug Test',
- command: JavaTestRunnerCommands.DEBUG_TEST_FROM_CODELENS,
- tooltip: 'Debug Test',
- arguments: [item],
- },
- ),
- );
- const resultCodeLens: CodeLens | undefined = this.getResultCodeLens(item);
- if (resultCodeLens) {
- codeLenses.push(resultCodeLens);
- }
- }
- return codeLenses;
- }
-
- private getResultCodeLens(item: ITestItem): CodeLens | undefined {
- if (item.level === TestLevel.Method) {
- const result: ITestResult | undefined = testResultManager.getResultById(item.id);
- if (result && result.status) {
- return new CodeLens(
- item.location.range,
- {
- title: this.getResultIcon(result),
- command: JavaTestRunnerCommands.SHOW_TEST_REPORT,
- tooltip: 'Show Test Report',
- arguments: [[result]],
- },
- );
- }
- } else if (item.level === TestLevel.Class) {
- const childResults: Array = [];
- this.getAllMethodResults(childResults, item);
- const title: string = this.getResultIcons(childResults);
- if (title) {
- return new CodeLens(
- item.location.range,
- {
- title,
- command: JavaTestRunnerCommands.SHOW_TEST_REPORT,
- tooltip: 'Show Test Report',
- arguments: [childResults],
- },
- );
- }
- }
- return undefined;
- }
-
- private getAllMethodResults(childResults: Array, item: ITestItem): void {
- if (!item.children) {
- return undefined;
- }
- for (const childId of item.children) {
- const child: ITestItem | undefined = testItemModel.getItemById(childId);
- if (!child) {
- continue;
- }
- if (child.level === TestLevel.Class) {
- this.getAllMethodResults(childResults, child);
- } else if (child.level === TestLevel.Method) {
- childResults.push(testResultManager.getResultById(childId));
- }
- }
- }
-
- private getResultIcon(result: ITestResult): string {
- switch (result.status) {
- case TestStatus.Pass:
- return '$(check)';
- case TestStatus.Fail:
- return '$(x)';
- default:
- return '';
- }
- }
-
- private getResultIcons(results: Array): string {
- const passNum: number = results.filter((result: ITestResult | undefined) => result && result.status === TestStatus.Pass).length;
- const failNum: number = results.filter((result: ITestResult | undefined) => result && result.status === TestStatus.Fail).length;
- if (failNum > 0) {
- return '$(x)';
- } else if (passNum === 0) {
- return '';
- } else if (passNum === results.length) {
- return '$(check)';
- }
-
- return '?';
- }
-}
diff --git a/src/commands/explorerCommands.ts b/src/commands/explorerCommands.ts
deleted file mode 100644
index be14ca9a..00000000
--- a/src/commands/explorerCommands.ts
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license.
-
-import { CancellationToken, DebugConfiguration, Progress, ProgressLocation, Range, TextDocument, Uri, ViewColumn, window, workspace } from 'vscode';
-import { IProgressReporter } from '../debugger.api';
-import { progressProvider } from '../extension';
-import { logger } from '../logger/logger';
-import { ITestItem, TestKind, TestLevel } from '../protocols';
-import { IRunnerContext } from '../runners/models';
-import { runnerScheduler } from '../runners/runnerScheduler';
-import { testItemModel } from '../testItemModel';
-import { executeTestsFromUri } from './runFromUri';
-
-export async function openTextDocument(uri: Uri, range?: Range): Promise {
- const document: TextDocument = await workspace.openTextDocument(uri);
- await window.showTextDocument(document, {selection: range, viewColumn: ViewColumn.One});
-}
-
-export async function runTestsFromExplorer(node?: ITestItem, launchConfiguration?: DebugConfiguration): Promise {
- return executeTestsFromExplorer(false /* isDebug */, node, launchConfiguration);
-}
-
-export async function debugTestsFromExplorer(node?: ITestItem, launchConfiguration?: DebugConfiguration): Promise {
- return executeTestsFromExplorer(true /* isDebug */, node, launchConfiguration);
-}
-
-async function executeTestsFromExplorer(isDebug: boolean, node?: ITestItem, launchConfiguration?: DebugConfiguration): Promise {
- const runnerContext: IRunnerContext = {
- scope: TestLevel.Root,
- testUri: '',
- fullName: '',
- projectName: '',
- kind: TestKind.None,
- isDebug,
- tests: [],
- };
- if (node) {
- runnerContext.scope = node.level;
- runnerContext.projectName = node.project;
- runnerContext.testUri = Uri.parse(node.location.uri).toString();
- if (node.level >= TestLevel.Package) {
- runnerContext.fullName = node.fullName;
- }
- if (node.level === TestLevel.Method) {
- runnerContext.tests = [node];
- }
- }
- return executeTests(runnerContext, launchConfiguration);
-}
-
-export async function runTestsFromJavaProjectExplorer(node: any, isDebug: boolean): Promise {
- if (node._nodeData.kind === 6 /*PrimaryType*/) {
- return executeTestsFromUri(Uri.parse(node._nodeData.uri), undefined, isDebug);
- }
-
- const runnerContext: IRunnerContext = {
- scope: TestLevel.Package,
- testUri: node._nodeData.uri,
- fullName: node._nodeData.name,
- projectName: node._project.name,
- kind: TestKind.None,
- isDebug,
- tests: [],
- // isPackage is only available when the explorer is in hierarchical mode
- isHierarchicalPackage: node._nodeData.isPackage,
- };
-
- return executeTests(runnerContext);
-}
-
-async function executeTests(runnerContext: IRunnerContext, launchConfiguration?: DebugConfiguration): Promise {
- const progressReporter: IProgressReporter | undefined = progressProvider?.createProgressReporter(runnerContext.isDebug ? 'Debug Test' : 'Run Test', ProgressLocation.Notification, true);
- if (runnerContext.tests.length === 0) {
- try {
- await searchTestItems(runnerContext, progressReporter);
- } catch (e) {
- // so far the promise is only rejected on cancellation
- logger.info('Test job is canceled.\n');
- progressReporter?.done();
- return;
- }
- }
-
- if (runnerContext.tests.length === 0) {
- window.showInformationMessage(`No tests found under: '${Uri.parse(runnerContext.testUri).fsPath}'.`);
- progressReporter?.done();
- return;
- }
-
- return runnerScheduler.run(runnerContext, progressReporter, launchConfiguration);
-}
-
-async function searchTestItems(runnerContext: IRunnerContext, progressReporter?: IProgressReporter): Promise {
- return new Promise(async (resolve: () => void, reject: () => void): Promise => {
- const searchImpl: (token: CancellationToken) => Promise = async (token: CancellationToken) => {
- token.onCancellationRequested(reject);
- runnerContext.tests = await testItemModel.getAllNodes(runnerContext.scope, runnerContext.fullName, runnerContext.testUri, runnerContext.isHierarchicalPackage, token);
- return resolve();
- };
-
- if (progressReporter) {
- progressReporter.report('Searching tests...');
- return searchImpl(progressReporter.getCancellationToken());
- } else {
- window.withProgress(
- { location: ProgressLocation.Notification, cancellable: true },
- async (progress: Progress, token: CancellationToken): Promise => {
- progress.report({ message: 'Searching tests...' });
- return searchImpl(token);
- },
- );
- }
- });
-}
diff --git a/src/commands/generationCommands.ts b/src/commands/generationCommands.ts
index bfec677a..5e835efc 100644
--- a/src/commands/generationCommands.ts
+++ b/src/commands/generationCommands.ts
@@ -3,11 +3,12 @@
import { commands, Disposable, ExtensionContext, QuickInputButton, QuickPick, QuickPickItem, TextEdit, ThemeIcon, Uri, window, workspace, WorkspaceEdit } from 'vscode';
import * as protocolConverter from 'vscode-languageclient/lib/protocolConverter';
-import * as commandUtils from '../utils/commandUtils';
+import { JavaTestRunnerDelegateCommands } from '../constants';
+import { executeJavaLanguageServerCommand } from '../utils/commandUtils';
const converter: protocolConverter.Converter = protocolConverter.createConverter();
export async function generateTests(uri: Uri, cursorOffset: number): Promise {
- const edit: WorkspaceEdit = converter.asWorkspaceEdit(await commandUtils.generateTests(uri, cursorOffset));
+ const edit: WorkspaceEdit = converter.asWorkspaceEdit(await askServerToGenerateTests(uri, cursorOffset));
if (edit) {
await workspace.applyEdit(edit);
const entries: Array<[Uri, TextEdit[]]> = edit.entries();
@@ -115,6 +116,10 @@ export async function registerAskForInputCommand(context: ExtensionContext): Pro
}));
}
+async function askServerToGenerateTests(uri: Uri, cursorOffset: number): Promise {
+ return await executeJavaLanguageServerCommand(JavaTestRunnerDelegateCommands.GENERATE_TESTS, uri.toString(), cursorOffset);
+}
+
function checkJavaQualifiedName(value: string): string {
if (!value || !value.trim()) {
return 'Input cannot be empty.';
diff --git a/src/commands/logCommands.ts b/src/commands/logCommands.ts
deleted file mode 100644
index d9f5ae3f..00000000
--- a/src/commands/logCommands.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license.
-
-import * as fse from 'fs-extra';
-import * as path from 'path';
-import { TextDocument, ViewColumn, window, workspace } from 'vscode';
-import { LOG_FILE_NAME } from '../constants/configs';
-import { logger } from '../logger/logger';
-import { outputChannelTransport } from '../logger/outputChannelTransport';
-
-export async function openLogFile(storagePath: string): Promise {
- const logFilePath: string = path.join(storagePath, LOG_FILE_NAME);
- if (!await fse.pathExists(logFilePath)) {
- const errorMsg: string = 'The log file does not exist.';
- logger.error(errorMsg);
- await window.showErrorMessage(errorMsg);
- return;
- }
- const textDocument: TextDocument = await workspace.openTextDocument(logFilePath);
- window.showTextDocument(textDocument, ViewColumn.Active, false);
-}
-
-export function showOutputChannel(): void {
- outputChannelTransport.show();
-}
diff --git a/src/commands/projectExplorerCommands.ts b/src/commands/projectExplorerCommands.ts
new file mode 100644
index 00000000..886fd1e0
--- /dev/null
+++ b/src/commands/projectExplorerCommands.ts
@@ -0,0 +1,67 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+import * as path from 'path';
+import { TestItem, TestRunRequest, Uri } from 'vscode';
+import { sendError } from 'vscode-extension-telemetry-wrapper';
+import { loadChildren, runTests, testController } from '../controller/testController';
+import { loadJavaProjects, updateItemForDocument } from '../controller/utils';
+import { IProgressReporter } from '../debugger.api';
+import { progressProvider } from '../extension';
+import { TestLevel } from '../types';
+
+export async function runTestsFromJavaProjectExplorer(node: any, isDebug: boolean): Promise {
+ const testLevel: TestLevel = getTestLevel(node._nodeData);
+ const isHierarchicalMode: boolean = isHierarchical(node._nodeData);
+ const progressReporter: IProgressReporter | undefined = progressProvider?.createProgressReporter(isDebug ? 'Debug Test' : 'Run Test');
+ progressReporter?.report('Searching tests...');
+ const tests: TestItem[] = [];
+ if (testLevel === TestLevel.Class) {
+ tests.push(...await updateItemForDocument(node._nodeData.uri));
+ } else if (testLevel === TestLevel.Package) {
+ if (!testController?.items.size) {
+ await loadJavaProjects();
+ }
+ const projectName: string = node._project.name;
+ const projectItem: TestItem | undefined = testController!.items.get(projectName);
+ if (!projectItem) {
+ sendError(new Error('The project name of the node in java project explorer cannot be found in test explorer'));
+ return;
+ }
+ await loadChildren(projectItem);
+ const nodeFsPath: string = Uri.parse(node._nodeData.uri).fsPath;
+ projectItem.children.forEach((child: TestItem) => {
+ const itemPath: string = child.uri?.fsPath || '';
+ if (isHierarchicalMode || node._nodeData.kind === 4 /*packageRoot*/) {
+ // if the selected node is a package root or the view is in hierarchical mode,
+ // all the test items whose path start from the path of the selected node will be added
+ if (itemPath.startsWith(nodeFsPath)) {
+ tests.push(child);
+ }
+ } else {
+ // in flat mode, we require the paths exact match
+ if (path.relative(itemPath, nodeFsPath) === '') {
+ tests.push(child);
+ }
+ }
+ });
+ }
+
+ const request: TestRunRequest = new TestRunRequest(tests, undefined);
+
+ await runTests(request, { progressReporter, isDebug });
+}
+
+function getTestLevel(nodeData: any): TestLevel {
+ // The command will only register on the class/package/packageRoot
+ // nodes of the Java Project explorer
+ if (nodeData.kind === 6 /*PrimaryType*/) {
+ return TestLevel.Class;
+ } else {
+ return TestLevel.Package;
+ }
+}
+
+function isHierarchical(nodeData: any): boolean {
+ return !!nodeData.isPackage;
+}
diff --git a/src/commands/runFromCodeLens.ts b/src/commands/runFromCodeLens.ts
deleted file mode 100644
index 11e669c4..00000000
--- a/src/commands/runFromCodeLens.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license.
-
-import { ITestItem } from '../protocols';
-import { IRunnerContext } from '../runners/models';
-import { runnerScheduler } from '../runners/runnerScheduler';
-
-export async function runFromCodeLens(test: ITestItem, isDebug: boolean): Promise {
- const runnerContext: IRunnerContext = {
- scope: test.level,
- testUri: test.location.uri,
- fullName: test.fullName,
- kind: test.kind,
- projectName: test.project,
- tests: [test],
- isDebug,
- };
-
- await runnerScheduler.run(runnerContext);
-}
diff --git a/src/commands/runFromUri.ts b/src/commands/runFromUri.ts
deleted file mode 100644
index c8480bfb..00000000
--- a/src/commands/runFromUri.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license.
-
-import * as path from 'path';
-import { Uri, window, workspace } from 'vscode';
-import { IProgressReporter } from '../debugger.api';
-import { progressProvider } from '../extension';
-import { ITestItem, TestLevel } from '../protocols';
-import { IRunnerContext } from '../runners/models';
-import { runnerScheduler } from '../runners/runnerScheduler';
-import { testItemModel } from '../testItemModel';
-
-export async function executeTestsFromUri(uri: Uri | undefined, progressReporter: IProgressReporter | undefined, isDebug: boolean): Promise {
- if (!uri) {
- if (!window.activeTextEditor) {
- return;
- }
- uri = window.activeTextEditor.document.uri;
- }
-
- if (uri.scheme !== 'file' || path.extname(uri.fsPath) !== '.java') {
- return;
- }
-
- if (!workspace.getWorkspaceFolder(uri)) {
- window.showInformationMessage(`The file: '${uri.fsPath}' does not belong to the current workspace.`);
- return;
- }
-
- progressReporter = progressReporter || progressProvider?.createProgressReporter(isDebug ? 'Debug Test' : 'Run Test');
-
- progressReporter?.report('Searching tests...');
-
- const tests: ITestItem[] = await testItemModel.getItemsForCodeLens(uri);
- const testItemForPrimaryType: ITestItem | undefined = tests.find((test: ITestItem) => {
- return test.level === TestLevel.Class;
- });
-
- if (!testItemForPrimaryType) {
- window.showInformationMessage(`No tests in file: '${uri.fsPath}'.`);
- progressReporter?.done();
- return;
- }
-
- const runnerContext: IRunnerContext = {
- scope: testItemForPrimaryType.level,
- testUri: testItemForPrimaryType.location.uri,
- fullName: testItemForPrimaryType.fullName,
- kind: testItemForPrimaryType.kind,
- projectName: testItemForPrimaryType.project,
- tests: [testItemForPrimaryType],
- isDebug,
- };
-
- await runnerScheduler.run(runnerContext, progressReporter);
-}
diff --git a/src/commands/testExplorerCommands.ts b/src/commands/testExplorerCommands.ts
new file mode 100644
index 00000000..e61329c5
--- /dev/null
+++ b/src/commands/testExplorerCommands.ts
@@ -0,0 +1,44 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+import { DebugConfiguration, TestItem, TestRunRequest } from 'vscode';
+import { runTests, testController } from '../controller/testController';
+import { loadJavaProjects } from '../controller/utils';
+
+/**
+ * This function is used to exposed as a command, which other extensions can trigger
+ * their customized run/debug requests to test runner. (e.g. the PDE extension)
+ *
+ * Note that, the test item in the method parameters is not the exact test item in the test explorer (another instance).
+ * To get the metadata of the real test item, we have to record the path from test item to root, and then trace back that
+ * path to get the real one.
+ */
+export async function runTestsFromTestExplorer(testItem: TestItem, launchConfiguration: DebugConfiguration, isDebug: boolean): Promise {
+ const pathToRoot: string[] = [];
+ do {
+ pathToRoot.push(testItem.id);
+ testItem = testItem.parent!;
+ } while (testItem.parent);
+ let currentItem: TestItem | undefined = testController?.items.get(pathToRoot.pop()!);
+ if (!currentItem) {
+ return;
+ }
+ while (pathToRoot.length) {
+ const id: string = pathToRoot.pop()!;
+ currentItem = currentItem.children.get(id);
+ if (!currentItem) {
+ return;
+ }
+ }
+ const request: TestRunRequest = new TestRunRequest([currentItem], undefined);
+
+ await runTests(request, { launchConfiguration, isDebug });
+}
+
+export async function refresh(): Promise {
+ testController?.items.forEach((root: TestItem) => {
+ testController?.items.delete(root.id);
+ });
+
+ await loadJavaProjects();
+}
diff --git a/src/commands/testReportCommands.ts b/src/commands/testReportCommands.ts
index 203c5fe1..0ecfdd37 100644
--- a/src/commands/testReportCommands.ts
+++ b/src/commands/testReportCommands.ts
@@ -1,11 +1,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
-import { commands, Position, QuickPickItem, Range, Uri, ViewColumn, window } from 'vscode';
-import { ILocation } from '../../extension.bundle';
-import { JavaTestRunnerCommands } from '../constants/commands';
-import { logger } from '../logger/logger';
-import { resolveStackTraceLocation, searchTestLocation } from '../utils/commandUtils';
+import { commands, Position, Range, Uri, ViewColumn, window } from 'vscode';
+import { JavaLanguageServerCommands } from '../constants';
+import { executeJavaLanguageServerCommand } from '../utils/commandUtils';
export async function openStackTrace(trace: string, fullName: string): Promise {
if (!trace || !fullName) {
@@ -35,29 +33,7 @@ export async function openStackTrace(trace: string, fullName: string): Promise {
- if (uri && range) {
- return commands.executeCommand(JavaTestRunnerCommands.OPEN_DOCUMENT, Uri.parse(uri), JSON.parse(range) as Range);
- } else if (fullName) {
- const methodEndIndex: number = fullName.indexOf('[');
- const items: ILocation[] = await searchTestLocation(fullName.slice(fullName.indexOf('@') + 1, methodEndIndex < 0 ? undefined : methodEndIndex));
- if (items.length === 1) {
- return commands.executeCommand(JavaTestRunnerCommands.OPEN_DOCUMENT, Uri.parse(items[0].uri), items[0].range);
- } else if (items.length > 1) {
- const pick: ILocationQuickPick | undefined = await window.showQuickPick(items.map((item: ILocation) => {
- return { label: fullName, detail: Uri.parse(item.uri).fsPath, location: item };
- }), { placeHolder: 'Select the file you want to navigate to' });
- if (pick) {
- return commands.executeCommand(JavaTestRunnerCommands.OPEN_DOCUMENT, Uri.parse(pick.location.uri), pick.location.range);
- }
- } else {
- logger.error('No test item could be found from Language Server.');
- }
- } else {
- logger.error('Could not open the document, Neither the Uri nor full name is null.');
- }
-}
-
-interface ILocationQuickPick extends QuickPickItem {
- location: ILocation;
+async function resolveStackTraceLocation(trace: string, projectNames: string[]): Promise {
+ return await executeJavaLanguageServerCommand(
+ JavaLanguageServerCommands.RESOLVE_STACKTRACE_LOCATION, trace, projectNames) || '';
}
diff --git a/src/constants.ts b/src/constants.ts
new file mode 100644
index 00000000..5c7b7052
--- /dev/null
+++ b/src/constants.ts
@@ -0,0 +1,67 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+export namespace JavaLanguageServerCommands {
+ export const EXECUTE_WORKSPACE_COMMAND: string = 'java.execute.workspaceCommand';
+ export const RESOLVE_STACKTRACE_LOCATION: string = 'java.project.resolveStackTraceLocation';
+}
+
+export namespace JavaTestRunnerDelegateCommands {
+ export const GET_TEST_SOURCE_PATH: string = 'vscode.java.test.get.testpath';
+ export const RESOLVE_JUNIT_ARGUMENT: string = 'vscode.java.test.junit.argument';
+ export const GENERATE_TESTS: string = 'vscode.java.test.generateTests';
+ export const FIND_JAVA_PROJECTS: string = 'vscode.java.test.findJavaProjects';
+ export const FIND_TEST_PACKAGES_AND_TYPES: string = 'vscode.java.test.findTestPackagesAndTypes';
+ export const FIND_DIRECT_CHILDREN_FOR_CLASS: string = 'vscode.java.test.findDirectTestChildrenForClass';
+ export const FIND_TEST_TYPES_AND_METHODS: string = 'vscode.java.test.findTestTypesAndMethods';
+ export const RESOLVE_PATH: string = 'vscode.java.test.resolvePath';
+}
+
+export namespace JavaTestRunnerCommands {
+ export const RUN_TEST_FROM_EDITOR: string = 'java.test.editor.run';
+ export const DEBUG_TEST_FROM_EDITOR: string = 'java.test.editor.debug';
+ export const RUN_TEST_FROM_JAVA_PROJECT_EXPLORER: string = 'java.test.runFromJavaProjectExplorer';
+ export const DEBUG_TEST_FROM_JAVA_PROJECT_EXPLORER: string = 'java.test.debugFromJavaProjectExplorer';
+ export const RUN_FROM_TEST_EXPLORER: string = 'java.test.explorer.run';
+ export const DEBUG_FROM_TEST_EXPLORER: string = 'java.test.explorer.debug';
+ export const REFRESH_TEST_EXPLORER: string = 'java.test.refreshExplorer';
+ export const JAVA_TEST_GENERATE_TESTS: string = 'java.test.generateTests';
+ export const FIND_TEST_LOCATION: string = 'vscode.java.test.findTestLocation';
+ export const JAVA_TEST_OPEN_STACKTRACE: string = '_java.test.openStackTrace';
+}
+
+export namespace VSCodeCommands {
+ export const RUN_TESTS_IN_CURRENT_FILE: string = 'testing.runCurrentFile';
+ export const DEBUG_TESTS_IN_CURRENT_FILE: string = 'testing.debugCurrentFile';
+ export const REFRESH_TESTS: string = 'testing.refreshTests';
+}
+
+export namespace Configurations {
+ export const LOCAL_HOST: string = '127.0.0.1';
+ export const DEFAULT_CONFIG_NAME_SETTING_KEY: string = 'java.test.defaultConfig';
+ export const CONFIG_SETTING_KEY: string = 'java.test.config';
+ export const BUILTIN_CONFIG_NAME: string = 'default';
+ export const HINT_FOR_DEFAULT_CONFIG_SETTING_KEY: string = 'java.test.message.hintForSettingDefaultConfig';
+}
+
+export namespace Dialog {
+ export const NEVER_SHOW: string = 'Never Show again';
+ export const YES: string = 'Yes';
+ export const NO: string = 'No';
+}
+
+export namespace ExtensionName {
+ export const JAVA_DEBUGGER: string = 'vscjava.vscode-java-debug';
+ export const JAVA_LANGUAGE_SUPPORT: string = 'redhat.java';
+}
+
+export namespace Context {
+ export const ACTIVATION_CONTEXT_KEY: string = 'java:testRunnerActivated';
+}
+
+/**
+ * This is the prefix of the invocation test item's id.
+ * Invocation test items are created during test run.
+ * For example, the invocations from a parameterized test.
+ */
+export const INVOCATION_PREFIX: string = '[__INVOCATION__]-';
diff --git a/src/constants/commands.ts b/src/constants/commands.ts
deleted file mode 100644
index 3f69266b..00000000
--- a/src/constants/commands.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license.
-
-export namespace JavaLanguageServerCommands {
- export const EXECUTE_WORKSPACE_COMMAND: string = 'java.execute.workspaceCommand';
- export const RESOLVE_STACKTRACE_LOCATION: string = 'java.project.resolveStackTraceLocation';
-}
-
-export namespace JavaTestRunnerDelegateCommands {
- export const GET_TEST_SOURCE_PATH: string = 'vscode.java.test.get.testpath';
- export const SEARCH_TEST_ITEMS: string = 'vscode.java.test.search.items';
- export const SEARCH_TEST_ITEMS_ALL: string = 'vscode.java.test.search.items.all';
- export const SEARCH_TEST_CODE_LENS: string = 'vscode.java.test.search.codelens';
- export const SEARCH_TEST_LOCATION: string = 'vscode.java.test.search.location';
- export const RESOLVE_JUNIT_ARGUMENT: string = 'vscode.java.test.junit.argument';
- export const GENERATE_TESTS: string = 'vscode.java.test.generateTests';
-}
-
-export namespace JavaTestRunnerCommands {
- export const OPEN_DOCUMENT: string = 'java.test.open.document';
- export const REFRESH_EXPLORER: string = 'java.test.explorer.refresh';
- export const RUN_TEST_FROM_CODELENS: string = 'java.test.run';
- export const DEBUG_TEST_FROM_CODELENS: string = 'java.test.debug';
- export const RUN_TEST_FROM_EXPLORER: string = 'java.test.explorer.run';
- export const RUN_TEST_FROM_EDITOR: string = 'java.test.editor.run';
- export const DEBUG_TEST_FROM_EDITOR: string = 'java.test.editor.debug';
- export const DEBUG_ALL_TEST_FROM_EXPLORER: string = 'java.test.explorer.debugAll';
- export const RUN_ALL_TEST_FROM_EXPLORER: string = 'java.test.explorer.runAll';
- export const DEBUG_TEST_FROM_EXPLORER: string = 'java.test.explorer.debug';
- export const RUN_TEST_FROM_JAVA_PROJECT_EXPLORER: string = 'java.test.runFromJavaProjectExplorer';
- export const DEBUG_TEST_FROM_JAVA_PROJECT_EXPLORER: string = 'java.test.debugFromJavaProjectExplorer';
- export const SHOW_TEST_REPORT: string = 'java.test.show.report';
- export const SHOW_TEST_OUTPUT: string = 'java.test.show.output';
- export const OPEN_TEST_LOG: string = 'java.test.open.log';
- export const RELAUNCH_TESTS: string = 'java.test.relaunch';
- export const JAVA_TEST_CANCEL: string = 'java.test.cancel';
- export const JAVA_CONFIG_MIGRATE: string = 'java.test.config.migrate';
- export const JAVA_TEST_REPORT_OPEN_STACKTRACE: string = 'java.test.report.openStackTrace';
- export const JAVA_TEST_REPORT_OPEN_TEST_SOURCE_LOCATION: string = 'java.test.report.openTestSourceLocation';
- export const JAVA_TEST_GENERATE_TESTS: string = 'java.test.generateTests';
-}
-
-export namespace VsCodeCommands {
- export const VSCODE_OPEN: string = 'vscode.open';
-}
diff --git a/src/constants/configs.ts b/src/constants/configs.ts
deleted file mode 100644
index 3ca50a2c..00000000
--- a/src/constants/configs.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license.
-
-export const LOCAL_HOST: string = '127.0.0.1';
-
-export const LOG_FILE_NAME: string = 'java_test_runner.log';
-export const LOG_FILE_MAX_SIZE: number = 5 * 1024 * 1024;
-export const LOG_FILE_MAX_NUMBER: number = 2;
-export const LOG_LEVEL_SETTING_KEY: string = 'java.test.log.level';
-export const DEFAULT_LOG_LEVEL: string = 'info';
-
-export const DEFAULT_CONFIG_NAME_SETTING_KEY: string = 'java.test.defaultConfig';
-export const CONFIG_SETTING_KEY: string = 'java.test.config';
-export const BUILTIN_CONFIG_NAME: string = 'default';
-
-export const REPORT_POSITION_SETTING_KEY: string = 'java.test.report.position';
-export const DEFAULT_REPORT_POSITION: string = 'sideView';
-
-export const ENABLE_EDITOR_SHORTCUTS_KEY: string = 'java.test.editor.enableShortcuts';
-
-export const REPORT_SHOW_SETTING_KEY: string = 'java.test.report.showAfterExecution';
-export const DEFAULT_REPORT_SHOW: string = 'onFailure';
-
-export const HINT_FOR_DEPRECATED_CONFIG_SETTING_KEY: string = 'java.test.message.hintForDeprecatedConfig';
-export const CONFIG_DOCUMENT_URL: string = 'https://aka.ms/java-test-config';
-export const HINT_FOR_DEFAULT_CONFIG_SETTING_KEY: string = 'java.test.message.hintForSetingDefaultConfig';
-
-export const ACTIVATION_CONTEXT_KEY: string = 'java:testRunnerActivated';
-
-export enum ReportShowSetting {
- Always = 'always',
- OnFail = 'onFailure',
- Never = 'never',
-}
diff --git a/src/constants/dialogOptions.ts b/src/constants/dialogOptions.ts
deleted file mode 100644
index 4d257316..00000000
--- a/src/constants/dialogOptions.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license.
-
-export const LEARN_MORE: string = 'Learn More';
-export const NEVER_SHOW: string = 'Never Show again';
-export const YES: string = 'Yes';
-export const NO: string = 'No';
-export const OPEN_SETTING: string = 'Open Settings';
-export const OPEN_OUTPUT_CHANNEL: string = 'Open Output Channel';
diff --git a/src/controller/debouncing.ts b/src/controller/debouncing.ts
new file mode 100644
index 00000000..5783b233
--- /dev/null
+++ b/src/controller/debouncing.ts
@@ -0,0 +1,33 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+import * as LRUCache from 'lru-cache';
+import { Uri } from 'vscode';
+
+export const lruCache: LRUCache = new LRUCache(32);
+
+// See: https://github.com/microsoft/vscode/blob/94c9ea46838a9a619aeafb7e8afd1170c967bb55/src/vs/base/common/numbers.ts
+export class MovingAverage {
+
+ private _n: number = 1;
+ private _val: number = 0;
+
+ public update(value: number): this {
+ this._val = this._val + (value - this._val) / this._n;
+ this._n += 1;
+ return this;
+ }
+
+ public get value(): number {
+ return this._val;
+ }
+}
+
+export function getRequestDelay(uri: Uri): number {
+ const avg: MovingAverage | undefined = lruCache.get(uri);
+ if (!avg) {
+ return 350;
+ }
+ // See: https://github.com/microsoft/vscode/blob/94c9ea46838a9a619aeafb7e8afd1170c967bb55/src/vs/editor/common/modes/languageFeatureRegistry.ts#L204
+ return Math.max(350, Math.floor(1.3 * avg.value));
+}
diff --git a/src/controller/testController.ts b/src/controller/testController.ts
new file mode 100644
index 00000000..c99a2d23
--- /dev/null
+++ b/src/controller/testController.ts
@@ -0,0 +1,426 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+import * as _ from 'lodash';
+import { CancellationToken, DebugConfiguration, Disposable, FileSystemWatcher, RelativePattern, TestController, TestItem, TestRun, TestRunProfileKind, TestRunRequest, tests, Uri, window, workspace, WorkspaceFolder } from 'vscode';
+import { instrumentOperation, sendError, sendInfo } from 'vscode-extension-telemetry-wrapper';
+import { INVOCATION_PREFIX } from '../constants';
+import { IProgressReporter } from '../debugger.api';
+import { extensionContext, isStandardServerReady, progressProvider } from '../extension';
+import { testSourceProvider } from '../provider/testSourceProvider';
+import { IExecutionConfig } from '../runConfigs';
+import { BaseRunner } from '../runners/baseRunner/BaseRunner';
+import { JUnitRunner } from '../runners/junitRunner/JunitRunner';
+import { TestNGRunner } from '../runners/testngRunner/TestNGRunner';
+import { IJavaTestItem, IRunTestContext, TestKind, TestLevel } from '../types';
+import { loadRunConfig } from '../utils/configUtils';
+import { resolveLaunchConfigurationForRunner } from '../utils/launchUtils';
+import { dataCache, ITestItemData } from './testItemDataCache';
+import { findDirectTestChildrenForClass, findTestPackagesAndTypes, findTestTypesAndMethods, loadJavaProjects, resolvePath, synchronizeItemsRecursively, updateItemForDocumentWithDebounce } from './utils';
+
+export let testController: TestController | undefined;
+
+export function createTestController(): void {
+ if (!isStandardServerReady()) {
+ return;
+ }
+ testController?.dispose();
+ testController = tests.createTestController('javaTestController', 'Java Test');
+
+ testController.resolveHandler = async (item: TestItem) => {
+ await loadChildren(item);
+ };
+
+ testController.createRunProfile('Run Tests', TestRunProfileKind.Run, runHandler, true);
+ testController.createRunProfile('Debug Tests', TestRunProfileKind.Debug, runHandler, true);
+
+ startWatchingWorkspace();
+}
+
+export const loadChildren: (item: TestItem, token?: CancellationToken) => any = instrumentOperation('java.test.explorer.loadChildren', async (_operationId: string, item: TestItem, token?: CancellationToken) => {
+ if (!item) {
+ await loadJavaProjects();
+ return;
+ }
+
+ const data: ITestItemData | undefined = dataCache.get(item);
+ if (!data) {
+ return;
+ }
+ if (data.testLevel === TestLevel.Project) {
+ const packageAndTypes: IJavaTestItem[] = await findTestPackagesAndTypes(data.jdtHandler, token);
+ synchronizeItemsRecursively(item, packageAndTypes);
+ } else if (data.testLevel === TestLevel.Package) {
+ // unreachable code
+ } else if (data.testLevel === TestLevel.Class) {
+ if (!data.jdtHandler) {
+ sendError(new Error('The class node does not have jdt handler id.'));
+ return;
+ }
+ const testMethods: IJavaTestItem[] = await findDirectTestChildrenForClass(data.jdtHandler, token);
+ synchronizeItemsRecursively(item, testMethods);
+ }
+});
+
+async function startWatchingWorkspace(): Promise {
+ if (!workspace.workspaceFolders) {
+ return;
+ }
+
+ for (const workspaceFolder of workspace.workspaceFolders) {
+ const patterns: RelativePattern[] = await testSourceProvider.getTestSourcePattern(workspaceFolder);
+ for (const pattern of patterns) {
+ const watcher: FileSystemWatcher = workspace.createFileSystemWatcher(pattern);
+ extensionContext.subscriptions.push(
+ watcher,
+ watcher.onDidCreate(async (uri: Uri) => {
+ const testTypes: IJavaTestItem[] = await findTestTypesAndMethods(uri.toString());
+ if (testTypes.length === 0) {
+ return;
+ }
+ await updateItemForDocumentWithDebounce(uri, testTypes);
+ }),
+ watcher.onDidChange(async (uri: Uri) => {
+ await updateItemForDocumentWithDebounce(uri);
+ }),
+ watcher.onDidDelete(async (uri: Uri) => {
+ const pathsData: IJavaTestItem[] = await resolvePath(uri.toString());
+ if (_.isEmpty(pathsData) || pathsData.length < 2) {
+ return;
+ }
+
+ const projectData: IJavaTestItem = pathsData[0];
+ if (projectData.testLevel !== TestLevel.Project) {
+ return;
+ }
+
+ const belongingProject: TestItem | undefined = testController?.items.get(projectData.id);
+ if (!belongingProject) {
+ return;
+ }
+
+ const packageData: IJavaTestItem = pathsData[1];
+ if (packageData.testLevel !== TestLevel.Package) {
+ return;
+ }
+
+ const belongingPackage: TestItem | undefined = belongingProject.children.get(packageData.id);
+ if (!belongingPackage) {
+ return;
+ }
+
+ belongingPackage.children.forEach((item: TestItem) => {
+ if (item.uri?.toString() === uri.toString()) {
+ belongingPackage.children.delete(item.id);
+ }
+ });
+
+ if (belongingPackage.children.size === 0) {
+ belongingProject.children.delete(belongingPackage.id);
+ }
+ }),
+ );
+ }
+ }
+}
+
+async function runHandler(request: TestRunRequest, token: CancellationToken): Promise {
+ await runTests(request, { token, isDebug: !!request.profile?.label.includes('Debug') });
+}
+
+export const runTests: (request: TestRunRequest, option: IRunOption) => any = instrumentOperation('java.test.runTests', async (operationId: string, request: TestRunRequest, option: IRunOption) => {
+ sendInfo(operationId, {
+ isDebug: `${option.isDebug}`,
+ });
+
+ const testItems: TestItem[] = await new Promise(async (resolve: (result: TestItem[]) => void): Promise => {
+ option.progressReporter = option.progressReporter ?? progressProvider?.createProgressReporter(option.isDebug ? 'Debug Tests' : 'Run Tests');
+ option.token?.onCancellationRequested(() => {
+ option.progressReporter?.done();
+ return resolve([]);
+ });
+ const progressToken: CancellationToken | undefined = option.progressReporter?.getCancellationToken();
+ option.onProgressCancelHandler = progressToken?.onCancellationRequested(() => {
+ option.progressReporter?.done();
+ return resolve([]);
+ });
+ option.progressReporter?.report('Searching tests...');
+ const result: TestItem[] = await getIncludedItems(request, progressToken);
+ await expandTests(result, TestLevel.Method, progressToken);
+ return resolve(result);
+ });
+
+ if (testItems.length === 0) {
+ option.progressReporter?.done();
+ return;
+ }
+
+ const run: TestRun = testController!.createTestRun(request);
+ try {
+ await new Promise(async (resolve: () => void): Promise => {
+ const token: CancellationToken = option.token ?? run.token;
+ token.onCancellationRequested(() => {
+ option.progressReporter?.done();
+ run.end();
+ return resolve();
+ });
+ enqueueTestMethods(testItems, run);
+ const queue: TestItem[][] = mergeTestMethods(testItems);
+ for (const testsInQueue of queue) {
+ if (testsInQueue.length === 0) {
+ continue;
+ }
+ const testProjectMapping: Map = mapTestItemsByProject(testsInQueue);
+ for (const [projectName, itemsPerProject] of testProjectMapping.entries()) {
+ const testKindMapping: Map = mapTestItemsByKind(itemsPerProject);
+ for (const [kind, items] of testKindMapping.entries()) {
+ if (option.progressReporter?.isCancelled()) {
+ option.progressReporter = progressProvider?.createProgressReporter(option.isDebug ? 'Debug Tests' : 'Run Tests');
+ }
+ let delegatedToDebugger: boolean = false;
+ option.onProgressCancelHandler?.dispose();
+ option.progressReporter?.getCancellationToken().onCancellationRequested(() => {
+ if (delegatedToDebugger) {
+ // If the progress reporter has been delegated to debugger, a cancellation event
+ // might be emitted due to debug session finished, thus we will ignore such event.
+ return;
+ }
+ option.progressReporter?.done();
+ return resolve();
+ });
+ option.progressReporter?.report('Resolving launch configuration...');
+ // TODO: improve the config experience
+ const workspaceFolder: WorkspaceFolder | undefined = workspace.getWorkspaceFolder(items[0].uri!);
+ if (!workspaceFolder) {
+ window.showErrorMessage(`Failed to get workspace folder from test item: ${items[0].label}.`);
+ continue;
+ }
+ const config: IExecutionConfig | undefined = await loadRunConfig(workspaceFolder);
+ if (!config) {
+ continue;
+ }
+ const testContext: IRunTestContext = {
+ isDebug: option.isDebug,
+ kind,
+ projectName,
+ testItems: items,
+ testRun: run,
+ workspaceFolder,
+ };
+ const runner: BaseRunner | undefined = getRunnerByContext(testContext);
+ if (!runner) {
+ window.showErrorMessage(`Failed to get suitable runner for the test kind: ${testContext.kind}.`);
+ continue;
+ }
+ try {
+ await runner.setup();
+ const resolvedConfiguration: DebugConfiguration = option.launchConfiguration ?? await resolveLaunchConfigurationForRunner(runner, testContext, config);
+ resolvedConfiguration.__progressId = option.progressReporter?.getId();
+ delegatedToDebugger = true;
+ await runner.run(resolvedConfiguration, token, option.progressReporter);
+ } finally {
+ await runner.tearDown();
+ }
+ }
+ }
+ }
+ return resolve();
+ });
+ } finally {
+ run.end();
+ }
+});
+
+/**
+ * Set all the test item to queued state
+ */
+function enqueueTestMethods(testItems: TestItem[], run: TestRun): void {
+ const queuedTests: TestItem[] = [...testItems];
+ while (queuedTests.length) {
+ const queuedTest: TestItem = queuedTests.shift()!;
+ run.enqueued(queuedTest);
+ queuedTest.children.forEach((child: TestItem) => {
+ queuedTests.push(child);
+ });
+ }
+}
+
+/**
+ * Filter out the tests which are in the excluding list
+ * @param request the test run request
+ * @returns
+ */
+async function getIncludedItems(request: TestRunRequest, token?: CancellationToken): Promise {
+ let testItems: TestItem[] = [];
+ if (request.include) {
+ testItems.push(...request.include);
+ } else {
+ testController?.items.forEach((item: TestItem) => {
+ testItems.push(item);
+ });
+ }
+ if (testItems.length === 0) {
+ return [];
+ }
+ removeTestInvocations(testItems);
+ testItems = await expandTests(testItems, TestLevel.Class, token);
+ const excludingItems: TestItem[] = await expandTests(request.exclude || [], TestLevel.Class, token);
+ testItems = _.differenceBy(testItems, excludingItems, 'id');
+ return testItems;
+}
+
+/**
+ * Expand the test items to the target level
+ * @param testItems items to expand
+ * @param targetLevel target level to expand
+ */
+async function expandTests(testItems: TestItem[], targetLevel: TestLevel, token?: CancellationToken): Promise {
+ const results: Set = new Set();
+ const queue: TestItem[] = [...testItems];
+ while (queue.length) {
+ const item: TestItem = queue.shift()!;
+ const testLevel: TestLevel | undefined = dataCache.get(item)?.testLevel;
+ if (testLevel === undefined) {
+ continue;
+ }
+ if (testLevel >= targetLevel) {
+ results.add(item);
+ } else {
+ await loadChildren(item, token);
+ item.children.forEach((child: TestItem) => {
+ queue.push(child);
+ });
+ }
+ }
+ return Array.from(results);
+}
+
+/**
+ * Remove the test invocations since they might be changed
+ */
+function removeTestInvocations(testItems: TestItem[]): void {
+ const queue: TestItem[] = [...testItems];
+ while (queue.length) {
+ const item: TestItem = queue.shift()!;
+ if (item.id.startsWith(INVOCATION_PREFIX)) {
+ item.parent?.children.delete(item.id);
+ continue;
+ }
+ item.children.forEach((child: TestItem) => {
+ queue.push(child);
+ });
+ }
+}
+
+/**
+ * Eliminate the test methods if they are contained in the test class.
+ * Because the current test runner cannot run class and methods for the same time,
+ * in the returned array, all the classes are in one group and each method is a group.
+ */
+function mergeTestMethods(testItems: TestItem[]): TestItem[][] {
+ // tslint:disable-next-line: typedef
+ const classMapping: Map = testItems.reduce((map, i) => {
+ const testLevel: TestLevel | undefined = dataCache.get(i)?.testLevel;
+ if (testLevel === undefined) {
+ return map;
+ }
+ if (testLevel === TestLevel.Class) {
+ map.set(i.id, i);
+ }
+ return map;
+ }, new Map());
+
+ // tslint:disable-next-line: typedef
+ const testMapping: Map> = testItems.reduce((map, i) => {
+ const testLevel: TestLevel | undefined = dataCache.get(i)?.testLevel;
+ if (testLevel === undefined) {
+ return map;
+ }
+ if (testLevel !== TestLevel.Method) {
+ return map;
+ }
+
+ // skip the method if it's contained in test classes
+ if (classMapping.has(i.parent?.id || '')) {
+ return map;
+ }
+
+ const value: Set | undefined = map.get(i.parent);
+ if (value) {
+ value.add(i as TestItem);
+ } else {
+ map.set(i.parent, new Set([i]));
+ }
+
+ return map;
+ }, new Map());
+
+ const testMethods: TestItem[][] = [];
+
+ for (const [key, value] of testMapping) {
+ if (key.children.size === value.size) {
+ classMapping.set(key.id, key);
+ } else {
+ for (const method of value.values()) {
+ testMethods.push([method]);
+ }
+ }
+ }
+
+ return [[...classMapping.values()], ...testMethods];
+}
+
+function mapTestItemsByProject(items: TestItem[]): Map {
+ const map: Map = new Map();
+ for (const item of items) {
+ const projectName: string | undefined = dataCache.get(item)?.projectName;
+ if (!projectName) {
+ sendError(new Error('Item does not have project name.'));
+ continue;
+ }
+ const itemsPerProject: TestItem[] | undefined = map.get(projectName);
+ if (itemsPerProject) {
+ itemsPerProject.push(item);
+ } else {
+ map.set(projectName, [item]);
+ }
+ }
+ return map;
+}
+
+function mapTestItemsByKind(items: TestItem[]): Map {
+ const map: Map = new Map();
+ for (const item of items) {
+ const testKind: TestKind | undefined = dataCache.get(item)?.testKind;
+ if (testKind === undefined) {
+ continue;
+ }
+ const itemsPerKind: TestItem[] | undefined = map.get(testKind);
+ if (itemsPerKind) {
+ itemsPerKind.push(item);
+ } else {
+ map.set(testKind, [item]);
+ }
+ }
+ return map;
+}
+
+function getRunnerByContext(testContext: IRunTestContext): BaseRunner | undefined {
+ switch (testContext.kind) {
+ case TestKind.JUnit:
+ case TestKind.JUnit5:
+ return new JUnitRunner(testContext);
+ case TestKind.TestNG:
+ return new TestNGRunner(testContext);
+ default:
+ return undefined;
+ }
+}
+
+interface IRunOption {
+ isDebug: boolean;
+ progressReporter?: IProgressReporter;
+ onProgressCancelHandler?: Disposable;
+ launchConfiguration?: DebugConfiguration;
+ token?: CancellationToken;
+}
diff --git a/src/controller/testItemDataCache.ts b/src/controller/testItemDataCache.ts
new file mode 100644
index 00000000..ee504956
--- /dev/null
+++ b/src/controller/testItemDataCache.ts
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+import { TestItem } from 'vscode';
+import { TestKind, TestLevel } from '../types';
+
+/**
+ * A map cache to save the metadata of the test item.
+ * Use a WeakMap here so the key-value will be automatically collected when
+ * the actual item is disposed.
+ */
+class TestItemDataCache {
+ private cache: WeakMap = new WeakMap();
+
+ public set(item: TestItem, data: ITestItemData): void {
+ this.cache.set(item, data);
+ }
+
+ public get(item: TestItem): ITestItemData | undefined {
+ return this.cache.get(item);
+ }
+
+ public delete(item: TestItem): boolean {
+ return this.cache.delete(item);
+ }
+}
+
+export const dataCache: TestItemDataCache = new TestItemDataCache();
+
+export interface ITestItemData {
+ jdtHandler: string;
+ fullName: string;
+ projectName: string;
+ testLevel: TestLevel;
+ testKind: TestKind;
+}
diff --git a/src/controller/utils.ts b/src/controller/utils.ts
new file mode 100644
index 00000000..2a5047bf
--- /dev/null
+++ b/src/controller/utils.ts
@@ -0,0 +1,262 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+import * as _ from 'lodash';
+import { performance } from 'perf_hooks';
+import { CancellationToken, Range, TestItem, Uri, workspace, WorkspaceFolder } from 'vscode';
+import { sendError } from 'vscode-extension-telemetry-wrapper';
+import { INVOCATION_PREFIX, JavaTestRunnerDelegateCommands } from '../constants';
+import { IJavaTestItem, TestLevel } from '../types';
+import { executeJavaLanguageServerCommand } from '../utils/commandUtils';
+import { getRequestDelay, lruCache, MovingAverage } from './debouncing';
+import { testController } from './testController';
+import { dataCache } from './testItemDataCache';
+
+/**
+ * Load the Java projects, which are the root nodes of the test explorer
+ */
+export async function loadJavaProjects(): Promise {
+ for (const workspaceFolder of workspace.workspaceFolders || [] ) {
+ const testProjects: IJavaTestItem[] = await getJavaProjects(workspaceFolder);
+ for (const project of testProjects) {
+ if (testController?.items.get(project.id)) {
+ continue;
+ }
+ const projectItem: TestItem = createTestItem(project);
+ projectItem.canResolveChildren = true;
+ testController?.items.add(projectItem);
+ }
+ }
+}
+
+/**
+ * This method is used to synchronize the test items for the given parent node recursively. which means:
+ * - If an existing child is not contained in the childrenData parameter, it will be deleted
+ * - If a child does not exist, create it, otherwise, update it as well as its metadata.
+ */
+export function synchronizeItemsRecursively(parent: TestItem, childrenData: IJavaTestItem[] | undefined): void {
+ if (childrenData) {
+ // remove the out-of-date children
+ parent.children.forEach((child: TestItem) => {
+ if (child.id.startsWith(INVOCATION_PREFIX)) {
+ // only remove the invocation items before a new test session starts
+ return;
+ }
+ const existingItem: IJavaTestItem | undefined = childrenData.find((data: IJavaTestItem) => data.id === child.id);
+ if (!existingItem) {
+ parent.children.delete(child.id);
+ }
+ });
+ // update/create children
+ for (const child of childrenData) {
+ const childItem: TestItem = updateOrCreateTestItem(parent, child);
+ if (child.testLevel <= TestLevel.Class) {
+ childItem.canResolveChildren = true;
+ }
+ synchronizeItemsRecursively(childItem, child.children);
+ }
+ }
+}
+
+function updateOrCreateTestItem(parent: TestItem, childData: IJavaTestItem): TestItem {
+ let childItem: TestItem | undefined = parent.children.get(childData.id);
+ if (childItem) {
+ updateTestItem(childItem, childData);
+ } else {
+ childItem = createTestItem(childData, parent);
+ }
+ return childItem;
+}
+
+function updateTestItem(testItem: TestItem, metaInfo: IJavaTestItem): void {
+ testItem.range = asRange(metaInfo.range);
+ if (metaInfo.testLevel !== TestLevel.Invocation) {
+ dataCache.set(testItem, {
+ jdtHandler: metaInfo.jdtHandler,
+ fullName: metaInfo.fullName,
+ projectName: metaInfo.projectName,
+ testLevel: metaInfo.testLevel,
+ testKind: metaInfo.testKind,
+ });
+ }
+}
+
+/**
+ * Create test item which will be shown in the test explorer
+ * @param metaInfo The data from the server side of the test item
+ * @param parent The parent node of the test item (if it has)
+ * @returns The created test item
+ */
+export function createTestItem(metaInfo: IJavaTestItem, parent?: TestItem): TestItem {
+ if (!testController) {
+ throw new Error('Failed to create test item. The test controller is not initialized.');
+ }
+ const item: TestItem = testController.createTestItem(
+ metaInfo.id,
+ metaInfo.label,
+ metaInfo.uri ? Uri.parse(metaInfo.uri) : undefined,
+ );
+ item.range = asRange(metaInfo.range);
+ if (metaInfo.testLevel !== TestLevel.Invocation) {
+ dataCache.set(item, {
+ jdtHandler: metaInfo.jdtHandler,
+ fullName: metaInfo.fullName,
+ projectName: metaInfo.projectName,
+ testLevel: metaInfo.testLevel,
+ testKind: metaInfo.testKind,
+ });
+ }
+ if (parent) {
+ parent.children.add(item);
+ }
+ return item;
+}
+
+let updateNodeForDocumentTimeout: NodeJS.Timer;
+/**
+ * Update test item in a document with adaptive debounce enabled.
+ * @param uri uri of the document
+ * @param testTypes test metadata
+ */
+export async function updateItemForDocumentWithDebounce(uri: Uri, testTypes?: IJavaTestItem[]): Promise {
+ if (updateNodeForDocumentTimeout) {
+ clearTimeout(updateNodeForDocumentTimeout);
+ }
+ const timeout: number = getRequestDelay(uri);
+ return new Promise((resolve: (items: TestItem[]) => void): void => {
+ updateNodeForDocumentTimeout = setTimeout(async () => {
+ const startTime: number = performance.now();
+ const result: TestItem[] = await updateItemForDocument(uri, testTypes);
+ const executionTime: number = performance.now() - startTime;
+ const movingAverage: MovingAverage = lruCache.get(uri) || new MovingAverage();
+ movingAverage.update(executionTime);
+ lruCache.set(uri, movingAverage);
+ return resolve(result);
+ }, timeout);
+ });
+}
+
+/**
+ * Update test item in a document immediately.
+ * @param uri uri of the document
+ * @param testTypes test metadata
+ */
+export async function updateItemForDocument(uri: Uri, testTypes?: IJavaTestItem[]): Promise {
+ testTypes = testTypes ?? await findTestTypesAndMethods(uri.toString());
+ if (testTypes.length === 0) {
+ return [];
+ }
+
+ const belongingPackage: TestItem | undefined = findBelongingPackageItem(testTypes[0])
+ || await resolveBelongingPackage(uri);
+ if (!belongingPackage) {
+ sendError(new Error('Failed to find the belonging package'));
+ return [];
+ }
+
+ const tests: TestItem[] = [];
+ for (const testType of testTypes) {
+ // here we do not directly call synchronizeItemsRecursively() because testTypes here are just part of the
+ // children of the belonging package, we don't want to delete other children unexpectedly.
+ let testTypeItem: TestItem | undefined = belongingPackage.children.get(testType.id);
+ if (!testTypeItem) {
+ testTypeItem = createTestItem(testType, belongingPackage);
+ testTypeItem.canResolveChildren = true;
+ } else {
+ updateTestItem(testTypeItem, testType);
+ }
+ tests.push(testTypeItem);
+ synchronizeItemsRecursively(testTypeItem, testType.children);
+ }
+
+ return tests;
+}
+
+/**
+ * Give a test item for a type, find its belonging package item according to its id.
+ */
+function findBelongingPackageItem(testType: IJavaTestItem): TestItem | undefined {
+ const indexOfProjectSeparator: number = testType.id.indexOf('@');
+ if (indexOfProjectSeparator < 0) {
+ return undefined;
+ }
+ const projectId: string = testType.id.substring(0, indexOfProjectSeparator);
+ const projectItem: TestItem | undefined = testController?.items.get(projectId);
+ if (!projectItem) {
+ return undefined;
+ }
+ const indexOfPackageSeparator: number = testType.id.lastIndexOf('.');
+ const packageId: string = testType.id.substring(indexOfProjectSeparator + 1, indexOfPackageSeparator);
+ const packageItem: TestItem | undefined = projectItem.children.get(`${projectId}@${packageId}`);
+ return packageItem;
+}
+
+/**
+ * Give a document uri, resolve its belonging package item.
+ */
+async function resolveBelongingPackage(uri: Uri): Promise {
+ const pathsData: IJavaTestItem[] = await resolvePath(uri.toString());
+ if (_.isEmpty(pathsData) || pathsData.length < 2) {
+ return undefined;
+ }
+
+ const projectData: IJavaTestItem = pathsData[0];
+ if (projectData.testLevel !== TestLevel.Project) {
+ return undefined;
+ }
+
+ let belongingProject: TestItem | undefined = testController?.items.get(projectData.id);
+ if (!belongingProject) {
+ belongingProject = createTestItem(projectData);
+ testController?.items.add(belongingProject);
+ belongingProject.canResolveChildren = true;
+ }
+
+ const packageData: IJavaTestItem = pathsData[1];
+ if (packageData.testLevel !== TestLevel.Package) {
+ return undefined;
+ }
+
+ let belongingPackage: TestItem | undefined = belongingProject.children.get(packageData.id);
+ if (!belongingPackage) {
+ belongingPackage = createTestItem(packageData, belongingProject);
+ belongingPackage.canResolveChildren = true;
+ }
+ return belongingPackage;
+}
+
+/**
+ * Parse the range object with server mode to client format
+ * @param range range with server side format
+ */
+export function asRange(range: any): Range | undefined {
+ if (!range) {
+ return undefined;
+ }
+ return new Range(range.start.line, range.start.character, range.end.line, range.end.character);
+}
+
+export async function getJavaProjects(workspaceFolder: WorkspaceFolder, token?: CancellationToken): Promise {
+ return await executeJavaLanguageServerCommand(
+ JavaTestRunnerDelegateCommands.FIND_JAVA_PROJECTS, workspaceFolder.uri.toString(), token) || [];
+}
+
+export async function findTestPackagesAndTypes(handlerId: string, token?: CancellationToken): Promise {
+ return await executeJavaLanguageServerCommand(
+ JavaTestRunnerDelegateCommands.FIND_TEST_PACKAGES_AND_TYPES, handlerId, token) || [];
+}
+
+export async function findDirectTestChildrenForClass(handlerId: string, token?: CancellationToken): Promise {
+ return await executeJavaLanguageServerCommand(
+ JavaTestRunnerDelegateCommands.FIND_DIRECT_CHILDREN_FOR_CLASS, handlerId, token) || [];
+}
+
+export async function findTestTypesAndMethods(uri: string, token?: CancellationToken): Promise {
+ return await executeJavaLanguageServerCommand(
+ JavaTestRunnerDelegateCommands.FIND_TEST_TYPES_AND_METHODS, uri, token) || [];
+}
+
+export async function resolvePath(uri: string): Promise {
+ return await executeJavaLanguageServerCommand(
+ JavaTestRunnerDelegateCommands.RESOLVE_PATH, uri) || [];
+}
diff --git a/src/explorer/testExplorer.ts b/src/explorer/testExplorer.ts
deleted file mode 100644
index 8d9990cc..00000000
--- a/src/explorer/testExplorer.ts
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license.
-
-import * as path from 'path';
-import { Command, Disposable, Event, EventEmitter, ExtensionContext, extensions, Range, ThemeColor, ThemeIcon, TreeDataProvider, TreeItem, TreeItemCollapsibleState, Uri, workspace, WorkspaceFolder } from 'vscode';
-import { VsCodeCommands } from '../constants/commands';
-import { isLightWeightMode, isSwitchingServer } from '../extension';
-import { ITestItem, TestKind, TestLevel } from '../protocols';
-import { ITestResult, TestStatus } from '../runners/models';
-import { testFileWatcher } from '../testFileWatcher';
-import { testItemModel } from '../testItemModel';
-import { testResultManager } from '../testResultManager';
-
-export class TestExplorer implements TreeDataProvider, Disposable {
- public readonly testExplorerViewId: string = 'testExplorer';
-
- private onDidChangeTreeDataEventEmitter: EventEmitter = new EventEmitter();
- // tslint:disable-next-line:member-ordering
- public readonly onDidChangeTreeData: Event = this.onDidChangeTreeDataEventEmitter.event;
-
- private _context: ExtensionContext;
-
- public initialize(context: ExtensionContext): void {
- this._context = context;
- }
-
- public getTreeItem(element: ITestItem): TreeItem | Thenable {
- return {
- label: element.displayName,
- collapsibleState: this.resolveCollapsibleState(element),
- command: this.resolveCommand(element),
- iconPath: this.resolveIconPath(element),
- contextValue: element.level.toString(),
- };
- }
-
- public async getChildren(element?: ITestItem): Promise {
- if (isLightWeightMode()) {
- return [];
- }
- if (isSwitchingServer()) {
- await new Promise((resolve: () => void): void => {
- extensions.getExtension('redhat.java')!.exports.onDidServerModeChange(resolve);
- });
- }
-
- let nodes: ITestItem[] = [];
- if (!element) {
- nodes = this.getWorkspaceFolders();
- } else {
- nodes = await testItemModel.getNodeChildren(element);
- }
- return nodes.sort((a: ITestItem, b: ITestItem) => a.displayName.localeCompare(b.displayName));
- }
-
- public refresh(element?: ITestItem): void {
- this.onDidChangeTreeDataEventEmitter.fire(element);
- if (!element) {
- testFileWatcher.registerListeners();
- }
- }
-
- public dispose(): void {
- this.onDidChangeTreeDataEventEmitter.dispose();
- }
-
- private getWorkspaceFolders(): ITestItem[] {
- let results: ITestItem[] = [];
- if (workspace.workspaceFolders) {
- results = workspace.workspaceFolders.map((folder: WorkspaceFolder) => {
- return {
- id: folder.uri.fsPath,
- displayName: folder.name,
- fullName: folder.name,
- kind: TestKind.None,
- project: '',
- level: TestLevel.Folder,
- location: {
- uri: folder.uri.toString(),
- range: new Range(0, 0, 0, 0),
- },
- children: undefined,
- };
- });
- }
- return results;
- }
-
- private resolveCollapsibleState(element: ITestItem): TreeItemCollapsibleState {
- if (element.level === TestLevel.Method) {
- return TreeItemCollapsibleState.None;
- }
- return TreeItemCollapsibleState.Collapsed;
- }
-
- private resolveIconPath(element: ITestItem): undefined | { dark: string | Uri, light: string | Uri } | ThemeIcon {
- switch (element.level) {
- case TestLevel.Method:
- const result: ITestResult | undefined = testResultManager.getResultById(element.id);
- if (result) {
- switch (result.status) {
- case TestStatus.Pass:
- return new ThemeIcon('pass', new ThemeColor('charts.green'));
- case TestStatus.Fail:
- return new ThemeIcon('error', new ThemeColor('charts.red'));
- case TestStatus.Running:
- return {
- dark: this._context.asAbsolutePath(path.join('resources', 'media', 'dark', 'running.svg')),
- light: this._context.asAbsolutePath(path.join('resources', 'media', 'light', 'running.svg')),
- };
- case TestStatus.Pending:
- return new ThemeIcon('history');
- default:
- break;
- }
- }
-
- return new ThemeIcon('symbol-method');
- case TestLevel.Class:
- return new ThemeIcon('symbol-class');
- case TestLevel.Package:
- return new ThemeIcon('symbol-package');
- default:
- return undefined;
- }
- }
-
- private resolveCommand(element: ITestItem): Command | undefined {
- if (element.level >= TestLevel.Class) {
- return {
- command: VsCodeCommands.VSCODE_OPEN,
- title: 'Open File',
- arguments: [Uri.parse(element.location.uri), { preserveFocus: true, selection: element.location.range }],
- };
- }
- return undefined;
- }
-}
-
-export const testExplorer: TestExplorer = new TestExplorer();
diff --git a/src/extension.ts b/src/extension.ts
index e2dc98c5..ed85fcc1 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1,60 +1,39 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
-import * as fse from 'fs-extra';
-import * as os from 'os';
import * as path from 'path';
-import { commands, DebugConfiguration, Event, Extension, ExtensionContext, extensions, Range, TreeView, TreeViewExpansionEvent, TreeViewSelectionChangeEvent, Uri, window, workspace } from 'vscode';
+import { commands, DebugConfiguration, Event, Extension, ExtensionContext, extensions, TestItem, TextDocument, TextDocumentChangeEvent, TextEditor, Uri, window, workspace } from 'vscode';
import { dispose as disposeTelemetryWrapper, initializeFromJsonFile, instrumentOperation, instrumentOperationAsVsCodeCommand } from 'vscode-extension-telemetry-wrapper';
-import { sendInfo } from 'vscode-extension-telemetry-wrapper';
-import { testSourceProvider } from '../extension.bundle';
-import { testCodeLensController } from './codelens/TestCodeLensController';
-import { debugTestsFromExplorer, openTextDocument, runTestsFromExplorer, runTestsFromJavaProjectExplorer } from './commands/explorerCommands';
import { generateTests, registerAdvanceAskForChoice, registerAskForChoiceCommand, registerAskForInputCommand } from './commands/generationCommands';
-import { openLogFile, showOutputChannel } from './commands/logCommands';
-import { runFromCodeLens } from './commands/runFromCodeLens';
-import { executeTestsFromUri } from './commands/runFromUri';
-import { openStackTrace, openTestSourceLocation } from './commands/testReportCommands';
-import { JavaTestRunnerCommands } from './constants/commands';
-import { ACTIVATION_CONTEXT_KEY } from './constants/configs';
-import { IProgressProvider, IProgressReporter } from './debugger.api';
+import { runTestsFromJavaProjectExplorer } from './commands/projectExplorerCommands';
+import { refresh, runTestsFromTestExplorer } from './commands/testExplorerCommands';
+import { openStackTrace } from './commands/testReportCommands';
+import { Context, ExtensionName, JavaTestRunnerCommands, VSCodeCommands } from './constants';
+import { createTestController, testController } from './controller/testController';
+import { updateItemForDocument, updateItemForDocumentWithDebounce } from './controller/utils';
+import { IProgressProvider } from './debugger.api';
import { initExpService } from './experimentationService';
-import { testExplorer } from './explorer/testExplorer';
-import { logger } from './logger/logger';
-import { ITestItem } from './protocols';
import { disposeCodeActionProvider, registerTestCodeActionProvider } from './provider/codeActionProvider';
-import { ITestResult } from './runners/models';
-import { runnerScheduler } from './runners/runnerScheduler';
-import { testFileWatcher } from './testFileWatcher';
-import { testItemModel } from './testItemModel';
-import { testReportProvider } from './testReportProvider';
-import { testResultManager } from './testResultManager';
-import { testStatusBarProvider } from './testStatusBarProvider';
-import { migrateTestConfig } from './utils/configUtils';
+import { testSourceProvider } from './provider/testSourceProvider';
+
+export let extensionContext: ExtensionContext;
export async function activate(context: ExtensionContext): Promise {
+ extensionContext = context;
await initializeFromJsonFile(context.asAbsolutePath('./package.json'), { firstParty: true });
await initExpService(context);
await instrumentOperation('activation', doActivate)(context);
- await commands.executeCommand('setContext', ACTIVATION_CONTEXT_KEY, true);
+ await commands.executeCommand('setContext', Context.ACTIVATION_CONTEXT_KEY, true);
}
export async function deactivate(): Promise {
- sendInfo('treeViewEventSummary', EventCounter.dict);
- testFileWatcher.dispose();
- testCodeLensController.dispose();
disposeCodeActionProvider();
await disposeTelemetryWrapper();
- await runnerScheduler.cleanUp(false /* isCancel */);
+ testController?.dispose();
}
async function doActivate(_operationId: string, context: ExtensionContext): Promise {
- const storagePath: string = context.storageUri?.fsPath || path.join(os.tmpdir(), 'java_test_runner');
- await fse.ensureDir(storagePath);
- logger.initialize(storagePath, context.subscriptions);
-
- const javaLanguageSupport: Extension | undefined = extensions.getExtension('redhat.java');
- let javaLanguageSupportVersion: string = '0.0.0';
+ const javaLanguageSupport: Extension | undefined = extensions.getExtension(ExtensionName.JAVA_LANGUAGE_SUPPORT);
if (javaLanguageSupport?.isActive) {
const extensionApi: any = javaLanguageSupport.exports;
if (!extensionApi) {
@@ -66,9 +45,8 @@ async function doActivate(_operationId: string, context: ExtensionContext): Prom
if (extensionApi.onDidClasspathUpdate) {
const onDidClasspathUpdate: Event = extensionApi.onDidClasspathUpdate;
context.subscriptions.push(onDidClasspathUpdate(async () => {
- await testSourceProvider.initialize();
- await testFileWatcher.registerListeners(true /*enableDebounce*/);
- await testCodeLensController.registerCodeLensProvider();
+ testSourceProvider.clear();
+ commands.executeCommand(VSCodeCommands.REFRESH_TESTS);
}));
}
@@ -78,14 +56,13 @@ async function doActivate(_operationId: string, context: ExtensionContext): Prom
if (serverMode === mode) {
return;
}
- // Only re-initialize the component when its lightweight -> standard
serverMode = mode;
- if (serverMode !== LanguageServerMode.Hybrid) {
- testExplorer.refresh();
- await testSourceProvider.initialize();
- await testFileWatcher.registerListeners();
- await testCodeLensController.registerCodeLensProvider();
- await registerTestCodeActionProvider();
+ // Only re-initialize the component when its lightweight -> standard
+ if (mode === LanguageServerMode.Standard) {
+ testSourceProvider.clear();
+ registerTestCodeActionProvider();
+ createTestController();
+ await showTestItemsInCurrentFile();
}
}));
}
@@ -93,80 +70,69 @@ async function doActivate(_operationId: string, context: ExtensionContext): Prom
if (extensionApi.onDidProjectsImport) {
const onDidProjectsImport: Event = extensionApi.onDidProjectsImport;
context.subscriptions.push(onDidProjectsImport(async () => {
- await testSourceProvider.initialize();
- await testFileWatcher.registerListeners(true /*enableDebounce*/);
- await testCodeLensController.registerCodeLensProvider();
+ testSourceProvider.clear();
+ commands.executeCommand(VSCodeCommands.REFRESH_TESTS);
}));
}
-
- javaLanguageSupportVersion = javaLanguageSupport.packageJSON.version;
}
- const javaDebugger: Extension | undefined = extensions.getExtension('vscjava.vscode-java-debug');
+ const javaDebugger: Extension | undefined = extensions.getExtension(ExtensionName.JAVA_DEBUGGER);
if (javaDebugger?.isActive) {
progressProvider = javaDebugger.exports?.progressProvider;
}
- testExplorer.initialize(context);
- const testTreeView: TreeView = window.createTreeView(testExplorer.testExplorerViewId, { treeDataProvider: testExplorer, showCollapseAll: true });
- runnerScheduler.initialize(context);
- testReportProvider.initialize(context, javaLanguageSupportVersion);
registerAskForChoiceCommand(context);
registerAdvanceAskForChoice(context);
registerAskForInputCommand(context);
- if (isStandardServerReady()) {
- await testSourceProvider.initialize();
- await testFileWatcher.registerListeners();
- await testCodeLensController.registerCodeLensProvider();
- await registerTestCodeActionProvider();
- }
-
context.subscriptions.push(
- testExplorer,
- testTreeView,
- testStatusBarProvider,
- testResultManager,
- testReportProvider,
- testFileWatcher,
- logger,
- testCodeLensController,
- testItemModel,
- instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.OPEN_DOCUMENT, async (uri: Uri, range?: Range) => await openTextDocument(uri, range)),
- instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.REFRESH_EXPLORER, (node: ITestItem) => testExplorer.refresh(node)),
- instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.RUN_TEST_FROM_CODELENS, async (test: ITestItem) => await runFromCodeLens(test, false /* isDebug */)),
- instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.DEBUG_TEST_FROM_CODELENS, async (test: ITestItem) => await runFromCodeLens(test, true /* isDebug */)),
- instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.RUN_ALL_TEST_FROM_EXPLORER, async () => await runTestsFromExplorer()),
- instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.DEBUG_ALL_TEST_FROM_EXPLORER, async () => await debugTestsFromExplorer()),
- instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.RUN_TEST_FROM_EXPLORER, async (node?: ITestItem, launchConfiguration?: DebugConfiguration) => await runTestsFromExplorer(node, launchConfiguration)),
- instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.DEBUG_TEST_FROM_EXPLORER, async (node?: ITestItem, launchConfiguration?: DebugConfiguration) => await debugTestsFromExplorer(node, launchConfiguration)),
- instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.RELAUNCH_TESTS, async () => await runnerScheduler.relaunch()),
- instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.SHOW_TEST_REPORT, async (tests?: ITestResult[]) => await testReportProvider.report(tests)),
- instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.SHOW_TEST_OUTPUT, () => showOutputChannel()),
- instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.OPEN_TEST_LOG, async () => await openLogFile(storagePath)),
- instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.JAVA_TEST_CANCEL, async () => await runnerScheduler.cleanUp(true /* isCancel */)),
- instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.JAVA_CONFIG_MIGRATE, async () => await migrateTestConfig()),
- instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.RUN_TEST_FROM_EDITOR, async (uri?: Uri, progressReporter?: IProgressReporter) => await executeTestsFromUri(uri, progressReporter, false /* isDebug */)),
- instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.DEBUG_TEST_FROM_EDITOR, async (uri?: Uri, progressReporter?: IProgressReporter) => await executeTestsFromUri(uri, progressReporter, true /* isDebug */)),
- instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.JAVA_TEST_REPORT_OPEN_STACKTRACE, async (trace: string, fullName: string) => await openStackTrace(trace, fullName)),
- instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.JAVA_TEST_REPORT_OPEN_TEST_SOURCE_LOCATION, async (uri: string, range: string, fullName: string) => await openTestSourceLocation(uri, range, fullName)),
+ instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.JAVA_TEST_OPEN_STACKTRACE, openStackTrace),
+ instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.RUN_TEST_FROM_EDITOR, async () => await commands.executeCommand(VSCodeCommands.RUN_TESTS_IN_CURRENT_FILE)),
+ instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.DEBUG_TEST_FROM_EDITOR, async () => await commands.executeCommand(VSCodeCommands.DEBUG_TESTS_IN_CURRENT_FILE)),
+ instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.JAVA_TEST_GENERATE_TESTS, ((uri: Uri, startPosition: number) => generateTests(uri, startPosition))),
+ instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.RUN_FROM_TEST_EXPLORER, async (node: TestItem, launchConfiguration: DebugConfiguration) => await runTestsFromTestExplorer(node, launchConfiguration, false)),
+ instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.DEBUG_FROM_TEST_EXPLORER, async (node: TestItem, launchConfiguration: DebugConfiguration) => await runTestsFromTestExplorer(node, launchConfiguration, false)),
+ instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.REFRESH_TEST_EXPLORER, async () => await refresh()),
instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.RUN_TEST_FROM_JAVA_PROJECT_EXPLORER, async (node: any) => await runTestsFromJavaProjectExplorer(node, false /* isDebug */)),
instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.DEBUG_TEST_FROM_JAVA_PROJECT_EXPLORER, async (node: any) => await runTestsFromJavaProjectExplorer(node, true /* isDebug */)),
- instrumentOperationAsVsCodeCommand(JavaTestRunnerCommands.JAVA_TEST_GENERATE_TESTS, ((uri: Uri, startPosition: number) => generateTests(uri, startPosition))),
+ window.onDidChangeActiveTextEditor(async (e: TextEditor | undefined) => {
+ if (e?.document) {
+ if (!isJavaFile(e.document)) {
+ return;
+ }
- // track the test tree view events.
- testTreeView.onDidChangeSelection((_e: TreeViewSelectionChangeEvent) => {
- EventCounter.increase('didChangeSelection');
- }),
- testTreeView.onDidCollapseElement((_e: TreeViewExpansionEvent) => {
- EventCounter.increase('didCollapseElement');
+ if (!await testSourceProvider.isOnTestSourcePath(e.document.uri)) {
+ return;
+ }
+ await updateItemForDocumentWithDebounce(e.document.uri);
+ }
}),
- testTreeView.onDidExpandElement((_e: TreeViewExpansionEvent) => {
- EventCounter.increase('didExpandElement');
+ workspace.onDidChangeTextDocument(async (e: TextDocumentChangeEvent) => {
+ if (!isJavaFile(e.document)) {
+ return;
+ }
+ if (!await testSourceProvider.isOnTestSourcePath(e.document.uri)) {
+ return;
+ }
+ await updateItemForDocumentWithDebounce(e.document.uri);
}),
);
- setContextKeyForDeprecatedConfig();
+ if (isStandardServerReady()) {
+ registerTestCodeActionProvider();
+ createTestController();
+ }
+
+ await showTestItemsInCurrentFile();
+}
+
+async function showTestItemsInCurrentFile(): Promise {
+ if (window.activeTextEditor && isJavaFile(window.activeTextEditor.document) &&
+ await testSourceProvider.isOnTestSourcePath(window.activeTextEditor.document.uri)) {
+ // we didn't call the debounced version to avoid first call takes a long time and expand too much
+ // for the debounce window. (cpu resources are limited during activation)
+ await updateItemForDocument(window.activeTextEditor.document.uri);
+ }
}
export function isStandardServerReady(): boolean {
@@ -182,22 +148,6 @@ export function isStandardServerReady(): boolean {
return true;
}
-export function isLightWeightMode(): boolean {
- return serverMode === LanguageServerMode.LightWeight;
-}
-
-export function isSwitchingServer(): boolean {
- return serverMode === LanguageServerMode.Hybrid;
-}
-
-async function setContextKeyForDeprecatedConfig(): Promise {
- const deprecatedConfigs: Uri[] = await workspace.findFiles('**/.vscode/launch.test.json', null, 1 /*maxResult*/);
- if (deprecatedConfigs.length > 0) {
- commands.executeCommand('setContext', 'java:hasDeprecatedTestConfig', true);
- sendInfo('', { hasDeprecatedTestConfig: 'true' });
- }
-}
-
let serverMode: string | undefined;
const enum LanguageServerMode {
@@ -208,11 +158,6 @@ const enum LanguageServerMode {
export let progressProvider: IProgressProvider | undefined;
-class EventCounter {
- public static dict: {[key: string]: number} = {};
-
- public static increase(event: string): void {
- const count: number = this.dict[event] ?? 0;
- this.dict[event] = count + 1;
- }
+function isJavaFile(document: TextDocument): boolean {
+ return path.extname(document.fileName) === '.java';
}
diff --git a/src/logger/logger.ts b/src/logger/logger.ts
deleted file mode 100644
index af24709a..00000000
--- a/src/logger/logger.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license.
-
-import * as path from 'path';
-import { ConfigurationChangeEvent, Disposable, workspace } from 'vscode';
-import * as winston from 'winston';
-import { LOG_FILE_MAX_NUMBER, LOG_FILE_MAX_SIZE, LOG_FILE_NAME, LOG_LEVEL_SETTING_KEY } from '../constants/configs';
-import { getLogLevel } from '../utils/settingUtils';
-import { outputChannelTransport } from './outputChannelTransport';
-
-class Logger implements Disposable {
- private logger: winston.Logger;
-
- public initialize(storagePath: string, disposables: Disposable[]): void {
- this.logger = winston.createLogger({
- transports: [
- new (winston.transports.File)({
- level: getLogLevel(),
- filename: path.join(storagePath, LOG_FILE_NAME),
- maxsize: LOG_FILE_MAX_SIZE,
- maxFiles: LOG_FILE_MAX_NUMBER,
- tailable: true,
- }),
- outputChannelTransport,
- ],
- });
- workspace.onDidChangeConfiguration((e: ConfigurationChangeEvent) => {
- if (e.affectsConfiguration(LOG_LEVEL_SETTING_KEY)) {
- const logLevel: string = getLogLevel();
- for (const transport of this.logger.transports) {
- transport.level = logLevel;
- }
- }
- }, null, disposables);
- }
-
- public dispose(): void {
- for (const transport of this.logger.transports) {
- if (transport.close) {
- transport.close();
- }
- }
- }
-
- public verbose(message: string): void {
- this.logger.verbose(message);
- }
-
- public info(message: string): void {
- this.logger.info(message);
- }
-
- public error(message: string, error?: Error): void {
- this.logger.error(`${message}.${error ? ' ' + error : ''}`);
- }
-}
-
-export const logger: Logger = new Logger();
diff --git a/src/logger/outputChannelTransport.ts b/src/logger/outputChannelTransport.ts
deleted file mode 100644
index 9626654b..00000000
--- a/src/logger/outputChannelTransport.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license.
-
-import { TransformableInfo } from 'logform';
-import { OutputChannel, window } from 'vscode';
-import * as Transport from 'winston-transport';
-import { getLogLevel } from '../utils/settingUtils';
-
-class OutputChannelTransport extends Transport {
- private channel: OutputChannel;
- constructor(options: Transport.TransportStreamOptions) {
- super(options);
- this.channel = window.createOutputChannel('Java Test Runner');
- }
-
- public log(msg: TransformableInfo, next?: () => void): any {
- this.channel.append(msg.message);
-
- if (next) {
- next();
- }
- }
-
- public close(): void {
- this.channel.dispose();
- }
-
- public show(): void {
- this.channel.show();
- }
-}
-
-export const outputChannelTransport: OutputChannelTransport = new OutputChannelTransport({level: getLogLevel()});
diff --git a/src/protocols.ts b/src/protocols.ts
deleted file mode 100644
index 6e9c20e1..00000000
--- a/src/protocols.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license.
-
-import { Range } from 'vscode';
-
-export interface ILocation {
- uri: string;
- range: Range;
-}
-
-export interface ITestItem {
- id: string;
- displayName: string;
- fullName: string;
- children: string[] | undefined;
- kind: TestKind;
- project: string;
- level: TestLevel;
- location: ILocation;
-}
-
-export interface ISearchTestItemParams {
- level: TestLevel;
- fullName: string;
- uri: string;
- isHierarchicalPackage?: boolean;
-}
-
-export enum TestLevel {
- Root = 0,
- Folder = 1,
- Package = 2,
- Class = 3,
- Method = 4,
-}
-
-export enum TestKind {
- JUnit5 = 0,
- JUnit = 1,
- TestNG = 2,
- None = 100,
-}
diff --git a/src/provider/testSourceProvider.ts b/src/provider/testSourceProvider.ts
index 421adf35..b9dafcbc 100644
--- a/src/provider/testSourceProvider.ts
+++ b/src/provider/testSourceProvider.ts
@@ -1,24 +1,17 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
+import * as path from 'path';
import { RelativePattern, Uri, workspace, WorkspaceFolder } from 'vscode';
-import { getTestSourcePaths } from '../utils/commandUtils';
+import { JavaTestRunnerDelegateCommands } from '../constants';
+import { executeJavaLanguageServerCommand } from '../utils/commandUtils';
class TestSourcePathProvider {
- private testSource: ITestSourcePath[];
+ private testSourceMapping: Map = new Map();
- public async initialize(): Promise {
- this.testSource = [];
- if (!workspace.workspaceFolders) {
- return;
- }
-
- this.testSource = await getTestSourcePaths(workspace.workspaceFolders.map((workspaceFolder: WorkspaceFolder) => workspaceFolder.uri.toString()));
- }
-
- public async getTestSourcePattern(containsGeneral: boolean = true): Promise {
+ public async getTestSourcePattern(workspaceFolder: WorkspaceFolder, containsGeneral: boolean = true): Promise {
const patterns: RelativePattern[] = [];
- const sourcePaths: string[] = await testSourceProvider.getTestSourcePath(containsGeneral);
+ const sourcePaths: string[] = await testSourceProvider.getTestSourcePath(workspaceFolder, containsGeneral);
for (const sourcePath of sourcePaths) {
const normalizedPath: string = Uri.file(sourcePath).fsPath;
const pattern: RelativePattern = new RelativePattern(normalizedPath, '**/*.java');
@@ -27,21 +20,53 @@ class TestSourcePathProvider {
return patterns;
}
- public async getTestSourcePath(containsGeneral: boolean = true): Promise {
- if (this.testSource === undefined) {
- await this.initialize();
- }
+ public async getTestSourcePath(workspaceFolder: WorkspaceFolder, containsGeneral: boolean = true): Promise {
+ const testPaths: ITestSourcePath[] = await this.getTestPaths(workspaceFolder);
if (containsGeneral) {
- return this.testSource.map((s: ITestSourcePath) => s.testSourcePath);
+ return testPaths.map((s: ITestSourcePath) => s.testSourcePath);
}
- return this.testSource.filter((s: ITestSourcePath) => s.isStrict)
+ return testPaths.filter((s: ITestSourcePath) => s.isStrict)
.map((s: ITestSourcePath) => s.testSourcePath);
}
+
+ public async isOnTestSourcePath(uri: Uri): Promise {
+ const workspaceFolder: WorkspaceFolder | undefined = workspace.getWorkspaceFolder(uri);
+ if (!workspaceFolder) {
+ return false;
+ }
+ const testPaths: ITestSourcePath[] = await this.getTestPaths(workspaceFolder);
+ const fsPath: string = uri.fsPath;
+ for (const testPath of testPaths) {
+ const relativePath: string = path.relative(testPath.testSourcePath, fsPath);
+ if (!relativePath.startsWith('..')) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public clear(): void {
+ this.testSourceMapping.clear();
+ }
+
+ private async getTestPaths(workspaceFolder: WorkspaceFolder): Promise {
+ let testPaths: ITestSourcePath[] | undefined = this.testSourceMapping.get(workspaceFolder.uri);
+ if (!testPaths) {
+ testPaths = await getTestSourcePaths([workspaceFolder.uri.toString()]);
+ this.testSourceMapping.set(workspaceFolder.uri, testPaths);
+ }
+ return testPaths;
+ }
+}
+
+async function getTestSourcePaths(uri: string[]): Promise {
+ return await executeJavaLanguageServerCommand(
+ JavaTestRunnerDelegateCommands.GET_TEST_SOURCE_PATH, uri) || [];
}
-export interface ITestSourcePath {
+interface ITestSourcePath {
testSourcePath: string;
/**
* All the source paths from eclipse and invisible project will be treated as test source
diff --git a/src/runConfigs.ts b/src/runConfigs.ts
index 5447b427..e977b876 100644
--- a/src/runConfigs.ts
+++ b/src/runConfigs.ts
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
-import { BUILTIN_CONFIG_NAME } from './constants/configs';
+import { Configurations } from './constants';
export interface IExecutionConfig {
name?: string;
@@ -14,21 +14,11 @@ export interface IExecutionConfig {
sourcePaths?: string[];
}
-export interface IExecutionConfigGroup {
- default: string;
- items: IExecutionConfig[];
-}
-
-export interface ITestConfig {
- run: IExecutionConfigGroup;
- debug: IExecutionConfigGroup;
-}
-
export function getBuiltinConfig(): IExecutionConfig {
return Object.assign({}, BUILTIN_CONFIG);
}
const BUILTIN_CONFIG: IExecutionConfig = {
- name: BUILTIN_CONFIG_NAME,
+ name: Configurations.BUILTIN_CONFIG_NAME,
workingDirectory: '${workspaceFolder}',
};
diff --git a/src/runners/ITestRunner.ts b/src/runners/ITestRunner.ts
index 5f008398..1f94d5c8 100644
--- a/src/runners/ITestRunner.ts
+++ b/src/runners/ITestRunner.ts
@@ -1,12 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
-import { DebugConfiguration } from 'vscode';
+import { CancellationToken, DebugConfiguration } from 'vscode';
import { IProgressReporter } from '../debugger.api';
-import { IRunnerContext } from './models';
+import { IRunTestContext } from '../types';
export interface ITestRunner {
- setup(context: IRunnerContext): Promise;
- run(launchConfiguration: DebugConfiguration, progressReporter?: IProgressReporter): Promise>;
+ setup(context: IRunTestContext): Promise;
+ run(launchConfiguration: DebugConfiguration, token: CancellationToken, progressReporter?: IProgressReporter): Promise;
tearDown(isCancel: boolean): Promise;
}
diff --git a/src/runners/baseRunner/BaseRunner.ts b/src/runners/baseRunner/BaseRunner.ts
index fd7a0c38..942beeef 100644
--- a/src/runners/baseRunner/BaseRunner.ts
+++ b/src/runners/baseRunner/BaseRunner.ts
@@ -1,47 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
-import * as fse from 'fs-extra';
import { default as getPort } from 'get-port';
import * as iconv from 'iconv-lite';
import { AddressInfo, createServer, Server, Socket } from 'net';
import * as os from 'os';
-import * as path from 'path';
-import { debug, DebugConfiguration, DebugSession, Disposable, Uri, workspace } from 'vscode';
-import { LOCAL_HOST } from '../../constants/configs';
+import { CancellationToken, debug, DebugConfiguration, DebugSession, Disposable } from 'vscode';
+import { sendError } from 'vscode-extension-telemetry-wrapper';
+import { Configurations } from '../../constants';
import { IProgressReporter } from '../../debugger.api';
-import { logger } from '../../logger/logger';
-import { ITestItem, TestLevel } from '../../protocols';
import { IExecutionConfig } from '../../runConfigs';
-import { testResultManager } from '../../testResultManager';
+import { IRunTestContext } from '../../types';
import { ITestRunner } from '../ITestRunner';
-import { IRunnerContext, ITestResult, TestStatus } from '../models';
-import { BaseRunnerResultAnalyzer } from './BaseRunnerResultAnalyzer';
+import { IRunnerResultAnalyzer } from './IRunnerResultAnalyzer';
export abstract class BaseRunner implements ITestRunner {
- protected testIds: string[];
- protected context: IRunnerContext;
protected server: Server;
protected socket: Socket;
- protected runnerResultAnalyzer: BaseRunnerResultAnalyzer;
+ protected runnerResultAnalyzer: IRunnerResultAnalyzer;
private disposables: Disposable[] = [];
- constructor(
- protected extensionPath: string) {}
+ constructor(protected testContext: IRunTestContext) {}
- public async setup(context: IRunnerContext): Promise {
- this.context = context;
+ public async setup(): Promise {
await this.startSocketServer();
- const flattenedTestIds: string[] = [];
- for (const test of context.tests) {
- this.flattenTestIds(test, flattenedTestIds);
- }
- this.testIds = flattenedTestIds;
- this.updateTestResultsToPending();
+ this.runnerResultAnalyzer = this.getAnalyzer();
}
- public async run(launchConfiguration: DebugConfiguration, progressReporter?: IProgressReporter): Promise> {
+ public async run(launchConfiguration: DebugConfiguration, token: CancellationToken, progressReporter?: IProgressReporter): Promise {
let data: string = '';
this.server.on('connection', (socket: Socket) => {
this.socket = socket;
@@ -53,7 +40,7 @@ export abstract class BaseRunner implements ITestRunner {
data = data.concat(iconv.decode(buffer, launchConfiguration.encoding || 'utf8'));
const index: number = data.lastIndexOf(os.EOL);
if (index >= 0) {
- this.testResultAnalyzer.analyzeData(data.substring(0, index + os.EOL.length));
+ this.runnerResultAnalyzer.analyzeData(data.substring(0, index + os.EOL.length));
data = data.substring(index + os.EOL.length);
}
});
@@ -70,49 +57,49 @@ export abstract class BaseRunner implements ITestRunner {
// Run from integrated terminal will terminate the debug session immediately after launching,
// So we force to use internal console here to make sure the session is still under debugger's control.
launchConfiguration.console = 'internalConsole';
- launchConfiguration.internalConsoleOptions = 'openOnSessionStart';
-
- launchConfiguration.__progressId = progressReporter?.getId();
- const uri: Uri = Uri.parse(this.context.tests[0].location.uri);
- logger.verbose(`Launching with the following launch configuration: '${JSON.stringify(launchConfiguration, null, 2)}'\n`);
+ let debugSession: DebugSession | undefined;
+ this.disposables.push(debug.onDidStartDebugSession((session: DebugSession) => {
+ if (session.name === launchConfiguration.name) {
+ debugSession = session;
+ }
+ }));
- return await debug.startDebugging(workspace.getWorkspaceFolder(uri), launchConfiguration).then(async (success: boolean) => {
+ if (token.isCancellationRequested || progressReporter?.isCancelled()) {
+ this.tearDown();
+ return;
+ }
+ return await debug.startDebugging(this.testContext.workspaceFolder, launchConfiguration).then(async (success: boolean) => {
if (!success) {
this.tearDown();
- return this.testResultAnalyzer.tearDown();
+ return;
}
- return await new Promise>((resolve: (ids: Set) => void): void => {
+ token.onCancellationRequested(() => {
+ debugSession?.customRequest('disconnect', { restart: false });
+ });
+
+ return await new Promise((resolve: () => void): void => {
this.disposables.push(
debug.onDidTerminateDebugSession((session: DebugSession): void => {
if (launchConfiguration.name === session.name) {
+ debugSession = undefined;
this.tearDown();
if (data.length > 0) {
- this.testResultAnalyzer.analyzeData(data);
+ this.runnerResultAnalyzer.analyzeData(data);
}
- return resolve(this.testResultAnalyzer.tearDown());
+ return resolve();
}
}),
);
});
- }, ((reason: any): any => {
- logger.error(`${reason}`);
+ }, ((): any => {
this.tearDown();
- return this.testResultAnalyzer.tearDown();
+ return;
}));
}
public async tearDown(): Promise {
- for (const id of this.testIds) {
- const result: ITestResult | undefined = testResultManager.getResultById(id);
- // In case that unexpected errors terminate the execution
- if (result && (result.status === TestStatus.Pending || result.status === TestStatus.Running)) {
- result.status = undefined;
- testResultManager.storeResult(result);
- }
- }
-
try {
if (this.socket) {
this.socket.removeAllListeners();
@@ -128,29 +115,8 @@ export abstract class BaseRunner implements ITestRunner {
disposable.dispose();
}
} catch (error) {
- logger.error('Failed to clean up', error);
- }
- }
-
- public get runnerJarFilePath(): Promise {
- return this.getPath('com.microsoft.java.test.runner.jar');
- }
-
- public get runnerLibPath(): Promise {
- return this.getPath('lib');
- }
-
- public get runnerMainClassName(): string {
- return 'com.microsoft.java.test.runner.Launcher';
- }
-
- public get serverPort(): number {
- const address: AddressInfo = this.server.address() as AddressInfo;
- if (address) {
- return address.port;
+ sendError(error);
}
-
- throw new Error('The socket server is not started yet.');
}
public getApplicationArgs(config?: IExecutionConfig): string[] {
@@ -166,59 +132,19 @@ export abstract class BaseRunner implements ITestRunner {
return applicationArgs;
}
- protected abstract get testResultAnalyzer(): BaseRunnerResultAnalyzer;
-
- protected get runnerDir(): string {
- return path.join(this.extensionPath, 'server');
- }
-
- protected getRunnerCommandParams(_config?: IExecutionConfig): string[] {
- return [];
- }
-
protected async startSocketServer(): Promise {
this.server = createServer();
const socketPort: number = await getPort();
await new Promise((resolve: () => void): void => {
- this.server.listen(socketPort, LOCAL_HOST, resolve);
+ this.server.listen(socketPort, Configurations.LOCAL_HOST, resolve);
});
}
- private async getPath(subPath: string): Promise {
- const fullPath: string = path.join(this.runnerDir, subPath);
- if (await fse.pathExists(fullPath)) {
- return fullPath;
- }
- throw new Error(`Failed to find path: ${fullPath}`);
- }
-
- private updateTestResultsToPending(): void {
- const runningResults: ITestResult[] = [];
- for (const id of this.testIds) {
- runningResults.push({
- id,
- status: TestStatus.Pending,
- });
- }
- testResultManager.storeResult(...runningResults);
+ protected getRunnerCommandParams(_config?: IExecutionConfig): string[] {
+ return [];
}
- /**
- * Add all of the method IDs into `flattenedItemIds`. No need to recursively call this method,
- * since the input `test` is not tree-like
- */
- private flattenTestIds(test: ITestItem, flattenedItemIds: string[]): void {
- if (test.level === TestLevel.Method) {
- flattenedItemIds.push(test.id);
- } else if (test.level === TestLevel.Class) {
- if (test.children) {
- flattenedItemIds.push(...test.children);
- } else {
- // for JUnit 4's @Suite.SuiteClasses
- flattenedItemIds.push(test.id);
- }
- }
- }
+ protected abstract getAnalyzer(): IRunnerResultAnalyzer;
}
export interface IJUnitLaunchArguments {
diff --git a/src/runners/baseRunner/BaseRunnerResultAnalyzer.ts b/src/runners/baseRunner/BaseRunnerResultAnalyzer.ts
deleted file mode 100644
index b83c2b54..00000000
--- a/src/runners/baseRunner/BaseRunnerResultAnalyzer.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license.
-
-import { logger } from '../../logger/logger';
-import { testResultManager } from '../../testResultManager';
-import { ITestOutputData, ITestResult, TestStatus } from '../models';
-
-export abstract class BaseRunnerResultAnalyzer {
- protected testIds: Set = new Set();
- private readonly regex: RegExp = /@@/;
-
- constructor(protected projectName: string) {
- }
-
- public analyzeData(data: string): void {
- const lines: string[] = data.split(/\r?\n/);
- for (const line of lines) {
- if (!line) {
- continue;
- }
- const match: RegExpExecArray | null = this.regex.exec(line);
- if (match) {
- // Message from Test Runner executable
- try {
- this.processData(match[1]);
- } catch (error) {
- logger.error(`Failed to parse output data: ${data}`, error);
- }
- } else {
- // Message from the test case itself
- logger.info(line);
- }
- }
- }
-
- public tearDown(): Set {
- for (const id of this.testIds) {
- const result: ITestResult | undefined = testResultManager.getResultById(id);
- // In case that unexpected errors terminate the execution
- if (result && (!result.status || result.status === TestStatus.Pending || result.status === TestStatus.Running)) {
- testResultManager.removeResultById(id);
- this.testIds.delete(id);
- }
- }
- return this.testIds;
- }
-
- protected processData(data: string): void {
- const outputData: ITestOutputData = JSON.parse(data) as ITestOutputData;
- if (outputData.name.toLocaleLowerCase() === 'error') {
- logger.error(this.unescape(data));
- } else {
- // Append '\n' becuase the original line separator has been splitted
- logger.verbose(this.unescape(data) + '\n');
- }
- }
-
- protected unescape(content: string): string {
- return content.replace(/\\r/gm, '\r')
- .replace(/\\f/gm, '\f')
- .replace(/\\n/gm, '\n')
- .replace(/\\t/gm, '\t')
- .replace(/\\b/gm, '\b')
- .replace(/\\"/gm, '"');
- }
-}
diff --git a/src/runners/baseRunner/IRunnerResultAnalyzer.ts b/src/runners/baseRunner/IRunnerResultAnalyzer.ts
new file mode 100644
index 00000000..d8b93d1d
--- /dev/null
+++ b/src/runners/baseRunner/IRunnerResultAnalyzer.ts
@@ -0,0 +1,7 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+export interface IRunnerResultAnalyzer {
+ analyzeData(data: string): void;
+ processData(data: string): void;
+}
diff --git a/src/runners/junitRunner/JUnitRunnerResultAnalyzer.ts b/src/runners/junitRunner/JUnitRunnerResultAnalyzer.ts
index c46cfeac..be64bbaa 100644
--- a/src/runners/junitRunner/JUnitRunnerResultAnalyzer.ts
+++ b/src/runners/junitRunner/JUnitRunnerResultAnalyzer.ts
@@ -1,16 +1,48 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
-import { logger } from '../../logger/logger';
-import { testResultManager } from '../../testResultManager';
-import { BaseRunnerResultAnalyzer } from '../baseRunner/BaseRunnerResultAnalyzer';
-import { ITestResult, TestStatus } from '../models';
+import * as path from 'path';
+import { Location, MarkdownString, Range, TestItem, TestMessage, TestResultState } from 'vscode';
+import { INVOCATION_PREFIX } from '../../constants';
+import { dataCache, ITestItemData } from '../../controller/testItemDataCache';
+import { createTestItem } from '../../controller/utils';
+import { IJavaTestItem, IRunTestContext, TestKind, TestLevel } from '../../types';
+import { IRunnerResultAnalyzer } from '../baseRunner/IRunnerResultAnalyzer';
+import { findTestLocation, setTestState } from '../utils';
-export class JUnitRunnerResultAnalyzer extends BaseRunnerResultAnalyzer {
+export class JUnitRunnerResultAnalyzer implements IRunnerResultAnalyzer {
- private currentTestItem: string;
- private traces: string;
- private isRecordingTraces: boolean;
+ private testOutputMapping: Map = new Map();
+ private triggeredTestsMapping: Map = new Map();
+ private currentTestState: TestResultState;
+ private currentItem: TestItem | undefined;
+ private currentDuration: number = 0;
+ private traces: MarkdownString;
+ private assertionFailure: TestMessage | undefined;
+ private recordingType: RecordingType;
+ private expectString: string;
+ private actualString: string;
+ private projectName: string;
+ private incompleteTestSuite: ITestInfo[] = [];
+
+ constructor(private testContext: IRunTestContext) {
+ this.projectName = testContext.projectName;
+ const queue: TestItem[] = [...testContext.testItems];
+ while (queue.length) {
+ const item: TestItem = queue.shift()!;
+ const testLevel: TestLevel | undefined = dataCache.get(item)?.testLevel;
+ if (testLevel === undefined || testLevel === TestLevel.Invocation) {
+ continue;
+ } else if (testLevel === TestLevel.Method && item.parent) {
+ this.triggeredTestsMapping.set(item.parent.id, item.parent);
+ } else {
+ item.children.forEach((child: TestItem) => {
+ queue.push(child);
+ });
+ }
+ this.triggeredTestsMapping.set(item.id, item);
+ }
+ }
public analyzeData(data: string): void {
const lines: string[] = data.split(/\r?\n/);
@@ -19,100 +51,136 @@ export class JUnitRunnerResultAnalyzer extends BaseRunnerResultAnalyzer {
continue;
}
this.processData(line);
- logger.verbose(line + '\n');
+ this.testContext.testRun.appendOutput(line + '\r\n');
}
}
- protected processData(data: string): void {
- if (data.startsWith(MessageId.TestStart)) {
- const testId: string = this.getTestId(data);
- if (!testId) {
+ public processData(data: string): void {
+ if (data.startsWith(MessageId.TestTree)) {
+ this.enlistToTestMapping(data.substr(MessageId.TestTree.length).trim());
+ } else if (data.startsWith(MessageId.TestStart)) {
+ const item: TestItem | undefined = this.getTestItem(data.substr(MessageId.TestStart.length));
+ if (!item) {
return;
}
- this.currentTestItem = testId;
-
- let result: ITestResult;
- if (this.testIds.has(testId)) {
- result = Object.assign({}, testResultManager.getResultById(testId), {
- id: testId,
- status: TestStatus.Running,
- });
- } else {
- // the test has not been executed in current test session.
- // create a new result object
- result = {
- id: testId,
- status: TestStatus.Running,
- };
- this.testIds.add(testId);
+ if (item.id !== this.currentItem?.id) {
+ this.initializeCache(item);
}
+ this.testContext.testRun.started(item);
const start: number = Date.now();
- if (data.indexOf(MessageId.IGNORE_TEST_PREFIX) > -1) {
- result.status = TestStatus.Skip;
- } else if (result.duration === undefined) {
- result.duration = -start;
- } else if (result.duration >= 0) {
+ if (this.currentDuration === 0) {
+ this.currentDuration = -start;
+ } else if (this.currentDuration > 0) {
// Some test cases may executed multiple times (@RepeatedTest), we need to calculate the time for each execution
- result.duration -= start;
+ this.currentDuration -= start;
}
- testResultManager.storeResult(result);
} else if (data.startsWith(MessageId.TestEnd)) {
- const testId: string = this.getTestId(data);
- if (testId) {
- const finishedResult: ITestResult | undefined = testResultManager.getResultById(testId);
- if (!finishedResult) {
- return;
- }
- if (finishedResult.status === TestStatus.Running) {
- if (finishedResult.trace) {
- finishedResult.status = TestStatus.Fail;
- } else {
- finishedResult.status = TestStatus.Pass;
- }
- }
- updateElapsedTime(finishedResult);
- testResultManager.storeResult(finishedResult);
- }
- } else if (data.startsWith(MessageId.TestFailed) || data.startsWith(MessageId.TestError)) {
- const testId: string = this.getTestId(data);
- if (testId) {
- this.currentTestItem = testId;
- const failedResult: ITestResult = Object.assign({}, testResultManager.getResultById(testId), {
- id: testId,
- status: data.indexOf(MessageId.ASSUMPTION_FAILED_TEST_PREFIX) > -1 ? TestStatus.Skip : TestStatus.Fail,
- });
- updateElapsedTime(failedResult);
- testResultManager.storeResult(failedResult);
- this.testIds.add(testId);
+ if (!this.currentItem) {
+ return;
+ }
+
+ if (this.currentDuration < 0) {
+ const end: number = Date.now();
+ this.currentDuration += end;
+ }
+
+ if (data.indexOf(MessageId.IGNORE_TEST_PREFIX) > -1) {
+ this.currentTestState = TestResultState.Skipped;
+ } else if (this.currentTestState === TestResultState.Running) {
+ this.currentTestState = TestResultState.Passed;
+ }
+ setTestState(this.testContext.testRun, this.currentItem, this.currentTestState, undefined, this.currentDuration);
+ } else if (data.startsWith(MessageId.TestFailed)) {
+ if (data.indexOf(MessageId.ASSUMPTION_FAILED_TEST_PREFIX) > -1) {
+ this.currentTestState = TestResultState.Skipped;
+ } else {
+ this.currentTestState = TestResultState.Failed;
+ }
+ } else if (data.startsWith(MessageId.TestError)) {
+ const item: TestItem | undefined = this.getTestItem(data.substr(MessageId.TestError.length));
+ if (!item) {
+ return;
}
+ if (item.id !== this.currentItem?.id) {
+ this.initializeCache(item);
+ }
+ this.currentTestState = TestResultState.Errored;
} else if (data.startsWith(MessageId.TraceStart)) {
- this.traces = '';
- this.isRecordingTraces = true;
+ this.traces = new MarkdownString();
+ this.traces.isTrusted = true;
+ this.recordingType = RecordingType.StackTrace;
} else if (data.startsWith(MessageId.TraceEnd)) {
- const failedResult: ITestResult | undefined = testResultManager.getResultById(this.currentTestItem);
- if (!failedResult) {
+ if (!this.currentItem) {
return;
}
- failedResult.trace = this.traces;
- this.isRecordingTraces = false;
- testResultManager.storeResult(failedResult);
- } else if (this.isRecordingTraces) {
- this.traces += data + '\n';
+
+ const testMessage: TestMessage = new TestMessage(this.traces);
+ this.tryAppendMessage(this.currentItem, testMessage);
+ this.recordingType = RecordingType.None;
+ if (this.currentTestState === TestResultState.Errored) {
+ setTestState(this.testContext.testRun, this.currentItem, this.currentTestState);
+ }
+ } else if (data.startsWith(MessageId.ExpectStart)) {
+ this.recordingType = RecordingType.ExpectMessage;
+ } else if (data.startsWith(MessageId.ExpectEnd)) {
+ this.recordingType = RecordingType.None;
+ this.expectString = this.expectString.replace(/\n$/, '');
+ } else if (data.startsWith(MessageId.ActualStart)) {
+ this.recordingType = RecordingType.ActualMessage;
+ } else if (data.startsWith(MessageId.ActualEnd)) {
+ this.recordingType = RecordingType.None;
+ this.actualString = this.actualString.replace(/\n$/, '');
+ if (!this.assertionFailure && this.expectString && this.actualString) {
+ this.assertionFailure = TestMessage.diff(`Expected [${this.expectString}] but was [${this.actualString}]`, this.expectString, this.actualString);
+ }
+ } else if (this.recordingType === RecordingType.ExpectMessage) {
+ this.expectString += data + '\n';
+ } else if (this.recordingType === RecordingType.ActualMessage) {
+ this.actualString += data + '\n';
+ } else if (this.recordingType === RecordingType.StackTrace) {
+ if (!this.assertionFailure) {
+ const assertionRegExp: RegExp = /expected.*:.*<(.+?)>.*but.*:.*<(.+?)>/mi;
+ const assertionResults: RegExpExecArray | null = assertionRegExp.exec(data);
+ if (assertionResults && assertionResults.length === 3) {
+ this.assertionFailure = TestMessage.diff(`Expected [${assertionResults[1]}] but was [${assertionResults[2]}]`, assertionResults[1], assertionResults[2]);
+ }
+ }
+ const traceRegExp: RegExp = /(\s?at\s+)([\w$\\.]+\/)?((?:[\w$]+\.)+[<\w$>]+)\(([\w-$]+\.java):(\d+)\)/;
+ const traceResults: RegExpExecArray | null = traceRegExp.exec(data);
+ if (traceResults && traceResults.length === 6) {
+ this.traces.appendText(traceResults[1]);
+ this.traces.appendMarkdown(`${(traceResults[2] || '') + traceResults[3]}([${traceResults[4]}:${traceResults[5]}](command:_java.test.openStackTrace?${encodeURIComponent(JSON.stringify([data, this.projectName]))}))`);
+ if (this.assertionFailure && this.currentItem && path.basename(this.currentItem.uri?.fsPath || '') === traceResults[4]) {
+ const lineNum: number = parseInt(traceResults[5], 10);
+ if (this.currentItem.uri) {
+ this.assertionFailure.location = new Location(this.currentItem.uri, new Range(lineNum - 1, 0, lineNum, 0));
+ }
+ setTestState(this.testContext.testRun, this.currentItem, TestResultState.Failed, this.assertionFailure);
+ }
+ } else {
+ // in case the message contains message like: 'expected: <..> but was: <..>'
+ this.traces.appendText(data.replace(//g, '>'));
+ }
+ this.traces.appendText('\n');
}
}
+ protected getTestItem(message: string): TestItem | undefined {
+ const index: string = message.substring(0, message.indexOf(',')).trim();
+ return this.testOutputMapping.get(index)?.testItem;
+ }
+
protected getTestId(message: string): string {
/**
* The following regex expression is used to parse the test runner's output, which match the following components:
- * '\d+,' - index from the test runner
* '(?:@AssumptionFailure: |@Ignore: )?' - indicate if the case is ignored due to assumption failure or disabled
* '(.*?)' - test method name
- * '(?:\[\d+\])?' - execution index, it will appear for the JUnit4's parameterized test
+ * '(?:\[\d+.*?\])?' - execution index, it will appear for the JUnit4's parameterized test
* '\(([^)]*)\)[^(]*$' - class fully qualified name which wrapped by the last paired brackets, see:
* https://github.com/microsoft/vscode-java-test/issues/1075
*/
- const regexp: RegExp = /\d+,(?:@AssumptionFailure: |@Ignore: )?(.*?)(?:\[\d+\])?\(([^)]*)\)[^(]*$/;
+ const regexp: RegExp = /(?:@AssumptionFailure: |@Ignore: )?(.*?)(?:\[\d+.*?\])?\(([^)]*)\)[^(]*$/;
const matchResults: RegExpExecArray | null = regexp.exec(message);
if (matchResults && matchResults.length === 3) {
return `${this.projectName}@${matchResults[2]}#${matchResults[1]}`;
@@ -121,28 +189,160 @@ export class JUnitRunnerResultAnalyzer extends BaseRunnerResultAnalyzer {
// In case the output is class level, i.e.: `%ERROR 2,a.class.FullyQualifiedName`
const indexOfSpliter: number = message.lastIndexOf(',');
if (indexOfSpliter > -1) {
- return `${this.projectName}@${message.slice(indexOfSpliter + 1)}#`;
+ return `${this.projectName}@${message.slice(indexOfSpliter + 1)}`;
}
- logger.error(`Failed to parse the message: ${message}`);
- return '';
+ return `${this.projectName}@${message}`;
+ }
+
+ protected initializeCache(item: TestItem): void {
+ this.currentTestState = TestResultState.Running;
+ this.currentItem = item;
+ this.currentDuration = 0;
+ this.assertionFailure = undefined;
+ this.expectString = '';
+ this.actualString = '';
+ this.recordingType = RecordingType.None;
+ }
+
+ private enlistToTestMapping(message: string): void {
+ const regExp: RegExp = /([^\\,]|\\\,?)+/gm;
+ // See MessageId.TestTree's comment for its format
+ const result: RegExpMatchArray | null = message.match(regExp);
+ if (result && result.length > 6) {
+ // for now, skip the param test for JUnit 4
+ if (/^\[\d+.*?\]$/.test(result[1])) {
+ return;
+ }
+ const index: string = result[0];
+ const testId: string = this.getTestId(result[1]);
+ const isSuite: boolean = result[2] === 'true';
+ const testCount: number = parseInt(result[3], 10);
+ const isDynamic: boolean = result[4] === 'true';
+ const parentIndex: string = result[5];
+ const displayName: string = result[6].replace(/\\,/g, ',');
+
+ let testItem: TestItem | undefined;
+ if (isDynamic) {
+ const parentInfo: ITestInfo | undefined = this.testOutputMapping.get(parentIndex);
+ const parent: TestItem | undefined = parentInfo?.testItem;
+ if (parent) {
+ const parentData: ITestItemData | undefined = dataCache.get(parent);
+ if (parentData?.testLevel === TestLevel.Method) {
+ testItem = createTestItem({
+ children: [],
+ uri: parent.uri?.toString(),
+ range: parent.range,
+ jdtHandler: parentData.jdtHandler,
+ fullName: parentData.fullName,
+ label: displayName,
+ id: `${INVOCATION_PREFIX}${parent.id}[#${parent.children.size + 1}]`,
+ projectName: parentData.projectName,
+ testKind: parentData.testKind,
+ testLevel: TestLevel.Invocation,
+ }, parent);
+ }
+ }
+ } else {
+ testItem = this.triggeredTestsMapping.get(testId);
+
+ if (this.incompleteTestSuite.length) {
+ const suiteIdx: number = this.incompleteTestSuite.length - 1;
+ const parentSuite: ITestInfo = this.incompleteTestSuite[suiteIdx];
+ parentSuite.testCount--;
+ if (parentSuite.testCount <= 0) {
+ this.incompleteTestSuite.pop();
+ }
+ if (!testItem && parentSuite.testItem) {
+ const itemData: IJavaTestItem | undefined = {
+ children: [],
+ uri: undefined,
+ range: undefined,
+ jdtHandler: '',
+ fullName: testId.substr(testId.indexOf('@') + 1),
+ label: displayName,
+ id: `${INVOCATION_PREFIX}${testId}`,
+ projectName: this.projectName,
+ testKind: this.testContext.kind,
+ testLevel: TestLevel.Invocation,
+ };
+ testItem = createTestItem(itemData, parentSuite.testItem);
+ }
+ }
+
+ if (isSuite && testCount > 0) {
+ this.incompleteTestSuite.push({
+ testId,
+ testCount,
+ testItem,
+ });
+ }
+
+ if (testItem && dataCache.get(testItem)?.testKind === TestKind.JUnit5 && testItem.label !== displayName) {
+ testItem.description = displayName;
+ }
+ }
+
+ this.testOutputMapping.set(index, {
+ testId,
+ testCount,
+ testItem,
+ });
+ }
}
-}
-function updateElapsedTime(result: ITestResult): void {
- if (result.duration && result.duration < 0) {
- const end: number = Date.now();
- result.duration += end;
+ private async tryAppendMessage(item: TestItem, testMessage: TestMessage): Promise {
+ if (item.uri && item.range) {
+ testMessage.location = new Location(item.uri, item.range);
+ } else {
+ let id: string = item.id;
+ if (id.startsWith(INVOCATION_PREFIX)) {
+ id = id.substring(INVOCATION_PREFIX.length);
+ }
+ const location: Location | undefined = await findTestLocation(id);
+ testMessage.location = location;
+ }
+ setTestState(this.testContext.testRun, item, TestResultState.Failed, testMessage);
}
}
enum MessageId {
+ /**
+ * Notification about a test inside the test suite.
+ * TEST_TREE + testId + "," + testName + "," + isSuite + "," + testCount + "," + isDynamicTest +
+ * "," + parentId + "," + displayName + "," + parameterTypes + "," + uniqueId
+ * isSuite = "true" or "false"
+ * isDynamicTest = "true" or "false"
+ * parentId = the unique id of its parent if it is a dynamic test, otherwise can be "-1"
+ * displayName = the display name of the test
+ * parameterTypes = comma-separated list of method parameter types if applicable, otherwise an
+ * empty string
+ * uniqueId = the unique ID of the test provided by JUnit launcher, otherwise an empty string
+ */
+ TestTree = '%TSTTREE',
TestStart = '%TESTS',
TestEnd = '%TESTE',
TestFailed = '%FAILED',
TestError = '%ERROR',
+ ExpectStart = '%EXPECTS',
+ ExpectEnd = '%EXPECTE',
+ ActualStart = '%ACTUALS',
+ ActualEnd = '%ACTUALE',
TraceStart = '%TRACES',
TraceEnd = '%TRACEE',
IGNORE_TEST_PREFIX = '@Ignore: ',
ASSUMPTION_FAILED_TEST_PREFIX = '@AssumptionFailure: ',
}
+
+interface ITestInfo {
+ testId: string;
+ testCount: number;
+ testItem: TestItem | undefined;
+}
+
+enum RecordingType {
+ None,
+ StackTrace,
+ ExpectMessage,
+ ActualMessage,
+}
diff --git a/src/runners/junitRunner/JunitRunner.ts b/src/runners/junitRunner/JunitRunner.ts
index 7f50eaba..f02461bc 100644
--- a/src/runners/junitRunner/JunitRunner.ts
+++ b/src/runners/junitRunner/JunitRunner.ts
@@ -2,15 +2,14 @@
// Licensed under the MIT license.
import { AddressInfo } from 'net';
-import { DebugConfiguration } from 'vscode';
+import { CancellationToken, DebugConfiguration } from 'vscode';
import { IProgressReporter } from '../../debugger.api';
import { BaseRunner } from '../baseRunner/BaseRunner';
-import { BaseRunnerResultAnalyzer } from '../baseRunner/BaseRunnerResultAnalyzer';
+import { IRunnerResultAnalyzer } from '../baseRunner/IRunnerResultAnalyzer';
import { JUnitRunnerResultAnalyzer } from './JUnitRunnerResultAnalyzer';
export class JUnitRunner extends BaseRunner {
-
- public async run(launchConfiguration: DebugConfiguration, progressReporter?: IProgressReporter): Promise> {
+ public async run(launchConfiguration: DebugConfiguration, token: CancellationToken, progressReporter?: IProgressReporter): Promise {
if (launchConfiguration.args) {
// We need to replace the socket port number since the socket is established from the client side.
// The port number returned from the server side is a fake one.
@@ -22,13 +21,11 @@ export class JUnitRunner extends BaseRunner {
args.push('-port', `${(this.server.address() as AddressInfo).port}`);
}
}
- return super.run(launchConfiguration, progressReporter);
+
+ return super.run(launchConfiguration, token, progressReporter);
}
- protected get testResultAnalyzer(): BaseRunnerResultAnalyzer {
- if (!this.runnerResultAnalyzer) {
- this.runnerResultAnalyzer = new JUnitRunnerResultAnalyzer(this.context.projectName);
- }
- return this.runnerResultAnalyzer;
+ protected getAnalyzer(): IRunnerResultAnalyzer {
+ return new JUnitRunnerResultAnalyzer(this.testContext);
}
}
diff --git a/src/runners/models.ts b/src/runners/models.ts
deleted file mode 100644
index b687a1d8..00000000
--- a/src/runners/models.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license.
-
-import { ITestItem, TestKind, TestLevel } from '../protocols';
-
-export interface ITestResult {
- id: string;
- status?: TestStatus;
- trace?: string;
- message?: string;
- duration?: number;
- summary?: string;
-}
-
-export enum TestStatus {
- Pending = 'Pending',
- Running = 'Running',
- Pass = 'Pass',
- Fail = 'Fail',
- Skip = 'Skip',
-}
-
-export interface ITestOutputData {
- type: TestOutputType;
- name: string;
-}
-
-export enum TestOutputType {
- Info,
- Error,
-}
-
-export interface IRunnerContext {
- scope: TestLevel;
- testUri: string;
- fullName: string;
- projectName: string;
- isDebug: boolean;
- kind: TestKind;
- tests: ITestItem[];
- isHierarchicalPackage?: boolean;
-}
diff --git a/src/runners/runnerScheduler.ts b/src/runners/runnerScheduler.ts
deleted file mode 100644
index 176e644e..00000000
--- a/src/runners/runnerScheduler.ts
+++ /dev/null
@@ -1,217 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license.
-
-import * as _ from 'lodash';
-import { DebugConfiguration, ExtensionContext, ProgressLocation, Uri, window, workspace, WorkspaceFolder } from 'vscode';
-import { testCodeLensController } from '../codelens/TestCodeLensController';
-import { ReportShowSetting } from '../constants/configs';
-import { IProgressReporter } from '../debugger.api';
-import { progressProvider } from '../extension';
-import { logger } from '../logger/logger';
-import { ITestItem, TestKind } from '../protocols';
-import { IExecutionConfig } from '../runConfigs';
-import { testReportProvider } from '../testReportProvider';
-import { testResultManager } from '../testResultManager';
-import { testStatusBarProvider } from '../testStatusBarProvider';
-import { loadRunConfig } from '../utils/configUtils';
-import { resolveLaunchConfigurationForRunner } from '../utils/launchUtils';
-import { getShowReportSetting } from '../utils/settingUtils';
-import * as uiUtils from '../utils/uiUtils';
-import { BaseRunner } from './baseRunner/BaseRunner';
-import { JUnitRunner } from './junitRunner/JunitRunner';
-import { IRunnerContext, ITestResult, TestStatus } from './models';
-import { TestNGRunner } from './testngRunner/TestNGRunner';
-
-class RunnerScheduler {
- private _context: ExtensionContext;
- private _isRunning: boolean;
- private _runnerMap: Map | undefined;
- private _executionCache: IExecutionCache | undefined;
-
- public initialize(context: ExtensionContext): void {
- this._context = context;
- }
-
- public async relaunch(): Promise {
- if (!this._executionCache || !this._executionCache.context) {
- logger.info('No test history available, please run some test cases first to relaunch the tests.\n');
- return;
- }
-
- await this.run(this._executionCache.context);
- }
-
- public async run(runnerContext: IRunnerContext, progressReporter?: IProgressReporter, launchConfiguration?: DebugConfiguration): Promise {
- if (this._isRunning) {
- window.showInformationMessage('A test session is currently running. Please wait until it finishes.\n');
- return;
- }
-
- this._isRunning = true;
-
- progressReporter = progressReporter || progressProvider?.createProgressReporter(runnerContext.isDebug ? 'Debug Test' : 'Run Test');
-
- this._executionCache = {
- context: _.cloneDeep(runnerContext),
- };
-
- let allIds: Set = new Set();
- try {
- this._runnerMap = this.classifyTestsByKind(runnerContext.tests);
- for (const [runner, tests] of this._runnerMap.entries()) {
- runnerContext.kind = tests[0].kind;
- runnerContext.projectName = tests[0].project;
- runnerContext.tests = tests;
-
- await runner.setup(runnerContext);
- let resolvedConfiguration: DebugConfiguration | undefined = launchConfiguration;
- if (!resolvedConfiguration) {
- // The test items that belong to a test runner, here the test items should be in the same workspace folder.
- const workspaceFolder: WorkspaceFolder | undefined = workspace.getWorkspaceFolder(Uri.parse(tests[0].location.uri));
- const config: IExecutionConfig | undefined = await loadRunConfig(workspaceFolder);
- if (!config) {
- logger.info('Test job is canceled.\n');
- continue;
- }
- if (progressReporter?.isCancelled()) {
- progressReporter = progressProvider?.createProgressReporter(runnerContext.isDebug ? 'Debug Test' : 'Run Test', ProgressLocation.Notification, true);
- }
- progressReporter?.report('Resolving launch configuration...');
- resolvedConfiguration = await resolveLaunchConfigurationForRunner(runner, runnerContext, config);
- }
- const ids: Set = await runner.run(resolvedConfiguration, progressReporter);
- allIds = new Set([...allIds, ...ids]);
- }
- const finalResults: ITestResult[] = testResultManager.getResultsByIds(Array.from(allIds));
- testStatusBarProvider.showTestResult(finalResults);
- testCodeLensController.refresh();
- this.showReportIfNeeded(finalResults);
- this._executionCache.results = finalResults;
- } catch (error) {
- logger.error(error.toString());
- uiUtils.showError(error);
- } finally {
- progressReporter?.done();
- await this.cleanUp(false);
- }
- }
-
- public getExecutionCache(): IExecutionCache | undefined {
- return this._executionCache;
- }
-
- public async cleanUp(isCancel: boolean): Promise {
- try {
- const promises: Array> = [];
- if (this._runnerMap) {
- for (const runner of this._runnerMap.keys()) {
- promises.push(runner.tearDown());
- }
- this._runnerMap.clear();
- this._runnerMap = undefined;
- }
- await Promise.all(promises);
-
- if (isCancel) {
- logger.info('Test job is canceled.\n');
- }
- } catch (error) {
- logger.error('Failed to clean up', error);
- }
- this._isRunning = false;
- }
-
- private classifyTestsByKind(tests: ITestItem[]): Map {
- const testMap: Map = this.mapTestsByProjectAndKind(tests);
- return this.mapTestsByRunner(testMap);
- }
-
- private mapTestsByProjectAndKind(tests: ITestItem[]): Map {
- const map: Map = new Map();
- // Store all the covered test items, e.g. if a class will be run, all the child method will be added into it
- const coveredSet: Set = new Set();
- for (const test of tests) {
- if (coveredSet.has(test.id)) {
- continue;
- }
- if (!(test.kind in TestKind)) {
- logger.error(`Unknown kind of test item: ${test.fullName}`);
- continue;
- }
- const key: string = `${test.project}/${test.kind}`;
- const testArray: ITestItem[] | undefined = map.get(key);
- if (testArray) {
- testArray.push(test);
- } else {
- map.set(key, [test]);
- }
- coveredSet.add(test.id);
- if (test.children) {
- for (const childId of test.children) {
- coveredSet.add(childId);
- }
- }
- }
- return map;
- }
-
- private mapTestsByRunner(testsPerProjectAndKind: Map): Map {
- const map: Map = new Map();
- for (const tests of testsPerProjectAndKind.values()) {
- const runner: BaseRunner | undefined = this.getRunnerByKind(tests[0].kind);
- if (runner) {
- map.set(runner, tests);
- } else {
- window.showWarningMessage(`Cannot find matched runner to run the test: ${tests[0].kind}`);
- }
- }
- return map;
- }
-
- private getRunnerByKind(kind: TestKind): BaseRunner | undefined {
- switch (kind) {
- case TestKind.JUnit:
- case TestKind.JUnit5:
- return new JUnitRunner(this._context.extensionPath);
- case TestKind.TestNG:
- return new TestNGRunner(this._context.extensionPath);
- default:
- return undefined;
- }
- }
-
- private showReportIfNeeded(finalResults: ITestResult[]): void {
- if (finalResults.length === 0) {
- return;
- }
-
- const showSetting: string = getShowReportSetting();
- switch (showSetting) {
- case ReportShowSetting.Always:
- testReportProvider.report(finalResults);
- break;
- case ReportShowSetting.OnFail:
- const hasFailedTests: boolean = finalResults.some((result: ITestResult) => {
- return result.status === TestStatus.Fail;
- });
- if (hasFailedTests) {
- testReportProvider.report(finalResults);
- } else {
- testReportProvider.update(finalResults);
- }
- break;
- case ReportShowSetting.Never:
- testReportProvider.update(finalResults);
- break;
- default:
- break;
- }
- }
-}
-
-export interface IExecutionCache {
- context: IRunnerContext;
- results?: ITestResult[];
-}
-
-export const runnerScheduler: RunnerScheduler = new RunnerScheduler();
diff --git a/src/runners/testngRunner/TestNGRunner.ts b/src/runners/testngRunner/TestNGRunner.ts
index 4fd3979c..85c1101a 100644
--- a/src/runners/testngRunner/TestNGRunner.ts
+++ b/src/runners/testngRunner/TestNGRunner.ts
@@ -1,29 +1,44 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
-import { logger } from '../../logger/logger';
+import { TestItem } from 'vscode';
+import { dataCache } from '../../controller/testItemDataCache';
+import { TestLevel } from '../../types';
import { BaseRunner } from '../baseRunner/BaseRunner';
-import { BaseRunnerResultAnalyzer } from '../baseRunner/BaseRunnerResultAnalyzer';
+import { IRunnerResultAnalyzer } from '../baseRunner/IRunnerResultAnalyzer';
import { TestNGRunnerResultAnalyzer } from './TestNGRunnerResultAnalyzer';
export class TestNGRunner extends BaseRunner {
public getRunnerCommandParams(): string[] {
- return ['testng', ...this.testIds.map((id: string) => {
+ const testMethods: TestItem[] = [];
+ const queue: TestItem[] = [...this.testContext.testItems];
+ while (queue.length) {
+ const item: TestItem = queue.shift()!;
+ const testLevel: TestLevel | undefined = dataCache.get(item)?.testLevel;
+ if (testLevel === undefined) {
+ continue;
+ }
+ if (testLevel === TestLevel.Method) {
+ testMethods.push(item);
+ } else {
+ item.children.forEach((child: TestItem) => {
+ queue.push(child);
+ });
+ }
+ }
+
+ return ['testng', ...testMethods.map((method: TestItem) => {
// parse to fullName
- const index: number = id.indexOf('@');
+ const index: number = method.id.indexOf('@');
if (index < 0) {
- logger.error(`Invalid ID: ${id}`);
return '';
}
- return id.slice(index + 1);
+ return method.id.slice(index + 1);
}).filter(Boolean)];
}
- protected get testResultAnalyzer(): BaseRunnerResultAnalyzer {
- if (!this.runnerResultAnalyzer) {
- this.runnerResultAnalyzer = new TestNGRunnerResultAnalyzer(this.context.projectName);
- }
- return this.runnerResultAnalyzer;
+ protected getAnalyzer(): IRunnerResultAnalyzer {
+ return new TestNGRunnerResultAnalyzer(this.testContext);
}
}
diff --git a/src/runners/testngRunner/TestNGRunnerResultAnalyzer.ts b/src/runners/testngRunner/TestNGRunnerResultAnalyzer.ts
index 89d1c941..d85dff06 100644
--- a/src/runners/testngRunner/TestNGRunnerResultAnalyzer.ts
+++ b/src/runners/testngRunner/TestNGRunnerResultAnalyzer.ts
@@ -1,55 +1,167 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
-import { testResultManager } from '../../testResultManager';
-import { BaseRunnerResultAnalyzer } from '../baseRunner/BaseRunnerResultAnalyzer';
-import { ITestOutputData, ITestResult, TestStatus } from '../models';
+import { Location, MarkdownString, TestItem, TestMessage, TestResultState } from 'vscode';
+import { dataCache } from '../../controller/testItemDataCache';
+import { IRunTestContext, TestLevel } from '../../types';
+import { IRunnerResultAnalyzer } from '../baseRunner/IRunnerResultAnalyzer';
+import { setTestState } from '../utils';
const TEST_START: string = 'testStarted';
const TEST_FAIL: string = 'testFailed';
const TEST_FINISH: string = 'testFinished';
-export class TestNGRunnerResultAnalyzer extends BaseRunnerResultAnalyzer {
+export class TestNGRunnerResultAnalyzer implements IRunnerResultAnalyzer {
- protected processData(data: string): void {
- super.processData(data);
+ private readonly regex: RegExp = /@@/;
+
+ private triggeredTestsMapping: Map = new Map();
+ private currentTestState: TestResultState;
+ private currentItem: TestItem | undefined;
+ private projectName: string;
+
+ constructor(private testContext: IRunTestContext) {
+ this.projectName = testContext.projectName;
+ const queue: TestItem[] = [...testContext.testItems];
+ while (queue.length) {
+ const item: TestItem = queue.shift()!;
+ const testLevel: TestLevel | undefined = dataCache.get(item)?.testLevel;
+ if (testLevel === undefined) {
+ continue;
+ }
+ if (testLevel === TestLevel.Method) {
+ this.triggeredTestsMapping.set(item.id, item);
+ } else {
+ item.children.forEach((child: TestItem) => {
+ queue.push(child);
+ });
+ }
+ }
+ }
+
+ public analyzeData(data: string): void {
+ const lines: string[] = data.split(/\r?\n/);
+ for (const line of lines) {
+ if (!line) {
+ continue;
+ }
+ const match: RegExpExecArray | null = this.regex.exec(line);
+ if (match) {
+ // Message from Test Runner executable
+ try {
+ this.processData(match[1]);
+ } catch (error) {
+ this.testContext.testRun.appendOutput(`[ERROR] Failed to parse output data: ${line}\n`);
+ }
+ } else {
+ this.testContext.testRun.appendOutput(line + '\r\n');
+ }
+ }
+ }
+
+ public processData(data: string): void {
const outputData: ITestNGOutputData = JSON.parse(data) as ITestNGOutputData;
+ if (outputData.name.toLocaleLowerCase() === 'error') {
+ this.testContext.testRun.appendOutput(`[ERROR] ${this.unescape(data)}\r\n`);
+ } else {
+ this.testContext.testRun.appendOutput(`${this.unescape(data)}\r\n`);
+ }
+
const id: string = `${this.projectName}@${outputData.attributes.name}`;
- switch (outputData.name) {
- case TEST_START:
- testResultManager.storeResult({
- id,
- status: TestStatus.Running,
- });
- this.testIds.add(id);
- break;
- case TEST_FAIL:
- const failedResult: ITestResult | undefined = testResultManager.getResultById(id);
- if (!failedResult) {
- return;
+ if (outputData.name === TEST_START) {
+ this.initializeCache();
+ const item: TestItem | undefined = this.getTestItem(id);
+ if (!item) {
+ return;
+ }
+ this.currentTestState = TestResultState.Running;
+ this.testContext.testRun.started(item);
+ } else if (outputData.name === TEST_FAIL) {
+ const item: TestItem | undefined = this.getTestItem(id);
+ if (!item) {
+ return;
+ }
+ this.currentTestState = TestResultState.Failed;
+ const testMessages: TestMessage[] = [];
+ if (outputData.attributes.message) {
+ const message: TestMessage = new TestMessage(outputData.attributes.message.trim());
+ if (item.uri && item.range) {
+ message.location = new Location(item.uri, item.range);
}
- failedResult.status = TestStatus.Fail;
- failedResult.message = outputData.attributes.message;
- failedResult.trace = outputData.attributes.trace;
- testResultManager.storeResult(failedResult);
- break;
- case TEST_FINISH:
- const finishedResult: ITestResult | undefined = testResultManager.getResultById(id);
- if (!finishedResult) {
- return;
+ testMessages.push(message);
+ }
+
+ if (outputData.attributes.trace) {
+ const traceString: string = outputData.attributes.trace.trim();
+ const markdownTrace: MarkdownString = new MarkdownString();
+ markdownTrace.isTrusted = true;
+ const traceRegExp: RegExp = /(\s?at\s+)([\w$\\.]+\/)?((?:[\w$]+\.)+[<\w$>]+)\(([\w-$]+\.java):(\d+)\)/;
+ for (const line of traceString.split(/\r?\n/)) {
+ const traceResults: RegExpExecArray | null = traceRegExp.exec(line);
+ if (traceResults && traceResults.length === 6) {
+ markdownTrace.appendText(traceResults[1]);
+ markdownTrace.appendMarkdown(`${(traceResults[2] || '') + traceResults[3]}([${traceResults[4]}:${traceResults[5]}](command:_java.test.openStackTrace?${encodeURIComponent(JSON.stringify([data, this.projectName]))}))`);
+ } else {
+ // in case the message contains message like: 'expected: <..> but was: <..>'
+ markdownTrace.appendText(line.replace(//g, '>'));
+ }
+ markdownTrace.appendText('\n');
}
- if (finishedResult.status === TestStatus.Running) {
- finishedResult.status = TestStatus.Pass;
+ const testMessage: TestMessage = new TestMessage(markdownTrace);
+ if (item.uri && item.range) {
+ testMessage.location = new Location(item.uri, item.range);
}
- finishedResult.duration = Number.parseInt(outputData.attributes.duration, 10);
- testResultManager.storeResult(finishedResult);
- break;
+ testMessages.push(testMessage);
+ }
+
+ const duration: number = Number.parseInt(outputData.attributes.duration, 10);
+ setTestState(this.testContext.testRun, item, this.currentTestState, testMessages, duration);
+ } else if (outputData.name === TEST_FINISH) {
+ const item: TestItem | undefined = this.getTestItem(data);
+ if (!item) {
+ return;
+ }
+ if (this.currentTestState === TestResultState.Running) {
+ this.currentTestState = TestResultState.Passed;
+ }
+ const duration: number = Number.parseInt(outputData.attributes.duration, 10);
+ setTestState(this.testContext.testRun, item, this.currentTestState, undefined, duration);
}
}
+
+ protected getTestItem(testId: string): TestItem | undefined {
+ if (this.currentItem) {
+ return this.currentItem;
+ }
+
+ this.currentItem = this.triggeredTestsMapping.get(testId);
+ return this.currentItem;
+ }
+
+ protected unescape(content: string): string {
+ return content.replace(/\\r/gm, '\r')
+ .replace(/\\f/gm, '\f')
+ .replace(/\\n/gm, '\n')
+ .replace(/\\t/gm, '\t')
+ .replace(/\\b/gm, '\b')
+ .replace(/\\"/gm, '"');
+ }
+
+ protected initializeCache(): void {
+ this.currentTestState = TestResultState.Queued;
+ this.currentItem = undefined;
+ }
}
-interface ITestNGOutputData extends ITestOutputData {
+interface ITestNGOutputData {
attributes: ITestNGAttributes;
+ type: TestOutputType;
+ name: string;
+}
+
+enum TestOutputType {
+ Info,
+ Error,
}
interface ITestNGAttributes {
diff --git a/src/runners/utils.ts b/src/runners/utils.ts
new file mode 100644
index 00000000..2cf48f93
--- /dev/null
+++ b/src/runners/utils.ts
@@ -0,0 +1,38 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+import { Location, TestItem, TestMessage, TestResultState, TestRun, Uri } from 'vscode';
+import { JavaTestRunnerCommands } from '../constants';
+import { asRange } from '../controller/utils';
+import { executeJavaLanguageServerCommand } from '../utils/commandUtils';
+
+export async function findTestLocation(fullName: string): Promise {
+ const location: any | undefined = await executeJavaLanguageServerCommand