@@ -19,15 +19,18 @@ | |||
*/ | |||
package org.sonar.server.batch; | |||
import com.google.common.collect.Maps; | |||
import java.util.Date; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import java.util.stream.Collectors; | |||
import org.sonar.api.server.ws.Change; | |||
import org.sonar.api.server.ws.Request; | |||
import org.sonar.api.server.ws.Response; | |||
import org.sonar.api.server.ws.WebService; | |||
import org.sonar.scanner.protocol.input.FileData; | |||
import org.sonar.scanner.protocol.input.MultiModuleProjectRepository; | |||
import org.sonar.scanner.protocol.input.ProjectRepositories; | |||
import org.sonar.scanner.protocol.input.SingleProjectRepository; | |||
import org.sonarqube.ws.Batch.WsProjectResponse; | |||
import org.sonarqube.ws.Batch.WsProjectResponse.FileData.Builder; | |||
@@ -109,33 +112,31 @@ public class ProjectAction implements BatchWsAction { | |||
WsProjectResponse.Builder response = WsProjectResponse.newBuilder(); | |||
setNullable(data.lastAnalysisDate(), response::setLastAnalysisDate, Date::getTime); | |||
response.setTimestamp(data.timestamp()); | |||
response.getMutableFileDataByModuleAndPath() | |||
.putAll(buildFileDataByModuleAndPath(data)); | |||
if (data instanceof SingleProjectRepository) { | |||
response.putAllFileDataByPath(buildFileDataByPath((SingleProjectRepository) data)); | |||
} else { | |||
response.putAllFileDataByModuleAndPath(buildFileDataByModuleAndPath((MultiModuleProjectRepository) data)); | |||
} | |||
return response.build(); | |||
} | |||
private static Map<String, WsProjectResponse.FileDataByPath> buildFileDataByModuleAndPath(ProjectRepositories data) { | |||
Map<String, WsProjectResponse.FileDataByPath> fileDataByModuleAndPathResponse = new HashMap<>(); | |||
for (Map.Entry<String, Map<String, FileData>> moduleAndFileDataByPathEntry : data.fileDataByModuleAndPath().entrySet()) { | |||
fileDataByModuleAndPathResponse.put( | |||
moduleAndFileDataByPathEntry.getKey(), | |||
buildFileDataByPath(moduleAndFileDataByPathEntry.getValue())); | |||
} | |||
private static Map<String, WsProjectResponse.FileDataByPath> buildFileDataByModuleAndPath(MultiModuleProjectRepository data) { | |||
return data.repositoriesByModule().entrySet() | |||
.stream() | |||
.map(entry -> Maps.immutableEntry(entry.getKey(), buildFileDataByPath(entry.getValue().fileData()))) | |||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); | |||
} | |||
return fileDataByModuleAndPathResponse; | |||
private static Map<String, WsProjectResponse.FileData> buildFileDataByPath(SingleProjectRepository data) { | |||
return data.fileData().entrySet() | |||
.stream() | |||
.collect(Collectors.toMap(Map.Entry::getKey, e -> toFileDataResponse(e.getValue()))); | |||
} | |||
private static WsProjectResponse.FileDataByPath buildFileDataByPath(Map<String, FileData> fileDataByPath) { | |||
WsProjectResponse.FileDataByPath.Builder response = WsProjectResponse.FileDataByPath.newBuilder(); | |||
Map<String, WsProjectResponse.FileData> fileDataByPathResponse = response.getMutableFileDataByPath(); | |||
for (Map.Entry<String, FileData> pathFileDataEntry : fileDataByPath.entrySet()) { | |||
fileDataByPathResponse.put( | |||
pathFileDataEntry.getKey(), | |||
toFileDataResponse(pathFileDataEntry.getValue())); | |||
} | |||
fileDataByPath.forEach((key, value) -> response.putFileDataByPath(key, toFileDataResponse(value))); | |||
return response.build(); | |||
} | |||
@@ -32,7 +32,9 @@ import org.sonar.db.component.ComponentDto; | |||
import org.sonar.db.component.FilePathWithHashDto; | |||
import org.sonar.db.permission.OrganizationPermission; | |||
import org.sonar.scanner.protocol.input.FileData; | |||
import org.sonar.scanner.protocol.input.MultiModuleProjectRepository; | |||
import org.sonar.scanner.protocol.input.ProjectRepositories; | |||
import org.sonar.scanner.protocol.input.SingleProjectRepository; | |||
import org.sonar.server.component.ComponentFinder; | |||
import org.sonar.server.exceptions.ForbiddenException; | |||
import org.sonar.server.user.UserSession; | |||
@@ -56,7 +58,6 @@ public class ProjectDataLoader { | |||
public ProjectRepositories load(ProjectDataQuery query) { | |||
try (DbSession session = dbClient.openSession(false)) { | |||
ProjectRepositories data = new ProjectRepositories(); | |||
String projectKey = query.getProjectKey(); | |||
String branch = query.getBranch(); | |||
String pullRequest = query.getPullRequest(); | |||
@@ -72,12 +73,23 @@ public class ProjectDataLoader { | |||
List<ComponentDto> modulesTree = dbClient.componentDao().selectEnabledDescendantModules(session, branchOrMainModule.uuid()); | |||
List<FilePathWithHashDto> files = searchFilesWithHashAndRevision(session, branchOrMainModule); | |||
addFileData(data, modulesTree, files); | |||
// MMF-365 we still have to support multi-module projects because it's not possible to transform from logical to | |||
// physical structure for some multi-module projects | |||
ProjectRepositories data; | |||
if (modulesTree.size() > 1) { | |||
MultiModuleProjectRepository repository = new MultiModuleProjectRepository(); | |||
addFileDataPerModule(repository, modulesTree, files); | |||
data = repository; | |||
} else { | |||
SingleProjectRepository repository = new SingleProjectRepository(); | |||
addFileData(repository, files); | |||
data = repository; | |||
} | |||
// FIXME need real value but actually only used to know if there is a previous analysis in local issue tracking mode so any value is | |||
// ok | |||
data.setLastAnalysisDate(new Date()); | |||
return data; | |||
} | |||
} | |||
@@ -90,7 +102,7 @@ public class ProjectDataLoader { | |||
: dbClient.componentDao().selectEnabledDescendantFiles(session, module.uuid()); | |||
} | |||
private static void addFileData(ProjectRepositories data, List<ComponentDto> moduleChildren, List<FilePathWithHashDto> files) { | |||
private static void addFileDataPerModule(MultiModuleProjectRepository data, List<ComponentDto> moduleChildren, List<FilePathWithHashDto> files) { | |||
Map<String, String> moduleKeysByUuid = newHashMap(); | |||
for (ComponentDto module : moduleChildren) { | |||
moduleKeysByUuid.put(module.uuid(), module.getKey()); | |||
@@ -98,7 +110,14 @@ public class ProjectDataLoader { | |||
for (FilePathWithHashDto file : files) { | |||
FileData fileData = new FileData(file.getSrcHash(), file.getRevision()); | |||
data.addFileData(moduleKeysByUuid.get(file.getModuleUuid()), file.getPath(), fileData); | |||
data.addFileDataToModule(moduleKeysByUuid.get(file.getModuleUuid()), file.getPath(), fileData); | |||
} | |||
} | |||
private static void addFileData(SingleProjectRepository data, List<FilePathWithHashDto> files) { | |||
for (FilePathWithHashDto file : files) { | |||
FileData fileData = new FileData(file.getSrcHash(), file.getRevision()); | |||
data.addFileData(file.getPath(), fileData); | |||
} | |||
} | |||
@@ -22,7 +22,9 @@ package org.sonar.server.batch; | |||
import org.junit.Test; | |||
import org.mockito.ArgumentCaptor; | |||
import org.sonar.scanner.protocol.input.FileData; | |||
import org.sonar.scanner.protocol.input.MultiModuleProjectRepository; | |||
import org.sonar.scanner.protocol.input.ProjectRepositories; | |||
import org.sonar.scanner.protocol.input.SingleProjectRepository; | |||
import org.sonar.server.ws.TestResponse; | |||
import org.sonar.server.ws.WsActionTester; | |||
import org.sonarqube.ws.Batch.WsProjectResponse; | |||
@@ -42,7 +44,7 @@ public class ProjectActionTest { | |||
public void project_referentials() { | |||
String projectKey = "org.codehaus.sonar:sonar"; | |||
ProjectRepositories projectReferentials = mock(ProjectRepositories.class); | |||
ProjectRepositories projectReferentials = mock(SingleProjectRepository.class); | |||
ArgumentCaptor<ProjectDataQuery> queryArgumentCaptor = ArgumentCaptor.forClass(ProjectDataQuery.class); | |||
when(projectDataLoader.load(queryArgumentCaptor.capture())).thenReturn(projectReferentials); | |||
@@ -53,7 +55,7 @@ public class ProjectActionTest { | |||
.setParam("profile", "Default") | |||
.setParam("preview", "false") | |||
.execute(); | |||
assertJson(response.getInput()).isSimilarTo("{\"fileDataByModuleAndPath\": {}}"); | |||
assertJson(response.getInput()).isSimilarTo("{\"fileDataByPath\": {}}"); | |||
assertThat(queryArgumentCaptor.getValue().getProjectKey()).isEqualTo(projectKey); | |||
assertThat(queryArgumentCaptor.getValue().getProfileName()).isEqualTo("Default"); | |||
@@ -68,13 +70,55 @@ public class ProjectActionTest { | |||
public void do_not_fail_when_a_path_is_null() { | |||
String projectKey = "org.codehaus.sonar:sonar"; | |||
ProjectRepositories projectRepositories = new ProjectRepositories().addFileData("module-1", null, new FileData(null, null)); | |||
ProjectRepositories projectRepositories = new MultiModuleProjectRepository() | |||
.addFileDataToModule("module-1", null, new FileData(null, null)); | |||
when(projectDataLoader.load(any(ProjectDataQuery.class))).thenReturn(projectRepositories); | |||
WsProjectResponse wsProjectResponse = ws.newRequest() | |||
.setParam("key", projectKey) | |||
.setParam("profile", "Default") | |||
.executeProtobuf(WsProjectResponse.class); | |||
assertThat(wsProjectResponse.getFileDataByModuleAndPath()).isEmpty(); | |||
assertThat(wsProjectResponse.getFileDataByModuleAndPathMap()).isEmpty(); | |||
} | |||
@Test | |||
public void use_new_file_structure_for_projects_without_submodules() { | |||
String projectKey = "org.codehaus.sonar:sonar"; | |||
ProjectRepositories projectRepositories = new SingleProjectRepository() | |||
.addFileData("src/main/java/SomeClass.java", new FileData("789456", "123456789")); | |||
when(projectDataLoader.load(any(ProjectDataQuery.class))).thenReturn(projectRepositories); | |||
WsProjectResponse wsProjectResponse = ws.newRequest() | |||
.setParam("key", projectKey) | |||
.setParam("profile", "Default") | |||
.executeProtobuf(WsProjectResponse.class); | |||
assertThat(wsProjectResponse.getFileDataByModuleAndPathMap()).isEmpty(); | |||
assertThat(wsProjectResponse.getFileDataByPathCount()).isEqualTo(1); | |||
assertThat(wsProjectResponse.getFileDataByPathMap().get("src/main/java/SomeClass.java")).isNotNull(); | |||
} | |||
@Test | |||
public void use_old_file_structure_for_projects_with_submodules() { | |||
String projectKey = "org.codehaus.sonar:sonar"; | |||
ProjectRepositories projectRepositories = new MultiModuleProjectRepository() | |||
.addFileDataToModule("module-1", "src/main/java/SomeClass.java", new FileData("789456", "123456789")); | |||
when(projectDataLoader.load(any(ProjectDataQuery.class))).thenReturn(projectRepositories); | |||
WsProjectResponse wsProjectResponse = ws.newRequest() | |||
.setParam("key", projectKey) | |||
.setParam("profile", "Default") | |||
.executeProtobuf(WsProjectResponse.class); | |||
assertThat(wsProjectResponse.getFileDataByPathMap()).isEmpty(); | |||
assertThat(wsProjectResponse.getFileDataByModuleAndPathCount()).isEqualTo(1); | |||
WsProjectResponse.FileDataByPath moduleData = wsProjectResponse.getFileDataByModuleAndPathMap().get("module-1"); | |||
assertThat(moduleData).isNotNull(); | |||
assertThat(moduleData.getFileDataByPathCount()).isEqualTo(1); | |||
WsProjectResponse.FileData fileData = moduleData.getFileDataByPathMap().get("src/main/java/SomeClass.java"); | |||
assertThat(fileData).isNotNull(); | |||
assertThat(fileData.getHash()).isEqualTo("789456"); | |||
assertThat(fileData.getRevision()).isEqualTo("123456789"); | |||
} | |||
} |
@@ -34,7 +34,9 @@ import org.sonar.db.component.ResourceTypesRule; | |||
import org.sonar.db.organization.OrganizationDto; | |||
import org.sonar.db.source.FileSourceDto; | |||
import org.sonar.scanner.protocol.input.FileData; | |||
import org.sonar.scanner.protocol.input.MultiModuleProjectRepository; | |||
import org.sonar.scanner.protocol.input.ProjectRepositories; | |||
import org.sonar.scanner.protocol.input.SingleProjectRepository; | |||
import org.sonar.server.component.ComponentFinder; | |||
import org.sonar.server.exceptions.BadRequestException; | |||
import org.sonar.server.exceptions.ForbiddenException; | |||
@@ -44,6 +46,7 @@ import org.sonar.server.tester.UserSessionRule; | |||
import static com.google.common.collect.ImmutableList.of; | |||
import static java.lang.String.format; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.junit.Assert.assertTrue; | |||
import static org.junit.Assert.fail; | |||
import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION; | |||
import static org.sonar.db.component.ComponentTesting.newFileDto; | |||
@@ -89,8 +92,11 @@ public class ProjectDataLoaderTest { | |||
ProjectRepositories ref = underTest.load(ProjectDataQuery.create().setProjectKey(project.getKey())); | |||
assertThat(ref.fileDataByPath(project.getKey())).hasSize(1); | |||
FileData fileData = ref.fileData(project.getKey(), file.path()); | |||
assertTrue(ref instanceof SingleProjectRepository); | |||
SingleProjectRepository singleProjectRepository = ((SingleProjectRepository) ref); | |||
assertThat(singleProjectRepository.fileData()).hasSize(1); | |||
FileData fileData = singleProjectRepository.fileDataByPath(file.path()); | |||
assertThat(fileData).isNotNull(); | |||
assertThat(fileData.hash()).isEqualTo("123456"); | |||
} | |||
@@ -110,8 +116,10 @@ public class ProjectDataLoaderTest { | |||
ProjectRepositories ref = underTest.load(ProjectDataQuery.create().setProjectKey(project.getKey())); | |||
assertThat(ref.fileData(project.getKey(), projectFile.path()).hash()).isEqualTo("123456"); | |||
assertThat(ref.fileData(module.getKey(), moduleFile.path()).hash()).isEqualTo("789456"); | |||
assertTrue(ref instanceof MultiModuleProjectRepository); | |||
MultiModuleProjectRepository repository = ((MultiModuleProjectRepository) ref); | |||
assertThat(repository.fileData(project.getKey(), projectFile.path()).hash()).isEqualTo("123456"); | |||
assertThat(repository.fileData(module.getKey(), moduleFile.path()).hash()).isEqualTo("789456"); | |||
} | |||
@Test | |||
@@ -133,8 +141,10 @@ public class ProjectDataLoaderTest { | |||
.setProjectKey(project.getKey()) | |||
.setBranch("my_branch")); | |||
assertThat(ref.fileData(branch.getKey(), projectFile.path()).hash()).isEqualTo("123456"); | |||
assertThat(ref.fileData(moduleBranch.getKey(), moduleFile.path()).hash()).isEqualTo("789456"); | |||
assertTrue(ref instanceof MultiModuleProjectRepository); | |||
MultiModuleProjectRepository repository = ((MultiModuleProjectRepository) ref); | |||
assertThat(repository.fileData(branch.getKey(), projectFile.path()).hash()).isEqualTo("123456"); | |||
assertThat(repository.fileData(moduleBranch.getKey(), moduleFile.path()).hash()).isEqualTo("789456"); | |||
} | |||
@Test |
@@ -20,12 +20,11 @@ | |||
package org.sonar.scanner.repository; | |||
import com.google.common.base.Throwables; | |||
import com.google.common.collect.HashBasedTable; | |||
import com.google.common.collect.Table; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.net.HttpURLConnection; | |||
import java.util.Date; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import javax.annotation.Nullable; | |||
import org.apache.commons.io.IOUtils; | |||
@@ -34,9 +33,7 @@ import org.slf4j.LoggerFactory; | |||
import org.sonar.api.utils.MessageException; | |||
import org.sonar.scanner.bootstrap.ScannerWsClient; | |||
import org.sonar.scanner.util.ScannerUtils; | |||
import org.sonarqube.ws.Batch; | |||
import org.sonarqube.ws.Batch.WsProjectResponse; | |||
import org.sonarqube.ws.Batch.WsProjectResponse.FileDataByPath; | |||
import org.sonarqube.ws.client.GetRequest; | |||
import org.sonarqube.ws.client.HttpException; | |||
import org.sonarqube.ws.client.WsResponse; | |||
@@ -62,7 +59,7 @@ public class DefaultProjectRepositoriesLoader implements ProjectRepositoriesLoad | |||
} | |||
LOG.debug("Project repository not available - continuing without it"); | |||
return new ProjectRepositories(); | |||
return new SingleProjectRepository(); | |||
} | |||
} | |||
@@ -97,22 +94,32 @@ public class DefaultProjectRepositoriesLoader implements ProjectRepositoriesLoad | |||
private static ProjectRepositories processStream(InputStream is, String projectKey) { | |||
try { | |||
WsProjectResponse response = WsProjectResponse.parseFrom(is); | |||
Table<String, String, FileData> fileDataTable = HashBasedTable.create(); | |||
Map<String, FileDataByPath> fileDataByModuleAndPath = response.getFileDataByModuleAndPath(); | |||
for (Map.Entry<String, FileDataByPath> e1 : fileDataByModuleAndPath.entrySet()) { | |||
for (Map.Entry<String, Batch.WsProjectResponse.FileData> e2 : e1.getValue().getFileDataByPath().entrySet()) { | |||
FileData fd = new FileData(e2.getValue().getHash(), e2.getValue().getRevision()); | |||
fileDataTable.put(e1.getKey(), e2.getKey(), fd); | |||
} | |||
if (response.getFileDataByModuleAndPathCount() == 0) { | |||
return new SingleProjectRepository(constructFileDataMap(response.getFileDataByPathMap()), new Date(response.getLastAnalysisDate())); | |||
} else { | |||
final Map<String, SingleProjectRepository> repositoriesPerModule = new HashMap<>(); | |||
response.getFileDataByModuleAndPathMap().keySet().forEach(moduleKey -> { | |||
WsProjectResponse.FileDataByPath filePaths = response.getFileDataByModuleAndPathMap().get(moduleKey); | |||
repositoriesPerModule.put(moduleKey, new SingleProjectRepository( | |||
constructFileDataMap(filePaths.getFileDataByPathMap()), new Date(response.getLastAnalysisDate()))); | |||
}); | |||
return new MultiModuleProjectRepository(repositoriesPerModule, new Date(response.getLastAnalysisDate())); | |||
} | |||
return new ProjectRepositories(fileDataTable, new Date(response.getLastAnalysisDate())); | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Couldn't load project repository for " + projectKey, e); | |||
} finally { | |||
IOUtils.closeQuietly(is); | |||
} | |||
} | |||
private static Map<String, FileData> constructFileDataMap(Map<String, WsProjectResponse.FileData> content) { | |||
Map<String, FileData> fileDataMap = new HashMap<>(); | |||
content.forEach((key, value) -> { | |||
FileData fd = new FileData(value.getHash(), value.getRevision()); | |||
fileDataMap.put(key, fd); | |||
}); | |||
return fileDataMap; | |||
} | |||
} |
@@ -0,0 +1,45 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.scanner.repository; | |||
import com.google.common.collect.ImmutableMap; | |||
import java.util.Date; | |||
import java.util.Map; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import javax.annotation.concurrent.Immutable; | |||
@Immutable | |||
public class MultiModuleProjectRepository extends ProjectRepositories { | |||
private Map<String, SingleProjectRepository> repositoriesPerModule; | |||
public MultiModuleProjectRepository(Map<String, SingleProjectRepository> repositoriesPerModule, @Nullable Date lastAnalysisDate) { | |||
super(lastAnalysisDate, true); | |||
this.repositoriesPerModule = ImmutableMap.copyOf(repositoriesPerModule); | |||
} | |||
@CheckForNull | |||
public FileData fileData(String moduleKeyWithBranch, String path) { | |||
SingleProjectRepository repository = repositoriesPerModule.get(moduleKeyWithBranch); | |||
return repository == null ? null : repository.fileData(path); | |||
} | |||
} |
@@ -19,48 +19,33 @@ | |||
*/ | |||
package org.sonar.scanner.repository; | |||
import com.google.common.collect.ImmutableTable; | |||
import com.google.common.collect.Table; | |||
import java.util.Date; | |||
import java.util.Map; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import javax.annotation.concurrent.Immutable; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
@Immutable | |||
public class ProjectRepositories { | |||
private final ImmutableTable<String, String, FileData> fileDataByModuleAndPath; | |||
public abstract class ProjectRepositories { | |||
private final Date lastAnalysisDate; | |||
private final boolean exists; | |||
public ProjectRepositories() { | |||
this.exists = false; | |||
this.fileDataByModuleAndPath = new ImmutableTable.Builder<String, String, FileData>().build(); | |||
this.lastAnalysisDate = null; | |||
} | |||
public ProjectRepositories(Table<String, String, FileData> fileDataByModuleAndPath, | |||
@Nullable Date lastAnalysisDate) { | |||
this.fileDataByModuleAndPath = ImmutableTable.copyOf(fileDataByModuleAndPath); | |||
public ProjectRepositories(@Nullable Date lastAnalysisDate, boolean exists) { | |||
this.lastAnalysisDate = lastAnalysisDate; | |||
this.exists = true; | |||
this.exists = exists; | |||
} | |||
public boolean exists() { | |||
return exists; | |||
} | |||
public Map<String, FileData> fileDataByPath(String moduleKey) { | |||
return fileDataByModuleAndPath.row(moduleKey); | |||
} | |||
public Table<String, String, FileData> fileDataByModuleAndPath() { | |||
return fileDataByModuleAndPath; | |||
} | |||
@CheckForNull | |||
public FileData fileData(String projectKeyWithBranch, String path) { | |||
return fileDataByModuleAndPath.get(projectKeyWithBranch, path); | |||
public FileData fileData(String moduleKeyWithBranch, DefaultInputFile inputFile) { | |||
if (this instanceof SingleProjectRepository) { | |||
return ((SingleProjectRepository) this).fileData(inputFile.getProjectRelativePath()); | |||
} else { | |||
return ((MultiModuleProjectRepository) this).fileData(moduleKeyWithBranch, inputFile.getModuleRelativePath()); | |||
} | |||
} | |||
@CheckForNull |
@@ -0,0 +1,49 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.scanner.repository; | |||
import com.google.common.collect.ImmutableMap; | |||
import java.util.Date; | |||
import java.util.Map; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import javax.annotation.concurrent.Immutable; | |||
@Immutable | |||
public class SingleProjectRepository extends ProjectRepositories { | |||
private final ImmutableMap<String, FileData> fileDataByPath; | |||
public SingleProjectRepository() { | |||
super(null, false); | |||
this.fileDataByPath = ImmutableMap.<String, FileData>builder().build(); | |||
} | |||
public SingleProjectRepository(Map<String, FileData> fileDataByPath, | |||
@Nullable Date lastAnalysisDate) { | |||
super(lastAnalysisDate, true); | |||
this.fileDataByPath = ImmutableMap.copyOf(fileDataByPath); | |||
} | |||
@CheckForNull | |||
public FileData fileData(String path) { | |||
return fileDataByPath.get(path); | |||
} | |||
} |
@@ -34,7 +34,6 @@ import static org.sonar.api.batch.fs.InputFile.Status.SAME; | |||
@Immutable | |||
public class StatusDetection { | |||
private final ProjectRepositories projectRepositories; | |||
private final ScmChangedFiles scmChangedFiles; | |||
@@ -43,8 +42,8 @@ public class StatusDetection { | |||
this.scmChangedFiles = scmChangedFiles; | |||
} | |||
InputFile.Status status(String projectKeyWithBranch, DefaultInputFile inputFile, String hash) { | |||
FileData fileDataPerPath = projectRepositories.fileData(projectKeyWithBranch, inputFile.relativePath()); | |||
InputFile.Status status(String moduleKeyWithBranch, DefaultInputFile inputFile, String hash) { | |||
FileData fileDataPerPath = projectRepositories.fileData(moduleKeyWithBranch, inputFile); | |||
if (fileDataPerPath == null) { | |||
return checkChanged(ADDED, inputFile); | |||
} | |||
@@ -64,8 +63,8 @@ public class StatusDetection { | |||
* @return null if it was not possible to get the status without calculating metadata | |||
*/ | |||
@CheckForNull | |||
public InputFile.Status getStatusWithoutMetadata(String projectKeyWithBranch, DefaultInputFile inputFile) { | |||
FileData fileDataPerPath = projectRepositories.fileData(projectKeyWithBranch, inputFile.relativePath()); | |||
public InputFile.Status getStatusWithoutMetadata(String moduleKeyWithBranch, DefaultInputFile inputFile) { | |||
FileData fileDataPerPath = projectRepositories.fileData(moduleKeyWithBranch, inputFile); | |||
if (fileDataPerPath == null) { | |||
return checkChanged(ADDED, inputFile); | |||
} |
@@ -27,8 +27,8 @@ import org.sonar.api.batch.InstantiationStrategy; | |||
import org.sonar.api.batch.ScannerSide; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.fs.InputFile.Status; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.fs.internal.AbstractProjectOrModule; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.scm.ScmProvider; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
@@ -57,7 +57,7 @@ public final class ScmPublisher { | |||
private final BranchConfiguration branchConfiguration; | |||
public ScmPublisher(AbstractProjectOrModule inputModule, ScmConfiguration configuration, ProjectRepositories projectRepositories, | |||
ModuleInputComponentStore componentStore, DefaultModuleFileSystem fs, ReportPublisher reportPublisher, BranchConfiguration branchConfiguration) { | |||
ModuleInputComponentStore componentStore, DefaultModuleFileSystem fs, ReportPublisher reportPublisher, BranchConfiguration branchConfiguration) { | |||
this.inputModule = inputModule; | |||
this.configuration = configuration; | |||
this.projectRepositories = projectRepositories; | |||
@@ -108,7 +108,7 @@ public final class ScmPublisher { | |||
addIfNotEmpty(filesToBlame, f); | |||
} else if (!branchConfiguration.isShortOrPullRequest()) { | |||
// File status is SAME so that mean fileData exists | |||
FileData fileData = projectRepositories.fileData(inputModule.definition().getKeyWithBranch(), inputFile.getModuleRelativePath()); | |||
FileData fileData = projectRepositories.fileData(inputModule.definition().getKeyWithBranch(), inputFile); | |||
if (StringUtils.isEmpty(fileData.revision())) { | |||
addIfNotEmpty(filesToBlame, f); | |||
} else { |
@@ -20,6 +20,7 @@ | |||
package org.sonar.scanner.mediumtest; | |||
import com.google.common.collect.HashBasedTable; | |||
import com.google.common.collect.Maps; | |||
import com.google.common.collect.Table; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
@@ -62,6 +63,7 @@ import org.sonar.scanner.repository.ProjectRepositories; | |||
import org.sonar.scanner.repository.ProjectRepositoriesLoader; | |||
import org.sonar.scanner.repository.QualityProfileLoader; | |||
import org.sonar.scanner.repository.ServerIssuesLoader; | |||
import org.sonar.scanner.repository.SingleProjectRepository; | |||
import org.sonar.scanner.repository.settings.SettingsLoader; | |||
import org.sonar.scanner.rule.ActiveRulesLoader; | |||
import org.sonar.scanner.rule.LoadedActiveRule; | |||
@@ -199,22 +201,22 @@ public class ScannerMediumTester extends ExternalResource { | |||
} | |||
public ScannerMediumTester addActiveRule(String repositoryKey, String ruleKey, @Nullable String templateRuleKey, String name, @Nullable String severity, | |||
@Nullable String internalKey, @Nullable String languag) { | |||
@Nullable String internalKey, @Nullable String language) { | |||
LoadedActiveRule r = new LoadedActiveRule(); | |||
r.setInternalKey(internalKey); | |||
r.setRuleKey(RuleKey.of(repositoryKey, ruleKey)); | |||
r.setName(name); | |||
r.setTemplateRuleKey(templateRuleKey); | |||
r.setLanguage(languag); | |||
r.setLanguage(language); | |||
r.setSeverity(severity); | |||
activeRules.addActiveRule(r); | |||
return this; | |||
} | |||
public ScannerMediumTester addFileData(String moduleKey, String path, FileData fileData) { | |||
projectRefProvider.addFileData(moduleKey, path, fileData); | |||
public ScannerMediumTester addFileData(String path, FileData fileData) { | |||
projectRefProvider.addFileData(path, fileData); | |||
return this; | |||
} | |||
@@ -368,17 +370,16 @@ public class ScannerMediumTester extends ExternalResource { | |||
} | |||
private static class FakeProjectRepositoriesLoader implements ProjectRepositoriesLoader { | |||
private Table<String, String, FileData> fileDataTable = HashBasedTable.create(); | |||
private Map<String, FileData> fileDataMap = Maps.newHashMap(); | |||
private Date lastAnalysisDate; | |||
@Override | |||
public ProjectRepositories load(String projectKey, boolean isIssuesMode, @Nullable String branchBase) { | |||
return new ProjectRepositories(fileDataTable, lastAnalysisDate); | |||
return new SingleProjectRepository(fileDataMap, lastAnalysisDate); | |||
} | |||
public FakeProjectRepositoriesLoader addFileData(String moduleKey, String path, FileData fileData) { | |||
fileDataTable.put(moduleKey, path, fileData); | |||
public FakeProjectRepositoriesLoader addFileData(String path, FileData fileData) { | |||
fileDataMap.put(path, fileData); | |||
return this; | |||
} | |||
@@ -66,7 +66,7 @@ public class BranchMediumTest { | |||
String md5sum = new FileMetadata() | |||
.readMetadata(Files.newInputStream(filepath), StandardCharsets.UTF_8, FILE_PATH) | |||
.hash(); | |||
tester.addFileData(PROJECT_KEY, FILE_PATH, new FileData(md5sum, "1.1")); | |||
tester.addFileData(FILE_PATH, new FileData(md5sum, "1.1")); | |||
} | |||
@Test |
@@ -47,9 +47,9 @@ import org.sonar.api.CoreProperties; | |||
import org.sonar.api.batch.fs.internal.FileMetadata; | |||
import org.sonar.api.utils.log.LogTester; | |||
import org.sonar.scanner.issue.tracking.TrackedIssue; | |||
import org.sonar.scanner.mediumtest.AnalysisResult; | |||
import org.sonar.scanner.mediumtest.ScannerMediumTester; | |||
import org.sonar.scanner.mediumtest.ScannerMediumTester.AnalysisBuilder; | |||
import org.sonar.scanner.mediumtest.AnalysisResult; | |||
import org.sonar.scanner.protocol.Constants.Severity; | |||
import org.sonar.scanner.protocol.input.ScannerInput.ServerIssue; | |||
import org.sonar.scanner.repository.FileData; | |||
@@ -104,7 +104,7 @@ public class ScanOnlyChangedTest { | |||
tester | |||
// this will cause the file to have status==SAME | |||
.addFileData(projectKey, filePath, new FileData(md5sum, null)) | |||
.addFileData(filePath, new FileData(md5sum, null)) | |||
.setPreviousAnalysisDate(new Date()) | |||
// Existing issue that is copied | |||
.mockServerIssue(ServerIssue.newBuilder().setKey("xyz") |
@@ -69,10 +69,10 @@ public class ScmMediumTest { | |||
.addRules(new XooRulesDefinition()) | |||
// active a rule just to be sure that xoo files are published | |||
.addActiveRule("xoo", "xoo:OneIssuePerFile", null, "One Issue Per File", null, null, null) | |||
.addFileData("com.foo.project", CHANGED_CONTENT_SCM_ON_SERVER_XOO, new FileData(DigestUtils.md5Hex(SAMPLE_XOO_CONTENT), null)) | |||
.addFileData("com.foo.project", SAME_CONTENT_NO_SCM_ON_SERVER_XOO, new FileData(DigestUtils.md5Hex(SAMPLE_XOO_CONTENT), null)) | |||
.addFileData("com.foo.project", SAME_CONTENT_SCM_ON_SERVER_XOO, new FileData(DigestUtils.md5Hex(SAMPLE_XOO_CONTENT), "1.1")) | |||
.addFileData("com.foo.project", NO_BLAME_SCM_ON_SERVER_XOO, new FileData(DigestUtils.md5Hex(SAMPLE_XOO_CONTENT), "1.1")); | |||
.addFileData(CHANGED_CONTENT_SCM_ON_SERVER_XOO, new FileData(DigestUtils.md5Hex(SAMPLE_XOO_CONTENT), null)) | |||
.addFileData(SAME_CONTENT_NO_SCM_ON_SERVER_XOO, new FileData(DigestUtils.md5Hex(SAMPLE_XOO_CONTENT), null)) | |||
.addFileData(SAME_CONTENT_SCM_ON_SERVER_XOO, new FileData(DigestUtils.md5Hex(SAMPLE_XOO_CONTENT), "1.1")) | |||
.addFileData(NO_BLAME_SCM_ON_SERVER_XOO, new FileData(DigestUtils.md5Hex(SAMPLE_XOO_CONTENT), "1.1")); | |||
@Test | |||
public void testScmMeasure() throws IOException, URISyntaxException { |
@@ -28,6 +28,7 @@ import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.utils.MessageException; | |||
import org.sonar.scanner.WsTestUtil; | |||
import org.sonar.scanner.bootstrap.ScannerWsClient; | |||
@@ -124,9 +125,11 @@ public class DefaultProjectRepositoriesLoaderTest { | |||
InputStream is = getTestResource("project.protobuf"); | |||
WsTestUtil.mockStream(wsClient, "/batch/project.protobuf?key=org.sonarsource.github%3Asonar-github-plugin&issues_mode=true", is); | |||
DefaultInputFile file = mock(DefaultInputFile.class); | |||
when(file.getModuleRelativePath()).thenReturn("src/test/java/org/sonar/plugins/github/PullRequestIssuePostJobTest.java"); | |||
ProjectRepositories proj = loader.load("org.sonarsource.github:sonar-github-plugin", true, null); | |||
FileData fd = proj.fileData("org.sonarsource.github:sonar-github-plugin", | |||
"src/test/java/org/sonar/plugins/github/PullRequestIssuePostJobTest.java"); | |||
FileData fd = proj.fileData("org.sonarsource.github:sonar-github-plugin", file); | |||
assertThat(fd.revision()).isEqualTo("27bf2c54633d05c5df402bbe09471fe43bd9e2e5"); | |||
assertThat(fd.hash()).isEqualTo("edb6b3b9ab92d8dc53ba90ab86cd422e"); |
@@ -0,0 +1,61 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.scanner.repository; | |||
import java.util.Date; | |||
import java.util.Map; | |||
import org.assertj.core.util.Maps; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.junit.Assert.assertNotNull; | |||
public class MultiModuleProjectRepositoryTest { | |||
private MultiModuleProjectRepository repository; | |||
@Before | |||
public void setUp() { | |||
Date lastAnalysisDate = new Date(); | |||
SingleProjectRepository repository1 = new SingleProjectRepository(Maps.newHashMap("/Abc.java", new FileData("123", "456")), lastAnalysisDate); | |||
SingleProjectRepository repository2 = new SingleProjectRepository(Maps.newHashMap("/Def.java", new FileData("567", "321")), lastAnalysisDate); | |||
Map<String, SingleProjectRepository> moduleRepositories = Maps.newHashMap("module1", repository1); | |||
moduleRepositories.put("module2", repository2); | |||
repository = new MultiModuleProjectRepository(moduleRepositories, lastAnalysisDate); | |||
} | |||
@Test | |||
public void test_file_data_when_module_and_file_exist() { | |||
FileData fileData = repository.fileData("module2", "/Def.java"); | |||
assertNotNull(fileData); | |||
assertThat(fileData.hash()).isEqualTo("567"); | |||
assertThat(fileData.revision()).isEqualTo("321"); | |||
} | |||
@Test | |||
public void test_file_data_when_module_does_not_exist() { | |||
FileData fileData = repository.fileData("unknown", "/Def.java"); | |||
assertThat(fileData).isNull(); | |||
} | |||
} |
@@ -19,9 +19,9 @@ | |||
*/ | |||
package org.sonar.scanner.repository; | |||
import com.google.common.collect.HashBasedTable; | |||
import com.google.common.collect.Table; | |||
import com.google.common.collect.Maps; | |||
import java.util.Date; | |||
import java.util.Map; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.mockito.Mock; | |||
@@ -55,9 +55,9 @@ public class ProjectRepositoriesProviderTest { | |||
public void setUp() { | |||
MockitoAnnotations.initMocks(this); | |||
Table<String, String, FileData> t2 = HashBasedTable.create(); | |||
Map<String, FileData> fileMap = Maps.newHashMap(); | |||
project = new ProjectRepositories(t2, new Date()); | |||
project = new SingleProjectRepository(fileMap, new Date()); | |||
provider = new ProjectRepositoriesProvider(); | |||
when(props.getKeyWithBranch()).thenReturn("key"); |
@@ -0,0 +1,55 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.scanner.repository; | |||
import java.util.Date; | |||
import org.assertj.core.util.Maps; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.junit.Assert.assertNotNull; | |||
public class SingleProjectRepositoryTest { | |||
private SingleProjectRepository repository; | |||
@Before | |||
public void setUp() { | |||
Date lastAnalysisDate = new Date(); | |||
repository = new SingleProjectRepository(Maps.newHashMap("/Abc.java", new FileData("123", "456")), lastAnalysisDate); | |||
} | |||
@Test | |||
public void test_file_data_when_file_exists() { | |||
FileData fileData = repository.fileData("/Abc.java"); | |||
assertNotNull(fileData); | |||
assertThat(fileData.hash()).isEqualTo("123"); | |||
assertThat(fileData.revision()).isEqualTo("456"); | |||
} | |||
@Test | |||
public void test_file_data_when_file_does_not_exist() { | |||
FileData fileData = repository.fileData("/Def.java"); | |||
assertThat(fileData).isNull(); | |||
} | |||
} |
@@ -19,16 +19,16 @@ | |||
*/ | |||
package org.sonar.scanner.scan.filesystem; | |||
import com.google.common.collect.HashBasedTable; | |||
import com.google.common.collect.Table; | |||
import java.nio.file.Paths; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import org.junit.Test; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.fs.internal.TestInputFileBuilder; | |||
import org.sonar.scanner.repository.FileData; | |||
import org.sonar.scanner.repository.ProjectRepositories; | |||
import org.sonar.scanner.repository.SingleProjectRepository; | |||
import org.sonar.scanner.scm.ScmChangedFiles; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
@@ -40,7 +40,7 @@ import static org.mockito.Mockito.when; | |||
public class StatusDetectionTest { | |||
@Test | |||
public void detect_status() { | |||
ProjectRepositories ref = new ProjectRepositories(createTable(), null); | |||
SingleProjectRepository ref = new SingleProjectRepository(createFileDataPerPathMap(), null); | |||
ScmChangedFiles changedFiles = new ScmChangedFiles(null); | |||
StatusDetection statusDetection = new StatusDetection(ref, changedFiles); | |||
@@ -51,7 +51,7 @@ public class StatusDetectionTest { | |||
@Test | |||
public void detect_status_branches_exclude() { | |||
ProjectRepositories ref = new ProjectRepositories(createTable(), null); | |||
SingleProjectRepository ref = new SingleProjectRepository(createFileDataPerPathMap(), null); | |||
ScmChangedFiles changedFiles = new ScmChangedFiles(Collections.emptyList()); | |||
StatusDetection statusDetection = new StatusDetection(ref, changedFiles); | |||
@@ -65,34 +65,34 @@ public class StatusDetectionTest { | |||
@Test | |||
public void detect_status_without_metadata() { | |||
DefaultInputFile mockedFile = mock(DefaultInputFile.class); | |||
when(mockedFile.relativePath()).thenReturn("module/src/Foo.java"); | |||
when(mockedFile.getProjectRelativePath()).thenReturn("module/src/Foo.java"); | |||
when(mockedFile.path()).thenReturn(Paths.get("module", "src", "Foo.java")); | |||
ProjectRepositories ref = new ProjectRepositories(createTable(), null); | |||
SingleProjectRepository ref = new SingleProjectRepository(createFileDataPerPathMap(), null); | |||
ScmChangedFiles changedFiles = new ScmChangedFiles(Collections.singletonList(Paths.get("module", "src", "Foo.java"))); | |||
StatusDetection statusDetection = new StatusDetection(ref, changedFiles); | |||
assertThat(statusDetection.getStatusWithoutMetadata("foo", mockedFile)).isEqualTo(InputFile.Status.ADDED); | |||
verify(mockedFile).path(); | |||
verify(mockedFile).relativePath(); | |||
verify(mockedFile).getProjectRelativePath(); | |||
verifyNoMoreInteractions(mockedFile); | |||
} | |||
@Test | |||
public void detect_status_branches_confirm() { | |||
ProjectRepositories ref = new ProjectRepositories(createTable(), null); | |||
SingleProjectRepository ref = new SingleProjectRepository(createFileDataPerPathMap(), null); | |||
ScmChangedFiles changedFiles = new ScmChangedFiles(Collections.singletonList(Paths.get("module", "src", "Foo.java"))); | |||
StatusDetection statusDetection = new StatusDetection(ref, changedFiles); | |||
assertThat(statusDetection.status("foo", createFile("src/Foo.java"), "XXXXX")).isEqualTo(InputFile.Status.CHANGED); | |||
} | |||
private static Table<String, String, FileData> createTable() { | |||
Table<String, String, FileData> t = HashBasedTable.create(); | |||
private static Map<String, FileData> createFileDataPerPathMap() { | |||
Map<String, FileData> t = new HashMap<>(); | |||
t.put("foo", "src/Foo.java", new FileData("ABCDE", "12345789")); | |||
t.put("foo", "src/Bar.java", new FileData("FGHIJ", "123456789")); | |||
t.put("src/Foo.java", new FileData("ABCDE", "12345789")); | |||
t.put("src/Bar.java", new FileData("FGHIJ", "123456789")); | |||
return t; | |||
} |
@@ -0,0 +1,52 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.scanner.protocol.input; | |||
import com.google.common.collect.Maps; | |||
import java.util.Map; | |||
import java.util.Optional; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
public class MultiModuleProjectRepository extends ProjectRepositories { | |||
private Map<String, SingleProjectRepository> repositoryPerModule = Maps.newHashMap(); | |||
public ProjectRepositories addFileDataToModule(String moduleKey, @Nullable String path, FileData fileData) { | |||
if (path == null || (fileData.hash() == null && fileData.revision() == null)) { | |||
return this; | |||
} | |||
SingleProjectRepository repository = repositoryPerModule.computeIfAbsent(moduleKey, k -> new SingleProjectRepository()); | |||
repository.addFileData(path, fileData); | |||
return this; | |||
} | |||
public Map<String, SingleProjectRepository> repositoriesByModule() { | |||
return repositoryPerModule; | |||
} | |||
@CheckForNull | |||
public FileData fileData(String moduleKeyWithBranch, @Nullable String path) { | |||
Optional<SingleProjectRepository> moduleRepository = Optional.ofNullable(repositoryPerModule.get(moduleKeyWithBranch)); | |||
return moduleRepository | |||
.map(singleProjectRepository -> singleProjectRepository.fileDataByPath(path)) | |||
.orElse(null); | |||
} | |||
} |
@@ -19,10 +19,7 @@ | |||
*/ | |||
package org.sonar.scanner.protocol.input; | |||
import java.util.Collections; | |||
import java.util.Date; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
@@ -30,34 +27,11 @@ import javax.annotation.Nullable; | |||
* Container for all project data going from server to batch. | |||
* This is not an API since server and batch always share the same version. | |||
*/ | |||
public class ProjectRepositories { | |||
public abstract class ProjectRepositories { | |||
private long timestamp; | |||
private Map<String, Map<String, FileData>> fileDataByModuleAndPath = new HashMap<>(); | |||
private Date lastAnalysisDate; | |||
public Map<String, Map<String, FileData>> fileDataByModuleAndPath() { | |||
return fileDataByModuleAndPath; | |||
} | |||
public Map<String, FileData> fileDataByPath(String moduleKey) { | |||
return fileDataByModuleAndPath.containsKey(moduleKey) ? fileDataByModuleAndPath.get(moduleKey) : Collections.<String, FileData>emptyMap(); | |||
} | |||
public ProjectRepositories addFileData(String moduleKey, @Nullable String path, FileData fileData) { | |||
if (path == null || (fileData.hash() == null && fileData.revision() == null)) { | |||
return this; | |||
} | |||
Map<String, FileData> existingFileDataByPath = fileDataByModuleAndPath.computeIfAbsent(moduleKey, k -> new HashMap<>()); | |||
existingFileDataByPath.put(path, fileData); | |||
return this; | |||
} | |||
@CheckForNull | |||
public FileData fileData(String projectKey, String path) { | |||
return fileDataByPath(projectKey).get(path); | |||
} | |||
private Date lastAnalysisDate; | |||
public long timestamp() { | |||
return timestamp; |
@@ -0,0 +1,45 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.scanner.protocol.input; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import javax.annotation.Nullable; | |||
public class SingleProjectRepository extends ProjectRepositories { | |||
private Map<String, FileData> fileDataByPath = new HashMap<>(); | |||
public ProjectRepositories addFileData(@Nullable String path, FileData fileData) { | |||
if (path == null || (fileData.hash() == null && fileData.revision() == null)) { | |||
return this; | |||
} | |||
fileDataByPath.put(path, fileData); | |||
return this; | |||
} | |||
public Map<String, FileData> fileData() { | |||
return fileDataByPath; | |||
} | |||
public FileData fileDataByPath(@Nullable String path) { | |||
return fileDataByPath.get(path); | |||
} | |||
} |
@@ -0,0 +1,68 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.scanner.protocol.input; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class MultiModuleProjectRepositoryTest { | |||
private MultiModuleProjectRepository repository; | |||
@Before | |||
public void setUp() { | |||
repository = new MultiModuleProjectRepository(); | |||
} | |||
@Test | |||
public void add_file_data_to_nodule() { | |||
FileData fileData1 = new FileData("123", "456"); | |||
FileData fileData2 = new FileData("153", "6432"); | |||
FileData fileData3 = new FileData("987", "6343"); | |||
repository.addFileDataToModule("Module1", "/Abc.java", fileData1); | |||
repository.addFileDataToModule("Module1", "/Xyz.java", fileData2); | |||
repository.addFileDataToModule("Module2", "/Def.java", fileData3); | |||
assertThat(repository.repositoriesByModule()).hasSize(2); | |||
assertThat(repository.fileData("Module1", "/Xyz.java")).isEqualTo(fileData2); | |||
assertThat(repository.fileData("Module2", "/Def.java")).isEqualTo(fileData3); | |||
} | |||
@Test | |||
public void add_file_does_not_add_the_file_without_path() { | |||
FileData fileData = new FileData("123", "456"); | |||
repository.addFileDataToModule("module1", null, fileData); | |||
assertThat(repository.repositoriesByModule()).hasSize(0); | |||
} | |||
@Test | |||
public void add_file_does_not_add_the_file_without_revision_and_hash() { | |||
FileData fileData = new FileData(null, null); | |||
repository.addFileDataToModule("module2", "/Abc.java", fileData); | |||
assertThat(repository.repositoriesByModule()).hasSize(0); | |||
} | |||
} |
@@ -0,0 +1,64 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.scanner.protocol.input; | |||
import com.google.common.collect.Maps; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class SingleProjectRepositoryTest { | |||
private SingleProjectRepository repository; | |||
@Before | |||
public void setUp() { | |||
repository = new SingleProjectRepository(); | |||
} | |||
@Test | |||
public void add_file_data() { | |||
FileData fileData = new FileData("123", "456"); | |||
repository.addFileData("/Abc.java", fileData); | |||
assertThat(repository.fileData()).hasSize(1); | |||
assertThat(repository.fileData()).contains(Maps.immutableEntry("/Abc.java", fileData)); | |||
assertThat(repository.fileDataByPath("/Abc.java")).isEqualTo(fileData); | |||
} | |||
@Test | |||
public void add_file_data_doesnt_add_the_file_without_path() { | |||
FileData fileData = new FileData("123", "456"); | |||
repository.addFileData(null, fileData); | |||
assertThat(repository.fileData()).hasSize(0); | |||
} | |||
@Test | |||
public void add_file_data_doesnt_add_the_file_without_revision_and_hash() { | |||
FileData fileData = new FileData(null, null); | |||
repository.addFileData("/Abc.java", fileData); | |||
assertThat(repository.fileData()).hasSize(0); | |||
} | |||
} |
@@ -30,6 +30,7 @@ message WsProjectResponse { | |||
optional int64 timestamp = 1; | |||
map<string, FileDataByPath> fileDataByModuleAndPath = 3; | |||
optional int64 lastAnalysisDate = 4; | |||
map<string, FileData> fileDataByPath = 5; | |||
message FileDataByPath { | |||
map<string, FileData> FileDataByPath = 1; |