diff --git a/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/utils/PackageUtils.java b/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/utils/PackageUtils.java index 7b127501a7f8..0ee0b4a8eba6 100644 --- a/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/utils/PackageUtils.java +++ b/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/utils/PackageUtils.java @@ -34,16 +34,18 @@ import java.io.File; import java.net.URI; import java.net.URISyntaxException; -import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.util.AbstractMap; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.StringJoiner; +import static io.ballerina.projects.util.ProjectConstants.BALLERINA_TOML; +import static io.ballerina.projects.util.ProjectPaths.isBalFile; import static org.ballerinalang.debugadapter.DebugSourceType.DEPENDENCY; import static org.ballerinalang.debugadapter.DebugSourceType.PACKAGE; import static org.ballerinalang.debugadapter.evaluation.IdentifierModifier.encodeModuleName; @@ -60,6 +62,7 @@ public final class PackageUtils { public static final String GENERATED_VAR_PREFIX = "$"; static final String USER_MODULE_DIR = "modules"; static final String GEN_MODULE_DIR = "generated"; + static final String PERSIST_DIR = "persist"; static final String TEST_PKG_POSTFIX = "$test"; private static final String URI_SCHEME_FILE = "file"; private static final String URI_SCHEME_BALA = "bala"; @@ -111,15 +114,57 @@ public static Optional> getStackFrameSourcePath * @return A pair of project kind and the project root. */ public static Map.Entry computeProjectKindAndRoot(Path path) { - if (ProjectPaths.isStandaloneBalFile(path)) { + if (ProjectPaths.isStandaloneBalFile(path) && !isBalToolSpecificFile(path)) { return new AbstractMap.SimpleEntry<>(ProjectKind.SINGLE_FILE_PROJECT, path); } - // Following is a temp fix to distinguish Bala and Build projects. - Path tomlPath = ProjectPaths.packageRoot(path).resolve(ProjectConstants.BALLERINA_TOML); - if (Files.exists(tomlPath)) { - return new AbstractMap.SimpleEntry<>(ProjectKind.BUILD_PROJECT, ProjectPaths.packageRoot(path)); + // TODO: Revert 'findProjectRoot()' to `ProjectPaths.packageRoot()` API once + // https://github.com/ballerina-platform/ballerina-lang/issues/43538#issuecomment-2469488458 + // is addressed from the Ballerina platform side. + Optional packageRoot = findProjectRoot(path); + if (packageRoot.isEmpty()) { + return new AbstractMap.SimpleEntry<>(ProjectKind.SINGLE_FILE_PROJECT, path); + } else if (hasBallerinaToml(packageRoot.get())) { + return new AbstractMap.SimpleEntry<>(ProjectKind.BUILD_PROJECT, packageRoot.get()); + } else { + return new AbstractMap.SimpleEntry<>(ProjectKind.BALA_PROJECT, packageRoot.get()); } - return new AbstractMap.SimpleEntry<>(ProjectKind.BALA_PROJECT, ProjectPaths.packageRoot(path)); + } + + private static boolean isBalToolSpecificFile(Path filePath) { + // TODO: Remove after https://github.com/ballerina-platform/ballerina-lang/issues/43538#issuecomment-2469488458 + // is addressed from the Ballerina platform side. + Path parentPath = filePath.toAbsolutePath().normalize().getParent(); + return isBalFile(filePath) + && parentPath.toFile().isDirectory() + && parentPath.toFile().getName().equals(PERSIST_DIR) + && hasBallerinaToml(parentPath.getParent()); + } + + private static boolean hasBallerinaToml(Path filePath) { + if (Objects.isNull(filePath)) { + return false; + } + Path absFilePath = filePath.toAbsolutePath().normalize(); + return absFilePath.resolve(BALLERINA_TOML).toFile().exists(); + } + + private static Optional findProjectRoot(Path filePath) { + if (filePath == null) { + return Optional.empty(); + } + + filePath = filePath.toAbsolutePath().normalize(); + if (filePath.toFile().isDirectory()) { + if (hasBallerinaToml(filePath) || hasPackageJson(filePath)) { + return Optional.of(filePath); + } + } + return findProjectRoot(filePath.getParent()); + } + + private static boolean hasPackageJson(Path filePath) { + Path absFilePath = filePath.toAbsolutePath().normalize(); + return absFilePath.resolve(ProjectConstants.PACKAGE_JSON).toFile().exists(); } /** diff --git a/tests/jballerina-debugger-integration-test/src/test/java/org/ballerinalang/debugger/test/adapter/run/DebugEngageTest.java b/tests/jballerina-debugger-integration-test/src/test/java/org/ballerinalang/debugger/test/adapter/run/DebugEngageTest.java index ed6c1d2ffc9c..9d9e6388e76e 100644 --- a/tests/jballerina-debugger-integration-test/src/test/java/org/ballerinalang/debugger/test/adapter/run/DebugEngageTest.java +++ b/tests/jballerina-debugger-integration-test/src/test/java/org/ballerinalang/debugger/test/adapter/run/DebugEngageTest.java @@ -40,20 +40,20 @@ public class DebugEngageTest extends BaseTestCase { DebugTestRunner debugTestRunner; + private static final String TEST_PROJECT_NAME = "basic-project"; @Override @BeforeClass public void setup() { - String testProjectName = "breakpoint-tests"; - String testModuleFileName = "Ballerina.toml"; - debugTestRunner = new DebugTestRunner(testProjectName, testModuleFileName, true); } - @Test(description = "Debug engage test for launch mode, with special ballerina project files as input(i.e. " + - "Ballerina.toml, Dependencies.toml, etc.)") - public void testNonBallerinaInputSource() throws BallerinaTestException { - Path breakpointFilePath = debugTestRunner.testProjectPath.resolve("main.bal"); - debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(breakpointFilePath, 22)); + @Test(description = "Debug engage test (launch mode), with special ballerina project files as inputs" + + "(i.e. Ballerina.toml, Dependencies.toml, etc.)") + public void testDebugLaunchWithNonBallerinaFiles() throws BallerinaTestException { + String balTomlFile = "Ballerina.toml"; + debugTestRunner = new DebugTestRunner(TEST_PROJECT_NAME, balTomlFile, true); + Path breakpointFilePath = debugTestRunner.testProjectPath.resolve("hello_world.bal"); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(breakpointFilePath, 19)); debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.RUN); // Test for debug engage @@ -61,6 +61,19 @@ public void testNonBallerinaInputSource() throws BallerinaTestException { Assert.assertEquals(debugHitInfo.getLeft(), debugTestRunner.testBreakpoints.get(0)); } + @Test(description = "Debug engage test (launch mode), with special Ballerina project files as inputs " + + "(i.e. persist/model.bal, etc.)") + public void testDebugLaunchWithBallerinaToolFiles() throws BallerinaTestException { + String balPersistModelFile = "persist/model.bal"; + debugTestRunner = new DebugTestRunner(TEST_PROJECT_NAME, balPersistModelFile, true); + Path breakpointFilePath = debugTestRunner.testProjectPath.resolve("hello_world.bal"); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(breakpointFilePath, 19)); + debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.RUN); + // Test for debug engage + Pair debugHitInfo = debugTestRunner.waitForDebugHit(25000); + Assert.assertEquals(debugHitInfo.getLeft(), debugTestRunner.testBreakpoints.get(0)); + } + @Override @AfterMethod(alwaysRun = true) public void cleanUp() {