From 2a397ae57c751ae2939432982dbe39d931dcc5ed Mon Sep 17 00:00:00 2001 From: aboyko Date: Tue, 5 Sep 2023 14:46:17 -0400 Subject: [PATCH] Unit test for code actions --- .../languageserver/testharness/Editor.java | 10 ++ .../testharness/LanguageServerHarness.java | 16 ++- .../ide/vscode/boot/app/RewriteConfig.java | 2 - .../test/CodeActionViaReconcilerTest.java | 120 ++++++++++++++++++ 4 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/reconcilers/test/CodeActionViaReconcilerTest.java diff --git a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java index 69deca8b1..d32d45bfa 100644 --- a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java +++ b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java @@ -935,6 +935,16 @@ public class Editor { public List getCodeActions(Diagnostic problem) throws Exception { return harness.getCodeActions(doc, problem); } + + public List getCodeActions(String overStr, int occurrence) throws Exception { + assertTrue(occurrence>0); + int offset = occurrences(getRawText(), overStr) + .elementAt(occurrence-1) + .map(o -> o + overStr.length()/2) + .block(); + Position position = doc.toPosition(offset); + return harness.getCodeActions(doc, new Range(position, position)); + } public CodeAction assertCodeAction(Diagnostic problem) throws Exception { List actions = getCodeActions(problem); diff --git a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java index 15ab14e88..31932a0f5 100644 --- a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java +++ b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java @@ -42,8 +42,10 @@ import org.assertj.core.api.Condition; import org.eclipse.lsp4j.ApplyWorkspaceEditParams; import org.eclipse.lsp4j.ApplyWorkspaceEditResponse; import org.eclipse.lsp4j.ClientCapabilities; +import org.eclipse.lsp4j.CodeActionCapabilities; import org.eclipse.lsp4j.CodeActionContext; import org.eclipse.lsp4j.CodeActionParams; +import org.eclipse.lsp4j.CodeActionResolveSupportCapabilities; import org.eclipse.lsp4j.CodeLens; import org.eclipse.lsp4j.CodeLensParams; import org.eclipse.lsp4j.Command; @@ -274,6 +276,9 @@ public class LanguageServerHarness { textCap.setDocumentSymbol(documentSymbolCap); CompletionCapabilities completionCap = new CompletionCapabilities(new CompletionItemCapabilities(true)); textCap.setCompletion(completionCap); + CodeActionCapabilities codeActionCapabilities = new CodeActionCapabilities(true); + codeActionCapabilities.setResolveSupport(new CodeActionResolveSupportCapabilities(List.of("edit"))); + textCap.setCodeAction(codeActionCapabilities); clientCap.setTextDocument(textCap); WorkspaceClientCapabilities workspaceCap = new WorkspaceClientCapabilities(); workspaceCap.setApplyEdit(true); @@ -773,7 +778,16 @@ public class LanguageServerHarness { .map(e -> new CodeAction(this, e)) .collect(Collectors.toList()); } - + + public List getCodeActions(TextDocumentInfo doc, Range range, Diagnostic... problems) throws Exception { + CodeActionContext context = new CodeActionContext(Arrays.asList(problems)); + List> actions = + getServer().getTextDocumentService().codeAction(new CodeActionParams(doc.getId(), range, context)).get(); + return actions.stream() + .map(e -> new CodeAction(this, e)) + .collect(Collectors.toList()); + } + @SuppressWarnings({ "unchecked", "rawtypes" }) public void perform(Command command) throws Exception { List args = command.getArguments(); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/RewriteConfig.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/RewriteConfig.java index 7887c4bff..6f28d6ac8 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/RewriteConfig.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/RewriteConfig.java @@ -11,7 +11,6 @@ package org.springframework.ide.vscode.boot.app; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.ide.vscode.boot.java.reconcilers.JdtReconciler; @@ -26,7 +25,6 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguage @Configuration(proxyBeanMethods = false) public class RewriteConfig { - @ConditionalOnMissingClass("org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness") @Bean RewriteRecipeRepository rewriteRecipesRepository(SimpleLanguageServer server, JavaProjectFinder projectFinder, BootJavaConfig config) { return new RewriteRecipeRepository(server, projectFinder, config); } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/reconcilers/test/CodeActionViaReconcilerTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/reconcilers/test/CodeActionViaReconcilerTest.java new file mode 100644 index 000000000..af76c540b --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/reconcilers/test/CodeActionViaReconcilerTest.java @@ -0,0 +1,120 @@ +/******************************************************************************* + * Copyright (c) 2023 VMware, Inc. + * 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 + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VMware, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.reconcilers.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.io.File; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Import; +import org.springframework.ide.vscode.boot.app.SpringSymbolIndex; +import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest; +import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf; +import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.util.Settings; +import org.springframework.ide.vscode.commons.util.text.LanguageId; +import org.springframework.ide.vscode.languageserver.testharness.CodeAction; +import org.springframework.ide.vscode.languageserver.testharness.Editor; +import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness; +import org.springframework.ide.vscode.project.harness.ProjectsHarness; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; + +@ExtendWith(SpringExtension.class) +@BootLanguageServerTest +@Import(SymbolProviderTestConf.class) +public class CodeActionViaReconcilerTest { + + @Autowired private BootLanguageServerHarness harness; + @Autowired private JavaProjectFinder projectFinder; + + private File directory; + @Autowired private SpringSymbolIndex indexer; + + @BeforeEach + public void setup() throws Exception { + harness.intialize(null); + + String changedSettings = "{\"boot-java\": {\"validation\": {\"java\": { \"reconcilers\": true}}}}"; + JsonElement settingsAsJson = new Gson().fromJson(changedSettings, JsonElement.class); + harness.changeConfiguration(new Settings(settingsAsJson)); + + directory = new File(ProjectsHarness.class.getResource("/test-projects/test-spring-validations/").toURI()); + + String projectDir = directory.toURI().toString(); + + // trigger project creation + projectFinder.find(new TextDocumentIdentifier(projectDir)).get(); + + CompletableFuture initProject = indexer.waitOperation(); + initProject.get(5, TimeUnit.SECONDS); + } + + @Test + void codeActionsFromReconcilingProblems() throws Exception { + String docUri = directory.toPath().resolve("src/main/java/org/test/BeanMethodNotPublic1.java").toUri().toString(); + Editor editor = harness.newEditor(LanguageId.JAVA, """ + package org.test; + + import org.springframework.context.annotation.Bean; + import org.springframework.context.annotation.Configuration; + + @Configuration + class BeanMethodNotPublic1 { + + @Bean + public BeanClass1 publicBeanMethod() { + return new BeanClass1(); + } + + @Bean + BeanClass2 nonPublicBeanMethod() { + return new BeanClass2(); + } + + } + """, docUri); + + List codeActions = editor.getCodeActions("public", 1); + assertEquals(0, codeActions.size()); + + Diagnostic problem = editor.assertProblem("public"); + assertNotNull(problem); + assertEquals(Boot2JavaProblemType.JAVA_PUBLIC_BEAN_METHOD.getCode(), problem.getCode().getLeft()); + assertEquals(Boot2JavaProblemType.JAVA_PUBLIC_BEAN_METHOD.getLabel(), problem.getMessage()); + + codeActions = editor.getCodeActions(problem); + assertEquals(3, codeActions.size()); + String changedSettings = "{\"spring-boot\": {\"ls\": {\"problem\": { \"boot2\": { \"JAVA_PUBLIC_BEAN_METHOD\": \"IGNORE\"}}}}}"; + JsonElement settingsAsJson = new Gson().fromJson(changedSettings, JsonElement.class); + harness.changeConfiguration(new Settings(settingsAsJson)); + + editor.assertProblems(); + + codeActions = editor.getCodeActions("public", 1); + assertEquals(3, codeActions.size()); + } + + +}