This is an automated email from the ASF dual-hosted git repository.
dbalek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new 47632f8d5f LSP: Create test class code action added. (#4939)
47632f8d5f is described below
commit 47632f8d5f35588db847e198ec977c15ee6c9fb2
Author: Dusan Balek <[email protected]>
AuthorDate: Wed Nov 9 17:52:15 2022 +0100
LSP: Create test class code action added. (#4939)
---
.../lsp/server/protocol/TestClassGenerator.java | 311 +++++++++++++++++++++
1 file changed, 311 insertions(+)
diff --git
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TestClassGenerator.java
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TestClassGenerator.java
new file mode 100644
index 0000000000..7a352e0787
--- /dev/null
+++
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TestClassGenerator.java
@@ -0,0 +1,311 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.lsp.server.protocol;
+
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSyntaxException;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.util.SourcePositions;
+import com.sun.source.util.TreePath;
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import org.eclipse.lsp4j.CodeAction;
+import org.eclipse.lsp4j.CodeActionKind;
+import org.eclipse.lsp4j.CodeActionParams;
+import org.eclipse.lsp4j.MessageParams;
+import org.eclipse.lsp4j.MessageType;
+import org.eclipse.lsp4j.ShowDocumentParams;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.project.JavaProjectConstants;
+import org.netbeans.api.java.queries.UnitTestForSourceQuery;
+import org.netbeans.api.java.source.ClasspathInfo;
+import org.netbeans.api.java.source.CompilationController;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.api.project.FileOwnerQuery;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.api.project.SourceGroup;
+import org.netbeans.modules.gsf.testrunner.api.TestCreatorProvider;
+import org.netbeans.modules.gsf.testrunner.plugin.CommonTestUtilProvider;
+import org.netbeans.modules.gsf.testrunner.plugin.GuiUtilsProvider;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.netbeans.modules.parsing.api.ResultIterator;
+import org.openide.filesystems.FileChangeAdapter;
+import org.openide.filesystems.FileChangeListener;
+import org.openide.filesystems.FileEvent;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+import org.openide.util.RequestProcessor;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+@ServiceProvider(service = CodeActionsProvider.class, position = 100)
+public final class TestClassGenerator extends CodeActionsProvider {
+
+ private static final String GENERATE_TEST_CLASS_COMMAND =
"java.generate.testClass";
+
+ private final Set<String> commands =
Collections.singleton(GENERATE_TEST_CLASS_COMMAND);
+
+ @Override
+ @NbBundle.Messages({
+ "# {0} - the testing framework to be used, e.g. JUnit, TestNG,...",
+ "# {1} - the location where the test class will be created",
+ "DN_GenerateTestClass=Create Test Class [{0} in {1}]"
+ })
+ public List<CodeAction> getCodeActions(ResultIterator resultIterator,
CodeActionParams params) throws Exception {
+ CompilationController info =
CompilationController.get(resultIterator.getParserResult());
+ if (info == null) {
+ return Collections.emptyList();
+ }
+ info.toPhase(JavaSource.Phase.RESOLVED);
+ int offset = getOffset(info, params.getRange().getStart());
+ TreePath tp = info.getTreeUtilities().pathFor(offset);
+ if (!TreeUtilities.CLASS_TREE_KINDS.contains(tp.getLeaf().getKind())) {
+ return Collections.emptyList();
+ }
+ ClassTree cls = (ClassTree) tp.getLeaf();
+ SourcePositions sourcePositions = info.getTrees().getSourcePositions();
+ int startPos = (int)
sourcePositions.getStartPosition(tp.getCompilationUnit(), cls);
+ String code = info.getText();
+ if (startPos < 0 || offset < 0 || offset < startPos || offset >=
code.length()) {
+ return Collections.emptyList();
+ }
+ String headerText = code.substring(startPos, offset);
+ int idx = headerText.indexOf('{');
+ if (idx >= 0) {
+ return Collections.emptyList();
+ }
+ ClassPath cp =
info.getClasspathInfo().getClassPath(ClasspathInfo.PathKind.SOURCE);
+ FileObject fileObject = info.getFileObject();
+ if (!fileObject.isValid()) {
+ return Collections.emptyList();
+ }
+ FileObject root = cp.findOwnerRoot(fileObject);
+ if (root == null) {
+ return Collections.emptyList();
+ }
+ Map<Object, List<String>> validCombinations =
getValidCombinations(info);
+ if (validCombinations == null || validCombinations.isEmpty()) {
+ return Collections.emptyList();
+ }
+ List<CodeAction> result = new ArrayList<>();
+ for (Map.Entry<Object, List<String>> entrySet :
validCombinations.entrySet()) {
+ Object location = entrySet.getKey();
+ for (String testingFramework : entrySet.getValue()) {
+
result.add((createCodeAction(Bundle.DN_GenerateTestClass(testingFramework,
getLocationText(location)), CodeActionKind.Refactor, null,
GENERATE_TEST_CLASS_COMMAND, Utils.toUri(fileObject), testingFramework,
Utils.toUri(getTargetFolder(location)))));
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public Set<String> getCommands() {
+ return commands;
+ }
+
+ @Override
+ public CompletableFuture<Object> processCommand(NbCodeLanguageClient
client, String command, List<Object> arguments) {
+ try {
+ if (arguments.size() > 2) {
+ String uri = ((JsonPrimitive) arguments.get(0)).getAsString();
+ FileObject fileObject = Utils.fromUri(uri);
+ if (fileObject == null) {
+ throw new IllegalArgumentException(String.format("Cannot
resolve source file from uri: %s", uri));
+ }
+ String testingFramework = ((JsonPrimitive)
arguments.get(1)).getAsString();
+ String targetUri = ((JsonPrimitive)
arguments.get(2)).getAsString();
+ FileObject targetFolder = Utils.fromUri(targetUri);
+ if (targetFolder == null) {
+ throw new IllegalArgumentException(String.format("Cannot
resolve target folder from uri: %s", targetUri));
+ }
+ Collection<? extends Lookup.Item<TestCreatorProvider>>
providers =
Lookup.getDefault().lookupResult(TestCreatorProvider.class).allItems();
+ for (final Lookup.Item<TestCreatorProvider> provider :
providers) {
+ if (provider.getDisplayName().equals(testingFramework)) {
+ final TestCreatorProvider.Context context = new
TestCreatorProvider.Context(new FileObject[]{fileObject});
+ context.setSingleClass(true);
+ context.setTargetFolder(targetFolder);
+ context.setTestClassName(getPreffiledName(fileObject,
testingFramework));
+ FileChangeListener fcl = new FileChangeAdapter() {
+ @Override
+ public void fileDataCreated(FileEvent fe) {
+ RequestProcessor.getDefault().post(() -> {
+ client.showDocument(new
ShowDocumentParams(Utils.toUri(fe.getFile())));
+ }, 1000);
+ }
+ };
+ targetFolder.addRecursiveListener(fcl);
+ try {
+ provider.getInstance().createTests(context);
+ } finally {
+ RequestProcessor.getDefault().post(() -> {
+ targetFolder.removeRecursiveListener(fcl);
+ }, 1000);
+ }
+ }
+ }
+ } else {
+ throw new IllegalArgumentException(String.format("Illegal
number of arguments received for command: %s", command));
+ }
+ } catch (JsonSyntaxException | IllegalArgumentException |
MalformedURLException ex) {
+ client.showMessage(new MessageParams(MessageType.Error,
ex.getLocalizedMessage()));
+ }
+ return CompletableFuture.completedFuture(true);
+ }
+
+ private static String getLocationText(Object location) {
+ String text = location instanceof SourceGroup
+ ? ((SourceGroup) location).getDisplayName()
+ : location instanceof FileObject
+ ? FileUtil.getFileDisplayName((FileObject) location)
+ : location.toString();
+ return text;
+ }
+
+ private static Map<Object, List<String>>
getValidCombinations(CompilationInfo info) {
+ List<String> testingFrameworks =
getTestingFrameworks(info.getFileObject());
+ if (testingFrameworks.isEmpty()) {
+ return null;
+ }
+ Map<Object, List<String>> validCombinations = new HashMap<>();
+ for (Object location : getLocations(info.getFileObject())) {
+ String targetFolderPath = getTargetFolderPath(location);
+ List<String> framework2Add = new ArrayList<>();
+ for (String framework : testingFrameworks) {
+ String preffiledName = getPreffiledName(info.getFileObject(),
framework);
+ preffiledName = preffiledName.replace('.',
File.separatorChar).concat(".java");
+ String path =
targetFolderPath.concat(File.separator).concat(preffiledName);
+ File f = new File(path);
+ FileObject fo = FileUtil.toFileObject(f);
+ if (fo == null) {
+ framework2Add.add(framework);
+ }
+ }
+ if (!framework2Add.isEmpty()) {
+ validCombinations.put(location, framework2Add);
+ }
+ }
+ return validCombinations;
+ }
+
+ private static List<String> getTestingFrameworks(FileObject fileObject) {
+ List<String> testingFrameworks = new ArrayList<>();
+ Collection<? extends Lookup.Item<TestCreatorProvider>>
testCreatorProviders =
Lookup.getDefault().lookupResult(TestCreatorProvider.class).allItems();
+ for (Lookup.Item<TestCreatorProvider> provider : testCreatorProviders) {
+ if (provider.getInstance().enable(new FileObject[]{fileObject})) {
+ testingFrameworks.add(provider.getDisplayName());
+ }
+ }
+ return testingFrameworks;
+ }
+
+ private static Object[] getLocations(FileObject activeFO) {
+ Object[] locations = null;
+ Collection<? extends CommonTestUtilProvider> testUtilProviders =
Lookup.getDefault().lookupAll(CommonTestUtilProvider.class);
+ for (CommonTestUtilProvider provider : testUtilProviders) {
+ locations = provider.getTestTargets(activeFO);
+ break;
+ }
+ if (locations != null && locations.length == 0) {
+ SourceGroup sourceGroupOwner = findSourceGroupOwner(activeFO);
+ if (sourceGroupOwner != null) {
+ locations =
UnitTestForSourceQuery.findUnitTests(sourceGroupOwner.getRootFolder());
+ }
+ }
+ return locations != null ? locations : new Object[0];
+ }
+
+ private static String getPreffiledName(FileObject fileObj, String
selectedFramework) {
+ ClassPath cp = ClassPath.getClassPath(fileObj, ClassPath.SOURCE);
+ String className = cp.getResourceName(fileObj, '.', false);
+ return className + getTestingFrameworkSuffix(selectedFramework) +
"Test";
+ }
+
+ private static String getTestingFrameworkSuffix(String selectedFramework) {
+ if (selectedFramework == null) {
+ return "";
+ }
+ String testngFramework = "";
+ Collection<? extends GuiUtilsProvider> providers =
Lookup.getDefault().lookupAll(GuiUtilsProvider.class);
+ for (GuiUtilsProvider provider : providers) {
+ testngFramework = provider.getTestngFramework();
+ break;
+ }
+ return selectedFramework.equals(testngFramework) ? "NG" : "";
+ }
+
+ private static FileObject getTargetFolder(Object selectedLocation) {
+ if (selectedLocation == null) {
+ return null;
+ }
+ if (selectedLocation instanceof SourceGroup) {
+ return ((SourceGroup) selectedLocation).getRootFolder();
+ }
+ if (selectedLocation instanceof URL) {
+ return URLMapper.findFileObject((URL) selectedLocation);
+ }
+ assert selectedLocation instanceof FileObject;
+ return (FileObject) selectedLocation;
+ }
+
+ private static String getTargetFolderPath(Object selectedLocation) {
+ if (selectedLocation == null) {
+ return null;
+ }
+ if (selectedLocation instanceof SourceGroup) {
+ return ((SourceGroup) selectedLocation).getRootFolder().getPath();
+ }
+ if (selectedLocation instanceof URL) {
+ return ((URL) selectedLocation).getPath();
+ }
+ assert selectedLocation instanceof FileObject;
+ return ((FileObject) selectedLocation).getPath();
+ }
+
+ private static SourceGroup findSourceGroupOwner(FileObject file) {
+ final Project project = FileOwnerQuery.getOwner(file);
+ if (project != null) {
+ final SourceGroup[] sourceGroups =
ProjectUtils.getSources(project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
+ for (int i = 0; i < sourceGroups.length; i++) {
+ SourceGroup srcGroup = sourceGroups[i];
+ FileObject root = srcGroup.getRootFolder();
+ if (((file==root)||(FileUtil.isParentOf(root,file))) &&
srcGroup.contains(file)) {
+ return srcGroup;
+ }
+ }
+ }
+ return null;
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists