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

Reply via email to