From 4364547d11250789f28b60db6c039f4fceef410d Mon Sep 17 00:00:00 2001 From: Nipuna Fernando Date: Sun, 16 Feb 2025 17:02:54 +0530 Subject: [PATCH] Add model-generator-commons dependency to projects --- .../build.gradle | 4 +- .../extension/AbstractLSTest.java | 300 ---------------- .../extension/DesignModelGeneratorTest.java | 6 + build.gradle | 34 ++ .../build.gradle | 44 +-- .../build.gradle | 26 +- .../extension/AbstractLSTest.java | 335 ------------------ .../extension/AddFieldTest.java | 6 + .../extension/AddFunctionTest.java | 7 + .../extension/AddServiceTest.java | 9 +- .../extension/GetFunctionModelTest.java | 13 +- .../GetServiceClassModelFromSourceTest.java | 11 +- .../GetServiceModelFromSourceTest.java | 11 +- .../extension/UpdateClassFieldTest.java | 15 +- .../extension/UpdateFunctionTest.java | 6 + .../extension/UpdateServiceClassTest.java | 15 +- .../build.gradle | 2 + .../extension/AbstractLSTest.java | 334 ----------------- .../extension/GetFunctionModelTest.java | 7 + .../extension/TestAddTestFunction.java | 7 + .../extension/TestFileTestDiscovery.java | 7 + .../extension/TestProjectTestDiscovery.java | 7 + .../extension/TestUpdateTestFunction.java | 7 + 23 files changed, 166 insertions(+), 1047 deletions(-) delete mode 100644 architecture-model-generator/modules/architecture-model-generator-ls-extension/src/test/java/io/ballerina/designmodelgenerator/extension/AbstractLSTest.java delete mode 100644 service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/AbstractLSTest.java delete mode 100644 test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/AbstractLSTest.java diff --git a/architecture-model-generator/modules/architecture-model-generator-ls-extension/build.gradle b/architecture-model-generator/modules/architecture-model-generator-ls-extension/build.gradle index fa92c4cc2..954ea3e80 100644 --- a/architecture-model-generator/modules/architecture-model-generator-ls-extension/build.gradle +++ b/architecture-model-generator/modules/architecture-model-generator-ls-extension/build.gradle @@ -30,12 +30,14 @@ configurations { } dependencies { + implementation project(':architecture-model-generator:architecture-model-generator-core') + implementation project(":model-generator-commons") + implementation "org.ballerinalang:ballerina-lang:${ballerinaLangVersion}" implementation "org.ballerinalang:ballerina-tools-api:${ballerinaLangVersion}" implementation "org.ballerinalang:language-server-commons:${ballerinaLangVersion}" implementation "org.eclipse.lsp4j:org.eclipse.lsp4j:${eclipseLsp4jVersion}" implementation "com.google.code.gson:gson:${gsonVersion}" - implementation project(':architecture-model-generator:architecture-model-generator-core') testImplementation project(':architecture-model-generator:architecture-model-generator-core') testImplementation "org.ballerinalang:language-server-core:${ballerinaLangVersion}" diff --git a/architecture-model-generator/modules/architecture-model-generator-ls-extension/src/test/java/io/ballerina/designmodelgenerator/extension/AbstractLSTest.java b/architecture-model-generator/modules/architecture-model-generator-ls-extension/src/test/java/io/ballerina/designmodelgenerator/extension/AbstractLSTest.java deleted file mode 100644 index e799caac6..000000000 --- a/architecture-model-generator/modules/architecture-model-generator-ls-extension/src/test/java/io/ballerina/designmodelgenerator/extension/AbstractLSTest.java +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) - * - * WSO2 LLC. 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 io.ballerina.designmodelgenerator.extension; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.JsonPrimitive; -import org.ballerinalang.langserver.BallerinaLanguageServer; -import org.ballerinalang.langserver.util.TestUtil; -import org.eclipse.lsp4j.DidCloseTextDocumentParams; -import org.eclipse.lsp4j.TextDocumentIdentifier; -import org.eclipse.lsp4j.jsonrpc.Endpoint; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.Assert; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Stream; - -/** - * Represents the abstract test class for the design model generator service. - * - * @since 2.0.0 - */ -abstract class AbstractLSTest { - - protected static Logger log; - protected static Path resDir, sourceDir, configDir; - protected final Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); - - protected Endpoint serviceEndpoint; - private BallerinaLanguageServer languageServer; - - @BeforeClass - public final void init() { - resDir = Paths.get("src/test/resources").resolve(getResourceDir()).toAbsolutePath(); - configDir = resDir.resolve("config"); - sourceDir = resDir.resolve("source"); - log = LoggerFactory.getLogger(clazz()); - this.languageServer = new BallerinaLanguageServer(); - TestUtil.LanguageServerBuilder builder = TestUtil.newLanguageServer().withLanguageServer(languageServer); - this.serviceEndpoint = builder.build(); - } - - /** - * Positive tests for the flow model generator service. - * - * @param config The path to the test config - * @throws IOException If an error occurs while reading the config - */ - @Test(dataProvider = "data-provider") - public abstract void test(Path config) throws IOException; - - /** - * Provides the list of test configs. - * - * @return The list of test configs - */ - @DataProvider(name = "data-provider") - protected Object[] getConfigsList() { - List skippedTests = Arrays.stream(this.skipList()).toList(); - try (Stream stream = Files.walk(resDir)) { - return stream - .filter(path -> { - File file = path.toFile(); - return file.isFile() && !file.getName().startsWith(".") - && file.getName().endsWith(".json") - && !skippedTests.contains(file.getName()); - }) - .toArray(Path[]::new); - } catch (IOException e) { - // If failed to load tests, then it's a failure - Assert.fail("Unable to load test config", e); - return new Object[0][]; - } - } - - /** - * Provides the list of tests to be skipped. - * - * @return The list of tests to be skipped - */ - protected String[] skipList() { - return new String[]{ }; - } - - /** - * Updates the test config with the result generated from the test case. - * - * @param configJsonPath The path to the test config - * @param updatedConfig The updated config - * @throws IOException If an error occurs while writing the config - */ - protected void updateConfig(Path configJsonPath, Object updatedConfig) throws IOException { - String objStr = gson.toJson(updatedConfig).concat(System.lineSeparator()); - Files.writeString(configJsonPath, objStr); - } - - protected JsonObject getResponse(Object request) throws IOException { - return getResponse(request, getApiName()); - } - - protected JsonObject getResponseAndCloseFile(Object request, String source) throws IOException { - JsonObject response = getResponse(request); - String fileUri = sourceDir.resolve(source).toAbsolutePath().toUri().toString(); - serviceEndpoint.notify("textDocument/didClose", - new DidCloseTextDocumentParams(new TextDocumentIdentifier(fileUri))); - return response; - } - - protected JsonObject getResponse(Object request, String api) { - CompletableFuture result = serviceEndpoint.request(getServiceName() + "/" + api, request); - String response = TestUtil.getResponseString(result); - JsonObject jsonObject = JsonParser.parseString(response).getAsJsonObject().getAsJsonObject("result"); - JsonPrimitive errorMsg = jsonObject.getAsJsonPrimitive("errorMsg"); - if (errorMsg != null) { - log.error("Stacktrace: {}", jsonObject.getAsJsonPrimitive("stacktrace").getAsString()); - Assert.fail("Error occurred: " + errorMsg.getAsString()); - } - return jsonObject; - } - - /** - * Asserts the equality of the actual and expected arrays. - * - * @param property The property name - * @param actualNodes The actual nodes - * @param expectedNodes The expected nodes - * @return True if the arrays are equal, false otherwise - */ - protected final boolean assertArray(String property, List actualNodes, List expectedNodes) { - List unmatchedExpectedNodes = new java.util.ArrayList<>(expectedNodes); - List mismatchedAvailableNodes = new java.util.ArrayList<>(); - - int actualTextEditsSize = actualNodes.size(); - int expectedTextEditsSize = expectedNodes.size(); - boolean hasCountMatch = actualTextEditsSize == expectedTextEditsSize; - if (!hasCountMatch) { - log.error(String.format("Mismatched %s count. Expected: %d, Found: %d", property, expectedTextEditsSize, - actualTextEditsSize)); - } - - for (Object actualNode : actualNodes) { - if (expectedNodes.contains(actualNode)) { - unmatchedExpectedNodes.remove(actualNode); - } else { - mismatchedAvailableNodes.add(actualNode); - } - } - - boolean hasAllExpectedTextEdits = unmatchedExpectedNodes.isEmpty(); - if (!hasAllExpectedTextEdits) { - log.error(String.format("Found in expected %s but not in actual %s: ", property, property) + - unmatchedExpectedNodes); - } - - boolean hasRelevantTextEdits = mismatchedAvailableNodes.isEmpty(); - if (!hasRelevantTextEdits) { - log.error(String.format("Found in actual %s but not in expected %s: ", property, property) + - mismatchedAvailableNodes); - } - - return hasCountMatch && hasAllExpectedTextEdits && hasRelevantTextEdits; - } - - /** - * Compare the actual JSON with the expected JSON. - * - * @param actualJson the actual JSON produced by the LS extension - * @param expectedJson the expected JSON - */ - protected void compareJsonElements(JsonElement actualJson, JsonElement expectedJson) { - log.info("Differences in JSON elements:"); - compareJsonElementsRecursive(actualJson, expectedJson, ""); - } - - private void compareJsonElementsRecursive(JsonElement actualJson, JsonElement expectedJson, String path) { - if (actualJson.isJsonObject() && expectedJson.isJsonObject()) { - compareJsonObjects(actualJson.getAsJsonObject(), expectedJson.getAsJsonObject(), path); - } else if (actualJson.isJsonArray() && expectedJson.isJsonArray()) { - compareJsonArrays(actualJson.getAsJsonArray(), expectedJson.getAsJsonArray(), path); - } else if (!actualJson.equals(expectedJson)) { - log.info("- Value mismatch at '" + path + "'\n actual: " + actualJson + "\n expected: " + expectedJson); - } - } - - private void compareJsonObjects(JsonObject actualJson, JsonObject expectedJson, String path) { - Set> entrySet1 = actualJson.entrySet(); - Set> entrySet2 = expectedJson.entrySet(); - - for (Map.Entry entry : entrySet1) { - String key = entry.getKey(); - String currentPath = path.isEmpty() ? key : path + "." + key; - - if (!expectedJson.has(key)) { - log.info("- Key '" + currentPath + "' is missing in the expected JSON"); - } else { - compareJsonElementsRecursive(entry.getValue(), expectedJson.get(key), currentPath); - } - } - - for (Map.Entry entry : entrySet2) { - String key = entry.getKey(); - String currentPath = path.isEmpty() ? key : path + "." + key; - - if (!actualJson.has(key)) { - log.info("- Key '" + currentPath + "' is missing in the actual JSON"); - } - } - } - - private void compareJsonArrays(JsonArray actualArray, JsonArray expectedArray, String path) { - int size1 = actualArray.size(); - int size2 = expectedArray.size(); - int minSize = Math.min(size1, size2); - - for (int i = 0; i < minSize; i++) { - compareJsonElementsRecursive(actualArray.get(i), expectedArray.get(i), path + "[" + i + "]"); - } - - if (size1 > size2) { - for (int i = size2; i < size1; i++) { - log.info("- Extra element in actual JSON at '" + path + "[" + i + "]': " + actualArray.get(i)); - } - } else if (size2 > size1) { - for (int i = size1; i < size2; i++) { - log.info("- Extra element in expected JSON at '" + path + "[" + i + "]': " + expectedArray.get(i)); - } - } - } - - protected String getSourcePath(String source) { - return sourceDir.resolve(source).toAbsolutePath().toString(); - } - - /** - * Returns the resource directory of the API test. - * - * @return The resource directory of the API test - */ - protected abstract String getResourceDir(); - - /** - * Returns the class of the API test. - * - * @return The class of the API test - */ - protected abstract Class clazz(); - - /** - * Returns the name of the API. - * - * @return The name of the API - */ - protected abstract String getApiName(); - - protected String getServiceName() { - return "designModelService"; - } - - @AfterClass - public void shutDownLanguageServer() { - TestUtil.shutdownLanguageServer(this.serviceEndpoint); - this.languageServer = null; - this.serviceEndpoint = null; - } -} diff --git a/architecture-model-generator/modules/architecture-model-generator-ls-extension/src/test/java/io/ballerina/designmodelgenerator/extension/DesignModelGeneratorTest.java b/architecture-model-generator/modules/architecture-model-generator-ls-extension/src/test/java/io/ballerina/designmodelgenerator/extension/DesignModelGeneratorTest.java index 762667ed8..141254f65 100644 --- a/architecture-model-generator/modules/architecture-model-generator-ls-extension/src/test/java/io/ballerina/designmodelgenerator/extension/DesignModelGeneratorTest.java +++ b/architecture-model-generator/modules/architecture-model-generator-ls-extension/src/test/java/io/ballerina/designmodelgenerator/extension/DesignModelGeneratorTest.java @@ -26,6 +26,7 @@ import io.ballerina.designmodelgenerator.core.model.Service; import io.ballerina.designmodelgenerator.extension.request.GetDesignModelRequest; import io.ballerina.designmodelgenerator.extension.response.GetDesignModelResponse; +import io.ballerina.modelgenerator.commons.AbstractLSTest; import org.testng.Assert; import org.testng.annotations.Test; @@ -112,6 +113,11 @@ protected Class clazz() { return DesignModelGeneratorTest.class; } + @Override + protected String getServiceName() { + return "designModelService"; + } + @Override protected String getApiName() { return "getDesignModel"; diff --git a/build.gradle b/build.gradle index ff028f6de..a3858d51b 100644 --- a/build.gradle +++ b/build.gradle @@ -125,6 +125,40 @@ def targetSequenceDiagramGeneratorCore = file("$project.rootDir/sequence-model-g def targetSequenceDiagramGeneratorLSExt = file("$project.rootDir/sequence-model-generator/modules/sequence-model-generator-ls-extension/build/libs/sequence-model-generator-ls-extension-${project.version}.jar") def targetTestManagerServiceLSExt = file("$project.rootDir/test-manager-service/modules/test-manager-service-ls-extension/build/libs/test-manager-service-ls-extension-${project.version}.jar") +// TODO: Remove this once the workspace manager is refactored to import modules where necessary. +def pullBallerinaModule(String packageName) { + tasks.register("pullBallerinaModule_${packageName.replace('/', '_')}") { + doLast { + def errOutput = new ByteArrayOutputStream() + def result = exec { + ignoreExitValue = true + + // Check if the package exists in the ballerina user + def centralRepoDir = new File(System.getProperty("user.home"), ".ballerina/repositories/central.ballerina.io/bala/ballerinax") + if (centralRepoDir.exists()) { + def pkgDir = new File(centralRepoDir, packageName) + if (pkgDir.exists()) { + commandLine 'echo', "Package ${packageName} exists" + return + } + } + + def ballPullCommand = "bal pull ballerinax/${packageName}" + if (org.gradle.internal.os.OperatingSystem.current().isWindows()) { + commandLine 'cmd', '/c', ballPullCommand + } else { + commandLine '/bin/sh', '-c', ballPullCommand + } + errorOutput = errOutput + } + if (result.exitValue != 0) { + println errOutput + throw new GradleException("Failed to pull Ballerina module: ${packageName}") + } + } + } +} + task copyArtifactZip { dependsOn(":model-generator-commons:build") dependsOn(":architecture-model-generator:architecture-model-generator-core:build") diff --git a/flow-model-generator/modules/flow-model-generator-ls-extension/build.gradle b/flow-model-generator/modules/flow-model-generator-ls-extension/build.gradle index b2ee89140..b406af4f8 100644 --- a/flow-model-generator/modules/flow-model-generator-ls-extension/build.gradle +++ b/flow-model-generator/modules/flow-model-generator-ls-extension/build.gradle @@ -101,45 +101,11 @@ task copyStdlibs() { } } -// TODO: Remove this once the workspace manager is refactored to import modules where necessary. -def pullBallerinaModule(String packageName) { - tasks.register("pullBallerinaModule_${packageName.replace('/', '_')}") { - doLast { - def errOutput = new ByteArrayOutputStream() - def result = exec { - ignoreExitValue = true - - // Check if the package exists in the ballerina user - def centralRepoDir = new File(System.getProperty("user.home"), ".ballerina/repositories/central.ballerina.io/bala/ballerinax") - if (centralRepoDir.exists()) { - def pkgDir = new File(centralRepoDir, packageName) - if (pkgDir.exists()) { - commandLine 'echo', "Package ${packageName} exists" - return - } - } - - def ballPullCommand = "bal pull ballerinax/${packageName}" - if (org.gradle.internal.os.OperatingSystem.current().isWindows()) { - commandLine 'cmd', '/c', ballPullCommand - } else { - commandLine '/bin/sh', '-c', ballPullCommand - } - errorOutput = errOutput - } - if (result.exitValue != 0) { - println errOutput - throw new GradleException("Failed to pull Ballerina module: ${packageName}") - } - } - } -} - -test.dependsOn pullBallerinaModule('redis') -test.dependsOn pullBallerinaModule('trigger.salesforce') -test.dependsOn pullBallerinaModule('github') -test.dependsOn pullBallerinaModule('snowflake') -test.dependsOn pullBallerinaModule('docusign.dsadmin') +test.dependsOn rootProject.pullBallerinaModule('redis') +test.dependsOn rootProject.pullBallerinaModule('trigger.salesforce') +test.dependsOn rootProject.pullBallerinaModule('github') +test.dependsOn rootProject.pullBallerinaModule('snowflake') +test.dependsOn rootProject.pullBallerinaModule('docusign.dsadmin') test { dependsOn { diff --git a/service-model-generator/modules/service-model-generator-ls-extension/build.gradle b/service-model-generator/modules/service-model-generator-ls-extension/build.gradle index 8cad6d5ca..b0bc960fb 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/build.gradle +++ b/service-model-generator/modules/service-model-generator-ls-extension/build.gradle @@ -34,6 +34,8 @@ configurations { } dependencies { + implementation project(":model-generator-commons") + implementation "org.ballerinalang:ballerina-lang:${ballerinaLangVersion}" implementation "org.ballerinalang:ballerina-parser:${ballerinaLangVersion}" implementation "org.ballerinalang:formatter-core:${ballerinaLangVersion}" @@ -103,29 +105,9 @@ task copyStdlibs() { } } -def pullBallerinaModule(String packageName) { - tasks.register("pullBallerinaModule_${packageName.replace('/', '_')}") { - doLast { - def errOutput = new ByteArrayOutputStream() - def result = exec { - ignoreExitValue = true - if (org.gradle.internal.os.OperatingSystem.current().isWindows()) { - commandLine 'cmd', '/c', 'bal', 'pull', packageName - } else { - commandLine 'bal', 'pull', packageName - } - errorOutput = errOutput - } - if (result.exitValue != 0 && !errOutput.toString().contains("package already exists in the home repository")) { - println errOutput - throw new GradleException("Failed to pull Ballerina module: ${packageName}") - } - } - } -} -test.dependsOn pullBallerinaModule('ballerinax/kafka') -test.dependsOn pullBallerinaModule('ballerinax/rabbitmq') +test.dependsOn rootProject.pullBallerinaModule('kafka') +test.dependsOn rootProject.pullBallerinaModule('rabbitmq') test { dependsOn { diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/AbstractLSTest.java b/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/AbstractLSTest.java deleted file mode 100644 index c9f00bd33..000000000 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/AbstractLSTest.java +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com) - * - * WSO2 LLC. 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 io.ballerina.servicemodelgenerator.extension; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.JsonPrimitive; -import io.ballerina.servicemodelgenerator.extension.util.Utils; -import org.ballerinalang.langserver.BallerinaLanguageServer; -import org.ballerinalang.langserver.util.TestUtil; -import org.eclipse.lsp4j.DidCloseTextDocumentParams; -import org.eclipse.lsp4j.DidOpenTextDocumentParams; -import org.eclipse.lsp4j.TextDocumentIdentifier; -import org.eclipse.lsp4j.TextDocumentItem; -import org.eclipse.lsp4j.jsonrpc.Endpoint; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.Assert; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Stream; - -/** - * Abstract class for testing Service Model Generator. - * - * @since 2.0.0 - */ -public abstract class AbstractLSTest { - - protected static Logger log; - protected static Path resDir, sourceDir, configDir; - protected final Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); - - protected Endpoint serviceEndpoint; - private BallerinaLanguageServer languageServer; - - @BeforeClass - public final void init() { - resDir = Paths.get("src/test/resources").resolve(getResourceDir()).toAbsolutePath(); - configDir = resDir.resolve("config"); - sourceDir = resDir.resolve("source"); - log = LoggerFactory.getLogger(clazz()); - this.languageServer = new BallerinaLanguageServer(); - TestUtil.LanguageServerBuilder builder = TestUtil.newLanguageServer().withLanguageServer(languageServer); - this.serviceEndpoint = builder.build(); - } - - /** - * Positive tests for the flow model generator service. - * - * @param config The path to the test config - * @throws IOException If an error occurs while reading the config - */ - @Test(dataProvider = "data-provider") - public abstract void test(Path config) throws IOException; - - /** - * Provides the list of test configs. - * - * @return The list of test configs - */ - @DataProvider(name = "data-provider") - protected Object[] getConfigsList() { - List skippedTests = Arrays.stream(this.skipList()).toList(); - try (Stream stream = Files.walk(resDir)) { - return stream - .filter(path -> { - File file = path.toFile(); - return file.isFile() && !file.getName().startsWith(".") - && file.getName().endsWith(".json") - && !skippedTests.contains(file.getName()); - }) - .toArray(Path[]::new); - } catch (IOException e) { - // If failed to load tests, then it's a failure - Assert.fail("Unable to load test config", e); - return new Object[0][]; - } - } - - /** - * Provides the list of tests to be skipped. - * - * @return The list of tests to be skipped - */ - protected String[] skipList() { - return new String[]{ }; - } - - /** - * Updates the test config with the result generated from the test case. - * - * @param configJsonPath The path to the test config - * @param updatedConfig The updated config - * @throws IOException If an error occurs while writing the config - */ - protected void updateConfig(Path configJsonPath, Object updatedConfig) throws IOException { - String objStr = gson.toJson(updatedConfig).concat(System.lineSeparator()); - Files.writeString(configJsonPath, objStr); - } - - protected JsonObject getResponse(Endpoint endpoint, Object request) throws IOException { - Endpoint tempEndPoint = serviceEndpoint; - serviceEndpoint = endpoint; - JsonObject response = getResponse(request); - serviceEndpoint = tempEndPoint; - return response; - } - - protected JsonObject getResponse(Object request) throws IOException { - return getResponse(request, getServiceName() + "/" + getApiName()); - } - - protected JsonObject getResponseAndCloseFile(Object request, String source) throws IOException { - JsonObject response = getResponse(request); - String fileUri = sourceDir.resolve(source).toAbsolutePath().toUri().toString(); - serviceEndpoint.notify("textDocument/didClose", - new DidCloseTextDocumentParams(new TextDocumentIdentifier(fileUri))); - return response; - } - - protected JsonObject getResponse(Object request, String api) { - CompletableFuture result = serviceEndpoint.request(api, request); - String response = TestUtil.getResponseString(result); - JsonObject jsonObject = JsonParser.parseString(response).getAsJsonObject().getAsJsonObject("result"); - JsonPrimitive errorMsg = jsonObject.getAsJsonPrimitive("errorMsg"); - if (errorMsg != null) { - log.error("Stacktrace: {}", jsonObject.getAsJsonPrimitive("stacktrace").getAsString()); - Assert.fail("Error occurred: " + errorMsg.getAsString()); - } - return jsonObject; - } - - protected void sendNotification(String api, Object request) { - serviceEndpoint.notify(api, request); - } - - protected void notifyDidOpen(String sourcePath) throws IOException { - TextDocumentItem textDocumentItem = new TextDocumentItem(); - String text; - try (FileInputStream fis = new FileInputStream(sourcePath)) { - text = new String(fis.readAllBytes()); - } - textDocumentItem.setUri(Utils.getExprUri(sourcePath)); - textDocumentItem.setText(text); - textDocumentItem.setLanguageId("ballerina"); - textDocumentItem.setVersion(1); - sendNotification("textDocument/didOpen", new DidOpenTextDocumentParams(textDocumentItem)); - } - - protected void notifyDidClose(String sourcePath) { - TextDocumentIdentifier textDocumentIdentifier = new TextDocumentIdentifier(); - textDocumentIdentifier.setUri(Utils.getExprUri(sourcePath)); - sendNotification("textDocument/didClose", new DidCloseTextDocumentParams(textDocumentIdentifier)); - } - - /** - * Asserts the equality of the actual and expected arrays. - * - * @param property The property name - * @param actualNodes The actual nodes - * @param expectedNodes The expected nodes - * @return True if the arrays are equal, false otherwise - */ - protected final boolean assertArray(String property, List actualNodes, List expectedNodes) { - List unmatchedExpectedNodes = new java.util.ArrayList<>(expectedNodes); - List mismatchedAvailableNodes = new java.util.ArrayList<>(); - - int actualTextEditsSize = actualNodes.size(); - int expectedTextEditsSize = expectedNodes.size(); - boolean hasCountMatch = actualTextEditsSize == expectedTextEditsSize; - if (!hasCountMatch) { - log.error(String.format("Mismatched %s count. Expected: %d, Found: %d", property, expectedTextEditsSize, - actualTextEditsSize)); - } - - for (Object actualNode : actualNodes) { - if (expectedNodes.contains(actualNode)) { - unmatchedExpectedNodes.remove(actualNode); - } else { - mismatchedAvailableNodes.add(actualNode); - } - } - - boolean hasAllExpectedTextEdits = unmatchedExpectedNodes.isEmpty(); - if (!hasAllExpectedTextEdits) { - log.error(String.format("Found in expected %s but not in actual %s: ", property, property) + - unmatchedExpectedNodes); - } - - boolean hasRelevantTextEdits = mismatchedAvailableNodes.isEmpty(); - if (!hasRelevantTextEdits) { - log.error(String.format("Found in actual %s but not in expected %s: ", property, property) + - mismatchedAvailableNodes); - } - - return hasCountMatch && hasAllExpectedTextEdits && hasRelevantTextEdits; - } - - /** - * Compare the actual JSON with the expected JSON. - * - * @param actualJson the actual JSON produced by the LS extension - * @param expectedJson the expected JSON - */ - protected void compareJsonElements(JsonElement actualJson, JsonElement expectedJson) { - log.info("Differences in JSON elements:"); - compareJsonElementsRecursive(actualJson, expectedJson, ""); - } - - private void compareJsonElementsRecursive(JsonElement actualJson, JsonElement expectedJson, String path) { - if (actualJson.isJsonObject() && expectedJson.isJsonObject()) { - compareJsonObjects(actualJson.getAsJsonObject(), expectedJson.getAsJsonObject(), path); - } else if (actualJson.isJsonArray() && expectedJson.isJsonArray()) { - compareJsonArrays(actualJson.getAsJsonArray(), expectedJson.getAsJsonArray(), path); - } else if (!actualJson.equals(expectedJson)) { - log.info("- Value mismatch at '" + path + "'\n actual: " + actualJson + "\n expected: " + expectedJson); - } - } - - private void compareJsonObjects(JsonObject actualJson, JsonObject expectedJson, String path) { - Set> entrySet1 = actualJson.entrySet(); - Set> entrySet2 = expectedJson.entrySet(); - - for (Map.Entry entry : entrySet1) { - String key = entry.getKey(); - String currentPath = path.isEmpty() ? key : path + "." + key; - - if (!expectedJson.has(key)) { - log.info("- Key '" + currentPath + "' is missing in the expected JSON"); - } else { - compareJsonElementsRecursive(entry.getValue(), expectedJson.get(key), currentPath); - } - } - - for (Map.Entry entry : entrySet2) { - String key = entry.getKey(); - String currentPath = path.isEmpty() ? key : path + "." + key; - - if (!actualJson.has(key)) { - log.info("- Key '" + currentPath + "' is missing in the actual JSON"); - } - } - } - - private void compareJsonArrays(JsonArray actualArray, JsonArray expectedArray, String path) { - int size1 = actualArray.size(); - int size2 = expectedArray.size(); - int minSize = Math.min(size1, size2); - - for (int i = 0; i < minSize; i++) { - compareJsonElementsRecursive(actualArray.get(i), expectedArray.get(i), path + "[" + i + "]"); - } - - if (size1 > size2) { - for (int i = size2; i < size1; i++) { - log.info("- Extra element in actual JSON at '" + path + "[" + i + "]': " + actualArray.get(i)); - } - } else if (size2 > size1) { - for (int i = size1; i < size2; i++) { - log.info("- Extra element in expected JSON at '" + path + "[" + i + "]': " + expectedArray.get(i)); - } - } - } - - protected String getSourcePath(String source) { - return sourceDir.resolve(source).toAbsolutePath().toString(); - } - - /** - * Returns the resource directory of the API test. - * - * @return The resource directory of the API test - */ - protected abstract String getResourceDir(); - - /** - * Returns the class of the API test. - * - * @return The class of the API test - */ - protected abstract Class clazz(); - - /** - * Returns the name of the API. - * - * @return The name of the API - */ - protected abstract String getApiName(); - - protected String getServiceName() { - return "serviceDesign"; - } - - @AfterClass - public void shutDownLanguageServer() { - TestUtil.shutdownLanguageServer(this.serviceEndpoint); - this.languageServer = null; - this.serviceEndpoint = null; - } -} diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/AddFieldTest.java b/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/AddFieldTest.java index 5505f84e3..9fad87da5 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/AddFieldTest.java +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/AddFieldTest.java @@ -20,6 +20,7 @@ import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; +import io.ballerina.modelgenerator.commons.AbstractLSTest; import io.ballerina.servicemodelgenerator.extension.model.Codedata; import io.ballerina.servicemodelgenerator.extension.model.Field; import io.ballerina.servicemodelgenerator.extension.request.AddFieldRequest; @@ -101,6 +102,11 @@ protected Class clazz() { return AddFieldTest.class; } + @Override + protected String getServiceName() { + return "serviceDesign"; + } + @Override protected String getApiName() { return "addField"; diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/AddFunctionTest.java b/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/AddFunctionTest.java index dd4b0baaf..0256a253b 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/AddFunctionTest.java +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/AddFunctionTest.java @@ -20,6 +20,7 @@ import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; +import io.ballerina.modelgenerator.commons.AbstractLSTest; import io.ballerina.servicemodelgenerator.extension.model.Codedata; import io.ballerina.servicemodelgenerator.extension.model.Function; import io.ballerina.servicemodelgenerator.extension.request.FunctionSourceRequest; @@ -101,6 +102,11 @@ protected Class clazz() { return AddFunctionTest.class; } + @Override + protected String getServiceName() { + return "serviceDesign"; + } + @Override protected String getApiName() { return "addFunction"; @@ -117,6 +123,7 @@ protected String getApiName() { */ private record TestConfig(String filePath, String description, Codedata codedata, Function function, Map> output) { + public String description() { return description == null ? "" : description; } diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/AddServiceTest.java b/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/AddServiceTest.java index b76d39e97..23b0861c4 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/AddServiceTest.java +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/AddServiceTest.java @@ -20,6 +20,7 @@ import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; +import io.ballerina.modelgenerator.commons.AbstractLSTest; import io.ballerina.servicemodelgenerator.extension.model.Service; import io.ballerina.servicemodelgenerator.extension.request.ServiceSourceRequest; import org.eclipse.lsp4j.TextEdit; @@ -98,6 +99,11 @@ protected Class clazz() { return AddServiceTest.class; } + @Override + protected String getServiceName() { + return "serviceDesign"; + } + @Override protected String getApiName() { return "addService"; @@ -108,11 +114,12 @@ protected String getApiName() { * * @param filePath The path to the source file. * @param description The description of the test. - * @param service The service to be added. + * @param service The service to be added. * @param output The expected output. */ private record TestConfig(String filePath, String description, Service service, Map> output) { + public String description() { return description == null ? "" : description; } diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/GetFunctionModelTest.java b/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/GetFunctionModelTest.java index 2a340a89f..b38d0b747 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/GetFunctionModelTest.java +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/GetFunctionModelTest.java @@ -19,6 +19,7 @@ package io.ballerina.servicemodelgenerator.extension; import com.google.gson.JsonObject; +import io.ballerina.modelgenerator.commons.AbstractLSTest; import io.ballerina.servicemodelgenerator.extension.model.Function; import io.ballerina.servicemodelgenerator.extension.request.FunctionModelRequest; import io.ballerina.servicemodelgenerator.extension.response.FunctionModelResponse; @@ -83,6 +84,11 @@ protected Class clazz() { return GetFunctionModelTest.class; } + @Override + protected String getServiceName() { + return "serviceDesign"; + } + @Override protected String getApiName() { return "getFunctionModel"; @@ -92,12 +98,13 @@ protected String getApiName() { * Represents the test configuration. * * @param functionName The name of the function - * @param type The type of the function - * @param description The description of the test - * @param response The expected response + * @param type The type of the function + * @param description The description of the test + * @param response The expected response */ private record TestConfig(String description, String type, String functionName, FunctionModelResponse response) { + public String description() { return description == null ? "" : description; } diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/GetServiceClassModelFromSourceTest.java b/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/GetServiceClassModelFromSourceTest.java index c719850ef..0843e4f82 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/GetServiceClassModelFromSourceTest.java +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/GetServiceClassModelFromSourceTest.java @@ -19,6 +19,7 @@ package io.ballerina.servicemodelgenerator.extension; import com.google.gson.JsonObject; +import io.ballerina.modelgenerator.commons.AbstractLSTest; import io.ballerina.servicemodelgenerator.extension.model.Codedata; import io.ballerina.servicemodelgenerator.extension.model.ServiceClass; import io.ballerina.servicemodelgenerator.extension.request.CommonModelFromSourceRequest; @@ -82,6 +83,11 @@ protected Class clazz() { return GetServiceClassModelFromSourceTest.class; } + @Override + protected String getServiceName() { + return "serviceDesign"; + } + @Override protected String getApiName() { return "getServiceClassModelFromSource"; @@ -92,12 +98,13 @@ protected String getApiName() { * * @param filePath The path to the source file * @param description The description of the test - * @param start The start position of the service declaration node - * @param end The end position of the service declaration node + * @param start The start position of the service declaration node + * @param end The end position of the service declaration node * @param response The expected response */ private record TestConfig(String filePath, String description, LinePosition start, LinePosition end, ServiceClass response) { + public String description() { return description == null ? "" : description; } diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/GetServiceModelFromSourceTest.java b/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/GetServiceModelFromSourceTest.java index f56392314..63f39abc9 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/GetServiceModelFromSourceTest.java +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/GetServiceModelFromSourceTest.java @@ -19,6 +19,7 @@ package io.ballerina.servicemodelgenerator.extension; import com.google.gson.JsonObject; +import io.ballerina.modelgenerator.commons.AbstractLSTest; import io.ballerina.servicemodelgenerator.extension.model.Codedata; import io.ballerina.servicemodelgenerator.extension.model.Service; import io.ballerina.servicemodelgenerator.extension.request.CommonModelFromSourceRequest; @@ -85,6 +86,11 @@ protected Class clazz() { return GetServiceModelFromSourceTest.class; } + @Override + protected String getServiceName() { + return "serviceDesign"; + } + @Override protected String getApiName() { return "getServiceFromSource"; @@ -95,12 +101,13 @@ protected String getApiName() { * * @param filePath The path to the source file * @param description The description of the test - * @param start The start position of the service declaration node - * @param end The end position of the service declaration node + * @param start The start position of the service declaration node + * @param end The end position of the service declaration node * @param response The expected response */ private record TestConfig(String filePath, String description, LinePosition start, LinePosition end, Service response) { + public String description() { return description == null ? "" : description; } diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/UpdateClassFieldTest.java b/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/UpdateClassFieldTest.java index ba8bb5be5..7532308cb 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/UpdateClassFieldTest.java +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/UpdateClassFieldTest.java @@ -20,6 +20,7 @@ import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; +import io.ballerina.modelgenerator.commons.AbstractLSTest; import io.ballerina.servicemodelgenerator.extension.model.Field; import io.ballerina.servicemodelgenerator.extension.request.ClassFieldModifierRequest; import org.eclipse.lsp4j.TextEdit; @@ -98,6 +99,11 @@ protected Class clazz() { return UpdateClassFieldTest.class; } + @Override + protected String getServiceName() { + return "serviceDesign"; + } + @Override protected String getApiName() { return "updateClassField"; @@ -106,13 +112,14 @@ protected String getApiName() { /** * Represents the test configuration for the source generator test. * - * @param filePath The path to the source file. - * @param description The description of the test. - * @param field The field to be updated. - * @param output The expected output. + * @param filePath The path to the source file. + * @param description The description of the test. + * @param field The field to be updated. + * @param output The expected output. */ private record TestConfig(String filePath, String description, Field field, Map> output) { + public String description() { return description == null ? "" : description; } diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/UpdateFunctionTest.java b/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/UpdateFunctionTest.java index d19b843d0..6dc2fb5e7 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/UpdateFunctionTest.java +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/UpdateFunctionTest.java @@ -20,6 +20,7 @@ import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; +import io.ballerina.modelgenerator.commons.AbstractLSTest; import io.ballerina.servicemodelgenerator.extension.model.Function; import io.ballerina.servicemodelgenerator.extension.request.FunctionModifierRequest; import org.eclipse.lsp4j.TextEdit; @@ -98,6 +99,11 @@ protected Class clazz() { return UpdateFunctionTest.class; } + @Override + protected String getServiceName() { + return "serviceDesign"; + } + @Override protected String getApiName() { return "updateFunction"; diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/UpdateServiceClassTest.java b/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/UpdateServiceClassTest.java index da9aab50e..6a1b939c5 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/UpdateServiceClassTest.java +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/test/java/io/ballerina/servicemodelgenerator/extension/UpdateServiceClassTest.java @@ -20,6 +20,7 @@ import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; +import io.ballerina.modelgenerator.commons.AbstractLSTest; import io.ballerina.servicemodelgenerator.extension.model.ServiceClass; import io.ballerina.servicemodelgenerator.extension.request.ServiceClassSourceRequest; import org.eclipse.lsp4j.TextEdit; @@ -98,6 +99,11 @@ protected Class clazz() { return UpdateServiceClassTest.class; } + @Override + protected String getServiceName() { + return "serviceDesign"; + } + @Override protected String getApiName() { return "updateServiceClass"; @@ -106,13 +112,14 @@ protected String getApiName() { /** * Represents the test configuration for the source generator test. * - * @param filePath The path to the source file. - * @param description The description of the test. - * @param serviceClass The service class to update. - * @param output The expected output. + * @param filePath The path to the source file. + * @param description The description of the test. + * @param serviceClass The service class to update. + * @param output The expected output. */ private record TestConfig(String filePath, String description, ServiceClass serviceClass, Map> output) { + public String description() { return description == null ? "" : description; } diff --git a/test-manager-service/modules/test-manager-service-ls-extension/build.gradle b/test-manager-service/modules/test-manager-service-ls-extension/build.gradle index bbb881d4a..9686df404 100644 --- a/test-manager-service/modules/test-manager-service-ls-extension/build.gradle +++ b/test-manager-service/modules/test-manager-service-ls-extension/build.gradle @@ -34,6 +34,8 @@ configurations { } dependencies { + implementation project(":model-generator-commons") + implementation "org.ballerinalang:ballerina-lang:${ballerinaLangVersion}" implementation "org.ballerinalang:ballerina-parser:${ballerinaLangVersion}" implementation "org.ballerinalang:formatter-core:${ballerinaLangVersion}" diff --git a/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/AbstractLSTest.java b/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/AbstractLSTest.java deleted file mode 100644 index 9af3174d2..000000000 --- a/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/AbstractLSTest.java +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com) - * - * WSO2 LLC. 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 io.ballerina.testmanagerservice.extension; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.JsonPrimitive; -import org.ballerinalang.langserver.BallerinaLanguageServer; -import org.ballerinalang.langserver.util.TestUtil; -import org.eclipse.lsp4j.DidCloseTextDocumentParams; -import org.eclipse.lsp4j.DidOpenTextDocumentParams; -import org.eclipse.lsp4j.TextDocumentIdentifier; -import org.eclipse.lsp4j.TextDocumentItem; -import org.eclipse.lsp4j.jsonrpc.Endpoint; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.Assert; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Stream; - -/** - * Abstract class for testing Test Manager Service. - * - * @since 2.0.0 - */ -public abstract class AbstractLSTest { - - protected static Logger log; - protected static Path resDir, sourceDir, configDir; - protected final Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); - - protected Endpoint serviceEndpoint; - private BallerinaLanguageServer languageServer; - - @BeforeClass - public final void init() { - resDir = Paths.get("src/test/resources").resolve(getResourceDir()).toAbsolutePath(); - configDir = resDir.resolve("config"); - sourceDir = resDir.resolve("source"); - log = LoggerFactory.getLogger(clazz()); - this.languageServer = new BallerinaLanguageServer(); - TestUtil.LanguageServerBuilder builder = TestUtil.newLanguageServer().withLanguageServer(languageServer); - this.serviceEndpoint = builder.build(); - } - - /** - * Positive tests for the flow model generator service. - * - * @param config The path to the test config - * @throws IOException If an error occurs while reading the config - */ - @Test(dataProvider = "data-provider") - public abstract void test(Path config) throws IOException; - - /** - * Provides the list of test configs. - * - * @return The list of test configs - */ - @DataProvider(name = "data-provider") - protected Object[] getConfigsList() { - List skippedTests = Arrays.stream(this.skipList()).toList(); - try (Stream stream = Files.walk(resDir)) { - return stream - .filter(path -> { - File file = path.toFile(); - return file.isFile() && !file.getName().startsWith(".") - && file.getName().endsWith(".json") - && !skippedTests.contains(file.getName()); - }) - .toArray(Path[]::new); - } catch (IOException e) { - // If failed to load tests, then it's a failure - Assert.fail("Unable to load test config", e); - return new Object[0][]; - } - } - - /** - * Provides the list of tests to be skipped. - * - * @return The list of tests to be skipped - */ - protected String[] skipList() { - return new String[]{ }; - } - - /** - * Updates the test config with the result generated from the test case. - * - * @param configJsonPath The path to the test config - * @param updatedConfig The updated config - * @throws IOException If an error occurs while writing the config - */ - protected void updateConfig(Path configJsonPath, Object updatedConfig) throws IOException { - String objStr = gson.toJson(updatedConfig).concat(System.lineSeparator()); - Files.writeString(configJsonPath, objStr); - } - - protected JsonObject getResponse(Endpoint endpoint, Object request) throws IOException { - Endpoint tempEndPoint = serviceEndpoint; - serviceEndpoint = endpoint; - JsonObject response = getResponse(request); - serviceEndpoint = tempEndPoint; - return response; - } - - protected JsonObject getResponse(Object request) throws IOException { - return getResponse(request, getServiceName() + "/" + getApiName()); - } - - protected JsonObject getResponseAndCloseFile(Object request, String source) throws IOException { - JsonObject response = getResponse(request); - String fileUri = sourceDir.resolve(source).toAbsolutePath().toUri().toString(); - serviceEndpoint.notify("textDocument/didClose", - new DidCloseTextDocumentParams(new TextDocumentIdentifier(fileUri))); - return response; - } - - protected JsonObject getResponse(Object request, String api) { - CompletableFuture result = serviceEndpoint.request(api, request); - String response = TestUtil.getResponseString(result); - JsonObject jsonObject = JsonParser.parseString(response).getAsJsonObject().getAsJsonObject("result"); - JsonPrimitive errorMsg = jsonObject.getAsJsonPrimitive("errorMsg"); - if (errorMsg != null) { - log.error("Stacktrace: {}", jsonObject.getAsJsonPrimitive("stacktrace").getAsString()); - Assert.fail("Error occurred: " + errorMsg.getAsString()); - } - return jsonObject; - } - - protected void sendNotification(String api, Object request) { - serviceEndpoint.notify(api, request); - } - - protected void notifyDidOpen(String sourcePath) throws IOException { - TextDocumentItem textDocumentItem = new TextDocumentItem(); - String text; - try (FileInputStream fis = new FileInputStream(sourcePath)) { - text = new String(fis.readAllBytes()); - } - textDocumentItem.setUri(Utils.getExprUri(sourcePath)); - textDocumentItem.setText(text); - textDocumentItem.setLanguageId("ballerina"); - textDocumentItem.setVersion(1); - sendNotification("textDocument/didOpen", new DidOpenTextDocumentParams(textDocumentItem)); - } - - protected void notifyDidClose(String sourcePath) { - TextDocumentIdentifier textDocumentIdentifier = new TextDocumentIdentifier(); - textDocumentIdentifier.setUri(Utils.getExprUri(sourcePath)); - sendNotification("textDocument/didClose", new DidCloseTextDocumentParams(textDocumentIdentifier)); - } - - /** - * Asserts the equality of the actual and expected arrays. - * - * @param property The property name - * @param actualNodes The actual nodes - * @param expectedNodes The expected nodes - * @return True if the arrays are equal, false otherwise - */ - protected final boolean assertArray(String property, List actualNodes, List expectedNodes) { - List unmatchedExpectedNodes = new java.util.ArrayList<>(expectedNodes); - List mismatchedAvailableNodes = new java.util.ArrayList<>(); - - int actualTextEditsSize = actualNodes.size(); - int expectedTextEditsSize = expectedNodes.size(); - boolean hasCountMatch = actualTextEditsSize == expectedTextEditsSize; - if (!hasCountMatch) { - log.error(String.format("Mismatched %s count. Expected: %d, Found: %d", property, expectedTextEditsSize, - actualTextEditsSize)); - } - - for (Object actualNode : actualNodes) { - if (expectedNodes.contains(actualNode)) { - unmatchedExpectedNodes.remove(actualNode); - } else { - mismatchedAvailableNodes.add(actualNode); - } - } - - boolean hasAllExpectedTextEdits = unmatchedExpectedNodes.isEmpty(); - if (!hasAllExpectedTextEdits) { - log.error(String.format("Found in expected %s but not in actual %s: ", property, property) + - unmatchedExpectedNodes); - } - - boolean hasRelevantTextEdits = mismatchedAvailableNodes.isEmpty(); - if (!hasRelevantTextEdits) { - log.error(String.format("Found in actual %s but not in expected %s: ", property, property) + - mismatchedAvailableNodes); - } - - return hasCountMatch && hasAllExpectedTextEdits && hasRelevantTextEdits; - } - - /** - * Compare the actual JSON with the expected JSON. - * - * @param actualJson the actual JSON produced by the LS extension - * @param expectedJson the expected JSON - */ - protected void compareJsonElements(JsonElement actualJson, JsonElement expectedJson) { - log.info("Differences in JSON elements:"); - compareJsonElementsRecursive(actualJson, expectedJson, ""); - } - - private void compareJsonElementsRecursive(JsonElement actualJson, JsonElement expectedJson, String path) { - if (actualJson.isJsonObject() && expectedJson.isJsonObject()) { - compareJsonObjects(actualJson.getAsJsonObject(), expectedJson.getAsJsonObject(), path); - } else if (actualJson.isJsonArray() && expectedJson.isJsonArray()) { - compareJsonArrays(actualJson.getAsJsonArray(), expectedJson.getAsJsonArray(), path); - } else if (!actualJson.equals(expectedJson)) { - log.info("- Value mismatch at '" + path + "'\n actual: " + actualJson + "\n expected: " + expectedJson); - } - } - - private void compareJsonObjects(JsonObject actualJson, JsonObject expectedJson, String path) { - Set> entrySet1 = actualJson.entrySet(); - Set> entrySet2 = expectedJson.entrySet(); - - for (Map.Entry entry : entrySet1) { - String key = entry.getKey(); - String currentPath = path.isEmpty() ? key : path + "." + key; - - if (!expectedJson.has(key)) { - log.info("- Key '" + currentPath + "' is missing in the expected JSON"); - } else { - compareJsonElementsRecursive(entry.getValue(), expectedJson.get(key), currentPath); - } - } - - for (Map.Entry entry : entrySet2) { - String key = entry.getKey(); - String currentPath = path.isEmpty() ? key : path + "." + key; - - if (!actualJson.has(key)) { - log.info("- Key '" + currentPath + "' is missing in the actual JSON"); - } - } - } - - private void compareJsonArrays(JsonArray actualArray, JsonArray expectedArray, String path) { - int size1 = actualArray.size(); - int size2 = expectedArray.size(); - int minSize = Math.min(size1, size2); - - for (int i = 0; i < minSize; i++) { - compareJsonElementsRecursive(actualArray.get(i), expectedArray.get(i), path + "[" + i + "]"); - } - - if (size1 > size2) { - for (int i = size2; i < size1; i++) { - log.info("- Extra element in actual JSON at '" + path + "[" + i + "]': " + actualArray.get(i)); - } - } else if (size2 > size1) { - for (int i = size1; i < size2; i++) { - log.info("- Extra element in expected JSON at '" + path + "[" + i + "]': " + expectedArray.get(i)); - } - } - } - - protected String getSourcePath(String source) { - return sourceDir.resolve(source).toAbsolutePath().toString(); - } - - /** - * Returns the resource directory of the API test. - * - * @return The resource directory of the API test - */ - protected abstract String getResourceDir(); - - /** - * Returns the class of the API test. - * - * @return The class of the API test - */ - protected abstract Class clazz(); - - /** - * Returns the name of the API. - * - * @return The name of the API - */ - protected abstract String getApiName(); - - protected String getServiceName() { - return "testManagerService"; - } - - @AfterClass - public void shutDownLanguageServer() { - TestUtil.shutdownLanguageServer(this.serviceEndpoint); - this.languageServer = null; - this.serviceEndpoint = null; - } -} diff --git a/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/GetFunctionModelTest.java b/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/GetFunctionModelTest.java index 433357cf3..9372cdb2c 100644 --- a/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/GetFunctionModelTest.java +++ b/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/GetFunctionModelTest.java @@ -19,6 +19,7 @@ package io.ballerina.testmanagerservice.extension; import com.google.gson.JsonObject; +import io.ballerina.modelgenerator.commons.AbstractLSTest; import io.ballerina.testmanagerservice.extension.model.TestFunction; import io.ballerina.testmanagerservice.extension.request.GetTestFunctionRequest; import io.ballerina.testmanagerservice.extension.response.GetTestFunctionResponse; @@ -76,6 +77,12 @@ protected Class clazz() { return GetFunctionModelTest.class; } + @Override + protected String getServiceName() { + return "testManagerService"; + } + + @Override protected String getApiName() { return "getTestFunction"; diff --git a/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/TestAddTestFunction.java b/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/TestAddTestFunction.java index 34955a907..5e57be621 100644 --- a/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/TestAddTestFunction.java +++ b/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/TestAddTestFunction.java @@ -20,6 +20,7 @@ import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; +import io.ballerina.modelgenerator.commons.AbstractLSTest; import io.ballerina.testmanagerservice.extension.model.TestFunction; import io.ballerina.testmanagerservice.extension.request.AddTestFunctionRequest; import org.eclipse.lsp4j.TextEdit; @@ -99,6 +100,11 @@ protected Class clazz() { return TestAddTestFunction.class; } + @Override + protected String getServiceName() { + return "testManagerService"; + } + @Override protected String getApiName() { return "addTestFunction"; @@ -114,6 +120,7 @@ protected String getApiName() { */ private record TestConfig(String filePath, String description, TestFunction function, Map> output) { + public String description() { return description == null ? "" : description; } diff --git a/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/TestFileTestDiscovery.java b/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/TestFileTestDiscovery.java index 842136f6c..fe6c505ac 100644 --- a/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/TestFileTestDiscovery.java +++ b/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/TestFileTestDiscovery.java @@ -19,6 +19,7 @@ package io.ballerina.testmanagerservice.extension; import com.google.gson.JsonObject; +import io.ballerina.modelgenerator.commons.AbstractLSTest; import io.ballerina.testmanagerservice.extension.request.TestsDiscoveryRequest; import io.ballerina.testmanagerservice.extension.response.TestsDiscoveryResponse; import org.testng.Assert; @@ -68,6 +69,11 @@ protected Class clazz() { return TestFileTestDiscovery.class; } + @Override + protected String getServiceName() { + return "testManagerService"; + } + @Override protected String getApiName() { return "discoverInFile"; @@ -81,6 +87,7 @@ protected String getApiName() { * @param response The expected response */ private record TestConfig(String filePath, String description, TestsDiscoveryResponse response) { + public String description() { return description == null ? "" : description; } diff --git a/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/TestProjectTestDiscovery.java b/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/TestProjectTestDiscovery.java index 16b172eee..d38972b05 100644 --- a/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/TestProjectTestDiscovery.java +++ b/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/TestProjectTestDiscovery.java @@ -19,6 +19,7 @@ package io.ballerina.testmanagerservice.extension; import com.google.gson.JsonObject; +import io.ballerina.modelgenerator.commons.AbstractLSTest; import io.ballerina.testmanagerservice.extension.request.TestsDiscoveryRequest; import io.ballerina.testmanagerservice.extension.response.TestsDiscoveryResponse; import org.testng.Assert; @@ -68,6 +69,11 @@ protected Class clazz() { return TestProjectTestDiscovery.class; } + @Override + protected String getServiceName() { + return "testManagerService"; + } + @Override protected String getApiName() { return "discoverInProject"; @@ -81,6 +87,7 @@ protected String getApiName() { * @param response The expected response */ private record TestConfig(String filePath, String description, TestsDiscoveryResponse response) { + public String description() { return description == null ? "" : description; } diff --git a/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/TestUpdateTestFunction.java b/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/TestUpdateTestFunction.java index 1cbeecf99..a1767ceb0 100644 --- a/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/TestUpdateTestFunction.java +++ b/test-manager-service/modules/test-manager-service-ls-extension/src/test/java/io/ballerina/testmanagerservice/extension/TestUpdateTestFunction.java @@ -20,6 +20,7 @@ import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; +import io.ballerina.modelgenerator.commons.AbstractLSTest; import io.ballerina.testmanagerservice.extension.model.TestFunction; import io.ballerina.testmanagerservice.extension.request.UpdateTestFunctionRequest; import org.eclipse.lsp4j.TextEdit; @@ -99,6 +100,11 @@ protected Class clazz() { return TestUpdateTestFunction.class; } + @Override + protected String getServiceName() { + return "testManagerService"; + } + @Override protected String getApiName() { return "updateTestFunction"; @@ -114,6 +120,7 @@ protected String getApiName() { */ private record TestConfig(String filePath, String description, TestFunction function, Map> output) { + public String description() { return description == null ? "" : description; }