diff --git a/build.gradle.kts b/build.gradle.kts index 2eac90c0765..851be1ca3df 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -74,6 +74,7 @@ dependencies { // spring api("org.springframework.boot:spring-boot-starter") api("org.springframework.boot:spring-boot-starter-websocket") + api("org.springframework.boot:spring-boot-starter-cache") api("info.picocli:picocli-spring-boot-starter:4.7.6") // lsp4j core diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/codelenses/AbstractRunTestsCodeLensSupplier.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/codelenses/AbstractRunTestsCodeLensSupplier.java index c96fc5d9690..983efcea4ec 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/codelenses/AbstractRunTestsCodeLensSupplier.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/codelenses/AbstractRunTestsCodeLensSupplier.java @@ -21,18 +21,36 @@ */ package com.github._1c_syntax.bsl.languageserver.codelenses; +import com.github._1c_syntax.bsl.languageserver.configuration.LanguageServerConfiguration; +import com.github._1c_syntax.bsl.languageserver.configuration.events.LanguageServerConfigurationChangedEvent; import com.github._1c_syntax.bsl.languageserver.context.DocumentContext; import com.github._1c_syntax.bsl.languageserver.context.FileType; import com.github._1c_syntax.bsl.languageserver.events.LanguageServerInitializeRequestReceivedEvent; +import com.github._1c_syntax.utils.Absolute; +import jakarta.annotation.Nullable; +import lombok.RequiredArgsConstructor; import org.eclipse.lsp4j.ClientInfo; import org.eclipse.lsp4j.InitializeParams; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.context.event.EventListener; +import java.net.URI; +import java.nio.file.Path; import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +@RequiredArgsConstructor +@CacheConfig(cacheNames = "testSources") public abstract class AbstractRunTestsCodeLensSupplier implements CodeLensSupplier { + protected final LanguageServerConfiguration configuration; + private boolean clientIsSupported; /** @@ -43,6 +61,7 @@ public abstract class AbstractRunTestsCodeLensSupplier * @param event Событие */ @EventListener + @CacheEvict(allEntries = true) public void handleEvent(LanguageServerInitializeRequestReceivedEvent event) { var clientName = Optional.of(event) .map(LanguageServerInitializeRequestReceivedEvent::getParams) @@ -52,11 +71,61 @@ public void handleEvent(LanguageServerInitializeRequestReceivedEvent event) { clientIsSupported = "Visual Studio Code".equals(clientName); } + /** + * Обработчик события {@link LanguageServerConfigurationChangedEvent}. + *

+ * Сбрасывает кеш при изменении конфигурации. + * + * @param event Событие + */ + @EventListener + @CacheEvict(allEntries = true) + public void handleLanguageServerConfigurationChange(LanguageServerConfigurationChangedEvent event) { + // No-op. Служит для сброса кеша при изменении конфигурации + } + /** * {@inheritDoc} */ @Override public boolean isApplicable(DocumentContext documentContext) { - return documentContext.getFileType() == FileType.OS && clientIsSupported; + var uri = documentContext.getUri(); + var testSources = getSelf().getTestSources(documentContext.getServerContext().getConfigurationRoot()); + + return clientIsSupported + && documentContext.getFileType() == FileType.OS + && testSources.stream().anyMatch(testSource -> isInside(uri, testSource)); + } + + /** + * Получить self-injected экземпляр себя для работы механизмов кэширования. + * + * @return Управляемый Spring'ом экземпляр себя + */ + protected abstract AbstractRunTestsCodeLensSupplier getSelf(); + + /** + * Получить список каталогов с тестами с учетом корня рабочей области. + *

+ * public для работы @Cachable. + * + * @param configurationRoot Корень конфигурации + * @return Список исходных файлов тестов + */ + @Cacheable + public Set getTestSources(@Nullable Path configurationRoot) { + var configurationRootString = Optional.ofNullable(configurationRoot) + .map(Path::toString) + .orElse(""); + + return configuration.getCodeLensOptions().getTestRunnerAdapterOptions().getTestSources() + .stream() + .map(testDir -> Path.of(configurationRootString, testDir)) + .map(path -> Absolute.path(path).toUri()) + .collect(Collectors.toSet()); + } + + private static boolean isInside(URI childURI, URI parentURI) { + return !parentURI.relativize(childURI).isAbsolute(); } } diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/codelenses/RunAllTestsCodeLensSupplier.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/codelenses/RunAllTestsCodeLensSupplier.java index 12e0a2b5d00..4ddf257847b 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/codelenses/RunAllTestsCodeLensSupplier.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/codelenses/RunAllTestsCodeLensSupplier.java @@ -26,10 +26,12 @@ import com.github._1c_syntax.bsl.languageserver.context.DocumentContext; import com.github._1c_syntax.bsl.languageserver.context.symbol.MethodSymbol; import com.github._1c_syntax.bsl.languageserver.utils.Resources; -import lombok.RequiredArgsConstructor; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.eclipse.lsp4j.CodeLens; import org.eclipse.lsp4j.Command; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import java.nio.file.Paths; @@ -41,7 +43,6 @@ * Поставщик линзы для запуска всех тестов в текущем файле. */ @Component -@RequiredArgsConstructor @Slf4j public class RunAllTestsCodeLensSupplier extends AbstractRunTestsCodeLensSupplier { @@ -49,9 +50,24 @@ public class RunAllTestsCodeLensSupplier private static final String COMMAND_ID = "language-1c-bsl.languageServer.runAllTests"; private final TestRunnerAdapter testRunnerAdapter; - private final LanguageServerConfiguration configuration; private final Resources resources; + // Self-injection для работы кэша в базовом классе. + @Autowired + @Lazy + @Getter + private RunAllTestsCodeLensSupplier self; + + public RunAllTestsCodeLensSupplier( + LanguageServerConfiguration configuration, + TestRunnerAdapter testRunnerAdapter, + Resources resources + ) { + super(configuration); + this.testRunnerAdapter = testRunnerAdapter; + this.resources = resources; + } + /** * {@inheritDoc} */ diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/codelenses/RunTestCodeLensSupplier.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/codelenses/RunTestCodeLensSupplier.java index ab17cce09a8..e084375c203 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/codelenses/RunTestCodeLensSupplier.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/codelenses/RunTestCodeLensSupplier.java @@ -28,12 +28,14 @@ import com.github._1c_syntax.bsl.languageserver.context.symbol.MethodSymbol; import com.github._1c_syntax.bsl.languageserver.utils.Resources; import lombok.EqualsAndHashCode; -import lombok.RequiredArgsConstructor; +import lombok.Getter; import lombok.ToString; import lombok.Value; import lombok.extern.slf4j.Slf4j; import org.eclipse.lsp4j.CodeLens; import org.eclipse.lsp4j.Command; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import java.beans.ConstructorProperties; @@ -43,13 +45,11 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.stream.Collectors; /** * Поставщик линз для запуска теста по конкретному тестовому методу. */ @Component -@RequiredArgsConstructor @Slf4j public class RunTestCodeLensSupplier extends AbstractRunTestsCodeLensSupplier { @@ -57,9 +57,24 @@ public class RunTestCodeLensSupplier private static final String COMMAND_ID = "language-1c-bsl.languageServer.runTest"; private final TestRunnerAdapter testRunnerAdapter; - private final LanguageServerConfiguration configuration; private final Resources resources; + // Self-injection для работы кэша в базовом классе. + @Autowired + @Lazy + @Getter + private RunTestCodeLensSupplier self; + + public RunTestCodeLensSupplier( + LanguageServerConfiguration configuration, + TestRunnerAdapter testRunnerAdapter, + Resources resources + ) { + super(configuration); + this.testRunnerAdapter = testRunnerAdapter; + this.resources = resources; + } + /** * {@inheritDoc} */ @@ -77,7 +92,7 @@ public List getCodeLenses(DocumentContext documentContext) { .map(symbolTree::getMethodSymbol) .flatMap(Optional::stream) .map(methodSymbol -> toCodeLens(methodSymbol, documentContext)) - .collect(Collectors.toList()); + .toList(); } /** diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/codelenses/testrunner/TestRunnerAdapter.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/codelenses/testrunner/TestRunnerAdapter.java index aa4a22b5deb..1e5d2b6c592 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/codelenses/testrunner/TestRunnerAdapter.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/codelenses/testrunner/TestRunnerAdapter.java @@ -22,7 +22,10 @@ package com.github._1c_syntax.bsl.languageserver.codelenses.testrunner; import com.github._1c_syntax.bsl.languageserver.configuration.LanguageServerConfiguration; +import com.github._1c_syntax.bsl.languageserver.configuration.events.LanguageServerConfigurationChangedEvent; import com.github._1c_syntax.bsl.languageserver.context.DocumentContext; +import com.github._1c_syntax.bsl.languageserver.context.symbol.MethodSymbol; +import com.github._1c_syntax.bsl.languageserver.context.symbol.annotations.Annotation; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.exec.CommandLine; @@ -32,7 +35,10 @@ import org.apache.commons.exec.PumpStreamHandler; import org.apache.commons.io.output.ByteArrayOutputStream; import org.apache.commons.lang3.SystemUtils; -import org.apache.commons.lang3.tuple.Pair; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; import java.io.IOException; @@ -42,11 +48,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.WeakHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; /** * Расчетчик списка тестов в документе. @@ -55,26 +58,44 @@ @Component @RequiredArgsConstructor @Slf4j +@CacheConfig(cacheNames = "testIds") public class TestRunnerAdapter { private static final Pattern NEW_LINE_PATTERN = Pattern.compile("\r?\n"); - private static final Map, List> CACHE = new WeakHashMap<>(); private final LanguageServerConfiguration configuration; + /** + * Обработчик события {@link LanguageServerConfigurationChangedEvent}. + *

+ * Очищает кэш при изменении конфигурации. + * + * @param event Событие + */ + @EventListener + @CacheEvict(allEntries = true) + public void handleEvent(LanguageServerConfigurationChangedEvent event) { + // No-op. Служит для сброса кеша при изменении конфигурации + } + /** * Получить идентификаторы тестов, содержащихся в файле. * * @param documentContext Контекст документа с тестами. * @return Список идентификаторов тестов. */ + @Cacheable public List getTestIds(DocumentContext documentContext) { - var cacheKey = Pair.of(documentContext, documentContext.getVersion()); + var options = configuration.getCodeLensOptions().getTestRunnerAdapterOptions(); + + if (options.isGetTestsByTestRunner()) { + return computeTestIdsByTestRunner(documentContext); + } - return CACHE.computeIfAbsent(cacheKey, pair -> computeTestIds(documentContext)); + return computeTestIdsByLanguageServer(documentContext); } - private List computeTestIds(DocumentContext documentContext) { + private List computeTestIdsByTestRunner(DocumentContext documentContext) { var options = configuration.getCodeLensOptions().getTestRunnerAdapterOptions(); var executable = SystemUtils.IS_OS_WINDOWS ? options.getExecutableWin() : options.getExecutable(); @@ -123,7 +144,18 @@ private List computeTestIds(DocumentContext documentContext) { .map(getTestsRegex::matcher) .filter(Matcher::matches) .map(matcher -> matcher.group(1)) - .collect(Collectors.toList()); + .toList(); } + private List computeTestIdsByLanguageServer(DocumentContext documentContext) { + var annotations = configuration.getCodeLensOptions().getTestRunnerAdapterOptions().getAnnotations(); + return documentContext.getSymbolTree() + .getMethods() + .stream() + .filter(methodSymbol -> methodSymbol.getAnnotations().stream() + .map(Annotation::getName) + .anyMatch(annotations::contains)) + .map(MethodSymbol::getName) + .toList(); + } } diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/configuration/codelens/TestRunnerAdapterOptions.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/configuration/codelens/TestRunnerAdapterOptions.java index d9374af6130..2b346f8ae71 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/configuration/codelens/TestRunnerAdapterOptions.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/configuration/codelens/TestRunnerAdapterOptions.java @@ -23,11 +23,17 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.github._1c_syntax.bsl.languageserver.configuration.databind.AnnotationsDeserializer; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.apache.commons.lang3.SystemUtils; +import java.util.Collections; +import java.util.Set; +import java.util.TreeSet; + /** * Параметры запускателя тестового фреймворка. */ @@ -37,6 +43,21 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class TestRunnerAdapterOptions { + public static final Set DEFAULT_ANNOTATIONS = getDefaultAnnotations(); + + /** + * Каталоги с исходными файлами тестов. + */ + private Set testSources = Set.of("tests"); + + /** + * Имена аннотаций, маркирующих тесты. + *

+ * Используется при получении списка тестов средствами сервера. + */ + @JsonDeserialize(using = AnnotationsDeserializer.class) + private Set annotations = DEFAULT_ANNOTATIONS; + /** * Имя исполняемого файла тестового фреймворка (linux и macOS). */ @@ -45,6 +66,10 @@ public class TestRunnerAdapterOptions { * Имя исполняемого файла тестового фреймворка (windows). */ private String executableWin = "1testrunner.bat"; + /** + * Флаг, указывающий на необходимость получения списка тестов через исполняемый файл тестового фреймворка. + */ + private boolean getTestsByTestRunner = false; /** * Аргументы для получения списка тестов. */ @@ -70,4 +95,12 @@ public class TestRunnerAdapterOptions { public String getExecutableForCurrentOS() { return SystemUtils.IS_OS_WINDOWS ? executableWin : executable; } + + private static Set getDefaultAnnotations() { + Set annotations = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + annotations.add("Test"); + annotations.add("Тест"); + + return Collections.unmodifiableSet(annotations); + } } diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/configuration/databind/AnnotationsDeserializer.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/configuration/databind/AnnotationsDeserializer.java new file mode 100644 index 00000000000..2788f534829 --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/configuration/databind/AnnotationsDeserializer.java @@ -0,0 +1,62 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright (c) 2018-2025 + * Alexey Sosnoviy , Nikita Fedkin and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.configuration.databind; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.util.Set; +import java.util.TreeSet; + +import static com.github._1c_syntax.bsl.languageserver.configuration.codelens.TestRunnerAdapterOptions.DEFAULT_ANNOTATIONS; + +/** + * Служебный класс-десериализатор для регистронезависимого списка имен аннотаций. + */ +@Slf4j +public class AnnotationsDeserializer extends JsonDeserializer> { + + @Override + public Set deserialize( + JsonParser p, + DeserializationContext context + ) throws IOException { + + JsonNode annotations = p.getCodec().readTree(p); + + if (annotations == null) { + return DEFAULT_ANNOTATIONS; + } + + Set annotationsSet = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + var objectMapper = (ObjectMapper) p.getCodec(); + objectMapper.readerForUpdating(annotationsSet).readValue(annotations); + + return annotationsSet; + } + +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/ServerContext.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/ServerContext.java index 0910a3c454a..1473e115340 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/ServerContext.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/ServerContext.java @@ -32,6 +32,7 @@ import com.github._1c_syntax.utils.Absolute; import com.github._1c_syntax.utils.Lazy; import edu.umd.cs.findbugs.annotations.Nullable; +import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -67,6 +68,7 @@ public class ServerContext { private final Lazy configurationMetadata = new Lazy<>(this::computeConfigurationMetadata); @Nullable @Setter + @Getter private Path configurationRoot; private final Map mdoRefs = Collections.synchronizedMap(new HashMap<>()); private final Map> documentsByMDORef diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/infrastructure/CacheConfiguration.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/infrastructure/CacheConfiguration.java new file mode 100644 index 00000000000..fe4340dae3d --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/infrastructure/CacheConfiguration.java @@ -0,0 +1,33 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright (c) 2018-2025 + * Alexey Sosnoviy , Nikita Fedkin and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.infrastructure; + +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Configuration; + +/** + * Spring-конфигурация кэширования. + */ +@Configuration +@EnableCaching +public class CacheConfiguration { +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8c52d83e32d..c9941c18934 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,6 +1,7 @@ server.port=8025 spring.main.banner-mode=off spring.main.log-startup-info=false +spring.cache.cache-names=testIds,testSources logging.level.org.springframework.boot.autoconfigure.logging=INFO logging.level.org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler=warn logging.level.com.zaxxer.hikari=warn diff --git a/src/main/resources/com/github/_1c_syntax/bsl/languageserver/configuration/schema.json b/src/main/resources/com/github/_1c_syntax/bsl/languageserver/configuration/schema.json index f117154cae3..c2f60a1fafb 100644 --- a/src/main/resources/com/github/_1c_syntax/bsl/languageserver/configuration/schema.json +++ b/src/main/resources/com/github/_1c_syntax/bsl/languageserver/configuration/schema.json @@ -675,6 +675,29 @@ "type": "object", "title": "Test runner configuration to use for 'Run test' code lenses.", "properties": { + "testSources": { + "$id": "#/properties/codeLens/testRunner/testSources", + "type": "array", + "title": "List of directories with test sources.", + "items": { + "type": "string" + }, + "default": [ + "tests" + ] + }, + "annotations": { + "$id": "#/properties/codeLens/testRunner/annotations", + "type": "array", + "title": "List of annotations to find test methods.", + "items": { + "type": "string" + }, + "default": [ + "Тест", + "Test" + ] + }, "executable": { "$id": "#/properties/codeLens/testRunner/executable", "type": "string", @@ -687,6 +710,12 @@ "title": "Path to test runner executable on Windows systems.", "default": "1testrunner.bat" }, + "getTestsByTestRunner": { + "$id": "#/properties/codeLens/testRunner/getTestsByTestRunner", + "type": "boolean", + "title": "Use testrunner to get test method names. By default, use internal parser to find methods annotated with &Тест.", + "default": false + }, "getTestsArguments": { "$id": "#/properties/codeLens/testRunner/getTestsArguments", "type": "string", diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/codelenses/AbstractRunTestsCodeLensSupplierTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/codelenses/AbstractRunTestsCodeLensSupplierTest.java new file mode 100644 index 00000000000..bef79329e0f --- /dev/null +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/codelenses/AbstractRunTestsCodeLensSupplierTest.java @@ -0,0 +1,116 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright (c) 2018-2025 + * Alexey Sosnoviy , Nikita Fedkin and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.codelenses; + +import com.github._1c_syntax.bsl.languageserver.configuration.LanguageServerConfiguration; +import com.github._1c_syntax.bsl.languageserver.context.AbstractServerContextAwareTest; +import com.github._1c_syntax.bsl.languageserver.context.DocumentContext; +import com.github._1c_syntax.bsl.languageserver.events.LanguageServerInitializeRequestReceivedEvent; +import com.github._1c_syntax.bsl.languageserver.util.CleanupContextBeforeClassAndAfterEachTestMethod; +import com.github._1c_syntax.bsl.languageserver.util.TestUtils; +import org.eclipse.lsp4j.ClientInfo; +import org.eclipse.lsp4j.CodeLens; +import org.eclipse.lsp4j.InitializeParams; +import org.eclipse.lsp4j.services.LanguageServer; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Bean; + +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +@SpringBootTest +@CleanupContextBeforeClassAndAfterEachTestMethod +class AbstractRunTestsCodeLensSupplierTest extends AbstractServerContextAwareTest { + + @Autowired + private AbstractRunTestsCodeLensSupplier supplier; + + @Autowired + private ApplicationEventPublisher eventPublisher; + + @ParameterizedTest + @CsvSource({ + "./src/test/resources/codelenses/AbstractRunTestCodeLensSupplier.os, unknown, false", + "./src/test/resources/codelenses/tests/AbstractRunTestCodeLensSupplier.os, unknown, false", + "./src/test/resources/codelenses/AbstractRunTestCodeLensSupplier.os, Visual Studio Code, false", + "./src/test/resources/codelenses/tests/AbstractRunTestCodeLensSupplier.os, Visual Studio Code, true" + }) + void testIsApplicable(String filePath, String clientName, boolean expected) { + // given + var documentContext = TestUtils.getDocumentContextFromFile(filePath); + initializeServer("./src/test/resources/codelenses", clientName); + + // when + var result = supplier.isApplicable(documentContext); + + // then + assertThat(result).isEqualTo(expected); + } + + private void initializeServer(String path, String clientName) { + initServerContext(path); + + var initializeParams = new InitializeParams(); + initializeParams.setClientInfo( + new ClientInfo(clientName, "1.0.0") + ); + + var event = new LanguageServerInitializeRequestReceivedEvent( + mock(LanguageServer.class), + initializeParams + ); + eventPublisher.publishEvent(event); + } + + @TestConfiguration + static class TestConfig { + @Bean + public AbstractRunTestsCodeLensSupplier supplier(LanguageServerConfiguration configuration) { + return new AbstractRunTestsCodeLensSupplier<>(configuration) { + + @Override + public List getCodeLenses(DocumentContext documentContext) { + return Collections.emptyList(); + } + + @Override + public Class getCodeLensDataClass() { + return DefaultCodeLensData.class; + } + + @Override + protected AbstractRunTestsCodeLensSupplier getSelf() { + return this; + } + }; + } + } + +} \ No newline at end of file diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/codelenses/RunAllTestsCodeLensSupplierTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/codelenses/RunAllTestsCodeLensSupplierTest.java index 805075b271b..44afd6bd973 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/codelenses/RunAllTestsCodeLensSupplierTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/codelenses/RunAllTestsCodeLensSupplierTest.java @@ -34,8 +34,8 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.context.ApplicationEventPublisher; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import java.util.List; @@ -53,7 +53,7 @@ class RunAllTestsCodeLensSupplierTest { @Autowired private ApplicationEventPublisher eventPublisher; - @SpyBean + @MockitoSpyBean private TestRunnerAdapter testRunnerAdapter; private DocumentContext documentContext; @@ -64,18 +64,6 @@ void init() { documentContext = TestUtils.getDocumentContextFromFile(filePath); } - @Test - void noLensesIfClientIsNotSupported() { - // given - initializeServer("Unknown client"); - - // when - var codeLenses = supplier.getCodeLenses(documentContext); - - // then - assertThat(codeLenses).isEmpty(); - } - @Test void testDryRun() { // given diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/codelenses/RunTestCodeLensSupplierTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/codelenses/RunTestCodeLensSupplierTest.java index 4c0a56500f2..160ddef03a5 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/codelenses/RunTestCodeLensSupplierTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/codelenses/RunTestCodeLensSupplierTest.java @@ -34,8 +34,8 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.context.ApplicationEventPublisher; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import java.util.List; @@ -53,7 +53,7 @@ class RunTestCodeLensSupplierTest { @Autowired private ApplicationEventPublisher eventPublisher; - @SpyBean + @MockitoSpyBean private TestRunnerAdapter testRunnerAdapter; private DocumentContext documentContext; @@ -64,18 +64,6 @@ void init() { documentContext = TestUtils.getDocumentContextFromFile(filePath); } - @Test - void noLensesIfClientIsNotSupported() { - // given - initializeServer("Unknown client"); - - // when - var codeLenses = supplier.getCodeLenses(documentContext); - - // then - assertThat(codeLenses).isEmpty(); - } - @Test void testDryRun() { // given diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/codelenses/testrunner/TestRunnerAdapterTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/codelenses/testrunner/TestRunnerAdapterTest.java new file mode 100644 index 00000000000..1d8a06003aa --- /dev/null +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/codelenses/testrunner/TestRunnerAdapterTest.java @@ -0,0 +1,54 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright (c) 2018-2025 + * Alexey Sosnoviy , Nikita Fedkin and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.codelenses.testrunner; + +import com.github._1c_syntax.bsl.languageserver.configuration.LanguageServerConfiguration; +import com.github._1c_syntax.bsl.languageserver.util.TestUtils; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +class TestRunnerAdapterTest { + + @Autowired + private TestRunnerAdapter testRunnerAdapter; + + @Autowired + private LanguageServerConfiguration configuration; + + @Test + void whenComputeTestsByLanguageServer_thenContainsTests() { + // given + var documentContext = TestUtils.getDocumentContextFromFile("src/test/resources/codelenses/testrunner/TestRunnerAdapter.os"); + configuration.getCodeLensOptions().getTestRunnerAdapterOptions().setGetTestsByTestRunner(false); + + // when + var testIds = testRunnerAdapter.getTestIds(documentContext); + + // then + assertThat(testIds).hasSize(1); + assertThat(testIds.get(0)).isEqualTo("Тест1"); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/configuration/LanguageServerConfigurationTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/configuration/LanguageServerConfigurationTest.java index c5326e6adcb..5eda351131c 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/configuration/LanguageServerConfigurationTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/configuration/LanguageServerConfigurationTest.java @@ -109,6 +109,10 @@ void createFromFile() { assertThat(configuration.isUseDevSite()).isTrue(); assertThat(configuration.getDiagnosticsOptions().isOrdinaryAppSupport()).isFalse(); + var annotations = configuration.getCodeLensOptions().getTestRunnerAdapterOptions().getAnnotations(); + assertThat(annotations) + .hasSize(2) + .contains("Test", "Test2"); } @Test diff --git a/src/test/resources/.bsl-language-server.json b/src/test/resources/.bsl-language-server.json index 2e404350461..04de080aa5c 100644 --- a/src/test/resources/.bsl-language-server.json +++ b/src/test/resources/.bsl-language-server.json @@ -4,6 +4,12 @@ "parameters": { "cognitiveComplexity": true, "cyclomaticComplexity": true + }, + "testRunner": { + "annotations": [ + "Test", + "Test2" + ] } }, "diagnostics": { diff --git a/src/test/resources/codelenses/AbstractRunTestCodeLensSupplier.os b/src/test/resources/codelenses/AbstractRunTestCodeLensSupplier.os new file mode 100644 index 00000000000..36cbb010e60 --- /dev/null +++ b/src/test/resources/codelenses/AbstractRunTestCodeLensSupplier.os @@ -0,0 +1,6 @@ +&Тест +Процедура Тест1() Экспорт +КонецПроцедуры + +Процедура НеТест1() Экспорт +КонецПроцедуры \ No newline at end of file diff --git a/src/test/resources/codelenses/testrunner/TestRunnerAdapter.os b/src/test/resources/codelenses/testrunner/TestRunnerAdapter.os new file mode 100644 index 00000000000..4c60847796f --- /dev/null +++ b/src/test/resources/codelenses/testrunner/TestRunnerAdapter.os @@ -0,0 +1,6 @@ +&Тест +Процедура Тест1() Экспорт +КонецПроцедуры + +Процедура НеТест1() Экспорт +КонецПроцедуры diff --git a/src/test/resources/codelenses/tests/AbstractRunTestCodeLensSupplier.os b/src/test/resources/codelenses/tests/AbstractRunTestCodeLensSupplier.os new file mode 100644 index 00000000000..36cbb010e60 --- /dev/null +++ b/src/test/resources/codelenses/tests/AbstractRunTestCodeLensSupplier.os @@ -0,0 +1,6 @@ +&Тест +Процедура Тест1() Экспорт +КонецПроцедуры + +Процедура НеТест1() Экспорт +КонецПроцедуры \ No newline at end of file