diff options
author | Janos Gyerik <janos.gyerik@sonarsource.com> | 2017-12-01 12:01:27 +0100 |
---|---|---|
committer | Janos Gyerik <janos.gyerik@sonarsource.com> | 2017-12-05 10:47:46 +0100 |
commit | 7fb4ae00fc9915a1377b46f558fde655805153f6 (patch) | |
tree | 60c6ef0a940c6081b7f0c05ed8f3607b7fc79948 | |
parent | 734a658fc6fb58de2c807c646eaae5eb008adf69 (diff) | |
download | sonarqube-7fb4ae00fc9915a1377b46f558fde655805153f6.tar.gz sonarqube-7fb4ae00fc9915a1377b46f558fde655805153f6.zip |
SONAR-10125 Add project relative path for all components
5 files changed, 229 insertions, 23 deletions
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputDir.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputDir.java index 79031632b08..0acef07079c 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputDir.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputDir.java @@ -84,7 +84,7 @@ public class DefaultInputDir extends DefaultInputComponent implements InputDir { } /** - * For testing purpose. Will be automaticall set when dir is added to {@link DefaultFileSystem} + * For testing purpose. Will be automatically set when dir is added to {@link DefaultFileSystem} */ public DefaultInputDir setModuleBaseDir(Path moduleBaseDir) { this.moduleBaseDir = moduleBaseDir.normalize(); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TestInputFileBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TestInputFileBuilder.java index 671790bdf43..3d93259b8b3 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TestInputFileBuilder.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TestInputFileBuilder.java @@ -23,9 +23,11 @@ import java.io.File; import java.io.IOException; import java.io.StringReader; import java.nio.charset.Charset; +import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.Paths; +import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.fs.InputFile; @@ -55,6 +57,8 @@ public class TestInputFileBuilder { private final int id; private final String relativePath; private final String moduleKey; + @CheckForNull + private Path projectBaseDir; private Path moduleBaseDir; private String language; private InputFile.Type type = InputFile.Type.MAIN; @@ -77,7 +81,7 @@ public class TestInputFileBuilder { } /** - * Create a InputFile with a given module key and module base directory. + * Create a InputFile with a given module key and module base directory. * The relative path is generated comparing the file path to the module base directory. * filePath must point to a file that is within the module base directory. */ @@ -108,13 +112,22 @@ public class TestInputFileBuilder { return batchId++; } + public TestInputFileBuilder setProjectBaseDir(Path projectBaseDir) { + this.projectBaseDir = normalize(projectBaseDir); + return this; + } + public TestInputFileBuilder setModuleBaseDir(Path moduleBaseDir) { + this.moduleBaseDir = normalize(moduleBaseDir); + return this; + } + + private static Path normalize(Path path) { try { - this.moduleBaseDir = moduleBaseDir.normalize().toRealPath(LinkOption.NOFOLLOW_LINKS); + return path.normalize().toRealPath(LinkOption.NOFOLLOW_LINKS); } catch (IOException e) { - this.moduleBaseDir = moduleBaseDir.normalize(); + return path.normalize(); } - return this; } public TestInputFileBuilder setLanguage(@Nullable String language) { @@ -192,7 +205,12 @@ public class TestInputFileBuilder { } public DefaultInputFile build() { - DefaultIndexedFile indexedFile = new DefaultIndexedFile(moduleBaseDir.resolve(relativePath), moduleKey, relativePath, relativePath, type, language, id, new SensorStrategy()); + Path absolutePath = moduleBaseDir.resolve(relativePath); + if (projectBaseDir == null) { + projectBaseDir = moduleBaseDir; + } + String projectRelativePath = projectBaseDir.relativize(absolutePath).toString(); + DefaultIndexedFile indexedFile = new DefaultIndexedFile(absolutePath, moduleKey, projectRelativePath, relativePath, type, language, id, new SensorStrategy()); DefaultInputFile inputFile = new DefaultInputFile(indexedFile, f -> f.setMetadata(new Metadata(lines, nonBlankLines, hash, originalLineOffsets, lastValidOffset)), contents); @@ -203,11 +221,35 @@ public class TestInputFileBuilder { } public static DefaultInputModule newDefaultInputModule(String moduleKey, File baseDir) { - ProjectDefinition definition = ProjectDefinition.create().setKey(moduleKey).setBaseDir(baseDir).setWorkDir(new File(baseDir, ".sonar")); + ProjectDefinition definition = ProjectDefinition.create() + .setKey(moduleKey) + .setBaseDir(baseDir) + .setWorkDir(new File(baseDir, ".sonar")); return newDefaultInputModule(definition); } public static DefaultInputModule newDefaultInputModule(ProjectDefinition projectDefinition) { return new DefaultInputModule(projectDefinition, TestInputFileBuilder.nextBatchId()); } + + public static DefaultInputModule newDefaultInputModule(DefaultInputModule parent, String key) throws IOException { + Path basedir = parent.getBaseDir().resolve(key); + Files.createDirectory(basedir); + return newDefaultInputModule(key, basedir.toFile()); + } + + public static DefaultInputDir newDefaultInputDir(DefaultInputModule module, String relativePath) throws IOException { + Path basedir = module.getBaseDir().resolve(relativePath); + Files.createDirectory(basedir); + return new DefaultInputDir(module.key(), relativePath) + .setModuleBaseDir(module.getBaseDir()); + } + + public static DefaultInputFile newDefaultInputFile(Path projectBaseDir, DefaultInputModule module, String relativePath) { + return new TestInputFileBuilder(module.key(), relativePath) + .setStatus(InputFile.Status.SAME) + .setProjectBaseDir(projectBaseDir) + .setModuleBaseDir(module.getBaseDir()) + .build(); + } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java index 9863ba0cdba..0426048478b 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java @@ -19,6 +19,7 @@ */ package org.sonar.scanner.report; +import java.nio.file.Path; import java.util.Collection; import java.util.stream.Collectors; import javax.annotation.CheckForNull; @@ -122,6 +123,11 @@ public class ComponentsPublisher implements ReportPublisherStep { String path = getPath(component); if (path != null) { builder.setPath(path); + + String projectRelativePath = getProjectRelativePath(component); + if (projectRelativePath != null) { + builder.setProjectRelativePath(projectRelativePath); + } } for (InputComponent child : children) { @@ -187,7 +193,26 @@ public class ComponentsPublisher implements ReportPublisherStep { InputModule module = (InputModule) component; return moduleHierarchy.relativePath(module); } - throw new IllegalStateException("Unkown component: " + component.getClass()); + throw new IllegalStateException("Unknown component: " + component.getClass()); + } + + @CheckForNull + private String getProjectRelativePath(DefaultInputComponent component) { + if (component instanceof InputFile) { + DefaultInputFile inputFile = (DefaultInputFile) component; + return inputFile.getProjectRelativePath(); + } + + Path projectBaseDir = moduleHierarchy.root().getBaseDir(); + if (component instanceof InputDir) { + InputDir inputDir = (InputDir) component; + return projectBaseDir.relativize(inputDir.path()).toString(); + } + if (component instanceof InputModule) { + DefaultInputModule module = (DefaultInputModule) component; + return projectBaseDir.relativize(module.getBaseDir()).toString(); + } + throw new IllegalStateException("Unknown component: " + component.getClass()); } private String getVersion(DefaultInputModule module) { diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ComponentsPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ComponentsPublisherTest.java index 1f017e7ada9..51922d73ade 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ComponentsPublisherTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ComponentsPublisherTest.java @@ -21,8 +21,12 @@ package org.sonar.scanner.report; import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -45,13 +49,15 @@ import org.sonar.scanner.protocol.output.ScannerReport.Component.FileStatus; import org.sonar.scanner.protocol.output.ScannerReport.ComponentLink.ComponentLinkType; import org.sonar.scanner.protocol.output.ScannerReportReader; import org.sonar.scanner.protocol.output.ScannerReportWriter; +import org.sonar.scanner.scan.DefaultComponentTree; +import org.sonar.scanner.scan.DefaultInputModuleHierarchy; import org.sonar.scanner.scan.branch.BranchConfiguration; import org.sonar.scanner.scan.branch.BranchType; -import org.sonar.scanner.scan.DefaultComponentTree; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.sonar.api.batch.fs.internal.TestInputFileBuilder.*; public class ComponentsPublisherTest { @Rule @@ -91,11 +97,12 @@ public class ComponentsPublisherTest { .setWorkDir(temp.newFolder()); DefaultInputModule root = new DefaultInputModule(rootDef, 1); + Path moduleBaseDir = temp.newFolder().toPath(); ProjectDefinition module1Def = ProjectDefinition.create() .setKey("module1") .setName("Module1") .setDescription("Module description") - .setBaseDir(temp.newFolder()) + .setBaseDir(moduleBaseDir.toFile()) .setWorkDir(temp.newFolder()); rootDef.addSubProject(module1Def); @@ -107,12 +114,20 @@ public class ComponentsPublisherTest { when(moduleHierarchy.parent(module1)).thenReturn(root); tree.index(module1, root); - DefaultInputDir dir = new DefaultInputDir("module1", "src", 3); + DefaultInputDir dir = new DefaultInputDir("module1", "src", 3) + .setModuleBaseDir(moduleBaseDir); tree.index(dir, module1); + DefaultInputDir dir2 = new DefaultInputDir("module1", "src2", 17) + .setModuleBaseDir(moduleBaseDir); + tree.index(dir2, module1); + DefaultInputFile file = new TestInputFileBuilder("module1", "src/Foo.java", 4).setLines(2).setStatus(InputFile.Status.SAME).build(); tree.index(file, dir); + DefaultInputFile file18 = new TestInputFileBuilder("module1", "src2/Foo.java", 18).setLines(2).setStatus(InputFile.Status.SAME).build(); + tree.index(file18, dir2); + DefaultInputFile file2 = new TestInputFileBuilder("module1", "src/Foo2.java", 5).setPublish(false).setLines(2).build(); tree.index(file2, dir); @@ -183,12 +198,13 @@ public class ComponentsPublisherTest { ProjectAnalysisInfo projectAnalysisInfo = mock(ProjectAnalysisInfo.class); when(projectAnalysisInfo.analysisDate()).thenReturn(DateUtils.parseDate("2012-12-12")); + Path moduleBaseDir = temp.newFolder().toPath(); ProjectDefinition rootDef = ProjectDefinition.create() .setKey("foo") .setProperty(CoreProperties.PROJECT_VERSION_PROPERTY, "1.0") .setName("Root project") .setDescription("Root description") - .setBaseDir(temp.newFolder()) + .setBaseDir(moduleBaseDir.toFile()) .setWorkDir(temp.newFolder()); DefaultInputModule root = new DefaultInputModule(rootDef, 1); @@ -197,15 +213,18 @@ public class ComponentsPublisherTest { when(moduleHierarchy.children(root)).thenReturn(Collections.emptyList()); // dir with files - DefaultInputDir dir = new DefaultInputDir("module1", "src", 2); + DefaultInputDir dir = new DefaultInputDir("module1", "src", 2) + .setModuleBaseDir(moduleBaseDir); tree.index(dir, root); // dir without files and issues - DefaultInputDir dir2 = new DefaultInputDir("module1", "src2", 3); + DefaultInputDir dir2 = new DefaultInputDir("module1", "src2", 3) + .setModuleBaseDir(moduleBaseDir); tree.index(dir2, root); // dir without files but has issues - DefaultInputDir dir3 = new DefaultInputDir("module1", "src3", 4); + DefaultInputDir dir3 = new DefaultInputDir("module1", "src3", 4) + .setModuleBaseDir(moduleBaseDir); tree.index(dir3, root); writeIssue(4); @@ -294,12 +313,13 @@ public class ComponentsPublisherTest { ProjectAnalysisInfo projectAnalysisInfo = mock(ProjectAnalysisInfo.class); when(projectAnalysisInfo.analysisDate()).thenReturn(DateUtils.parseDate("2012-12-12")); + Path moduleBaseDir = temp.newFolder().toPath(); ProjectDefinition rootDef = ProjectDefinition.create() .setKey("foo") .setProperty(CoreProperties.PROJECT_VERSION_PROPERTY, "1.0") .setName("Root project") .setDescription("Root description") - .setBaseDir(temp.newFolder()) + .setBaseDir(moduleBaseDir.toFile()) .setWorkDir(temp.newFolder()); DefaultInputModule root = new DefaultInputModule(rootDef, 1); @@ -308,15 +328,18 @@ public class ComponentsPublisherTest { when(moduleHierarchy.children(root)).thenReturn(Collections.emptyList()); // dir with changed files - DefaultInputDir dir = new DefaultInputDir("module1", "src", 2); + DefaultInputDir dir = new DefaultInputDir("module1", "src", 2) + .setModuleBaseDir(moduleBaseDir); tree.index(dir, root); // dir without changed files or issues - DefaultInputDir dir2 = new DefaultInputDir("module1", "src2", 3); + DefaultInputDir dir2 = new DefaultInputDir("module1", "src2", 3) + .setModuleBaseDir(moduleBaseDir); tree.index(dir2, root); // dir without changed files but has issues - DefaultInputDir dir3 = new DefaultInputDir("module1", "src3", 4); + DefaultInputDir dir3 = new DefaultInputDir("module1", "src3", 4) + .setModuleBaseDir(moduleBaseDir); tree.index(dir3, root); writeIssue(4); @@ -366,10 +389,11 @@ public class ComponentsPublisherTest { .setWorkDir(temp.newFolder()); DefaultInputModule root = new DefaultInputModule(rootDef, 1); + Path moduleBaseDir = temp.newFolder().toPath(); ProjectDefinition module1Def = ProjectDefinition.create() .setKey("module1") .setDescription("Module description") - .setBaseDir(temp.newFolder()) + .setBaseDir(moduleBaseDir.toFile()) .setWorkDir(temp.newFolder()); rootDef.addSubProject(module1Def); DefaultInputModule module1 = new DefaultInputModule(module1Def, 2); @@ -379,7 +403,8 @@ public class ComponentsPublisherTest { when(moduleHierarchy.children(root)).thenReturn(Collections.singleton(module1)); tree.index(module1, root); - DefaultInputDir dir = new DefaultInputDir("module1", "src", 3); + DefaultInputDir dir = new DefaultInputDir("module1", "src", 3) + .setModuleBaseDir(moduleBaseDir); tree.index(dir, module1); DefaultInputFile file = new TestInputFileBuilder("module1", "src/Foo.java", 4).setLines(2).setStatus(InputFile.Status.SAME).build(); @@ -435,12 +460,13 @@ public class ComponentsPublisherTest { .setWorkDir(temp.newFolder()); DefaultInputModule root = new DefaultInputModule(rootDef, 1); + Path moduleBaseDir = temp.newFolder().toPath(); ProjectDefinition module1Def = ProjectDefinition.create() .setKey("module1") .setName("Module1") .setProperty(CoreProperties.LINKS_CI, "http://ci") .setDescription("Module description") - .setBaseDir(temp.newFolder()) + .setBaseDir(moduleBaseDir.toFile()) .setWorkDir(temp.newFolder()); rootDef.addSubProject(module1Def); DefaultInputModule module1 = new DefaultInputModule(module1Def, 2); @@ -451,7 +477,8 @@ public class ComponentsPublisherTest { when(moduleHierarchy.parent(module1)).thenReturn(root); tree.index(module1, root); - DefaultInputDir dir = new DefaultInputDir("module1", "src", 3); + DefaultInputDir dir = new DefaultInputDir("module1", "src", 3) + .setModuleBaseDir(moduleBaseDir); tree.index(dir, module1); DefaultInputFile file = new TestInputFileBuilder("module1", "src/Foo.java", 4).setLines(2).setStatus(InputFile.Status.SAME).build(); @@ -473,4 +500,111 @@ public class ComponentsPublisherTest { assertThat(module1Protobuf.getLink(0).getType()).isEqualTo(ComponentLinkType.CI); assertThat(module1Protobuf.getLink(0).getHref()).isEqualTo("http://ci"); } + + @Test + public void add_components_with_correct_project_relative_path() throws Exception { + Map<DefaultInputModule, DefaultInputModule> parents = new HashMap<>(); + + DefaultInputModule root = newDefaultInputModule("foo", temp.newFolder()); + + DefaultInputFile file = newDefaultInputFile(root.getBaseDir(), root, "Foo.java"); + tree.index(file, root); + + DefaultInputDir dir1 = newDefaultInputDir(root, "dir1"); + tree.index(dir1, root); + + DefaultInputFile dir1_file = newDefaultInputFile(root.getBaseDir(), root, "dir1/Foo.java"); + tree.index(dir1_file, dir1); + + DefaultInputDir dir1_dir1 = newDefaultInputDir(root, "dir1/dir1"); + tree.index(dir1_dir1, dir1); + + DefaultInputFile dir1_dir1_file = newDefaultInputFile(root.getBaseDir(), root, "dir1/dir1/Foo.java"); + tree.index(dir1_dir1_file, dir1_dir1); + + // module in root + + DefaultInputModule mod1 = newDefaultInputModule(root, "mod1"); + parents.put(mod1, root); + tree.index(mod1, root); + + DefaultInputFile mod1_file = newDefaultInputFile(root.getBaseDir(), mod1, "Foo.java"); + tree.index(mod1_file, mod1); + + DefaultInputDir mod1_dir2 = newDefaultInputDir(mod1, "dir2"); + tree.index(mod1_dir2, mod1); + + DefaultInputFile mod1_dir2_file = newDefaultInputFile(root.getBaseDir(), mod1, "dir2/Foo.java"); + tree.index(mod1_dir2_file, mod1_dir2); + + // module in module + + DefaultInputModule mod1_mod2 = newDefaultInputModule(mod1, "mod2"); + parents.put(mod1_mod2, mod1); + tree.index(mod1_mod2, mod1); + + DefaultInputFile mod1_mod2_file = newDefaultInputFile(root.getBaseDir(), mod1_mod2, "Foo.java"); + tree.index(mod1_mod2_file, mod1_mod2); + + DefaultInputDir mod1_mod2_dir = newDefaultInputDir(mod1_mod2, "dir"); + tree.index(mod1_mod2_dir, mod1_mod2); + + DefaultInputFile mod1_mod2_dir_file = newDefaultInputFile(root.getBaseDir(), mod1_mod2, "dir/Foo.java"); + tree.index(mod1_mod2_dir_file, mod1_mod2_dir); + + moduleHierarchy = new DefaultInputModuleHierarchy(parents); + + ComponentsPublisher publisher = new ComponentsPublisher(moduleHierarchy, tree, branchConfiguration); + publisher.publish(writer); + + ScannerReportReader reader = new ScannerReportReader(outputDir); + + // project root + assertThat(reader.readComponent(root.batchId()).getPath()).isEmpty(); + assertThat(reader.readComponent(root.batchId()).getProjectRelativePath()).isEmpty(); + + // file in root + assertThat(reader.readComponent(file.batchId()).getPath()).isEqualTo("Foo.java"); + assertThat(reader.readComponent(file.batchId()).getProjectRelativePath()).isEqualTo("Foo.java"); + + // dir in root + assertThat(reader.readComponent(dir1.batchId()).getPath()).isEqualTo("dir1"); + assertThat(reader.readComponent(dir1.batchId()).getProjectRelativePath()).isEqualTo("dir1"); + + // file in dir in root + assertThat(reader.readComponent(dir1_file.batchId()).getPath()).isEqualTo("dir1/Foo.java"); + assertThat(reader.readComponent(dir1_file.batchId()).getProjectRelativePath()).isEqualTo("dir1/Foo.java"); + + // dir in dir in root + assertThat(reader.readComponent(dir1_dir1.batchId()).getPath()).isEqualTo("dir1/dir1"); + assertThat(reader.readComponent(dir1_dir1.batchId()).getProjectRelativePath()).isEqualTo("dir1/dir1"); + + // module in root + assertThat(reader.readComponent(mod1.batchId()).getPath()).isEqualTo("mod1"); + assertThat(reader.readComponent(mod1.batchId()).getProjectRelativePath()).isEqualTo("mod1"); + + // dir in module in root + assertThat(reader.readComponent(mod1_dir2.batchId()).getPath()).isEqualTo("dir2"); + assertThat(reader.readComponent(mod1_dir2.batchId()).getProjectRelativePath()).isEqualTo("mod1/dir2"); + + // file in dir in module in root + assertThat(reader.readComponent(mod1_dir2_file.batchId()).getPath()).isEqualTo("dir2/Foo.java"); + assertThat(reader.readComponent(mod1_dir2_file.batchId()).getProjectRelativePath()).isEqualTo("mod1/dir2/Foo.java"); + + // module in module + assertThat(reader.readComponent(mod1_mod2.batchId()).getPath()).isEqualTo("mod2"); + assertThat(reader.readComponent(mod1_mod2.batchId()).getProjectRelativePath()).isEqualTo("mod1/mod2"); + + // file in module in module + assertThat(reader.readComponent(mod1_mod2_file.batchId()).getPath()).isEqualTo("Foo.java"); + assertThat(reader.readComponent(mod1_mod2_file.batchId()).getProjectRelativePath()).isEqualTo("mod1/mod2/Foo.java"); + + // dir in module in module + assertThat(reader.readComponent(mod1_mod2_dir.batchId()).getPath()).isEqualTo("dir"); + assertThat(reader.readComponent(mod1_mod2_dir.batchId()).getProjectRelativePath()).isEqualTo("mod1/mod2/dir"); + + // file in dir in module in module + assertThat(reader.readComponent(mod1_mod2_dir_file.batchId()).getPath()).isEqualTo("dir/Foo.java"); + assertThat(reader.readComponent(mod1_mod2_dir_file.batchId()).getProjectRelativePath()).isEqualTo("mod1/mod2/dir/Foo.java"); + } } diff --git a/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto b/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto index e939fa8855e..75f915b9501 100644 --- a/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto +++ b/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto @@ -94,6 +94,8 @@ message ComponentLink { message Component { int32 ref = 1; + + // Path relative to module base directory string path = 2; string name = 3; ComponentType type = 4; @@ -111,6 +113,9 @@ message Component { // Only available on PROJECT and MODULE types string description = 12; FileStatus status = 13; + + // Path relative to project base directory + string project_relative_path = 14; enum ComponentType { UNSET = 0; |