diff --git a/gradle.properties b/gradle.properties
index 4805b6b..d875f58 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,5 +1,5 @@
group=io.ballerina.scan
-version=0.9.1-SNAPSHOT
+version=0.10.0-SNAPSHOT
# Plugin versions
spotbugsPluginVersion=6.1.5
diff --git a/scan-command-test-utils/build.gradle b/scan-command-test-utils/build.gradle
new file mode 100644
index 0000000..519e624
--- /dev/null
+++ b/scan-command-test-utils/build.gradle
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://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.
+ */
+
+import com.github.spotbugs.snom.Confidence
+import com.github.spotbugs.snom.Effort
+
+plugins {
+ id 'java-library'
+ id 'checkstyle'
+ id 'maven-publish'
+ id 'com.github.spotbugs'
+ id 'de.undercouch.download'
+}
+
+group = "${group}"
+version = "${version}"
+
+repositories {
+ mavenLocal()
+ mavenCentral()
+ maven {
+ url = 'https://maven.pkg.github.com/ballerina-platform/*'
+ credentials {
+ username System.getenv("packageUser")
+ password System.getenv("packagePAT")
+ }
+ }
+}
+
+dependencies {
+ implementation project(':scan-command')
+ implementation group: 'org.ballerinalang', name: 'ballerina-lang', version: "${ballerinaLangVersion}"
+ implementation group: 'org.ballerinalang', name: 'ballerina-cli', version: "${ballerinaLangVersion}"
+ implementation group: 'org.ballerinalang', name: 'ballerina-tools-api', version: "${ballerinaLangVersion}"
+ implementation group: 'org.testng', name: 'testng', version: "${testngVersion}"
+}
+
+// Publish scan-command-test-utils package to GitHub packages
+publishing {
+ publications {
+ mavenJava(MavenPublication) {
+ from components.java
+ groupId group
+ artifactId project.name
+ version version
+ }
+ }
+
+ repositories {
+ maven {
+ name = "GitHubPackages"
+ url = uri("https://maven.pkg.github.com/ballerina-platform/static-code-analysis-tool")
+ credentials {
+ username = System.getenv("publishUser")
+ password = System.getenv("publishPAT")
+ }
+ }
+ }
+}
+
+tasks.register('downloadCheckstyleRuleFiles', Download) {
+ src([
+ 'https://raw.githubusercontent.com/wso2/code-quality-tools/v1.4/checkstyle/jdk-17/checkstyle.xml',
+ 'https://raw.githubusercontent.com/wso2/code-quality-tools/v1.4/checkstyle/jdk-17/suppressions.xml'
+ ])
+ overwrite false
+ onlyIfNewer true
+ dest buildDir
+}
+
+artifacts.add('default', file("${project.buildDir}/checkstyle.xml")) {
+ builtBy(downloadCheckstyleRuleFiles)
+}
+
+artifacts.add('default', file("${project.buildDir}/suppressions.xml")) {
+ builtBy(downloadCheckstyleRuleFiles)
+}
+
+def excludePattern = '**/module-info.java'
+tasks.withType(Checkstyle).configureEach {
+ exclude excludePattern
+}
+
+checkstyle {
+ toolVersion "${project.puppycrawlCheckstyleVersion}"
+ configFile rootProject.file("${project.buildDir}/checkstyle.xml")
+ configProperties = ["suppressionFile": file("${project.buildDir}/suppressions.xml")]
+}
+
+checkstyleMain.dependsOn(downloadCheckstyleRuleFiles)
+checkstyleTest.dependsOn(downloadCheckstyleRuleFiles)
+
+spotbugsMain {
+ effort = Effort.valueOf("MAX")
+ reportLevel = Confidence.valueOf("LOW")
+
+ reportsDir = file("$project.buildDir/reports/spotbugs")
+
+ reports {
+ html.required.set(true)
+ text.required.set(true)
+ }
+
+ def excludeFile = file("${projectDir}/spotbugs-exclude.xml")
+ if (excludeFile.exists()) {
+ excludeFilter = excludeFile
+ }
+}
diff --git a/scan-command-test-utils/spotbugs-exclude.xml b/scan-command-test-utils/spotbugs-exclude.xml
new file mode 100644
index 0000000..2a4c079
--- /dev/null
+++ b/scan-command-test-utils/spotbugs-exclude.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
diff --git a/scan-command-test-utils/src/main/java/io/ballerina/scan/test/Assertions.java b/scan-command-test-utils/src/main/java/io/ballerina/scan/test/Assertions.java
new file mode 100644
index 0000000..39ecd15
--- /dev/null
+++ b/scan-command-test-utils/src/main/java/io/ballerina/scan/test/Assertions.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://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.scan.test;
+
+import io.ballerina.scan.Issue;
+import io.ballerina.scan.Rule;
+import io.ballerina.scan.RuleKind;
+import io.ballerina.scan.Source;
+import org.testng.Assert;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Test utility class to assert the issues found during a scan.
+ *
+ * @since 0.10.0
+ */
+public class Assertions {
+
+ private Assertions() {
+ }
+
+ /**
+ * Assert that the list of issues contains an issue with the given rule ID, file name, start line, end line,
+ * and source at the given index.
+ *
+ * @param issues list of issues from which the issue should be found
+ * @param index index of the issue to check
+ * @param ruleId rule ID to check for
+ * @param fileName file name to check for
+ * @param startLine start line to check for
+ * @param endLine end line to check for
+ * @param source source to check for
+ */
+ public static void assertIssue(List issues,
+ int index,
+ String ruleId,
+ String fileName,
+ int startLine,
+ int endLine,
+ Source source) {
+ Assert.assertTrue(index < issues.size(), "Index out of bounds for the issues list");
+ Issue issue = issues.get(index);
+ Assert.assertEquals(issue.rule().id(), ruleId, "Rule ID mismatch");
+ Assert.assertEquals(issue.source(), source, "Source mismatch");
+ Assert.assertEquals(issue.location().lineRange().fileName(), fileName, "File name mismatch");
+ Assert.assertEquals(issue.location().lineRange().startLine().line(), startLine, "Start line mismatch");
+ Assert.assertEquals(issue.location().lineRange().endLine().line(), endLine, "End line mismatch");
+ }
+
+ /**
+ * Assert that the list of rules contains a rule with the given ID, description, and kind.
+ *
+ * @param rules list of rules from which the rule should be found
+ * @param id rule ID to check for
+ * @param description rule description to check for
+ * @param kind rule kind to check for
+ */
+ public static void assertRule(List rules, String id, String description, RuleKind kind) {
+ boolean found = rules.stream().anyMatch(rule ->
+ rule.id().equals(id) &&
+ rule.description().equals(description) &&
+ rule.kind() == kind);
+
+ if (!found) {
+ String summary = rules.stream()
+ .map(rule -> String.format("Rule ID: %s, Description: %s, Kind: %s", rule.id(),
+ rule.description(), rule.kind()))
+ .collect(Collectors.joining("\n"));
+ Assert.fail(String.format("Expected rule with ID '%s' not found.%nFound rules:%n%s", id, summary));
+ }
+ }
+}
diff --git a/scan-command-test-utils/src/main/java/io/ballerina/scan/test/TestOptions.java b/scan-command-test-utils/src/main/java/io/ballerina/scan/test/TestOptions.java
new file mode 100644
index 0000000..deac2bb
--- /dev/null
+++ b/scan-command-test-utils/src/main/java/io/ballerina/scan/test/TestOptions.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://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.scan.test;
+
+import io.ballerina.projects.Project;
+import io.ballerina.scan.Rule;
+
+import java.io.PrintStream;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Test utility class to hold scan options for testing purposes.
+ *
+ * @since 0.10.0
+ */
+public class TestOptions {
+ private final Project project;
+ private final PrintStream outputStream;
+ private final boolean helpFlag;
+ private final boolean platformTriggered;
+ private final String targetDir;
+ private final boolean scanReport;
+ private final boolean listRules;
+ private final List includeRules;
+ private final List excludeRules;
+ private final List platforms;
+
+ private TestOptions(Project project, PrintStream outputStream, boolean helpFlag, boolean platformTriggered,
+ String targetDir, boolean scanReport, boolean listRules, List includeRules,
+ List excludeRules, List platforms) {
+ this.project = project;
+ this.outputStream = outputStream;
+ this.helpFlag = helpFlag;
+ this.platformTriggered = platformTriggered;
+ this.targetDir = targetDir;
+ this.scanReport = scanReport;
+ this.listRules = listRules;
+ this.includeRules = includeRules;
+ this.excludeRules = excludeRules;
+ this.platforms = platforms;
+ }
+
+ /**
+ * Create a new {@code TestOptionsBuilder} instance.
+ *
+ * @param project the project to be scanned
+ * @return a new {@code TestOptionsBuilder} instance
+ */
+ public static TestOptionsBuilder builder(Project project) {
+ return new TestOptionsBuilder(project);
+ }
+
+ /**
+ * Get the project to be scanned.
+ *
+ * @return the project to be scanned
+ */
+ Project project() {
+ return project;
+ }
+
+ /**
+ * Get the output stream.
+ *
+ * @return the output stream
+ */
+ PrintStream outputStream() {
+ return outputStream;
+ }
+
+ /**
+ * Get if the help flag is enabled or not.
+ *
+ * @return true if the help flag is enabled, false otherwise
+ */
+ boolean helpFlag() {
+ return helpFlag;
+ }
+
+ /**
+ * Get if the scan is triggered by a platform or not.
+ *
+ * @return true if the scan is triggered by a platform, false otherwise
+ */
+ boolean platformTriggered() {
+ return platformTriggered;
+ }
+
+ /**
+ * Get the target directory.
+ *
+ * @return the target directory
+ */
+ String targetDir() {
+ return targetDir;
+ }
+
+ /**
+ * Get if the scan report is enabled or not.
+ *
+ * @return true if the scan report is enabled, false otherwise
+ */
+ boolean scanReport() {
+ return scanReport;
+ }
+
+ /**
+ * Get if the rules should be listed or not.
+ *
+ * @return true if the rules should be listed, false otherwise
+ */
+ boolean listRules() {
+ return listRules;
+ }
+
+ /**
+ * Get the list of rules to be included.
+ *
+ * @return the list of rules to be included
+ */
+ List includeRules() {
+ return includeRules;
+ }
+
+ /**
+ * Get the list of rules to be excluded.
+ *
+ * @return the list of rules to be excluded
+ */
+ List excludeRules() {
+ return excludeRules;
+ }
+
+ /**
+ * Get the list of platforms.
+ *
+ * @return the list of platforms
+ */
+ List platforms() {
+ return platforms;
+ }
+
+ public static class TestOptionsBuilder {
+ private final Project project;
+ private PrintStream outputStream;
+ private boolean helpFlag;
+ private boolean platformTriggered;
+ private String targetDir;
+ private boolean scanReport;
+ private boolean listRules;
+ private List includeRules = List.of();
+ private List excludeRules = List.of();
+ private List platforms = List.of();
+
+ private TestOptionsBuilder(Project project) {
+ this.project = project;
+ }
+
+ /**
+ * Set the output stream.
+ *
+ * @param outputStream the output stream
+ * @return this builder
+ */
+ public TestOptionsBuilder setOutputStream(PrintStream outputStream) {
+ this.outputStream = outputStream;
+ return this;
+ }
+
+ /**
+ * Set the help flag.
+ *
+ * @param helpFlag true if the help flag needs to be enabled, false otherwise
+ * @return this builder
+ */
+ public TestOptionsBuilder setHelpFlag(boolean helpFlag) {
+ this.helpFlag = helpFlag;
+ return this;
+ }
+
+ /**
+ * Set if the scan is triggered by a platform.
+ *
+ * @param platformTriggered true if the scan is triggered by a platform, false otherwise
+ * @return this builder
+ */
+ public TestOptionsBuilder setPlatformTriggered(boolean platformTriggered) {
+ this.platformTriggered = platformTriggered;
+ return this;
+ }
+
+ /**
+ * Set the target directory.
+ *
+ * @param targetDir the target directory
+ * @return this builder
+ */
+ public TestOptionsBuilder setTargetDir(String targetDir) {
+ this.targetDir = targetDir;
+ return this;
+ }
+
+ /**
+ * Set if the scan report needs to be enabled.
+ *
+ * @param scanReport true if the scan report needs to be enabled, false otherwise
+ * @return this builder
+ */
+ public TestOptionsBuilder setScanReport(boolean scanReport) {
+ this.scanReport = scanReport;
+ return this;
+ }
+
+ /**
+ * Set if the rules should be listed.
+ *
+ * @param listRules true if the rules should be listed, false otherwise
+ * @return this builder
+ */
+ public TestOptionsBuilder setListRules(boolean listRules) {
+ this.listRules = listRules;
+ return this;
+ }
+
+ /**
+ * Set the list of rules to be included.
+ *
+ * @param includeRules the list of rules to be included
+ * @return this builder
+ */
+ public TestOptionsBuilder setIncludeRules(List includeRules) {
+ this.includeRules = Collections.unmodifiableList(includeRules);
+ return this;
+ }
+
+ /**
+ * Set the list of rules to be excluded.
+ *
+ * @param excludeRules the list of rules to be excluded
+ * @return this builder
+ */
+ public TestOptionsBuilder setExcludeRules(List excludeRules) {
+ this.excludeRules = Collections.unmodifiableList(excludeRules);
+ return this;
+ }
+
+ /**
+ * Set the list of platforms.
+ *
+ * @param platforms the list of platforms
+ * @return this builder
+ */
+ public TestOptionsBuilder setPlatforms(List platforms) {
+ this.platforms = Collections.unmodifiableList(platforms);
+ return this;
+ }
+
+ /**
+ * Build the {@code TestOptions} instance.
+ *
+ * @return the built {@code TestOptions} instance
+ */
+ public TestOptions build() {
+ return new TestOptions(project, outputStream, helpFlag, platformTriggered,
+ targetDir, scanReport, listRules, includeRules, excludeRules, platforms);
+ }
+ }
+}
diff --git a/scan-command-test-utils/src/main/java/io/ballerina/scan/test/TestProjectAnalyzer.java b/scan-command-test-utils/src/main/java/io/ballerina/scan/test/TestProjectAnalyzer.java
new file mode 100644
index 0000000..d194777
--- /dev/null
+++ b/scan-command-test-utils/src/main/java/io/ballerina/scan/test/TestProjectAnalyzer.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://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.scan.test;
+
+import io.ballerina.projects.Project;
+import io.ballerina.scan.Rule;
+import io.ballerina.scan.ScannerContext;
+import io.ballerina.scan.internal.ProjectAnalyzer;
+import io.ballerina.scan.utils.ScanTomlFile;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test utility class to analyze a project and return the reporters.
+ *
+ * @since 0.10.0
+ */
+class TestProjectAnalyzer extends ProjectAnalyzer {
+ private final List reporters;
+
+ TestProjectAnalyzer(Project project, ScanTomlFile scanTomlFile) {
+ super(project, scanTomlFile);
+ this.reporters = new ArrayList<>();
+ }
+
+ @Override
+ protected ScannerContext getScannerContext(List rules) {
+ TestScannerContext scannerContext = new TestScannerContext(rules);
+ reporters.add(scannerContext.getReporter());
+ return scannerContext;
+ }
+
+ List getReporters() {
+ return reporters;
+ }
+}
diff --git a/scan-command-test-utils/src/main/java/io/ballerina/scan/test/TestReporter.java b/scan-command-test-utils/src/main/java/io/ballerina/scan/test/TestReporter.java
new file mode 100644
index 0000000..cecbdd2
--- /dev/null
+++ b/scan-command-test-utils/src/main/java/io/ballerina/scan/test/TestReporter.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://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.scan.test;
+
+import io.ballerina.scan.Issue;
+import io.ballerina.scan.Rule;
+import io.ballerina.scan.internal.ReporterImpl;
+
+import java.util.List;
+
+/**
+ * Test utility implementation of {@link ReporterImpl}.
+ *
+ * @since 0.10.0
+ */
+class TestReporter extends ReporterImpl {
+ TestReporter(List rules) {
+ super(rules);
+ }
+
+ @Override
+ protected List getIssues() {
+ return super.getIssues();
+ }
+}
diff --git a/scan-command-test-utils/src/main/java/io/ballerina/scan/test/TestRunner.java b/scan-command-test-utils/src/main/java/io/ballerina/scan/test/TestRunner.java
new file mode 100644
index 0000000..2b5093e
--- /dev/null
+++ b/scan-command-test-utils/src/main/java/io/ballerina/scan/test/TestRunner.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://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.scan.test;
+
+import io.ballerina.scan.Issue;
+import io.ballerina.scan.Rule;
+
+import java.nio.file.Path;
+import java.util.List;
+
+/**
+ * Test utility class to run a scan command and return the issues.
+ *
+ * @since 0.10.0
+ */
+public class TestRunner {
+ private final TestScanCmd scanCmd;
+
+ /**
+ * Create a new {@code ScanTestRunner} instance.
+ *
+ * @param testOptions options to perform the scan
+ */
+ public TestRunner(TestOptions testOptions) {
+ scanCmd = new TestScanCmd(testOptions);
+ }
+
+ /**
+ * Create a new {@code ScanTestRunner} instance.
+ *
+ * @param projectPath path to the project to be scanned
+ * @param distributionPath path to the Ballerina distribution to be used for the scan
+ */
+ public TestRunner(Path projectPath, Path distributionPath) {
+ scanCmd = new TestScanCmd(projectPath, distributionPath);
+ }
+
+ /**
+ * Perform a scan.
+ *
+ */
+ public void performScan() {
+ scanCmd.execute();
+ }
+
+ /**
+ * Get the issues found during the scan.
+ *
+ * @return list of issues found during the scan
+ */
+ public List getIssues() {
+ return scanCmd.getProjectAnalyzer().getReporters().stream()
+ .flatMap(reporter -> reporter.getIssues().stream()).toList();
+ }
+
+ /**
+ * Get the rules registered for the scan.
+ *
+ * @return list of rules registered for the scan
+ */
+ public List getRules() {
+ return scanCmd.getAllRules();
+ }
+}
diff --git a/scan-command-test-utils/src/main/java/io/ballerina/scan/test/TestScanCmd.java b/scan-command-test-utils/src/main/java/io/ballerina/scan/test/TestScanCmd.java
new file mode 100644
index 0000000..bac6b55
--- /dev/null
+++ b/scan-command-test-utils/src/main/java/io/ballerina/scan/test/TestScanCmd.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://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.scan.test;
+
+import io.ballerina.projects.Project;
+import io.ballerina.projects.ProjectEnvironmentBuilder;
+import io.ballerina.projects.directory.BuildProject;
+import io.ballerina.projects.directory.SingleFileProject;
+import io.ballerina.projects.environment.Environment;
+import io.ballerina.projects.environment.EnvironmentBuilder;
+import io.ballerina.scan.internal.ProjectAnalyzer;
+import io.ballerina.scan.internal.ScanCmd;
+import io.ballerina.scan.utils.ScanTomlFile;
+
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.Optional;
+
+/**
+ * TestScanCmd extends ScanCmd to extend it for testing purposes.
+ *
+ * @since 0.10.0
+ */
+public class TestScanCmd extends ScanCmd {
+ private final Project project;
+ private TestProjectAnalyzer projectAnalyzer;
+
+ TestScanCmd(TestOptions options) {
+ super(
+ options.project().sourceRoot(),
+ options.outputStream(),
+ options.helpFlag(),
+ options.platformTriggered(),
+ options.targetDir(),
+ options.scanReport(),
+ options.listRules(),
+ options.includeRules(),
+ options.excludeRules(),
+ options.platforms()
+ );
+ this.project = options.project();
+ }
+
+ TestScanCmd(Path projectPath, Path distributionPath) {
+ super(
+ projectPath,
+ System.out,
+ false,
+ false,
+ null,
+ false,
+ false,
+ Collections.emptyList(),
+ Collections.emptyList(),
+ Collections.emptyList()
+ );
+ if (projectPath.toFile().isDirectory()) {
+ project = BuildProject.load(getEnvironmentBuilder(distributionPath), projectPath);
+ } else {
+ project = SingleFileProject.load(getEnvironmentBuilder(distributionPath), projectPath);
+ }
+ }
+
+ @Override
+ protected Optional getProject() {
+ return Optional.of(this.project);
+ }
+
+ @Override
+ protected ProjectAnalyzer getProjectAnalyzer(Project project, ScanTomlFile scanTomlFile) {
+ this.projectAnalyzer = new TestProjectAnalyzer(project, scanTomlFile);
+ return this.projectAnalyzer;
+ }
+
+ TestProjectAnalyzer getProjectAnalyzer() {
+ return this.projectAnalyzer;
+ }
+
+ private static ProjectEnvironmentBuilder getEnvironmentBuilder(Path distributionPath) {
+ Environment environment = EnvironmentBuilder.getBuilder().setBallerinaHome(distributionPath).build();
+ return ProjectEnvironmentBuilder.getBuilder(environment);
+ }
+}
diff --git a/scan-command-test-utils/src/main/java/io/ballerina/scan/test/TestScannerContext.java b/scan-command-test-utils/src/main/java/io/ballerina/scan/test/TestScannerContext.java
new file mode 100644
index 0000000..86a5af6
--- /dev/null
+++ b/scan-command-test-utils/src/main/java/io/ballerina/scan/test/TestScannerContext.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2025, WSO2 LLC. (https://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.scan.test;
+
+import io.ballerina.scan.Rule;
+import io.ballerina.scan.ScannerContext;
+
+import java.util.List;
+
+/**
+ * Test utility subclass of {@link ScannerContext}.
+ *
+ * @since 0.10.0
+ */
+class TestScannerContext implements ScannerContext {
+ private final TestReporter reporter;
+
+ TestScannerContext(List rules) {
+ this.reporter = new TestReporter(rules);
+ }
+
+ /**
+ * Returns the {@link TestReporter} to be used to report identified issues.
+ *
+ * @return reporter that needs to be used to report issues identified.
+ */
+ @Override
+ public TestReporter getReporter() {
+ return reporter;
+ }
+}
diff --git a/scan-command-test-utils/src/main/java/module-info.java b/scan-command-test-utils/src/main/java/module-info.java
new file mode 100644
index 0000000..2cf8292
--- /dev/null
+++ b/scan-command-test-utils/src/main/java/module-info.java
@@ -0,0 +1,8 @@
+module io.ballerina.scan.test {
+ requires io.ballerina.lang;
+ requires io.ballerina.tools.api;
+ requires org.testng;
+ requires io.ballerina.scan;
+
+ exports io.ballerina.scan.test;
+}
diff --git a/scan-command/build.gradle b/scan-command/build.gradle
index 3c23f66..3bea904 100644
--- a/scan-command/build.gradle
+++ b/scan-command/build.gradle
@@ -76,6 +76,7 @@ dependencies {
// Required dependencies for running scan tool tests
testImplementation group: 'org.testng', name: 'testng', version: "${testngVersion}"
+ testImplementation group: 'org.ballerinalang', name: 'ballerina-lang', version: "${ballerinaLangVersion}"
// For including the scan report zip
implementation files("${projectDir}/src/main/resources/report.zip")
diff --git a/scan-command/src/main/java/io/ballerina/scan/internal/ProjectAnalyzer.java b/scan-command/src/main/java/io/ballerina/scan/internal/ProjectAnalyzer.java
index 84b7867..d68cbb6 100644
--- a/scan-command/src/main/java/io/ballerina/scan/internal/ProjectAnalyzer.java
+++ b/scan-command/src/main/java/io/ballerina/scan/internal/ProjectAnalyzer.java
@@ -82,13 +82,13 @@
*
* @since 0.1.0
* */
-class ProjectAnalyzer {
+public class ProjectAnalyzer {
private final Project project;
private final ScanTomlFile scanTomlFile;
private final Gson gson = new Gson();
private String pluginImportsDocumentName;
- ProjectAnalyzer(Project project, ScanTomlFile scanTomlFile) {
+ protected ProjectAnalyzer(Project project, ScanTomlFile scanTomlFile) {
this.project = project;
this.scanTomlFile = scanTomlFile;
Module defaultModule = project.currentPackage().getDefaultModule();
@@ -284,7 +284,7 @@ private RuleKind getRuleKind(String pluginName, String kind) {
List runExternalAnalyzers(Map> externalAnalyzers) {
List scannerContextList = new ArrayList<>(externalAnalyzers.size());
for (Map.Entry> externalAnalyzer : externalAnalyzers.entrySet()) {
- ScannerContextImpl scannerContext = new ScannerContextImpl(externalAnalyzer.getValue());
+ ScannerContext scannerContext = getScannerContext(externalAnalyzer.getValue());
scannerContextList.add(scannerContext);
// Save the scanner context to plugin cache for the compiler plugin to use during package compilation
@@ -307,4 +307,8 @@ List runExternalAnalyzers(Map> externalAnalyzers) {
}
return externalIssues;
}
+
+ protected ScannerContext getScannerContext(List rules) {
+ return new ScannerContextImpl(rules);
+ }
}
diff --git a/scan-command/src/main/java/io/ballerina/scan/internal/ReporterImpl.java b/scan-command/src/main/java/io/ballerina/scan/internal/ReporterImpl.java
index 6efd325..c4be783 100644
--- a/scan-command/src/main/java/io/ballerina/scan/internal/ReporterImpl.java
+++ b/scan-command/src/main/java/io/ballerina/scan/internal/ReporterImpl.java
@@ -46,14 +46,12 @@
*
* @since 0.1.0
* */
-class ReporterImpl implements Reporter {
+public class ReporterImpl implements Reporter {
private final List issues = new ArrayList<>();
private final Map rules = new HashMap<>();
- ReporterImpl(List rules) {
- rules.forEach(rule -> {
- this.rules.put(rule.numericId(), rule);
- });
+ protected ReporterImpl(List rules) {
+ rules.forEach(rule -> this.rules.put(rule.numericId(), rule));
}
@Override
@@ -96,7 +94,7 @@ private Issue createIssue(Document reportedDocument, Location location, Rule rul
issuesFilePath.toString());
}
- List getIssues() {
+ protected List getIssues() {
return issues;
}
}
diff --git a/scan-command/src/main/java/io/ballerina/scan/internal/ScanCmd.java b/scan-command/src/main/java/io/ballerina/scan/internal/ScanCmd.java
index 611fbe1..545720c 100644
--- a/scan-command/src/main/java/io/ballerina/scan/internal/ScanCmd.java
+++ b/scan-command/src/main/java/io/ballerina/scan/internal/ScanCmd.java
@@ -67,8 +67,8 @@
public class ScanCmd implements BLauncherCmd {
private final PrintStream outputStream;
- @CommandLine.Parameters(description = "Program arguments")
- private final List argList = new ArrayList<>();
+ @CommandLine.Parameters (arity = "0..1")
+ private final Path projectPath;
@CommandLine.Option(names = {"--help", "-h", "?"}, hidden = true)
private boolean helpFlag;
@@ -103,12 +103,39 @@ public class ScanCmd implements BLauncherCmd {
description = "Specify the comma separated list of static code analysis platforms to report issues")
private List platforms = new ArrayList<>();
+ private final List allRules = new ArrayList<>();
+
public ScanCmd() {
- this.outputStream = System.out;
+ this(System.out);
}
ScanCmd(PrintStream outputStream) {
+ this.projectPath = Paths.get(System.getProperty(ProjectConstants.USER_DIR));
+ this.outputStream = outputStream;
+ }
+
+ protected ScanCmd(
+ Path projectPath,
+ PrintStream outputStream,
+ boolean helpFlag,
+ boolean platformTriggered,
+ String targetDir,
+ boolean scanReport,
+ boolean listRules,
+ List includeRules,
+ List excludeRules,
+ List platforms
+ ) {
+ this.projectPath = projectPath;
this.outputStream = outputStream;
+ this.helpFlag = helpFlag;
+ this.platformTriggered = platformTriggered;
+ this.targetDir = targetDir;
+ this.scanReport = scanReport;
+ this.listRules = listRules;
+ this.includeRules.addAll(includeRules.stream().map(Rule::id).toList());
+ this.excludeRules.addAll(excludeRules.stream().map(Rule::id).toList());
+ this.platforms.addAll(platforms);
}
@Override
@@ -155,7 +182,7 @@ public void execute() {
return;
}
- ProjectAnalyzer projectAnalyzer = new ProjectAnalyzer(project.get(), scanTomlFile.get());
+ ProjectAnalyzer projectAnalyzer = getProjectAnalyzer(project.get(), scanTomlFile.get());
List coreRules = CoreRule.rules();
Map> externalAnalyzers;
try {
@@ -165,9 +192,10 @@ public void execute() {
return;
}
+ allRules.addAll(coreRules);
+ externalAnalyzers.values().forEach(allRules::addAll);
if (listRules) {
- externalAnalyzers.values().forEach(coreRules::addAll);
- ScanUtils.printRulesToConsole(coreRules, outputStream);
+ ScanUtils.printRulesToConsole(allRules, outputStream);
return;
}
@@ -275,33 +303,31 @@ private StringBuilder helpMessage() {
return builder;
}
- private Optional getProject() {
- if (argList.isEmpty()) {
- try {
- return Optional.of(BuildProject.load(Paths.get(System.getProperty(ProjectConstants.USER_DIR))));
- } catch (RuntimeException ex) {
- outputStream.println(ex.getMessage());
- return Optional.empty();
- }
- }
-
- if (argList.size() != 1) {
- outputStream.println(DiagnosticLog.error(DiagnosticCode.INVALID_NUMBER_OF_ARGUMENTS, argList.size()));
- return Optional.empty();
- }
-
- Path path = Paths.get(argList.get(0));
+ protected Optional getProject() {
try {
- if (path.toFile().isDirectory()) {
- return Optional.of(BuildProject.load(path));
+ if (projectPath.toFile().isDirectory()) {
+ return Optional.of(BuildProject.load(projectPath));
}
- return Optional.of(SingleFileProject.load(path));
+ return Optional.of(SingleFileProject.load(projectPath));
} catch (RuntimeException ex) {
outputStream.println(ex.getMessage());
return Optional.empty();
}
}
+ protected ProjectAnalyzer getProjectAnalyzer(Project project, ScanTomlFile scanTomlFile) {
+ return new ProjectAnalyzer(project, scanTomlFile);
+ }
+
+ /**
+ * Get the list of all rules available for the given project.
+ *
+ * @return an unmodifiable list of all rules
+ */
+ public List getAllRules() {
+ return Collections.unmodifiableList(allRules);
+ }
+
private URLClassLoader loadPlatformPlugins(List jarPaths) {
List jarUrls = new ArrayList<>(jarPaths.size());
jarPaths.forEach(jarPath -> {
diff --git a/scan-command/src/main/java/io/ballerina/scan/internal/ScanToolConstants.java b/scan-command/src/main/java/io/ballerina/scan/internal/ScanToolConstants.java
index d6efee1..592feca 100644
--- a/scan-command/src/main/java/io/ballerina/scan/internal/ScanToolConstants.java
+++ b/scan-command/src/main/java/io/ballerina/scan/internal/ScanToolConstants.java
@@ -26,7 +26,6 @@
public class ScanToolConstants {
static final String SCAN_COMMAND = "scan";
static final String BALLERINA_RULE_PREFIX = "ballerina:";
- static final String FORWARD_SLASH = "/";
static final String BALLERINA_ORG = "ballerina";
static final String BALLERINAI_ORG = "ballerinai";
static final String BALLERINAX_ORG = "ballerinax";
@@ -40,7 +39,8 @@ public class ScanToolConstants {
static final String RULE_ID = "id";
static final String RULE_DESCRIPTION = "description";
- static final String SCANNER_CONTEXT = "ScannerContext";
+ public static final String SCANNER_CONTEXT = "ScannerContext";
+ public static final String FORWARD_SLASH = "/";
private ScanToolConstants() {
}
diff --git a/scan-command/src/main/java/module-info.java b/scan-command/src/main/java/module-info.java
index 79eb380..fccf3af 100644
--- a/scan-command/src/main/java/module-info.java
+++ b/scan-command/src/main/java/module-info.java
@@ -1,4 +1,4 @@
-module io.ballerina.scan{
+module io.ballerina.scan {
uses io.ballerina.scan.StaticCodeAnalysisPlatformPlugin;
requires io.ballerina.cli;
requires io.ballerina.lang;
@@ -10,4 +10,6 @@
requires org.apache.commons.io;
exports io.ballerina.scan;
+ exports io.ballerina.scan.internal to io.ballerina.scan.test;
+ exports io.ballerina.scan.utils to io.ballerina.scan.test;
}
diff --git a/scan-command/src/test/java/io/ballerina/scan/internal/ScanCmdTest.java b/scan-command/src/test/java/io/ballerina/scan/internal/ScanCmdTest.java
index 1a4eed5..e8a940c 100644
--- a/scan-command/src/test/java/io/ballerina/scan/internal/ScanCmdTest.java
+++ b/scan-command/src/test/java/io/ballerina/scan/internal/ScanCmdTest.java
@@ -105,8 +105,8 @@ void testScanCommandWithHelpFlag() throws IOException {
@Test(description = "test scan command with Ballerina project")
void testScanCommandProject() throws IOException {
- ScanCmd scanCmd = new ScanCmd(printStream);
System.setProperty("user.dir", validBalProject.toString());
+ ScanCmd scanCmd = new ScanCmd(printStream);
scanCmd.execute();
System.setProperty("user.dir", userDir);
String expected = "Running Scans";
@@ -116,8 +116,8 @@ void testScanCommandProject() throws IOException {
@Test(description = "test scan command with an empty Ballerina project")
void testScanCommandEmptyProject() throws IOException {
Path emptyBalProject = testResources.resolve("test-resources").resolve("empty-bal-project");
- ScanCmd scanCmd = new ScanCmd(printStream);
System.setProperty("user.dir", emptyBalProject.toString());
+ ScanCmd scanCmd = new ScanCmd(printStream);
scanCmd.execute();
System.setProperty("user.dir", userDir);
String expected = DiagnosticLog.error(DiagnosticCode.EMPTY_PACKAGE);
@@ -161,8 +161,8 @@ void testScanCommandSingleFileProjectWithDirectoryAsArgument() throws IOExceptio
@Test(description = "test scan command with single file project without arguments")
void testScanCommandSingleFileProjectWithoutArgument() throws IOException {
Path validBalProject = testResources.resolve("test-resources").resolve("valid-single-file-project");
- ScanCmd scanCmd = new ScanCmd(printStream);
System.setProperty("user.dir", validBalProject.toString());
+ ScanCmd scanCmd = new ScanCmd(printStream);
scanCmd.execute();
System.setProperty("user.dir", userDir);
String expected = "Invalid Ballerina package directory: " + validBalProject +
@@ -173,14 +173,18 @@ void testScanCommandSingleFileProjectWithoutArgument() throws IOException {
@Test(description = "test scan command with single file project with too many arguments")
void testScanCommandSingleFileProjectWithTooManyArguments() throws IOException {
Path validBalProject = testResources.resolve("test-resources").resolve("valid-single-file-project");
+ System.setProperty("user.dir", validBalProject.toString());
ScanCmd scanCmd = new ScanCmd(printStream);
String[] args = {"main.bal", "argument2"};
- new CommandLine(scanCmd).parseArgs(args);
- System.setProperty("user.dir", validBalProject.toString());
- scanCmd.execute();
+ try {
+ new CommandLine(scanCmd).parseArgs(args);
+ Assert.fail("Expected CommandLine.UnmatchedArgumentException");
+ } catch (CommandLine.UnmatchedArgumentException e) {
+ String expected = "picocli.CommandLine$UnmatchedArgumentException: " +
+ "Unmatched argument at index 1: 'argument2'";
+ Assert.assertEquals(e.toString(), expected);
+ }
System.setProperty("user.dir", userDir);
- String expected = DiagnosticLog.error(DiagnosticCode.INVALID_NUMBER_OF_ARGUMENTS, 2);
- Assert.assertEquals(readOutput(true).trim(), expected);
}
@Test(description = "test scan command with method for saving results to file when analysis issues are present")
@@ -245,12 +249,12 @@ void testPrintRulesToConsole() throws IOException {
@Test(description = "test scan command with list rules flag")
void testScanCommandWithListRulesFlag() throws IOException {
- ScanCmd scanCmd = new ScanCmd(printStream);
- String[] args = {"--list-rules"};
- new CommandLine(scanCmd).parseArgs(args);
Path ballerinaProject = testResources.resolve("test-resources")
.resolve("bal-project-with-config-file");
System.setProperty("user.dir", ballerinaProject.toString());
+ ScanCmd scanCmd = new ScanCmd(printStream);
+ String[] args = {"--list-rules"};
+ new CommandLine(scanCmd).parseArgs(args);
scanCmd.execute();
System.setProperty("user.dir", userDir);
String expected = getExpectedOutput("list-rules-output.txt");
@@ -335,12 +339,12 @@ Object[][] rulesProvider() {
@Test(description = "test scan command with include rules flag")
void testScanCommandWithIncludeRulesFlag() throws IOException {
- ScanCmd scanCmd = new ScanCmd(printStream);
- String[] args = {"--include-rules=ballerina:1"};
- new CommandLine(scanCmd).parseArgs(args);
Path ballerinaProject = testResources.resolve("test-resources")
.resolve("bal-project-with-analyzer-configurations");
System.setProperty("user.dir", ballerinaProject.toString());
+ ScanCmd scanCmd = new ScanCmd(printStream);
+ String[] args = {"--include-rules=ballerina:1"};
+ new CommandLine(scanCmd).parseArgs(args);
scanCmd.execute();
System.setProperty("user.dir", userDir);
String result = Files.readString(ballerinaProject.resolve("target").resolve("report")
@@ -352,12 +356,12 @@ void testScanCommandWithIncludeRulesFlag() throws IOException {
@Test(description = "test scan command with exclude rules flag")
void testScanCommandWithExcludeRulesFlag() throws IOException {
- ScanCmd scanCmd = new ScanCmd(printStream);
- String[] args = {"--exclude-rules=ballerina:1"};
- new CommandLine(scanCmd).parseArgs(args);
Path ballerinaProject = testResources.resolve("test-resources")
.resolve("bal-project-with-analyzer-configurations");
System.setProperty("user.dir", ballerinaProject.toString());
+ ScanCmd scanCmd = new ScanCmd(printStream);
+ String[] args = {"--exclude-rules=ballerina:1"};
+ new CommandLine(scanCmd).parseArgs(args);
scanCmd.execute();
System.setProperty("user.dir", userDir);
String result = Files.readString(ballerinaProject.resolve("target").resolve("report")
@@ -369,12 +373,12 @@ void testScanCommandWithExcludeRulesFlag() throws IOException {
@Test(description = "test scan command with include and exclude rules flag")
void testScanCommandWithIncludeAndExcludeRulesFlags() throws IOException {
- ScanCmd scanCmd = new ScanCmd(printStream);
- String[] args = {"--include-rules=ballerina:1", "--exclude-rules=ballerina:1"};
- new CommandLine(scanCmd).parseArgs(args);
Path ballerinaProject = testResources.resolve("test-resources")
.resolve("bal-project-with-analyzer-configurations");
System.setProperty("user.dir", ballerinaProject.toString());
+ ScanCmd scanCmd = new ScanCmd(printStream);
+ String[] args = {"--include-rules=ballerina:1", "--exclude-rules=ballerina:1"};
+ new CommandLine(scanCmd).parseArgs(args);
scanCmd.execute();
System.setProperty("user.dir", userDir);
String expected = getExpectedOutput("include-exclude-rules.txt");
@@ -383,10 +387,10 @@ void testScanCommandWithIncludeAndExcludeRulesFlags() throws IOException {
@Test(description = "test scan command with include rules Scan.toml configurations")
void testScanCommandWithIncludeRulesScanTomlConfigurations() throws IOException {
- ScanCmd scanCmd = new ScanCmd(printStream);
Path ballerinaProject = testResources.resolve("test-resources")
.resolve("bal-project-with-include-rule-configurations");
System.setProperty("user.dir", ballerinaProject.toString());
+ ScanCmd scanCmd = new ScanCmd(printStream);
scanCmd.execute();
System.setProperty("user.dir", userDir);
String result = Files.readString(ballerinaProject.resolve("target").resolve("report")
@@ -398,12 +402,12 @@ void testScanCommandWithIncludeRulesScanTomlConfigurations() throws IOException
@Test(description = "test scan command with exclude rules Scan.toml configurations")
void testScanCommandWithExcludeRulesScanTomlConfigurations() throws IOException {
- ScanCmd scanCmd = new ScanCmd(printStream);
- String[] args = {"--exclude-rules=ballerina:1"};
- new CommandLine(scanCmd).parseArgs(args);
Path ballerinaProject = testResources.resolve("test-resources")
.resolve("bal-project-with-exclude-rule-configurations");
System.setProperty("user.dir", ballerinaProject.toString());
+ ScanCmd scanCmd = new ScanCmd(printStream);
+ String[] args = {"--exclude-rules=ballerina:1"};
+ new CommandLine(scanCmd).parseArgs(args);
scanCmd.execute();
System.setProperty("user.dir", userDir);
String result = Files.readString(ballerinaProject.resolve("target").resolve("report")
@@ -415,12 +419,12 @@ void testScanCommandWithExcludeRulesScanTomlConfigurations() throws IOException
@Test(description = "test scan command with include and exclude rules Scan.toml configurations")
void testScanCommandWithIncludeAndExcludeRulesScanTomlConfigurations() throws IOException {
- ScanCmd scanCmd = new ScanCmd(printStream);
- String[] args = {"--include-rules=ballerina:1", "--exclude-rules=ballerina:1"};
- new CommandLine(scanCmd).parseArgs(args);
Path ballerinaProject = testResources.resolve("test-resources")
.resolve("bal-project-with-include-exclude-rule-configurations");
System.setProperty("user.dir", ballerinaProject.toString());
+ ScanCmd scanCmd = new ScanCmd(printStream);
+ String[] args = {"--include-rules=ballerina:1", "--exclude-rules=ballerina:1"};
+ new CommandLine(scanCmd).parseArgs(args);
scanCmd.execute();
System.setProperty("user.dir", userDir);
String expected = getExpectedOutput("toml-include-exclude-rules.txt");
@@ -429,7 +433,6 @@ void testScanCommandWithIncludeAndExcludeRulesScanTomlConfigurations() throws IO
@Test(description = "test scan command with platform plugin configurations")
void testScanCommandWithPlatformPluginConfigurations() throws IOException {
- ScanCmd scanCmd = new ScanCmd(printStream);
Path ballerinaProject = testResources.resolve("test-resources")
.resolve("bal-project-with-platform-configurations");
Path rootProject = Path.of(System.getProperty("user.dir")).getParent();
@@ -450,6 +453,7 @@ void testScanCommandWithPlatformPluginConfigurations() throws IOException {
StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
System.setProperty("user.dir", ballerinaProject.toString());
+ ScanCmd scanCmd = new ScanCmd(printStream);
scanCmd.execute();
System.setProperty("user.dir", userDir);
@@ -471,7 +475,6 @@ void testScanCommandWithPlatformPluginConfigurations() throws IOException {
@Test(description = "test scan command with invalid platform plugin configurations")
void testScanCommandWithInvalidPlatformPluginConfigurations() throws IOException {
- ScanCmd scanCmd = new ScanCmd(printStream);
Path ballerinaProject = testResources.resolve("test-resources")
.resolve("bal-project-with-invalid-platform-configurations");
Path rootProject = Path.of(System.getProperty("user.dir")).getParent();
@@ -491,6 +494,7 @@ void testScanCommandWithInvalidPlatformPluginConfigurations() throws IOException
StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
System.setProperty("user.dir", ballerinaProject.toString());
+ ScanCmd scanCmd = new ScanCmd(printStream);
scanCmd.execute();
System.setProperty("user.dir", userDir);
String expected = getExpectedOutput("invalid-platform-plugin-configurations.txt");
diff --git a/scan-command/tool-scan/BalTool.toml b/scan-command/tool-scan/BalTool.toml
index f6d8fba..dd7229e 100644
--- a/scan-command/tool-scan/BalTool.toml
+++ b/scan-command/tool-scan/BalTool.toml
@@ -2,4 +2,4 @@
id = "scan"
[[dependency]]
-path = "../build/libs/scan-command-0.9.1-SNAPSHOT.jar"
+path = "../build/libs/scan-command-0.10.0-SNAPSHOT.jar"
diff --git a/scan-command/tool-scan/Ballerina.toml b/scan-command/tool-scan/Ballerina.toml
index c2cba22..c7649f4 100644
--- a/scan-command/tool-scan/Ballerina.toml
+++ b/scan-command/tool-scan/Ballerina.toml
@@ -1,7 +1,7 @@
[package]
org = "ballerina"
name = "tool_scan"
-version = "0.9.1-SNAPSHOT"
+version = "0.10.0-SNAPSHOT"
distribution = "2201.12.0"
authors = ["Ballerina"]
keywords = ["scan", "static code analysis"]
diff --git a/scan-command/tool-scan/Dependencies.toml b/scan-command/tool-scan/Dependencies.toml
index 45439e1..f6476c4 100644
--- a/scan-command/tool-scan/Dependencies.toml
+++ b/scan-command/tool-scan/Dependencies.toml
@@ -10,7 +10,7 @@ distribution-version = "2201.12.0"
[[package]]
org = "ballerina"
name = "tool_scan"
-version = "0.9.1-SNAPSHOT"
+version = "0.10.0-SNAPSHOT"
modules = [
{org = "ballerina", packageName = "tool_scan", moduleName = "tool_scan"}
]
diff --git a/settings.gradle b/settings.gradle
index 7f7194a..252f6c1 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -32,6 +32,7 @@ pluginManagement {
rootProject.name = 'static-code-analysis-tool'
include 'scan-command'
include 'static-code-analysis-report'
+include 'scan-command-test-utils'
include 'test-compiler-plugins:exampleOrg-plugin'
include 'test-compiler-plugins:ballerina-plugin'
include 'test-compiler-plugins:ballerinax-plugin'