diff --git a/bin/codeclimate-sonar b/bin/codeclimate-sonar index 53d1fda..2450080 100755 --- a/bin/codeclimate-sonar +++ b/bin/codeclimate-sonar @@ -12,5 +12,6 @@ java \ -Djava.awt.headless=true \ -Dsonarlint.home="${BUILD_DIR}" \ -Dproject.home="${CODE_DIR}" \ + -Dconfig="/config.json" \ -Dorg.freemarker.loggerLibrary=none \ cc.App --src "**/*.java" $@ diff --git a/fixtures/multiple_paths/Main.java b/fixtures/multiple_paths/Main.java new file mode 100644 index 0000000..ebb7893 --- /dev/null +++ b/fixtures/multiple_paths/Main.java @@ -0,0 +1,9 @@ +import pkg1; + +public class Main { + public void main(String[] args) { + for (int k = 0; k < 20; i++) { // cause issue + System.out.println(new Class1()); + } + } +} \ No newline at end of file diff --git a/fixtures/multiple_paths/config.json b/fixtures/multiple_paths/config.json index 48e7a98..a3c992c 100644 --- a/fixtures/multiple_paths/config.json +++ b/fixtures/multiple_paths/config.json @@ -1,7 +1,7 @@ { "enabled": true, "include_paths": [ - "src/main/java/", + "src/included/", "Main.java" ] } diff --git a/fixtures/multiple_paths/src/excluded/java/pkg1/HasIssue.java b/fixtures/multiple_paths/src/excluded/java/pkg1/HasIssue.java new file mode 100644 index 0000000..f45794e --- /dev/null +++ b/fixtures/multiple_paths/src/excluded/java/pkg1/HasIssue.java @@ -0,0 +1,11 @@ +package pkg1; + +public class HasIssue { + public void method() { + for (int i = 0; i < 10; i++) { + for (int k = 0; k < 20; i++) { + System.out.println("Hello"); + } + } + } +} \ No newline at end of file diff --git a/fixtures/multiple_paths/src/included/java/pkg1/HasIssue.java b/fixtures/multiple_paths/src/included/java/pkg1/HasIssue.java new file mode 100644 index 0000000..b85a861 --- /dev/null +++ b/fixtures/multiple_paths/src/included/java/pkg1/HasIssue.java @@ -0,0 +1,11 @@ +package pkg1; + +class HasIssue { + public void method() { + for (int i = 0; i < 10; i++) { + for (int k = 0; k < 20; i++) { + System.out.println("Hello"); + } + } + } +} \ No newline at end of file diff --git a/fixtures/multiple_paths/src/included/java/pkg1/HasNoIssue.java b/fixtures/multiple_paths/src/included/java/pkg1/HasNoIssue.java new file mode 100644 index 0000000..281d5df --- /dev/null +++ b/fixtures/multiple_paths/src/included/java/pkg1/HasNoIssue.java @@ -0,0 +1,4 @@ +package pkg1; + +public class HasNoIssue { +} \ No newline at end of file diff --git a/fixtures/multiple_paths/src/test/java/Test.java b/fixtures/multiple_paths/src/test/java/Test.java new file mode 100644 index 0000000..0b8bc08 --- /dev/null +++ b/fixtures/multiple_paths/src/test/java/Test.java @@ -0,0 +1,2 @@ +public class Test { +} \ No newline at end of file diff --git a/src/main/java/cc/App.java b/src/main/java/cc/App.java index 5ebb316..fedb4c3 100644 --- a/src/main/java/cc/App.java +++ b/src/main/java/cc/App.java @@ -1,9 +1,14 @@ package cc; import org.sonarlint.cli.CustomMain; +import org.sonarlint.cli.util.System2; public class App { public static void main(String[] args) { - CustomMain.main(args); + execute(args, System2.INSTANCE); + } + + public static void execute(String[] args, System2 system) { + CustomMain.execute(args, system); } } diff --git a/src/main/java/cc/Config.java b/src/main/java/cc/Config.java index 16f7095..5b9a48d 100644 --- a/src/main/java/cc/Config.java +++ b/src/main/java/cc/Config.java @@ -5,15 +5,22 @@ import com.google.gson.GsonBuilder; import com.google.gson.stream.JsonReader; +import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; public class Config { - List<String> includePaths; + public List<String> includePaths = Arrays.asList(""); - public static Config from(String file) throws FileNotFoundException { - return gson().fromJson(new JsonReader(new FileReader(file)), Config.class); + public static Config from(String file) { + try { + return gson().fromJson(new JsonReader(new FileReader(file)), Config.class); + } catch (Exception e) { + return new Config(); + } } private static Gson gson() { diff --git a/src/main/java/cc/files/Collector.java b/src/main/java/cc/files/Collector.java new file mode 100644 index 0000000..2429da5 --- /dev/null +++ b/src/main/java/cc/files/Collector.java @@ -0,0 +1,39 @@ +package cc.files; + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.List; + +public class Collector extends SimpleFileVisitor<Path> { + final List<Path> files; + final Path baseDir; + + public Collector(Path baseDir) { + this.baseDir = baseDir; + this.files = new ArrayList<>(); + } + + public List<Path> getFiles() { + return files; + } + + @Override + public FileVisitResult visitFile(final Path file, BasicFileAttributes attrs) throws IOException { + files.add(file); + return super.visitFile(file, attrs); + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + if (Files.isHidden(dir)) { + return FileVisitResult.SKIP_SUBTREE; + } + + return super.preVisitDirectory(dir, attrs); + } +} diff --git a/src/main/java/cc/files/Finder.java b/src/main/java/cc/files/Finder.java new file mode 100644 index 0000000..6481ce4 --- /dev/null +++ b/src/main/java/cc/files/Finder.java @@ -0,0 +1,67 @@ +package cc.files; + +import org.sonarlint.cli.InputFileFinder; +import org.sonarsource.sonarlint.core.client.api.common.analysis.ClientInputFile; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import static java.nio.file.Files.isDirectory; + +public class Finder extends InputFileFinder { + final List<String> includedPaths; + final Charset charset; + final Matcher matcher; + + public Finder(List<String> includedPaths, String testsGlobPattern, Charset charset) { + super(null, testsGlobPattern, null, charset); + this.includedPaths = includedPaths; + this.charset = charset; + this.matcher = new Matcher(testsGlobPattern, charset); + } + + @Override + public List<ClientInputFile> collect(Path baseDir) throws IOException { + return findPaths(baseDir).stream() + .map(path -> toClientInputFile(baseDir, path)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + List<Path> findPaths(Path baseDir) throws IOException { + List<Path> paths = new ArrayList<>(); + for (String path : includedPaths) { + Path resolvedPath = baseDir.resolve(path); + if (isDirectory(resolvedPath)) { + paths.addAll(collectDir(baseDir, resolvedPath)); + } else { + paths.add(resolvedPath); + } + } + return paths; + } + + ClientInputFile toClientInputFile(Path baseDir, Path path) { + return createInputFile(path, isTest(baseDir, path)); + } + + boolean isTest(Path baseDir, Path path) { + return matcher.isTest(baseDir, path); + } + + ClientInputFile createInputFile(Path resolvedPath, boolean test) { + return new DefaultClientInputFile(resolvedPath, test, charset); + } + + List<Path> collectDir(Path baseDir, Path dir) throws IOException { + Collector collector = new Collector(baseDir); + Files.walkFileTree(dir, collector); + return collector.getFiles(); + } +} \ No newline at end of file diff --git a/src/main/java/cc/files/Matcher.java b/src/main/java/cc/files/Matcher.java new file mode 100644 index 0000000..99b3e2b --- /dev/null +++ b/src/main/java/cc/files/Matcher.java @@ -0,0 +1,40 @@ +package cc.files; + +import java.nio.charset.Charset; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.PathMatcher; + +public class Matcher { + static final String GLOB_PREFIX = "glob:"; + static PathMatcher REFUSE_ALL = p -> false; + + final PathMatcher testsMatcher; + final Charset charset; + + public Matcher(PathMatcher testsMatcher, Charset charset) { + this.testsMatcher = testsMatcher; + this.charset = charset; + } + + public Matcher(String testsGlobPattern, Charset charset) { + this(createPathMatcher(testsGlobPattern, REFUSE_ALL), charset); + } + + public boolean isTest(Path baseDir, Path absoluteFilePath) { + Path relativeFilePath = baseDir.relativize(absoluteFilePath); + return testsMatcher.matches(absoluteFilePath) || testsMatcher.matches(relativeFilePath); + } + + static PathMatcher createPathMatcher(String pattern, PathMatcher defaultMatcher) { + try { + if (pattern != null) { + return FileSystems.getDefault().getPathMatcher(GLOB_PREFIX + pattern); + } else { + return defaultMatcher; + } + } catch (Exception e) { + throw new RuntimeException("Error creating matcher with pattern: " + pattern, e); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/sonarlint/cli/CustomMain.java b/src/main/java/org/sonarlint/cli/CustomMain.java index d1f2e2e..fa4b029 100644 --- a/src/main/java/org/sonarlint/cli/CustomMain.java +++ b/src/main/java/org/sonarlint/cli/CustomMain.java @@ -19,6 +19,8 @@ */ package org.sonarlint.cli; +import cc.Config; +import cc.files.Finder; import cc.JsonReport; import org.sonarlint.cli.analysis.SonarLintFactory; import org.sonarlint.cli.config.ConfigurationReader; @@ -46,11 +48,7 @@ public CustomMain(Options opts, SonarLintFactory sonarLintFactory, ReportFactory super(opts, sonarLintFactory, reportFactory, fileFinder, projectHome); } - public static void main(String[] args) { - execute(args, System2.INSTANCE); - } - - static void execute(String[] args, System2 system) { + public static void execute(String[] args, System2 system) { Options parsedOpts; try { parsedOpts = Options.parse(args); @@ -74,7 +72,8 @@ static void execute(String[] args, System2 system) { return; } - InputFileFinder fileFinder = new InputFileFinder(parsedOpts.src(), parsedOpts.tests(), parsedOpts.exclusions(), charset); + Config config = Config.from(system.getProperty("config")); + InputFileFinder fileFinder = new Finder(config.includePaths, parsedOpts.tests(), charset); ReportFactory reportFactory = new CustomReportFactory(charset); ConfigurationReader reader = new ConfigurationReader(); SonarLintFactory sonarLintFactory = new SonarLintFactory(reader); @@ -102,4 +101,4 @@ public List<Reporter> createReporters(Path basePath) { return Arrays.asList(new JsonReport()); } } -} +} \ No newline at end of file diff --git a/src/test/java/cc/ConfigTest.java b/src/test/java/cc/ConfigTest.java index cce177c..402d4d5 100644 --- a/src/test/java/cc/ConfigTest.java +++ b/src/test/java/cc/ConfigTest.java @@ -9,6 +9,12 @@ public class ConfigTest { @Test public void include_paths() throws Exception { Config config = Config.from("fixtures/multiple_paths/config.json"); - assertThat(config.includePaths).containsOnly("Main.java", "src/main/java/"); + assertThat(config.includePaths).containsOnly("Main.java", "src/included/"); } -} + + @Test + public void default_config_path_include_base_dir() throws Exception { + Config config = new Config(); + assertThat(config.includePaths).containsOnly(""); + } +} \ No newline at end of file diff --git a/src/test/java/cc/files/FinderTest.java b/src/test/java/cc/files/FinderTest.java new file mode 100644 index 0000000..d726224 --- /dev/null +++ b/src/test/java/cc/files/FinderTest.java @@ -0,0 +1,71 @@ +package cc.files; + +import org.junit.Test; +import org.sonarsource.sonarlint.core.client.api.common.analysis.ClientInputFile; + +import java.nio.charset.Charset; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class FinderTest { + + @Test + public void find_files_in_directory() throws Exception { + Finder finder = new Finder(Arrays.asList("src/included/"), null, Charset.defaultCharset()); + + List<ClientInputFile> files = finder.collect(Paths.get("fixtures/multiple_paths")); + List<String> paths = files.stream().map(ClientInputFile::getPath).collect(Collectors.toList()); + + assertThat(paths).containsOnly( + "fixtures/multiple_paths/src/included/java/pkg1/HasIssue.java", + "fixtures/multiple_paths/src/included/java/pkg1/HasNoIssue.java" + ); + } + + @Test + public void find_specified_files() throws Exception { + Finder finder = new Finder(Arrays.asList("config.json", "Main.java"), null, Charset.defaultCharset()); + + List<ClientInputFile> files = finder.collect(Paths.get("fixtures/multiple_paths")); + List<String> paths = files.stream().map(ClientInputFile::getPath).collect(Collectors.toList()); + + assertThat(paths).containsOnly( + "fixtures/multiple_paths/Main.java", + "fixtures/multiple_paths/config.json" + ); + } + + @Test + public void find_from_multiple_locations() throws Exception { + Finder finder = new Finder(Arrays.asList("config.json", "src/included/java/"), null, Charset.defaultCharset()); + + List<ClientInputFile> files = finder.collect(Paths.get("fixtures/multiple_paths")); + List<String> paths = files.stream().map(ClientInputFile::getPath).collect(Collectors.toList()); + + assertThat(paths).containsOnly( + "fixtures/multiple_paths/config.json", + "fixtures/multiple_paths/src/included/java/pkg1/HasIssue.java", + "fixtures/multiple_paths/src/included/java/pkg1/HasNoIssue.java" + ); + } + + @Test + public void differentiate_src_and_test() throws Exception { + Finder finder = new Finder(Arrays.asList("src/included/", "src/test/"), "{**/test/**}", Charset.defaultCharset()); + + List<ClientInputFile> files = finder.collect(Paths.get("fixtures/multiple_paths")); + + assertThat(files.stream().filter(f -> !f.isTest()).map(ClientInputFile::getPath).collect(Collectors.toList())).containsOnly( + "fixtures/multiple_paths/src/included/java/pkg1/HasIssue.java", + "fixtures/multiple_paths/src/included/java/pkg1/HasNoIssue.java" + ); + assertThat(files.stream().filter(f -> f.isTest()).map(ClientInputFile::getPath).collect(Collectors.toList())).containsOnly( + "fixtures/multiple_paths/src/test/java/Test.java" + ); + } +} \ No newline at end of file diff --git a/src/test/java/integration/ConfigurationOptionsTest.java b/src/test/java/integration/ConfigurationOptionsTest.java new file mode 100644 index 0000000..f898e92 --- /dev/null +++ b/src/test/java/integration/ConfigurationOptionsTest.java @@ -0,0 +1,60 @@ +package integration; + +import cc.App; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.sonarlint.cli.SonarProperties; +import support.TestSystem; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ConfigurationOptionsTest { + ByteArrayOutputStream stdout; + ByteArrayOutputStream stderr; + + TestSystem system; + + @BeforeClass + public static void beforeAll() { + System.setProperty(SonarProperties.SONARLINT_HOME, "build"); + } + + @Before + public void setUp() throws Exception { + system = new TestSystem(); + stdout = new ByteArrayOutputStream(); + stderr = new ByteArrayOutputStream(); + + System.setOut(new PrintStream(stdout)); + System.setErr(new PrintStream(stderr)); + + system.setProperty(SonarProperties.PROJECT_HOME, "fixtures/multiple_paths"); + } + + @Test + public void limit_path_included_within_analysis() throws Exception { + system.setProperty("config", "fixtures/multiple_paths/config.json"); + + App.execute(new String[]{}, system); + + String output = stdout.toString(); + assertThat(output).contains("issue", "fixtures/multiple_paths/src/included/java/pkg1/HasIssue.java"); + assertThat(output).doesNotContain("fixtures/multiple_paths/src/excluded/java/pkg1/HasIssue.java"); + } + + @Test + public void include_all_files_by_default() throws Exception { + App.execute(new String[]{}, system); + + String output = stdout.toString(); + assertThat(output).contains( + "issue", + "fixtures/multiple_paths/src/included/java/pkg1/HasIssue.java", + "fixtures/multiple_paths/src/excluded/java/pkg1/HasIssue.java" + ); + } +} diff --git a/src/test/java/support/TestSystem.java b/src/test/java/support/TestSystem.java new file mode 100644 index 0000000..687030e --- /dev/null +++ b/src/test/java/support/TestSystem.java @@ -0,0 +1,34 @@ +package support; + +import org.sonarlint.cli.util.System2; + +import java.util.Properties; + +public class TestSystem extends System2 { + public int exitCode; + final Properties properties = new Properties(); + + @Override + public void exit(int exitCode) { + this.exitCode = exitCode; + } + + @Override + public Properties properties() { + return properties; + } + + @Override + public String property(String key) { + return properties.getProperty(key); + } + + @Override + public String getProperty(String key) { + return properties.getProperty(key); + } + + public void setProperty(String key, String value) { + properties.setProperty(key, value); + } +}