aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJanos Gyerik <janos.gyerik@sonarsource.com>2017-12-01 12:01:27 +0100
committerJanos Gyerik <janos.gyerik@sonarsource.com>2017-12-05 10:47:46 +0100
commit7fb4ae00fc9915a1377b46f558fde655805153f6 (patch)
tree60c6ef0a940c6081b7f0c05ed8f3607b7fc79948
parent734a658fc6fb58de2c807c646eaae5eb008adf69 (diff)
downloadsonarqube-7fb4ae00fc9915a1377b46f558fde655805153f6.tar.gz
sonarqube-7fb4ae00fc9915a1377b46f558fde655805153f6.zip
SONAR-10125 Add project relative path for all components
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputDir.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TestInputFileBuilder.java54
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java27
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ComponentsPublisherTest.java164
-rw-r--r--sonar-scanner-protocol/src/main/protobuf/scanner_report.proto5
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;