/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2023 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.ce.task.projectanalysis.component; | |||||
import java.util.Map; | |||||
import java.util.function.Supplier; | |||||
import org.sonar.ce.task.projectanalysis.batch.BatchReportReader; | |||||
import org.sonar.scanner.protocol.output.ScannerReport; | |||||
public class ReportModulesPath implements Supplier<Map<String, String>> { | |||||
private final BatchReportReader reader; | |||||
private Map<String, String> cache; | |||||
public ReportModulesPath(BatchReportReader reader) { | |||||
this.reader = reader; | |||||
} | |||||
public Map<String, String> get() { | |||||
if (cache == null) { | |||||
ScannerReport.Metadata metadata = reader.readMetadata(); | |||||
cache = metadata.getModulesProjectRelativePathByKeyMap(); | |||||
} | |||||
return cache; | |||||
} | |||||
} |
import org.sonar.ce.task.projectanalysis.component.PreviousSourceHashRepositoryImpl; | import org.sonar.ce.task.projectanalysis.component.PreviousSourceHashRepositoryImpl; | ||||
import org.sonar.ce.task.projectanalysis.component.ProjectPersister; | import org.sonar.ce.task.projectanalysis.component.ProjectPersister; | ||||
import org.sonar.ce.task.projectanalysis.component.ReferenceBranchComponentUuids; | import org.sonar.ce.task.projectanalysis.component.ReferenceBranchComponentUuids; | ||||
import org.sonar.ce.task.projectanalysis.component.ReportModulesPath; | |||||
import org.sonar.ce.task.projectanalysis.component.SiblingComponentsWithOpenIssues; | import org.sonar.ce.task.projectanalysis.component.SiblingComponentsWithOpenIssues; | ||||
import org.sonar.ce.task.projectanalysis.component.TreeRootHolderImpl; | import org.sonar.ce.task.projectanalysis.component.TreeRootHolderImpl; | ||||
import org.sonar.ce.task.projectanalysis.duplication.CrossProjectDuplicationStatusHolderImpl; | import org.sonar.ce.task.projectanalysis.duplication.CrossProjectDuplicationStatusHolderImpl; | ||||
// File System | // File System | ||||
new ComputationTempFolderProvider(), | new ComputationTempFolderProvider(), | ||||
ReportModulesPath.class, | |||||
FileStatusesImpl.class, | FileStatusesImpl.class, | ||||
new MetricModule(), | new MetricModule(), | ||||
*/ | */ | ||||
package org.sonar.ce.task.projectanalysis.issue; | package org.sonar.ce.task.projectanalysis.issue; | ||||
import java.util.ArrayList; | |||||
import java.util.Collection; | |||||
import java.util.Date; | |||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | |||||
import java.util.Optional; | |||||
import java.util.Set; | |||||
import java.util.function.Function; | |||||
import java.util.stream.Collectors; | |||||
import java.util.stream.Stream; | |||||
import org.apache.commons.lang.StringUtils; | |||||
import org.sonar.api.resources.Qualifiers; | |||||
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder; | |||||
import org.sonar.ce.task.projectanalysis.component.Component; | import org.sonar.ce.task.projectanalysis.component.Component; | ||||
import org.sonar.ce.task.projectanalysis.component.ReportModulesPath; | |||||
import org.sonar.core.issue.DefaultIssue; | import org.sonar.core.issue.DefaultIssue; | ||||
import org.sonar.core.issue.IssueChangeContext; | |||||
import org.sonar.db.DbClient; | import org.sonar.db.DbClient; | ||||
import org.sonar.db.DbSession; | |||||
import org.sonar.db.component.ComponentDto; | |||||
import org.sonar.server.issue.IssueFieldsSetter; | |||||
import static org.apache.commons.lang.StringUtils.trimToEmpty; | |||||
import static org.sonar.core.issue.IssueChangeContext.issueChangeContextByUserBuilder; | |||||
class ProjectTrackerBaseLazyInput extends BaseInputFactory.BaseLazyInput { | class ProjectTrackerBaseLazyInput extends BaseInputFactory.BaseLazyInput { | ||||
private final AnalysisMetadataHolder analysisMetadataHolder; | |||||
private final ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues; | |||||
private final DbClient dbClient; | |||||
private final IssueFieldsSetter issueUpdater; | |||||
private final ComponentIssuesLoader issuesLoader; | private final ComponentIssuesLoader issuesLoader; | ||||
private final ReportModulesPath reportModulesPath; | |||||
ProjectTrackerBaseLazyInput(AnalysisMetadataHolder analysisMetadataHolder, ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues, DbClient dbClient, | |||||
IssueFieldsSetter issueUpdater, ComponentIssuesLoader issuesLoader, ReportModulesPath reportModulesPath, Component component) { | |||||
ProjectTrackerBaseLazyInput(DbClient dbClient, ComponentIssuesLoader issuesLoader, Component component) { | |||||
super(dbClient, component, null); | super(dbClient, component, null); | ||||
this.analysisMetadataHolder = analysisMetadataHolder; | |||||
this.componentsWithUnprocessedIssues = componentsWithUnprocessedIssues; | |||||
this.dbClient = dbClient; | |||||
this.issueUpdater = issueUpdater; | |||||
this.issuesLoader = issuesLoader; | this.issuesLoader = issuesLoader; | ||||
this.reportModulesPath = reportModulesPath; | |||||
} | } | ||||
@Override | @Override | ||||
protected List<DefaultIssue> loadIssues() { | protected List<DefaultIssue> loadIssues() { | ||||
List<DefaultIssue> result = new ArrayList<>(); | |||||
try (DbSession dbSession = dbClient.openSession(false)) { | |||||
Set<String> dirOrModulesUuidsWithIssues = dbClient.issueDao().selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(dbSession, component.getUuid()); | |||||
if (!dirOrModulesUuidsWithIssues.isEmpty()) { | |||||
Map<String, String> pathByModuleKey = reportModulesPath.get(); | |||||
// Migrate issues that were previously on modules or directories to the root project | |||||
String branchKey = analysisMetadataHolder.isBranch() ? analysisMetadataHolder.getBranch().getName() : null; | |||||
String prKey = analysisMetadataHolder.isPullRequest() ? analysisMetadataHolder.getBranch().getPullRequestKey() : null; | |||||
Map<String, ComponentDto> modulesByUuid = dbClient.componentDao() | |||||
.selectProjectAndModulesFromProjectKey(dbSession, component.getKey(), true, branchKey, prKey) | |||||
.stream().collect(Collectors.toMap(ComponentDto::uuid, Function.identity())); | |||||
List<ComponentDto> dirOrModulesWithIssues = dbClient.componentDao().selectByUuids(dbSession, dirOrModulesUuidsWithIssues); | |||||
dirOrModulesWithIssues.forEach(c -> { | |||||
List<DefaultIssue> issuesOnModuleOrDir = issuesLoader.loadOpenIssues(c.uuid()); | |||||
String moduleOrDirProjectRelativePath = c.qualifier().equals(Qualifiers.MODULE) ? buildModuleProjectRelativePath(pathByModuleKey, c) | |||||
: buildDirectoryProjectRelativePath(pathByModuleKey, c, modulesByUuid.get(c.moduleUuid())); | |||||
result.addAll(migrateIssuesToTheRoot(issuesOnModuleOrDir, moduleOrDirProjectRelativePath)); | |||||
componentsWithUnprocessedIssues.remove(c.uuid()); | |||||
}); | |||||
} | |||||
result.addAll(issuesLoader.loadOpenIssues(effectiveUuid)); | |||||
return result; | |||||
} | |||||
} | |||||
private static String buildDirectoryProjectRelativePath(Map<String, String> pathByModuleKey, ComponentDto c, ComponentDto parentModule) { | |||||
String moduleProjectRelativePath = buildModuleProjectRelativePath(pathByModuleKey, parentModule); | |||||
return Stream.of(moduleProjectRelativePath, c.path()) | |||||
.map(StringUtils::trimToNull) | |||||
.filter(s -> s != null && !"/".equals(s)) | |||||
.collect(Collectors.joining("/")); | |||||
} | |||||
private static String buildModuleProjectRelativePath(Map<String, String> pathByModuleKey, ComponentDto parentModule) { | |||||
return Optional.ofNullable(pathByModuleKey.get(parentModule.getKey())) | |||||
// If module is not in the scanner report, we can't guess the path accurately. Fallback on what we have in DB | |||||
.orElse(trimToEmpty(parentModule.path())); | |||||
} | |||||
private Collection<? extends DefaultIssue> migrateIssuesToTheRoot(List<DefaultIssue> issuesOnModule, String modulePath) { | |||||
for (DefaultIssue i : issuesOnModule) { | |||||
// changes the issue's component uuid, add a change and set issue as changed to enforce it is persisted to DB | |||||
IssueChangeContext context = issueChangeContextByUserBuilder(new Date(analysisMetadataHolder.getAnalysisDate()), null).build(); | |||||
if (StringUtils.isNotBlank(modulePath)) { | |||||
issueUpdater.setMessage(i, "[" + modulePath + "] " + i.getMessage(), context); | |||||
} | |||||
issueUpdater.setIssueComponent(i, component.getUuid(), component.getKey(), context.date()); | |||||
} | |||||
return issuesOnModule; | |||||
return issuesLoader.loadOpenIssues(effectiveUuid); | |||||
} | } | ||||
} | } |
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.List; | import java.util.List; | ||||
import javax.annotation.Nullable; | import javax.annotation.Nullable; | ||||
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder; | |||||
import org.sonar.ce.task.projectanalysis.component.Component; | import org.sonar.ce.task.projectanalysis.component.Component; | ||||
import org.sonar.ce.task.projectanalysis.component.ReportModulesPath; | |||||
import org.sonar.ce.task.projectanalysis.filemove.MovedFilesRepository; | import org.sonar.ce.task.projectanalysis.filemove.MovedFilesRepository; | ||||
import org.sonar.ce.task.projectanalysis.filemove.MovedFilesRepository.OriginalFile; | import org.sonar.ce.task.projectanalysis.filemove.MovedFilesRepository.OriginalFile; | ||||
import org.sonar.core.issue.DefaultIssue; | import org.sonar.core.issue.DefaultIssue; | ||||
import org.sonar.core.issue.tracking.Input; | import org.sonar.core.issue.tracking.Input; | ||||
import org.sonar.db.DbClient; | import org.sonar.db.DbClient; | ||||
import org.sonar.server.issue.IssueFieldsSetter; | |||||
/** | /** | ||||
* Factory of {@link Input} of base data for issue tracking. Data are lazy-loaded. | * Factory of {@link Input} of base data for issue tracking. Data are lazy-loaded. | ||||
private final ComponentIssuesLoader issuesLoader; | private final ComponentIssuesLoader issuesLoader; | ||||
private final DbClient dbClient; | private final DbClient dbClient; | ||||
private final MovedFilesRepository movedFilesRepository; | private final MovedFilesRepository movedFilesRepository; | ||||
private final ReportModulesPath reportModulesPath; | |||||
private final AnalysisMetadataHolder analysisMetadataHolder; | |||||
private final IssueFieldsSetter issueUpdater; | |||||
private final ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues; | |||||
public TrackerBaseInputFactory(ComponentIssuesLoader issuesLoader, DbClient dbClient, MovedFilesRepository movedFilesRepository, ReportModulesPath reportModulesPath, | |||||
AnalysisMetadataHolder analysisMetadataHolder, IssueFieldsSetter issueUpdater, ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues) { | |||||
public TrackerBaseInputFactory(ComponentIssuesLoader issuesLoader, DbClient dbClient, MovedFilesRepository movedFilesRepository) { | |||||
this.issuesLoader = issuesLoader; | this.issuesLoader = issuesLoader; | ||||
this.dbClient = dbClient; | this.dbClient = dbClient; | ||||
this.movedFilesRepository = movedFilesRepository; | this.movedFilesRepository = movedFilesRepository; | ||||
this.reportModulesPath = reportModulesPath; | |||||
this.analysisMetadataHolder = analysisMetadataHolder; | |||||
this.issueUpdater = issueUpdater; | |||||
this.componentsWithUnprocessedIssues = componentsWithUnprocessedIssues; | |||||
} | } | ||||
public Input<DefaultIssue> create(Component component) { | public Input<DefaultIssue> create(Component component) { | ||||
if (component.getType() == Component.Type.PROJECT) { | if (component.getType() == Component.Type.PROJECT) { | ||||
return new ProjectTrackerBaseLazyInput(analysisMetadataHolder, componentsWithUnprocessedIssues, dbClient, issueUpdater, issuesLoader, reportModulesPath, component); | |||||
return new ProjectTrackerBaseLazyInput(dbClient, issuesLoader, component); | |||||
} | } | ||||
if (component.getType() == Component.Type.DIRECTORY) { | if (component.getType() == Component.Type.DIRECTORY) { |
import org.sonar.ce.task.projectanalysis.component.ComponentUuidFactoryImpl; | import org.sonar.ce.task.projectanalysis.component.ComponentUuidFactoryImpl; | ||||
import org.sonar.ce.task.projectanalysis.component.MutableTreeRootHolder; | import org.sonar.ce.task.projectanalysis.component.MutableTreeRootHolder; | ||||
import org.sonar.ce.task.projectanalysis.component.ProjectAttributes; | import org.sonar.ce.task.projectanalysis.component.ProjectAttributes; | ||||
import org.sonar.ce.task.projectanalysis.component.ReportModulesPath; | |||||
import org.sonar.ce.task.step.ComputationStep; | import org.sonar.ce.task.step.ComputationStep; | ||||
import org.sonar.db.DbClient; | import org.sonar.db.DbClient; | ||||
import org.sonar.db.DbSession; | import org.sonar.db.DbSession; | ||||
private final BatchReportReader reportReader; | private final BatchReportReader reportReader; | ||||
private final MutableTreeRootHolder treeRootHolder; | private final MutableTreeRootHolder treeRootHolder; | ||||
private final MutableAnalysisMetadataHolder analysisMetadataHolder; | private final MutableAnalysisMetadataHolder analysisMetadataHolder; | ||||
private final ReportModulesPath reportModulesPath; | |||||
public BuildComponentTreeStep(DbClient dbClient, BatchReportReader reportReader, MutableTreeRootHolder treeRootHolder, | public BuildComponentTreeStep(DbClient dbClient, BatchReportReader reportReader, MutableTreeRootHolder treeRootHolder, | ||||
MutableAnalysisMetadataHolder analysisMetadataHolder, ReportModulesPath reportModulesPath) { | |||||
MutableAnalysisMetadataHolder analysisMetadataHolder) { | |||||
this.dbClient = dbClient; | this.dbClient = dbClient; | ||||
this.reportReader = reportReader; | this.reportReader = reportReader; | ||||
this.treeRootHolder = treeRootHolder; | this.treeRootHolder = treeRootHolder; | ||||
this.analysisMetadataHolder = analysisMetadataHolder; | this.analysisMetadataHolder = analysisMetadataHolder; | ||||
this.reportModulesPath = reportModulesPath; | |||||
} | } | ||||
@Override | @Override |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2023 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.ce.task.projectanalysis.component; | |||||
import java.util.Map; | |||||
import org.junit.Test; | |||||
import org.sonar.ce.task.projectanalysis.batch.BatchReportReader; | |||||
import org.sonar.scanner.protocol.output.ScannerReport; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
import static org.assertj.core.api.Assertions.entry; | |||||
import static org.mockito.Mockito.mock; | |||||
import static org.mockito.Mockito.verify; | |||||
import static org.mockito.Mockito.verifyNoMoreInteractions; | |||||
import static org.mockito.Mockito.when; | |||||
import static org.mockito.internal.verification.VerificationModeFactory.times; | |||||
public class ReportModulesPathTest { | |||||
private BatchReportReader reader = mock(BatchReportReader.class); | |||||
private ReportModulesPath reportModulesPath = new ReportModulesPath(reader); | |||||
@Test | |||||
public void should_cache_report_data() { | |||||
when(reader.readMetadata()).thenReturn(ScannerReport.Metadata.newBuilder() | |||||
.putModulesProjectRelativePathByKey("module1", "path1") | |||||
.setRootComponentRef(1) | |||||
.build()); | |||||
Map<String, String> pathByModuleKey = reportModulesPath.get(); | |||||
assertThat(pathByModuleKey).containsExactly(entry("module1", "path1")); | |||||
pathByModuleKey = reportModulesPath.get(); | |||||
assertThat(pathByModuleKey).containsExactly(entry("module1", "path1")); | |||||
verify(reader, times(1)).readMetadata(); | |||||
verifyNoMoreInteractions(reader); | |||||
} | |||||
} |
import org.sonar.ce.task.projectanalysis.component.FileStatuses; | import org.sonar.ce.task.projectanalysis.component.FileStatuses; | ||||
import org.sonar.ce.task.projectanalysis.component.ReferenceBranchComponentUuids; | import org.sonar.ce.task.projectanalysis.component.ReferenceBranchComponentUuids; | ||||
import org.sonar.ce.task.projectanalysis.component.ReportComponent; | import org.sonar.ce.task.projectanalysis.component.ReportComponent; | ||||
import org.sonar.ce.task.projectanalysis.component.ReportModulesPath; | |||||
import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule; | import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule; | ||||
import org.sonar.ce.task.projectanalysis.component.TypeAwareVisitor; | import org.sonar.ce.task.projectanalysis.component.TypeAwareVisitor; | ||||
import org.sonar.ce.task.projectanalysis.filemove.MovedFilesRepository; | import org.sonar.ce.task.projectanalysis.filemove.MovedFilesRepository; | ||||
DbClient dbClient = dbTester.getDbClient(); | DbClient dbClient = dbTester.getDbClient(); | ||||
TrackerRawInputFactory rawInputFactory = new TrackerRawInputFactory(treeRootHolder, reportReader, sourceLinesHash, new CommonRuleEngineImpl(), issueFilter, | TrackerRawInputFactory rawInputFactory = new TrackerRawInputFactory(treeRootHolder, reportReader, sourceLinesHash, new CommonRuleEngineImpl(), issueFilter, | ||||
ruleRepositoryRule, activeRulesHolder); | ruleRepositoryRule, activeRulesHolder); | ||||
TrackerBaseInputFactory baseInputFactory = new TrackerBaseInputFactory(issuesLoader, dbClient, movedFilesRepository, mock(ReportModulesPath.class), analysisMetadataHolder, | |||||
new IssueFieldsSetter(), mock(ComponentsWithUnprocessedIssues.class)); | |||||
TrackerBaseInputFactory baseInputFactory = new TrackerBaseInputFactory(issuesLoader, dbClient, movedFilesRepository); | |||||
TrackerTargetBranchInputFactory targetInputFactory = new TrackerTargetBranchInputFactory(issuesLoader, targetBranchComponentUuids, dbClient, movedFilesRepository); | TrackerTargetBranchInputFactory targetInputFactory = new TrackerTargetBranchInputFactory(issuesLoader, targetBranchComponentUuids, dbClient, movedFilesRepository); | ||||
TrackerReferenceBranchInputFactory mergeInputFactory = new TrackerReferenceBranchInputFactory(issuesLoader, mergeBranchComponentsUuids, dbClient); | TrackerReferenceBranchInputFactory mergeInputFactory = new TrackerReferenceBranchInputFactory(issuesLoader, mergeBranchComponentsUuids, dbClient); | ||||
ClosedIssuesInputFactory closedIssuesInputFactory = new ClosedIssuesInputFactory(issuesLoader, dbClient, movedFilesRepository); | ClosedIssuesInputFactory closedIssuesInputFactory = new ClosedIssuesInputFactory(issuesLoader, dbClient, movedFilesRepository); |
*/ | */ | ||||
package org.sonar.ce.task.projectanalysis.issue; | package org.sonar.ce.task.projectanalysis.issue; | ||||
import com.google.common.collect.ImmutableMap; | |||||
import java.util.Collections; | |||||
import java.util.Date; | import java.util.Date; | ||||
import org.junit.Before; | import org.junit.Before; | ||||
import org.junit.Rule; | import org.junit.Rule; | ||||
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule; | import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule; | ||||
import org.sonar.ce.task.projectanalysis.component.Component; | import org.sonar.ce.task.projectanalysis.component.Component; | ||||
import org.sonar.ce.task.projectanalysis.component.ReportComponent; | import org.sonar.ce.task.projectanalysis.component.ReportComponent; | ||||
import org.sonar.ce.task.projectanalysis.component.ReportModulesPath; | |||||
import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRulesHolderRule; | import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRulesHolderRule; | ||||
import org.sonar.core.issue.DefaultIssue; | import org.sonar.core.issue.DefaultIssue; | ||||
import org.sonar.db.DbClient; | import org.sonar.db.DbClient; | ||||
import org.sonar.db.component.ComponentDto; | import org.sonar.db.component.ComponentDto; | ||||
import org.sonar.db.issue.IssueDto; | import org.sonar.db.issue.IssueDto; | ||||
import org.sonar.db.rule.RuleDto; | import org.sonar.db.rule.RuleDto; | ||||
import org.sonar.server.issue.IssueFieldsSetter; | |||||
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
import static org.assertj.core.api.Assertions.tuple; | import static org.assertj.core.api.Assertions.tuple; | ||||
import static org.mockito.Mockito.mock; | import static org.mockito.Mockito.mock; | ||||
import static org.mockito.Mockito.when; | |||||
import static org.sonar.api.utils.DateUtils.parseDate; | import static org.sonar.api.utils.DateUtils.parseDate; | ||||
import static org.sonar.db.component.ComponentTesting.newDirectory; | import static org.sonar.db.component.ComponentTesting.newDirectory; | ||||
import static org.sonar.db.component.ComponentTesting.newFileDto; | import static org.sonar.db.component.ComponentTesting.newFileDto; | ||||
private ComponentDto rootProjectDto; | private ComponentDto rootProjectDto; | ||||
private ComponentIssuesLoader issuesLoader = new ComponentIssuesLoader(dbTester.getDbClient(), ruleRepositoryRule, activeRulesHolderRule, new MapSettings().asConfig(), | private ComponentIssuesLoader issuesLoader = new ComponentIssuesLoader(dbTester.getDbClient(), ruleRepositoryRule, activeRulesHolderRule, new MapSettings().asConfig(), | ||||
System2.INSTANCE, mock(IssueChangesToDeleteRepository.class)); | System2.INSTANCE, mock(IssueChangesToDeleteRepository.class)); | ||||
private ReportModulesPath reportModulesPath; | |||||
@Before | @Before | ||||
public void prepare() { | public void prepare() { | ||||
ReportComponent rootProject = ReportComponent.builder(Component.Type.FILE, 1) | ReportComponent rootProject = ReportComponent.builder(Component.Type.FILE, 1) | ||||
.setKey(rootProjectDto.getKey()) | .setKey(rootProjectDto.getKey()) | ||||
.setUuid(rootProjectDto.uuid()).build(); | .setUuid(rootProjectDto.uuid()).build(); | ||||
reportModulesPath = mock(ReportModulesPath.class); | |||||
underTest = new ProjectTrackerBaseLazyInput(analysisMetadataHolder, mock(ComponentsWithUnprocessedIssues.class), dbClient, new IssueFieldsSetter(), issuesLoader, | |||||
reportModulesPath, rootProject); | |||||
underTest = new ProjectTrackerBaseLazyInput(dbClient, issuesLoader, rootProject); | |||||
} | } | ||||
@Test | @Test | ||||
public void return_only_open_project_issues_if_no_modules_and_folders() { | |||||
public void return_only_open_project_issues_if_no_folders() { | |||||
ComponentDto file = dbTester.components().insertComponent(newFileDto(rootProjectDto)); | ComponentDto file = dbTester.components().insertComponent(newFileDto(rootProjectDto)); | ||||
IssueDto openIssueOnProject = dbTester.issues().insert(rule, rootProjectDto, rootProjectDto, i -> i.setStatus("OPEN").setResolution(null)); | IssueDto openIssueOnProject = dbTester.issues().insert(rule, rootProjectDto, rootProjectDto, i -> i.setStatus("OPEN").setResolution(null)); | ||||
IssueDto closedIssueOnProject = dbTester.issues().insert(rule, rootProjectDto, rootProjectDto, i -> i.setStatus("CLOSED").setResolution("FIXED")); | IssueDto closedIssueOnProject = dbTester.issues().insert(rule, rootProjectDto, rootProjectDto, i -> i.setStatus("CLOSED").setResolution("FIXED")); | ||||
assertThat(underTest.loadIssues()).extracting(DefaultIssue::key).containsOnly(openIssueOnProject.getKey()); | assertThat(underTest.loadIssues()).extracting(DefaultIssue::key).containsOnly(openIssueOnProject.getKey()); | ||||
} | } | ||||
@Test | |||||
public void migrate_and_return_folder_issues_on_root_project() { | |||||
when(reportModulesPath.get()).thenReturn(Collections.emptyMap()); | |||||
ComponentDto folder = dbTester.components().insertComponent(newDirectory(rootProjectDto, "src")); | |||||
ComponentDto file = dbTester.components().insertComponent(newFileDto(rootProjectDto)); | |||||
IssueDto openIssueOnProject = dbTester.issues().insert(rule, rootProjectDto, rootProjectDto, i -> i.setStatus("OPEN").setResolution(null)); | |||||
IssueDto openIssueOnDir = dbTester.issues().insert(rule, rootProjectDto, folder, i -> i.setStatus("OPEN").setMessage("Issue on dir").setResolution(null)); | |||||
IssueDto openIssue1OnFile = dbTester.issues().insert(rule, rootProjectDto, file, i -> i.setStatus("OPEN").setResolution(null)); | |||||
assertThat(underTest.loadIssues()).extracting(DefaultIssue::key, DefaultIssue::getMessage) | |||||
.containsExactlyInAnyOrder( | |||||
tuple(openIssueOnProject.getKey(), openIssueOnProject.getMessage()), | |||||
tuple(openIssueOnDir.getKey(), "[src] Issue on dir")); | |||||
} | |||||
@Test | |||||
public void migrate_and_return_module_and_folder_issues_on_module() { | |||||
ComponentDto module = dbTester.components().insertComponent(newModuleDto(rootProjectDto).setPath("moduleAInDb")); | |||||
when(reportModulesPath.get()).thenReturn(ImmutableMap.of(module.getKey(), "moduleAInReport")); | |||||
ComponentDto folder = dbTester.components().insertComponent(newDirectory(module, "src")); | |||||
ComponentDto file = dbTester.components().insertComponent(newFileDto(module)); | |||||
IssueDto openIssueOnProject = dbTester.issues().insert(rule, rootProjectDto, rootProjectDto, i -> i.setStatus("OPEN").setResolution(null)); | |||||
IssueDto openIssueOnModule = dbTester.issues().insert(rule, rootProjectDto, module, i -> i.setStatus("OPEN").setMessage("Issue on module").setResolution(null)); | |||||
IssueDto openIssueOnDir = dbTester.issues().insert(rule, rootProjectDto, folder, i -> i.setStatus("OPEN").setMessage("Issue on dir").setResolution(null)); | |||||
IssueDto openIssue1OnFile = dbTester.issues().insert(rule, rootProjectDto, file, i -> i.setStatus("OPEN").setResolution(null)); | |||||
assertThat(underTest.loadIssues()).extracting(DefaultIssue::key, DefaultIssue::getMessage) | |||||
.containsExactlyInAnyOrder( | |||||
tuple(openIssueOnProject.getKey(), openIssueOnProject.getMessage()), | |||||
tuple(openIssueOnModule.getKey(), "[moduleAInReport] Issue on module"), | |||||
tuple(openIssueOnDir.getKey(), "[moduleAInReport/src] Issue on dir")); | |||||
} | |||||
@Test | |||||
public void use_db_path_if_module_missing_in_report() { | |||||
ComponentDto module = dbTester.components().insertComponent(newModuleDto(rootProjectDto).setPath("moduleAInDb")); | |||||
when(reportModulesPath.get()).thenReturn(Collections.emptyMap()); | |||||
ComponentDto folder = dbTester.components().insertComponent(newDirectory(module, "src")); | |||||
IssueDto openIssueOnProject = dbTester.issues().insert(rule, rootProjectDto, rootProjectDto, i -> i.setStatus("OPEN").setResolution(null)); | |||||
IssueDto openIssueOnModule = dbTester.issues().insert(rule, rootProjectDto, module, i -> i.setStatus("OPEN").setMessage("Issue on module").setResolution(null)); | |||||
IssueDto openIssueOnDir = dbTester.issues().insert(rule, rootProjectDto, folder, i -> i.setStatus("OPEN").setMessage("Issue on dir").setResolution(null)); | |||||
assertThat(underTest.loadIssues()).extracting(DefaultIssue::key, DefaultIssue::getMessage) | |||||
.containsExactlyInAnyOrder( | |||||
tuple(openIssueOnProject.getKey(), openIssueOnProject.getMessage()), | |||||
tuple(openIssueOnModule.getKey(), "[moduleAInDb] Issue on module"), | |||||
tuple(openIssueOnDir.getKey(), "[moduleAInDb/src] Issue on dir")); | |||||
} | |||||
@Test | |||||
public void empty_path_if_module_missing_in_report_and_db_and_for_slash_folder() { | |||||
ComponentDto module = dbTester.components().insertComponent(newModuleDto(rootProjectDto).setPath(null)); | |||||
when(reportModulesPath.get()).thenReturn(Collections.emptyMap()); | |||||
ComponentDto folder = dbTester.components().insertComponent(newDirectory(module, "/")); | |||||
IssueDto openIssueOnProject = dbTester.issues().insert(rule, rootProjectDto, rootProjectDto, i -> i.setStatus("OPEN").setResolution(null)); | |||||
IssueDto openIssueOnModule = dbTester.issues().insert(rule, rootProjectDto, module, i -> i.setStatus("OPEN").setMessage("Issue on module").setResolution(null)); | |||||
IssueDto openIssueOnDir = dbTester.issues().insert(rule, rootProjectDto, folder, i -> i.setStatus("OPEN").setMessage("Issue on dir").setResolution(null)); | |||||
assertThat(underTest.loadIssues()).extracting(DefaultIssue::key, DefaultIssue::getMessage) | |||||
.containsExactlyInAnyOrder( | |||||
tuple(openIssueOnProject.getKey(), openIssueOnProject.getMessage()), | |||||
tuple(openIssueOnModule.getKey(), "Issue on module"), | |||||
tuple(openIssueOnDir.getKey(), "Issue on dir")); | |||||
} | |||||
} | } |
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule; | import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule; | ||||
import org.sonar.ce.task.projectanalysis.component.Component; | import org.sonar.ce.task.projectanalysis.component.Component; | ||||
import org.sonar.ce.task.projectanalysis.component.ReportComponent; | import org.sonar.ce.task.projectanalysis.component.ReportComponent; | ||||
import org.sonar.ce.task.projectanalysis.component.ReportModulesPath; | |||||
import org.sonar.ce.task.projectanalysis.filemove.MovedFilesRepository; | import org.sonar.ce.task.projectanalysis.filemove.MovedFilesRepository; | ||||
import org.sonar.core.issue.DefaultIssue; | import org.sonar.core.issue.DefaultIssue; | ||||
import org.sonar.core.issue.tracking.Input; | import org.sonar.core.issue.tracking.Input; | ||||
import org.sonar.db.DbClient; | import org.sonar.db.DbClient; | ||||
import org.sonar.db.DbSession; | import org.sonar.db.DbSession; | ||||
import org.sonar.db.source.FileSourceDao; | import org.sonar.db.source.FileSourceDao; | ||||
import org.sonar.server.issue.IssueFieldsSetter; | |||||
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
import static org.mockito.ArgumentMatchers.any; | import static org.mockito.ArgumentMatchers.any; | ||||
private MovedFilesRepository movedFilesRepository = mock(MovedFilesRepository.class); | private MovedFilesRepository movedFilesRepository = mock(MovedFilesRepository.class); | ||||
private TrackerBaseInputFactory underTest = new TrackerBaseInputFactory(issuesLoader, dbClient, movedFilesRepository, mock(ReportModulesPath.class), analysisMetadataHolder, | |||||
new IssueFieldsSetter(), mock(ComponentsWithUnprocessedIssues.class)); | |||||
private TrackerBaseInputFactory underTest = new TrackerBaseInputFactory(issuesLoader, dbClient, movedFilesRepository); | |||||
@Before | @Before | ||||
public void setUp() { | public void setUp() { |
import org.sonar.ce.task.projectanalysis.component.Component; | import org.sonar.ce.task.projectanalysis.component.Component; | ||||
import org.sonar.ce.task.projectanalysis.component.DefaultBranchImpl; | import org.sonar.ce.task.projectanalysis.component.DefaultBranchImpl; | ||||
import org.sonar.ce.task.projectanalysis.component.MutableTreeRootHolderRule; | import org.sonar.ce.task.projectanalysis.component.MutableTreeRootHolderRule; | ||||
import org.sonar.ce.task.projectanalysis.component.ReportModulesPath; | |||||
import org.sonar.ce.task.step.TestComputationStepContext; | import org.sonar.ce.task.step.TestComputationStepContext; | ||||
import org.sonar.db.DbClient; | import org.sonar.db.DbClient; | ||||
import org.sonar.db.DbTester; | import org.sonar.db.DbTester; | ||||
public MutableTreeRootHolderRule treeRootHolder = new MutableTreeRootHolderRule(); | public MutableTreeRootHolderRule treeRootHolder = new MutableTreeRootHolderRule(); | ||||
@Rule | @Rule | ||||
public MutableAnalysisMetadataHolderRule analysisMetadataHolder = new MutableAnalysisMetadataHolderRule(); | public MutableAnalysisMetadataHolderRule analysisMetadataHolder = new MutableAnalysisMetadataHolderRule(); | ||||
private ReportModulesPath reportModulesPath = new ReportModulesPath(reportReader); | |||||
private DbClient dbClient = dbTester.getDbClient(); | private DbClient dbClient = dbTester.getDbClient(); | ||||
private BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, reportModulesPath); | |||||
private BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder); | |||||
@Test | @Test | ||||
public void fails_if_root_component_does_not_exist_in_reportReader() { | public void fails_if_root_component_does_not_exist_in_reportReader() { | ||||
.setAnalysisDate(ANALYSIS_DATE) | .setAnalysisDate(ANALYSIS_DATE) | ||||
.setProject(Project.from(newPrivateProjectDto().setKey(REPORT_PROJECT_KEY))) | .setProject(Project.from(newPrivateProjectDto().setKey(REPORT_PROJECT_KEY))) | ||||
.setBranch(branch); | .setBranch(branch); | ||||
BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, reportModulesPath); | |||||
BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder); | |||||
reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF)); | reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF)); | ||||
reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1)); | reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1)); | ||||
.setAnalysisDate(ANALYSIS_DATE) | .setAnalysisDate(ANALYSIS_DATE) | ||||
.setProject(Project.from(projectDto)) | .setProject(Project.from(projectDto)) | ||||
.setBranch(branch); | .setBranch(branch); | ||||
BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, reportModulesPath); | |||||
BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder); | |||||
reportReader.putComponent(component(ROOT_REF, PROJECT, componentDto.getKey())); | reportReader.putComponent(component(ROOT_REF, PROJECT, componentDto.getKey())); | ||||
underTest.execute(new TestComputationStepContext()); | underTest.execute(new TestComputationStepContext()); | ||||
@Test | @Test | ||||
public void generate_keys_when_using_main_branch() { | public void generate_keys_when_using_main_branch() { | ||||
setAnalysisMetadataHolder(); | setAnalysisMetadataHolder(); | ||||
BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, reportModulesPath); | |||||
BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder); | |||||
reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF)); | reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF)); | ||||
reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1)); | reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1)); | ||||
return mapper(session).selectSubProjectsByComponentUuids(uuids); | return mapper(session).selectSubProjectsByComponentUuids(uuids); | ||||
} | } | ||||
public List<ComponentDto> selectEnabledDescendantModules(DbSession session, String rootComponentUuid) { | |||||
return mapper(session).selectDescendantModules(rootComponentUuid, Scopes.PROJECT, true); | |||||
} | |||||
public List<FilePathWithHashDto> selectEnabledDescendantFiles(DbSession session, String rootComponentUuid) { | |||||
return mapper(session).selectDescendantFiles(rootComponentUuid, Scopes.FILE, true); | |||||
public List<ComponentDto> selectEnabledViewsFromRootView(DbSession session, String rootViewUuid) { | |||||
return mapper(session).selectEnabledViewsFromRootView(rootViewUuid); | |||||
} | } | ||||
public List<FilePathWithHashDto> selectEnabledFilesFromProject(DbSession session, String rootComponentUuid) { | public List<FilePathWithHashDto> selectEnabledFilesFromProject(DbSession session, String rootComponentUuid) { | ||||
return mapper(session).selectUuidsByKeyFromProjectKeyAndBranchOrPr(projectKey, null, pullrequest); | return mapper(session).selectUuidsByKeyFromProjectKeyAndBranchOrPr(projectKey, null, pullrequest); | ||||
} | } | ||||
/** | |||||
* If no branch or pull request is provided, returns components in the main branch | |||||
*/ | |||||
public List<ComponentDto> selectProjectAndModulesFromProjectKey(DbSession session, String projectKey, boolean excludeDisabled, | |||||
@Nullable String branch, @Nullable String pullRequest) { | |||||
checkState(branch == null || pullRequest == null, "Can't set both branch and pull request"); | |||||
return mapper(session).selectComponentsFromProjectKeyAndScope(projectKey, Scopes.PROJECT, excludeDisabled, branch, pullRequest); | |||||
} | |||||
public List<ComponentDto> selectByKeys(DbSession session, Collection<String> keys) { | public List<ComponentDto> selectByKeys(DbSession session, Collection<String> keys) { | ||||
return selectByKeys(session, keys, null, null); | return selectByKeys(session, keys, null, null); | ||||
} | } |
List<ComponentDto> selectProjects(); | List<ComponentDto> selectProjects(); | ||||
/** | /** | ||||
* Return all descendant modules (including itself) from a given component uuid and scope | |||||
* Return all descendant views (including itself) from a given root view | |||||
*/ | */ | ||||
List<ComponentDto> selectDescendantModules(@Param("moduleUuid") String moduleUuid, @Param(value = "scope") String scope, | |||||
@Param(value = "excludeDisabled") boolean excludeDisabled); | |||||
List<ComponentDto> selectEnabledViewsFromRootView(@Param("rootViewUuid") String rootViewUuid); | |||||
/** | /** | ||||
* Return all files from a given project uuid and scope | * Return all files from a given project uuid and scope | ||||
*/ | */ | ||||
List<FilePathWithHashDto> selectEnabledFilesFromProject(@Param("projectUuid") String projectUuid); | List<FilePathWithHashDto> selectEnabledFilesFromProject(@Param("projectUuid") String projectUuid); | ||||
/** | |||||
* Return all descendant files from a given module uuid and scope | |||||
*/ | |||||
List<FilePathWithHashDto> selectDescendantFiles(@Param("moduleUuid") String moduleUuid, @Param(value = "scope") String scope, | |||||
@Param(value = "excludeDisabled") boolean excludeDisabled); | |||||
/** | /** | ||||
* Return uuids and project uuids from list of qualifiers | * Return uuids and project uuids from list of qualifiers | ||||
* <p/> | * <p/> | ||||
*/ | */ | ||||
List<UuidWithBranchUuidDto> selectUuidsForQualifiers(@Param("qualifiers") String... qualifiers); | List<UuidWithBranchUuidDto> selectUuidsForQualifiers(@Param("qualifiers") String... qualifiers); | ||||
/** | |||||
* Return components of a given scope of a project | |||||
* | |||||
* @param scope scope of components to return. If null, all components are returned | |||||
*/ | |||||
List<ComponentDto> selectComponentsFromProjectKeyAndScope(@Param("projectKey") String projectKey, @Nullable @Param("scope") String scope, | |||||
@Param(value = "excludeDisabled") boolean excludeDisabled, @Nullable @Param("branch") String branch, @Nullable @Param("pullRequest") String pullRequest); | |||||
/** | /** | ||||
* Return keys and UUIDs of all components belonging to a project | * Return keys and UUIDs of all components belonging to a project | ||||
*/ | */ |
import org.sonar.db.WildcardPosition; | import org.sonar.db.WildcardPosition; | ||||
import org.sonar.db.component.ComponentDto; | import org.sonar.db.component.ComponentDto; | ||||
import static java.util.Collections.emptyList; | |||||
import static org.sonar.db.DaoUtils.buildLikeValue; | import static org.sonar.db.DaoUtils.buildLikeValue; | ||||
import static org.sonar.db.DatabaseUtils.executeLargeInputs; | import static org.sonar.db.DatabaseUtils.executeLargeInputs; | ||||
return mapper(session).selectComponentUuidsOfOpenIssuesForProjectUuid(projectUuid); | return mapper(session).selectComponentUuidsOfOpenIssuesForProjectUuid(projectUuid); | ||||
} | } | ||||
public Set<String> selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(DbSession session, String projectUuid) { | |||||
return mapper(session).selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(projectUuid); | |||||
} | |||||
public List<IssueDto> selectNonClosedByComponentUuidExcludingExternalsAndSecurityHotspots(DbSession dbSession, String componentUuid) { | public List<IssueDto> selectNonClosedByComponentUuidExcludingExternalsAndSecurityHotspots(DbSession dbSession, String componentUuid) { | ||||
return mapper(dbSession).selectNonClosedByComponentUuidExcludingExternals(componentUuid); | return mapper(dbSession).selectNonClosedByComponentUuidExcludingExternals(componentUuid); | ||||
} | } |
Set<String> selectComponentUuidsOfOpenIssuesForProjectUuid(String projectUuid); | Set<String> selectComponentUuidsOfOpenIssuesForProjectUuid(String projectUuid); | ||||
Set<String> selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(String projectUuid); | |||||
List<IssueDto> selectByKeys(List<String> keys); | List<IssueDto> selectByKeys(List<String> keys); | ||||
Set<String> selectIssueKeysByComponentUuid(@Param("componentUuid") String componentUuid); | Set<String> selectIssueKeysByComponentUuid(@Param("componentUuid") String componentUuid); |
</foreach> | </foreach> | ||||
</select> | </select> | ||||
<select id="selectDescendantModules" parameterType="map" resultType="Component"> | |||||
<select id="selectEnabledViewsFromRootView" parameterType="map" resultType="Component"> | |||||
SELECT | SELECT | ||||
<include refid="componentColumns"/> | <include refid="componentColumns"/> | ||||
FROM components p | FROM components p | ||||
<include refid="modulesTreeQuery"/> | |||||
</select> | |||||
<sql id="modulesTreeQuery"> | |||||
INNER JOIN components module ON | |||||
module.branch_uuid = p.branch_uuid | |||||
and module.uuid = #{moduleUuid} | |||||
and module.scope='PRJ' AND module.enabled = ${_true} | |||||
where | where | ||||
p.scope = #{scope,jdbcType=VARCHAR} | |||||
<if test="excludeDisabled"> | |||||
and p.enabled = ${_true} | |||||
</if> | |||||
and | |||||
<choose> | |||||
<when test="_databaseId == 'mssql'"> | |||||
p.module_uuid_path LIKE module.module_uuid_path + '%' | |||||
</when> | |||||
<otherwise> | |||||
p.module_uuid_path LIKE module.module_uuid_path || '%' | |||||
</otherwise> | |||||
</choose> | |||||
</sql> | |||||
p.enabled=${_true} | |||||
and p.scope='PRJ' | |||||
and p.qualifier in ('VW', 'SVW', 'APP') | |||||
and p.branch_uuid=#{rootViewUuid,jdbcType=VARCHAR} | |||||
</select> | |||||
<select id="selectEnabledFilesFromProject" parameterType="map" resultType="FilePathWithHash"> | <select id="selectEnabledFilesFromProject" parameterType="map" resultType="FilePathWithHash"> | ||||
SELECT | SELECT | ||||
root.uuid=#{projectUuid,jdbcType=VARCHAR} | root.uuid=#{projectUuid,jdbcType=VARCHAR} | ||||
</select> | </select> | ||||
<select id="selectDescendantFiles" parameterType="map" resultType="FilePathWithHash"> | |||||
SELECT | |||||
p.uuid, | |||||
p.path, | |||||
p.module_uuid as moduleUuid, | |||||
fs.src_hash as srcHash, | |||||
fs.revision | |||||
FROM components p | |||||
INNER JOIN file_sources fs ON | |||||
fs.file_uuid=p.uuid | |||||
<include refid="modulesTreeQuery"/> | |||||
</select> | |||||
<select id="selectProjects" resultType="Component"> | <select id="selectProjects" resultType="Component"> | ||||
select | select | ||||
<include refid="componentColumns"/> | <include refid="componentColumns"/> | ||||
and p.copy_component_uuid is not null | and p.copy_component_uuid is not null | ||||
</select> | </select> | ||||
<select id="selectComponentsFromProjectKeyAndScope" parameterType="map" resultType="Component"> | |||||
SELECT | |||||
<include refid="componentColumns"/> | |||||
FROM components p | |||||
INNER JOIN components root ON root.uuid=p.branch_uuid AND root.kee=#{projectKey,jdbcType=VARCHAR} | |||||
<if test="branch != null || pullRequest != null"> | |||||
INNER JOIN project_branches pb ON pb.uuid=p.branch_uuid | |||||
</if> | |||||
<where> | |||||
<if test="excludeDisabled"> | |||||
p.enabled = ${_true} | |||||
</if> | |||||
<if test="scope != null"> | |||||
AND p.scope=#{scope,jdbcType=VARCHAR} | |||||
</if> | |||||
<choose> | |||||
<when test="branch != null"> | |||||
AND pb.kee=#{branch,jdbcType=VARCHAR} AND pb.branch_type = 'BRANCH' | |||||
</when> | |||||
<when test="pullRequest != null"> | |||||
AND pb.kee=#{pullRequest,jdbcType=VARCHAR} AND pb.branch_type = 'PULL_REQUEST' | |||||
</when> | |||||
<otherwise> | |||||
AND root.main_branch_project_uuid is null | |||||
</otherwise> | |||||
</choose> | |||||
</where> | |||||
</select> | |||||
<select id="selectByBranchUuid" parameterType="map" resultType="Component"> | <select id="selectByBranchUuid" parameterType="map" resultType="Component"> | ||||
SELECT | SELECT | ||||
<include refid="componentColumns"/> | <include refid="componentColumns"/> |
i.kee, ic.issue_change_creation_date desc | i.kee, ic.issue_change_creation_date desc | ||||
</select> | </select> | ||||
<select id="selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid" parameterType="string" resultType="string"> | |||||
select | |||||
distinct(i.component_uuid) | |||||
from issues i | |||||
inner join components p on | |||||
p.uuid = i.component_uuid | |||||
and p.enabled = ${_true} | |||||
where | |||||
i.project_uuid=#{projectUuid,jdbcType=VARCHAR} | |||||
and i.status <> 'CLOSED' | |||||
and (p.qualifier = 'DIR' OR p.qualifier = 'BRC') | |||||
</select> | |||||
<select id="selectComponentUuidsOfOpenIssuesForProjectUuid" parameterType="string" resultType="string"> | <select id="selectComponentUuidsOfOpenIssuesForProjectUuid" parameterType="string" resultType="string"> | ||||
select distinct(i.component_uuid) | select distinct(i.component_uuid) | ||||
from issues i | from issues i |
} | } | ||||
@Test | @Test | ||||
public void select_enabled_module_files_tree_from_module() { | |||||
ComponentDto project = db.components().insertPrivateProject(); | |||||
ComponentDto module = db.components().insertComponent(newModuleDto(project)); | |||||
ComponentDto fileDirectlyOnModule = db.components().insertComponent(newFileDto(module)); | |||||
FileSourceDto fileSourceDirectlyOnModule = db.fileSources().insertFileSource(fileDirectlyOnModule); | |||||
ComponentDto subModule = db.components().insertComponent(newModuleDto(module)); | |||||
ComponentDto directory = db.components().insertComponent(newDirectory(subModule, "src")); | |||||
ComponentDto file = db.components().insertComponent(newFileDto(subModule, directory)); | |||||
FileSourceDto fileSource = db.fileSources().insertFileSource(file); | |||||
// From root project | |||||
assertThat(underTest.selectEnabledDescendantFiles(dbSession, project.uuid())) | |||||
.extracting(FilePathWithHashDto::getUuid, FilePathWithHashDto::getModuleUuid, FilePathWithHashDto::getSrcHash, FilePathWithHashDto::getPath, FilePathWithHashDto::getRevision) | |||||
.containsExactlyInAnyOrder( | |||||
tuple(fileDirectlyOnModule.uuid(), module.uuid(), fileSourceDirectlyOnModule.getSrcHash(), fileDirectlyOnModule.path(), fileSourceDirectlyOnModule.getRevision()), | |||||
tuple(file.uuid(), subModule.uuid(), fileSource.getSrcHash(), file.path(), fileSource.getRevision())); | |||||
// From module | |||||
assertThat(underTest.selectEnabledDescendantFiles(dbSession, module.uuid())) | |||||
.extracting(FilePathWithHashDto::getUuid, FilePathWithHashDto::getModuleUuid, FilePathWithHashDto::getSrcHash, FilePathWithHashDto::getPath, FilePathWithHashDto::getRevision) | |||||
.containsExactlyInAnyOrder( | |||||
tuple(fileDirectlyOnModule.uuid(), module.uuid(), fileSourceDirectlyOnModule.getSrcHash(), fileDirectlyOnModule.path(), fileSourceDirectlyOnModule.getRevision()), | |||||
tuple(file.uuid(), subModule.uuid(), fileSource.getSrcHash(), file.path(), fileSource.getRevision())); | |||||
// From sub module | |||||
assertThat(underTest.selectEnabledDescendantFiles(dbSession, subModule.uuid())) | |||||
.extracting(FilePathWithHashDto::getUuid, FilePathWithHashDto::getModuleUuid, FilePathWithHashDto::getSrcHash, FilePathWithHashDto::getPath, FilePathWithHashDto::getRevision) | |||||
.containsExactlyInAnyOrder( | |||||
tuple(file.uuid(), subModule.uuid(), fileSource.getSrcHash(), file.path(), fileSource.getRevision())); | |||||
// From directory | |||||
assertThat(underTest.selectEnabledDescendantFiles(dbSession, directory.uuid())).isEmpty(); | |||||
assertThat(underTest.selectEnabledDescendantFiles(dbSession, "unknown")).isEmpty(); | |||||
} | |||||
@Test | |||||
public void select_enabled_module_files_tree_from_project() { | |||||
public void select_enabled_files_from_project() { | |||||
ComponentDto project = db.components().insertPrivateProject(); | ComponentDto project = db.components().insertPrivateProject(); | ||||
ComponentDto module = db.components().insertComponent(newModuleDto(project)); | ComponentDto module = db.components().insertComponent(newModuleDto(project)); | ||||
ComponentDto fileDirectlyOnModule = db.components().insertComponent(newFileDto(module)); | ComponentDto fileDirectlyOnModule = db.components().insertComponent(newFileDto(module)); | ||||
assertThat(underTest.selectProjectsFromView(dbSession, "Unknown", "Unknown")).isEmpty(); | assertThat(underTest.selectProjectsFromView(dbSession, "Unknown", "Unknown")).isEmpty(); | ||||
} | } | ||||
@Test | |||||
public void select_enabled_views_from_root_view() { | |||||
ComponentDto rootPortfolio = db.components().insertPrivatePortfolio(); | |||||
ComponentDto subPortfolio = db.components().insertSubView(rootPortfolio); | |||||
ComponentDto project = db.components().insertPrivateProject(); | |||||
db.components().insertComponent(newProjectCopy(project, subPortfolio)); | |||||
assertThat(underTest.selectEnabledViewsFromRootView(dbSession, rootPortfolio.uuid())) | |||||
.extracting(ComponentDto::uuid) | |||||
.containsOnly(rootPortfolio.uuid(), subPortfolio.uuid()); | |||||
assertThat(underTest.selectEnabledViewsFromRootView(dbSession, project.uuid())).isEmpty(); | |||||
} | |||||
@Test | @Test | ||||
public void select_projects_from_view_should_escape_like_sensitive_characters() { | public void select_projects_from_view_should_escape_like_sensitive_characters() { | ||||
ComponentDto project1 = db.components().insertPrivateProject(); | ComponentDto project1 = db.components().insertPrivateProject(); |
assertThat(result.stream().filter(g -> !g.isInLeak()).mapToLong(IssueGroupDto::getCount).sum()).isOne(); | assertThat(result.stream().filter(g -> !g.isInLeak()).mapToLong(IssueGroupDto::getCount).sum()).isOne(); | ||||
} | } | ||||
@Test | |||||
public void selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid() { | |||||
assertThat(underTest.selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(db.getSession(), randomAlphabetic(12))) | |||||
.isEmpty(); | |||||
ComponentDto project1 = db.components().insertPrivateProject(); | |||||
ComponentDto module11 = db.components().insertComponent(newModuleDto(project1)); | |||||
ComponentDto dir11 = db.components().insertComponent(newDirectory(module11, randomAlphabetic(10))); | |||||
ComponentDto dir12 = db.components().insertComponent(newDirectory(module11, randomAlphabetic(11))); | |||||
ComponentDto module12 = db.components().insertComponent(newModuleDto(project1)); | |||||
ComponentDto dir13 = db.components().insertComponent(newDirectory(module12, randomAlphabetic(12))); | |||||
ComponentDto dir14 = db.components().insertComponent(newDirectory(project1, randomAlphabetic(13))); | |||||
ComponentDto file11 = db.components().insertComponent(newFileDto(project1)); | |||||
ComponentDto application = db.components().insertPrivateApplication(); | |||||
ComponentDto view = db.components().insertPublicPortfolio(); | |||||
ComponentDto subview = db.components().insertSubView(view); | |||||
ComponentDto project2 = db.components().insertPublicProject(); | |||||
ComponentDto module21 = db.components().insertComponent(newModuleDto(project2)); | |||||
ComponentDto dir21 = db.components().insertComponent(newDirectory(project2, randomAlphabetic(15))); | |||||
ComponentDto file21 = db.components().insertComponent(newFileDto(project2)); | |||||
List<ComponentDto> allcomponents = asList(project1, module11, dir11, dir12, module12, dir13, dir14, file11, application, view, subview, project2, module21, dir21, file21); | |||||
List<ComponentDto> allModuleOrDirs = asList(module11, dir11, dir12, module12, dir13, dir14, module21, dir21); | |||||
// no issues => always empty | |||||
allcomponents.stream() | |||||
.map(ComponentDto::uuid) | |||||
.forEach(uuid -> assertThat(underTest.selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(db.getSession(), uuid)) | |||||
.isEmpty()); | |||||
// return module or dir only if has issue with status different from CLOSED | |||||
allModuleOrDirs | |||||
.forEach(moduleOrDir -> { | |||||
String projectUuid = moduleOrDir.branchUuid(); | |||||
// CLOSED issue => not returned | |||||
db.issues().insertIssue(t -> t.setProjectUuid(projectUuid).setComponent(moduleOrDir).setStatus(STATUS_CLOSED)); | |||||
assertThat(underTest.selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(db.getSession(), projectUuid)) | |||||
.isEmpty(); | |||||
// status != CLOSED => returned | |||||
STATUSES.stream() | |||||
.filter(t -> !STATUS_CLOSED.equals(t)) | |||||
.forEach(status -> { | |||||
IssueDto issue = db.issues().insertIssue(t -> t.setProjectUuid(projectUuid).setComponent(moduleOrDir).setStatus(status)); | |||||
assertThat(underTest.selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(db.getSession(), projectUuid)) | |||||
.containsOnly(moduleOrDir.uuid()); | |||||
db.executeDdl("delete from issues where kee='" + issue.getKey() + "'"); | |||||
db.commit(); | |||||
assertThat(underTest.selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(db.getSession(), projectUuid)) | |||||
.isEmpty(); | |||||
}); | |||||
}); | |||||
// never return project, view, subview, app or file, whatever the issue status | |||||
Stream.of(project1, file11, application, view, subview, project2, file21) | |||||
.forEach(neitherModuleNorDir -> { | |||||
String projectUuid = neitherModuleNorDir.branchUuid(); | |||||
STATUSES | |||||
.forEach(status -> { | |||||
db.issues().insertIssue(t -> t.setProjectUuid(projectUuid).setComponent(neitherModuleNorDir).setStatus(status)); | |||||
assertThat(underTest.selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(db.getSession(), projectUuid)) | |||||
.isEmpty(); | |||||
}); | |||||
}); | |||||
// never return whatever the component if it is disabled | |||||
allcomponents | |||||
.forEach(component -> { | |||||
String projectUuid = component.branchUuid(); | |||||
// issues for each status => returned if component is dir or module | |||||
STATUSES | |||||
.forEach(status -> db.issues().insertIssue(t -> t.setProjectUuid(projectUuid).setComponent(component).setStatus(status))); | |||||
if (allModuleOrDirs.contains(component)) { | |||||
assertThat(underTest.selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(db.getSession(), projectUuid)) | |||||
.containsOnly(component.uuid()); | |||||
} else { | |||||
assertThat(underTest.selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(db.getSession(), projectUuid)) | |||||
.isEmpty(); | |||||
} | |||||
// disable component and test again => not returned anymore | |||||
db.getDbClient().componentDao().update(db.getSession(), ComponentUpdateDto.copyFrom(component).setBEnabled(false).setBChanged(true), component.qualifier()); | |||||
db.getDbClient().componentDao().applyBChangesForRootComponentUuid(db.getSession(), projectUuid); | |||||
db.commit(); | |||||
assertThat(db.getDbClient().componentDao().selectByUuid(db.getSession(), component.uuid()).get().isEnabled()) | |||||
.isFalse(); | |||||
assertThat(underTest.selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(db.getSession(), projectUuid)) | |||||
.isEmpty(); | |||||
}); | |||||
} | |||||
@Test | @Test | ||||
public void selectByKey_givenOneIssueNewOnReferenceBranch_selectOneIssueWithNewOnReferenceBranch() { | public void selectByKey_givenOneIssueNewOnReferenceBranch_selectOneIssueWithNewOnReferenceBranch() { | ||||
prepareIssuesComponent(); | prepareIssuesComponent(); |
} | } | ||||
/** | /** | ||||
* Index a root view : it will load projects on each sub views and index it. | |||||
* Index a root view : it will a view and its subviews and index them. | |||||
* Used by the compute engine to reindex a root view. | * Used by the compute engine to reindex a root view. | ||||
* <p/> | * <p/> | ||||
* The views lookup cache will be cleared | * The views lookup cache will be cleared | ||||
public void index(String rootViewUuid) { | public void index(String rootViewUuid) { | ||||
try (DbSession dbSession = dbClient.openSession(false)) { | try (DbSession dbSession = dbClient.openSession(false)) { | ||||
Map<String, String> viewAndProjectViewUuidMap = new HashMap<>(); | Map<String, String> viewAndProjectViewUuidMap = new HashMap<>(); | ||||
for (ComponentDto viewOrSubView : dbClient.componentDao().selectEnabledDescendantModules(dbSession, rootViewUuid)) { | |||||
for (ComponentDto viewOrSubView : dbClient.componentDao().selectEnabledViewsFromRootView(dbSession, rootViewUuid)) { | |||||
viewAndProjectViewUuidMap.put(viewOrSubView.uuid(), viewOrSubView.branchUuid()); | viewAndProjectViewUuidMap.put(viewOrSubView.uuid(), viewOrSubView.branchUuid()); | ||||
} | } | ||||
index(dbSession, viewAndProjectViewUuidMap, true, Size.REGULAR); | index(dbSession, viewAndProjectViewUuidMap, true, Size.REGULAR); |
*/ | */ | ||||
package org.sonar.server.batch; | package org.sonar.server.batch; | ||||
import com.google.common.base.Splitter; | |||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.OutputStream; | import java.io.OutputStream; | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
private static final String PARAM_KEY = "key"; | private static final String PARAM_KEY = "key"; | ||||
private static final String PARAM_BRANCH = "branch"; | private static final String PARAM_BRANCH = "branch"; | ||||
private static final Splitter MODULE_PATH_SPLITTER = Splitter.on('.').trimResults().omitEmptyStrings(); | |||||
private final DbClient dbClient; | private final DbClient dbClient; | ||||
private final UserSession userSession; | private final UserSession userSession; |
*/ | */ | ||||
package org.sonar.server.batch; | package org.sonar.server.batch; | ||||
import com.google.common.collect.Maps; | |||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||
import org.sonar.api.server.ws.Change; | import org.sonar.api.server.ws.Change; | ||||
import org.sonar.api.server.ws.Response; | import org.sonar.api.server.ws.Response; | ||||
import org.sonar.api.server.ws.WebService; | import org.sonar.api.server.ws.WebService; | ||||
import org.sonar.scanner.protocol.input.FileData; | 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.ProjectRepositories; | ||||
import org.sonar.scanner.protocol.input.SingleProjectRepository; | |||||
import org.sonarqube.ws.Batch.WsProjectResponse; | import org.sonarqube.ws.Batch.WsProjectResponse; | ||||
import org.sonarqube.ws.Batch.WsProjectResponse.FileData.Builder; | import org.sonarqube.ws.Batch.WsProjectResponse.FileData.Builder; | ||||
.setChangelog( | .setChangelog( | ||||
new Change("7.6", String.format("The use of module keys in parameter '%s' is deprecated", PARAM_KEY)), | new Change("7.6", String.format("The use of module keys in parameter '%s' is deprecated", PARAM_KEY)), | ||||
new Change("7.6", "Stop returning settings"), | new Change("7.6", "Stop returning settings"), | ||||
new Change("7.7", "Stop supporting preview mode, removed timestamp and last analysis date")) | |||||
new Change("7.7", "Stop supporting preview mode, removed timestamp and last analysis date"), | |||||
new Change("10.0", String.format("No longer possible to use module keys with parameter '%s'", PARAM_KEY))) | |||||
.setInternal(true) | .setInternal(true) | ||||
.setHandler(this); | .setHandler(this); | ||||
action | action | ||||
.createParam(PARAM_KEY) | .createParam(PARAM_KEY) | ||||
.setRequired(true) | .setRequired(true) | ||||
.setDescription("Project or module key") | |||||
.setDescription("Project key") | |||||
.setExampleValue(KEY_PROJECT_EXAMPLE_001); | .setExampleValue(KEY_PROJECT_EXAMPLE_001); | ||||
action | action | ||||
private static WsProjectResponse buildResponse(ProjectRepositories data) { | private static WsProjectResponse buildResponse(ProjectRepositories data) { | ||||
WsProjectResponse.Builder response = WsProjectResponse.newBuilder(); | WsProjectResponse.Builder response = WsProjectResponse.newBuilder(); | ||||
if (data instanceof SingleProjectRepository singleProjectRepository) { | |||||
response.putAllFileDataByPath(buildFileDataByPath(singleProjectRepository)); | |||||
} else { | |||||
response.putAllFileDataByModuleAndPath(buildFileDataByModuleAndPath((MultiModuleProjectRepository) data)); | |||||
} | |||||
response.putAllFileDataByPath(buildFileDataByPath(data)); | |||||
return response.build(); | return response.build(); | ||||
} | } | ||||
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)); | |||||
} | |||||
private static Map<String, WsProjectResponse.FileData> buildFileDataByPath(SingleProjectRepository data) { | |||||
private static Map<String, WsProjectResponse.FileData> buildFileDataByPath(ProjectRepositories data) { | |||||
return data.fileData().entrySet() | return data.fileData().entrySet() | ||||
.stream() | .stream() | ||||
.collect(Collectors.toMap(Map.Entry::getKey, e -> toFileDataResponse(e.getValue()))); | .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(); | |||||
fileDataByPath.forEach((key, value) -> response.putFileDataByPath(key, toFileDataResponse(value))); | |||||
return response.build(); | |||||
} | |||||
private static WsProjectResponse.FileData toFileDataResponse(FileData fileData) { | private static WsProjectResponse.FileData toFileDataResponse(FileData fileData) { | ||||
Builder fileDataBuilder = WsProjectResponse.FileData.newBuilder(); | Builder fileDataBuilder = WsProjectResponse.FileData.newBuilder(); | ||||
ofNullable(fileData.hash()).ifPresent(fileDataBuilder::setHash); | ofNullable(fileData.hash()).ifPresent(fileDataBuilder::setHash); |
package org.sonar.server.batch; | package org.sonar.server.batch; | ||||
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.HashMap; | |||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | |||||
import javax.annotation.Nullable; | import javax.annotation.Nullable; | ||||
import org.sonar.api.server.ServerSide; | import org.sonar.api.server.ServerSide; | ||||
import org.sonar.api.web.UserRole; | import org.sonar.api.web.UserRole; | ||||
import org.sonar.db.component.FilePathWithHashDto; | import org.sonar.db.component.FilePathWithHashDto; | ||||
import org.sonar.db.permission.GlobalPermission; | import org.sonar.db.permission.GlobalPermission; | ||||
import org.sonar.scanner.protocol.input.FileData; | 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.ProjectRepositories; | ||||
import org.sonar.scanner.protocol.input.SingleProjectRepository; | |||||
import org.sonar.server.component.ComponentFinder; | import org.sonar.server.component.ComponentFinder; | ||||
import org.sonar.server.exceptions.ForbiddenException; | import org.sonar.server.exceptions.ForbiddenException; | ||||
import org.sonar.server.user.UserSession; | import org.sonar.server.user.UserSession; | ||||
boolean hasScanPerm = userSession.hasComponentPermission(UserRole.SCAN, project) || | boolean hasScanPerm = userSession.hasComponentPermission(UserRole.SCAN, project) || | ||||
userSession.hasPermission(GlobalPermission.SCAN); | userSession.hasPermission(GlobalPermission.SCAN); | ||||
checkPermission(hasScanPerm); | checkPermission(hasScanPerm); | ||||
ComponentDto branchOrMainModule = (branch == null && pullRequest == null) ? project | |||||
ComponentDto branchComponent = (branch == null && pullRequest == null) ? project | |||||
: componentFinder.getByKeyAndOptionalBranchOrPullRequest(session, projectKey, branch, pullRequest); | : componentFinder.getByKeyAndOptionalBranchOrPullRequest(session, projectKey, branch, pullRequest); | ||||
List<ComponentDto> modulesTree = dbClient.componentDao().selectEnabledDescendantModules(session, branchOrMainModule.uuid()); | |||||
List<FilePathWithHashDto> files = searchFilesWithHashAndRevision(session, branchOrMainModule); | |||||
// 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 | |||||
if (modulesTree.size() > 1) { | |||||
MultiModuleProjectRepository repository = new MultiModuleProjectRepository(); | |||||
addFileDataPerModule(repository, modulesTree, files); | |||||
return repository; | |||||
} else { | |||||
SingleProjectRepository repository = new SingleProjectRepository(); | |||||
addFileData(repository, files); | |||||
return repository; | |||||
} | |||||
List<FilePathWithHashDto> files = searchFilesWithHashAndRevision(session, branchComponent); | |||||
ProjectRepositories repository = new ProjectRepositories(); | |||||
addFileData(repository, files); | |||||
return repository; | |||||
} | } | ||||
} | } | ||||
private List<FilePathWithHashDto> searchFilesWithHashAndRevision(DbSession session, @Nullable ComponentDto module) { | |||||
if (module == null) { | |||||
private List<FilePathWithHashDto> searchFilesWithHashAndRevision(DbSession session, @Nullable ComponentDto branchComponent) { | |||||
if (branchComponent == null) { | |||||
return Collections.emptyList(); | return Collections.emptyList(); | ||||
} | } | ||||
return module.isRootProject() ? dbClient.componentDao().selectEnabledFilesFromProject(session, module.uuid()) | |||||
: dbClient.componentDao().selectEnabledDescendantFiles(session, module.uuid()); | |||||
} | |||||
private static void addFileDataPerModule(MultiModuleProjectRepository data, List<ComponentDto> moduleChildren, List<FilePathWithHashDto> files) { | |||||
Map<String, String> moduleKeysByUuid = new HashMap<>(); | |||||
for (ComponentDto module : moduleChildren) { | |||||
moduleKeysByUuid.put(module.uuid(), module.getKey()); | |||||
} | |||||
for (FilePathWithHashDto file : files) { | |||||
FileData fileData = new FileData(file.getSrcHash(), file.getRevision()); | |||||
data.addFileDataToModule(moduleKeysByUuid.get(file.getModuleUuid()), file.getPath(), fileData); | |||||
} | |||||
return dbClient.componentDao().selectEnabledFilesFromProject(session, branchComponent.uuid()); | |||||
} | } | ||||
private static void addFileData(SingleProjectRepository data, List<FilePathWithHashDto> files) { | |||||
private static void addFileData(ProjectRepositories data, List<FilePathWithHashDto> files) { | |||||
for (FilePathWithHashDto file : files) { | for (FilePathWithHashDto file : files) { | ||||
FileData fileData = new FileData(file.getSrcHash(), file.getRevision()); | FileData fileData = new FileData(file.getSrcHash(), file.getRevision()); | ||||
data.addFileData(file.getPath(), fileData); | data.addFileData(file.getPath(), fileData); |
import org.junit.Test; | import org.junit.Test; | ||||
import org.mockito.ArgumentCaptor; | import org.mockito.ArgumentCaptor; | ||||
import org.sonar.scanner.protocol.input.FileData; | 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.ProjectRepositories; | ||||
import org.sonar.scanner.protocol.input.SingleProjectRepository; | |||||
import org.sonar.server.ws.TestResponse; | import org.sonar.server.ws.TestResponse; | ||||
import org.sonar.server.ws.WsActionTester; | import org.sonar.server.ws.WsActionTester; | ||||
import org.sonarqube.ws.Batch.WsProjectResponse; | import org.sonarqube.ws.Batch.WsProjectResponse; | ||||
import static org.sonar.test.JsonAssert.assertJson; | import static org.sonar.test.JsonAssert.assertJson; | ||||
public class ProjectActionTest { | public class ProjectActionTest { | ||||
private ProjectDataLoader projectDataLoader = mock(ProjectDataLoader.class); | |||||
private WsActionTester ws = new WsActionTester(new ProjectAction(projectDataLoader)); | |||||
private final ProjectDataLoader projectDataLoader = mock(ProjectDataLoader.class); | |||||
private final WsActionTester ws = new WsActionTester(new ProjectAction(projectDataLoader)); | |||||
@Test | @Test | ||||
public void project_referentials() { | public void project_referentials() { | ||||
String projectKey = "org.codehaus.sonar:sonar"; | String projectKey = "org.codehaus.sonar:sonar"; | ||||
ProjectRepositories projectReferentials = mock(SingleProjectRepository.class); | |||||
ProjectRepositories projectReferentials = mock(ProjectRepositories.class); | |||||
ArgumentCaptor<ProjectDataQuery> queryArgumentCaptor = ArgumentCaptor.forClass(ProjectDataQuery.class); | ArgumentCaptor<ProjectDataQuery> queryArgumentCaptor = ArgumentCaptor.forClass(ProjectDataQuery.class); | ||||
when(projectDataLoader.load(queryArgumentCaptor.capture())).thenReturn(projectReferentials); | when(projectDataLoader.load(queryArgumentCaptor.capture())).thenReturn(projectReferentials); | ||||
public void do_not_fail_when_a_path_is_null() { | public void do_not_fail_when_a_path_is_null() { | ||||
String projectKey = "org.codehaus.sonar:sonar"; | String projectKey = "org.codehaus.sonar:sonar"; | ||||
ProjectRepositories projectRepositories = new MultiModuleProjectRepository() | |||||
.addFileDataToModule("module-1", null, new FileData(null, null)); | |||||
ProjectRepositories projectRepositories = new ProjectRepositories() | |||||
.addFileData(null, new FileData(null, null)); | |||||
when(projectDataLoader.load(any(ProjectDataQuery.class))).thenReturn(projectRepositories); | when(projectDataLoader.load(any(ProjectDataQuery.class))).thenReturn(projectRepositories); | ||||
WsProjectResponse wsProjectResponse = ws.newRequest() | WsProjectResponse wsProjectResponse = ws.newRequest() | ||||
public void use_new_file_structure_for_projects_without_submodules() { | public void use_new_file_structure_for_projects_without_submodules() { | ||||
String projectKey = "org.codehaus.sonar:sonar"; | String projectKey = "org.codehaus.sonar:sonar"; | ||||
ProjectRepositories projectRepositories = new SingleProjectRepository() | |||||
ProjectRepositories projectRepositories = new ProjectRepositories() | |||||
.addFileData("src/main/java/SomeClass.java", new FileData("789456", "123456789")); | .addFileData("src/main/java/SomeClass.java", new FileData("789456", "123456789")); | ||||
when(projectDataLoader.load(any(ProjectDataQuery.class))).thenReturn(projectRepositories); | when(projectDataLoader.load(any(ProjectDataQuery.class))).thenReturn(projectRepositories); | ||||
assertThat(wsProjectResponse.getFileDataByPathCount()).isOne(); | assertThat(wsProjectResponse.getFileDataByPathCount()).isOne(); | ||||
assertThat(wsProjectResponse.getFileDataByPathMap().get("src/main/java/SomeClass.java")).isNotNull(); | 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()).isOne(); | |||||
WsProjectResponse.FileDataByPath moduleData = wsProjectResponse.getFileDataByModuleAndPathMap().get("module-1"); | |||||
assertThat(moduleData).isNotNull(); | |||||
assertThat(moduleData.getFileDataByPathCount()).isOne(); | |||||
WsProjectResponse.FileData fileData = moduleData.getFileDataByPathMap().get("src/main/java/SomeClass.java"); | |||||
assertThat(fileData).isNotNull(); | |||||
assertThat(fileData.getHash()).isEqualTo("789456"); | |||||
assertThat(fileData.getRevision()).isEqualTo("123456789"); | |||||
} | |||||
} | } |
import org.sonar.db.component.ResourceTypesRule; | import org.sonar.db.component.ResourceTypesRule; | ||||
import org.sonar.db.source.FileSourceDto; | import org.sonar.db.source.FileSourceDto; | ||||
import org.sonar.scanner.protocol.input.FileData; | 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.ProjectRepositories; | ||||
import org.sonar.scanner.protocol.input.SingleProjectRepository; | |||||
import org.sonar.server.component.ComponentFinder; | import org.sonar.server.component.ComponentFinder; | ||||
import org.sonar.server.exceptions.BadRequestException; | import org.sonar.server.exceptions.BadRequestException; | ||||
import org.sonar.server.exceptions.ForbiddenException; | import org.sonar.server.exceptions.ForbiddenException; | ||||
dbClient.fileSourceDao().insert(dbSession, newFileSourceDto(file).setSrcHash("123456")); | dbClient.fileSourceDao().insert(dbSession, newFileSourceDto(file).setSrcHash("123456")); | ||||
db.commit(); | db.commit(); | ||||
ProjectRepositories ref = underTest.load(ProjectDataQuery.create().setProjectKey(project.getKey())); | |||||
assertTrue(ref instanceof SingleProjectRepository); | |||||
SingleProjectRepository singleProjectRepository = ((SingleProjectRepository) ref); | |||||
assertThat(singleProjectRepository.fileData()).hasSize(1); | |||||
FileData fileData = singleProjectRepository.fileDataByPath(file.path()); | |||||
ProjectRepositories projectRepository = underTest.load(ProjectDataQuery.create().setProjectKey(project.getKey())); | |||||
assertThat(projectRepository.fileData()).hasSize(1); | |||||
FileData fileData = projectRepository.fileDataByPath(file.path()); | |||||
assertThat(fileData).isNotNull(); | assertThat(fileData).isNotNull(); | ||||
assertThat(fileData.hash()).isEqualTo("123456"); | assertThat(fileData.hash()).isEqualTo("123456"); | ||||
} | } | ||||
@Test | |||||
public void return_file_data_from_multi_modules() { | |||||
ComponentDto project = db.components().insertPrivateProject(); | |||||
userSession.logIn().addProjectPermission(SCAN_EXECUTION, project); | |||||
ComponentDto module = db.components().insertComponent(newModuleDto(project)); | |||||
// File on project | |||||
ComponentDto projectFile = db.components().insertComponent(newFileDto(project)); | |||||
dbClient.fileSourceDao().insert(dbSession, newFileSourceDto(projectFile).setSrcHash("123456")); | |||||
// File on module | |||||
ComponentDto moduleFile = db.components().insertComponent(newFileDto(module)); | |||||
dbClient.fileSourceDao().insert(dbSession, newFileSourceDto(moduleFile).setSrcHash("789456")); | |||||
dbSession.commit(); | |||||
ProjectRepositories ref = underTest.load(ProjectDataQuery.create().setProjectKey(project.getKey())); | |||||
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 | @Test | ||||
public void return_file_data_from_branch() { | public void return_file_data_from_branch() { | ||||
ComponentDto project = db.components().insertPrivateProject(); | ComponentDto project = db.components().insertPrivateProject(); | ||||
ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch")); | ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch")); | ||||
userSession.logIn().addProjectPermission(SCAN_EXECUTION, project); | userSession.logIn().addProjectPermission(SCAN_EXECUTION, project); | ||||
ComponentDto moduleBranch = db.components().insertComponent(newModuleDto(branch)); | |||||
// File on branch | // File on branch | ||||
ComponentDto projectFile = db.components().insertComponent(newFileDto(branch)); | ComponentDto projectFile = db.components().insertComponent(newFileDto(branch)); | ||||
dbClient.fileSourceDao().insert(dbSession, newFileSourceDto(projectFile).setSrcHash("123456")); | dbClient.fileSourceDao().insert(dbSession, newFileSourceDto(projectFile).setSrcHash("123456")); | ||||
// File on moduleBranch branch | |||||
ComponentDto moduleFile = db.components().insertComponent(newFileDto(moduleBranch)); | |||||
dbClient.fileSourceDao().insert(dbSession, newFileSourceDto(moduleFile).setSrcHash("789456")); | |||||
dbSession.commit(); | dbSession.commit(); | ||||
ProjectRepositories ref = underTest.load(ProjectDataQuery.create() | ProjectRepositories ref = underTest.load(ProjectDataQuery.create() | ||||
.setProjectKey(project.getKey()) | .setProjectKey(project.getKey()) | ||||
.setBranch("my_branch")); | .setBranch("my_branch")); | ||||
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"); | |||||
assertThat(ref.fileDataByPath(projectFile.path()).hash()).isEqualTo("123456"); | |||||
} | } | ||||
@Test | @Test |
import java.io.File; | import java.io.File; | ||||
import java.nio.file.Path; | import java.nio.file.Path; | ||||
import java.util.LinkedList; | |||||
import java.util.Map.Entry; | import java.util.Map.Entry; | ||||
import java.util.regex.Pattern; | import java.util.regex.Pattern; | ||||
import org.sonar.api.batch.fs.internal.AbstractProjectOrModule; | import org.sonar.api.batch.fs.internal.AbstractProjectOrModule; | ||||
import org.sonar.api.batch.fs.internal.DefaultInputModule; | |||||
import org.sonar.api.batch.scm.ScmProvider; | import org.sonar.api.batch.scm.ScmProvider; | ||||
import org.sonar.api.utils.log.Logger; | import org.sonar.api.utils.log.Logger; | ||||
import org.sonar.api.utils.log.Loggers; | import org.sonar.api.utils.log.Loggers; | ||||
.setUpdatedAt(pluginEntry.getValue().getUpdatedAt()).build()); | .setUpdatedAt(pluginEntry.getValue().getUpdatedAt()).build()); | ||||
} | } | ||||
addModulesRelativePaths(builder); | |||||
addRelativePathFromScmRoot(builder); | |||||
writer.writeMetadata(builder.build()); | writer.writeMetadata(builder.build()); | ||||
} | } | ||||
private void addModulesRelativePaths(ScannerReport.Metadata.Builder builder) { | |||||
LinkedList<DefaultInputModule> queue = new LinkedList<>(); | |||||
queue.add(moduleHierarchy.root()); | |||||
while (!queue.isEmpty()) { | |||||
DefaultInputModule module = queue.removeFirst(); | |||||
queue.addAll(moduleHierarchy.children(module)); | |||||
String relativePath = moduleHierarchy.relativePathToRoot(module); | |||||
if (relativePath != null) { | |||||
builder.putModulesProjectRelativePathByKey(module.key(), relativePath); | |||||
} | |||||
} | |||||
private void addRelativePathFromScmRoot(ScannerReport.Metadata.Builder builder) { | |||||
ScmProvider scmProvider = scmConfiguration.provider(); | ScmProvider scmProvider = scmConfiguration.provider(); | ||||
if (scmProvider == null) { | if (scmProvider == null) { | ||||
return; | return; |
assertThat(metadata.getAnalysisDate()).isEqualTo(1234567L); | assertThat(metadata.getAnalysisDate()).isEqualTo(1234567L); | ||||
assertThat(metadata.getNewCodeReferenceBranch()).isEqualTo("newCodeReference"); | assertThat(metadata.getNewCodeReferenceBranch()).isEqualTo("newCodeReference"); | ||||
assertThat(metadata.getProjectKey()).isEqualTo("root"); | assertThat(metadata.getProjectKey()).isEqualTo("root"); | ||||
assertThat(metadata.getModulesProjectRelativePathByKeyMap()).containsOnly(entry("module", "modulePath"), entry("root", "")); | |||||
assertThat(metadata.getProjectVersion()).isEmpty(); | assertThat(metadata.getProjectVersion()).isEmpty(); | ||||
assertThat(metadata.getNotAnalyzedFilesByLanguageCount()).isZero(); | assertThat(metadata.getNotAnalyzedFilesByLanguageCount()).isZero(); | ||||
assertThat(metadata.getQprofilesPerLanguageMap()).containsOnly(entry("java", org.sonar.scanner.protocol.output.ScannerReport.Metadata.QProfile.newBuilder() | assertThat(metadata.getQprofilesPerLanguageMap()).containsOnly(entry("java", org.sonar.scanner.protocol.output.ScannerReport.Metadata.QProfile.newBuilder() |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2023 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 java.util.Optional; | |||||
import javax.annotation.CheckForNull; | |||||
import javax.annotation.Nullable; | |||||
public class MultiModuleProjectRepository implements ProjectRepositories { | |||||
private Map<String, SingleProjectRepository> repositoryPerModule = new HashMap<>(); | |||||
public MultiModuleProjectRepository 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); | |||||
} | |||||
} |
*/ | */ | ||||
package org.sonar.scanner.protocol.input; | package org.sonar.scanner.protocol.input; | ||||
public interface ProjectRepositories { | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
import javax.annotation.Nullable; | |||||
public class ProjectRepositories { | |||||
private final 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); | |||||
} | |||||
} | } |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2023 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 implements ProjectRepositories { | |||||
private Map<String, FileData> fileDataByPath = new HashMap<>(); | |||||
public SingleProjectRepository 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); | |||||
} | |||||
} |
string scm_revision_id = 13; | string scm_revision_id = 13; | ||||
string pull_request_key = 14; | string pull_request_key = 14; | ||||
map<string, string> modules_project_relative_path_by_key = 15; | |||||
reserved 15; // modules_project_relative_path_by_key (no longer used) | |||||
string projectVersion = 16; | string projectVersion = 16; | ||||
string buildString = 17; | string buildString = 17; |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2023 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()).isEmpty(); | |||||
} | |||||
@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()).isEmpty(); | |||||
} | |||||
} |
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
public class SingleProjectRepositoryTest { | |||||
private SingleProjectRepository repository; | |||||
@Before | |||||
public void setUp() { | |||||
repository = new SingleProjectRepository(); | |||||
} | |||||
public class ProjectRepositoriesTest { | |||||
private final ProjectRepositories repository = new ProjectRepositories(); | |||||
@Test | @Test | ||||
public void add_file_data() { | public void add_file_data() { |