diff options
author | Jacek <jacek.poreda@sonarsource.com> | 2024-01-05 14:58:48 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-01-05 20:02:36 +0000 |
commit | f8fd928e5a6627c245b85342af5f6d367f49eb28 (patch) | |
tree | 50284691970a1b66d793e6fb50330aa598680e0f /server/sonar-ce-task-projectanalysis | |
parent | 8c881ac397129bfef148499b85d047377a3c332f (diff) | |
download | sonarqube-f8fd928e5a6627c245b85342af5f6d367f49eb28.tar.gz sonarqube-f8fd928e5a6627c245b85342af5f6d367f49eb28.zip |
SONAR-18963 Index peristed issues only
Diffstat (limited to 'server/sonar-ce-task-projectanalysis')
17 files changed, 545 insertions, 102 deletions
diff --git a/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/IndexAnalysisStepIT.java b/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/IndexAnalysisStepIT.java index a1ac674645b..a9edff59ac0 100644 --- a/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/IndexAnalysisStepIT.java +++ b/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/IndexAnalysisStepIT.java @@ -19,20 +19,25 @@ */ package org.sonar.ce.task.projectanalysis.step; +import java.util.Optional; import java.util.Set; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule; import org.sonar.ce.task.projectanalysis.component.Component; -import org.sonar.ce.task.projectanalysis.component.FileStatuses; import org.sonar.ce.task.projectanalysis.component.ReportComponent; import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule; import org.sonar.ce.task.projectanalysis.component.ViewsComponent; +import org.sonar.ce.task.projectanalysis.index.IndexDiffResolver; import org.sonar.ce.task.step.ComputationStep; import org.sonar.ce.task.step.TestComputationStepContext; import org.sonar.db.DbClient; import org.sonar.db.DbSession; +import org.sonar.db.ce.CeActivityDao; +import org.sonar.db.ce.CeActivityDto; +import org.sonar.db.ce.CeQueueDto; +import org.sonar.db.ce.CeTaskTypes; import org.sonar.db.component.BranchDao; import org.sonar.server.es.AnalysisIndexer; @@ -53,11 +58,12 @@ public class IndexAnalysisStepIT extends BaseStepTest { public BatchReportReaderRule reportReader = new BatchReportReaderRule(); private final DbClient dbClient = mock(DbClient.class); - private final FileStatuses fileStatuses = mock(FileStatuses.class); + private final IndexDiffResolver indexDiffResolver = mock(IndexDiffResolver.class); private final AnalysisIndexer analysisIndexer = mock(AnalysisIndexer.class); private final DbSession dbSession = mock(DbSession.class); private final BranchDao branchDao = mock(BranchDao.class); - private final IndexAnalysisStep underTest = new IndexAnalysisStep(treeRootHolder, fileStatuses, dbClient, analysisIndexer); + private final CeActivityDao ceActivityDao = mock(CeActivityDao.class); + private final IndexAnalysisStep underTest = new IndexAnalysisStep(treeRootHolder, indexDiffResolver, dbClient, analysisIndexer); private TestComputationStepContext testComputationStepContext; @@ -67,6 +73,7 @@ public class IndexAnalysisStepIT extends BaseStepTest { when(dbClient.openSession(false)).thenReturn(dbSession); when(dbClient.branchDao()).thenReturn(branchDao); + when(dbClient.ceActivityDao()).thenReturn(ceActivityDao); } @Test @@ -76,36 +83,66 @@ public class IndexAnalysisStepIT extends BaseStepTest { underTest.execute(testComputationStepContext); - verify(analysisIndexer).indexOnAnalysis(PROJECT_UUID, Set.of()); + verify(analysisIndexer).indexOnAnalysis(PROJECT_UUID); } @Test - public void call_indexByProjectUuid_of_indexer_for_view() { + public void execute_indexByProjectUuid_of_indexer_for_view() { Component view = ViewsComponent.builder(VIEW, PROJECT_KEY).setUuid(PROJECT_UUID).build(); treeRootHolder.setRoot(view); underTest.execute(testComputationStepContext); - verify(analysisIndexer).indexOnAnalysis(PROJECT_UUID, Set.of()); + verify(analysisIndexer).indexOnAnalysis(PROJECT_UUID); } @Test - public void execute_whenMarkAsUnchangedFlagActivated_shouldCallIndexOnAnalysisWithChangedComponents() { + public void execute_whenBranchIsNeedIssueSync_shouldReindexEverything() { Component project = ReportComponent.builder(PROJECT, 1).setUuid(PROJECT_UUID).setKey(PROJECT_KEY).build(); treeRootHolder.setRoot(project); - Set<String> anyUuids = Set.of("any-uuid"); - when(fileStatuses.getFileUuidsMarkedAsUnchanged()).thenReturn(anyUuids); + when(branchDao.isBranchNeedIssueSync(dbSession, PROJECT_UUID)).thenReturn(true); underTest.execute(testComputationStepContext); - verify(analysisIndexer).indexOnAnalysis(PROJECT_UUID, anyUuids); + verify(analysisIndexer).indexOnAnalysis(PROJECT_UUID); } @Test - public void execute_whenBranchIsNeedIssueSync_shouldReindexEverything() { + public void execute_whenConditionsForDiffMet_shouldReindexDifferenceOnly() { Component project = ReportComponent.builder(PROJECT, 1).setUuid(PROJECT_UUID).setKey(PROJECT_KEY).build(); treeRootHolder.setRoot(project); - when(branchDao.isBranchNeedIssueSync(dbSession, PROJECT_UUID)).thenReturn(true); + when(ceActivityDao.selectLastByComponentUuidAndTaskType(dbSession, PROJECT_UUID, CeTaskTypes.REPORT)) + .thenReturn(Optional.of(new CeActivityDto(new CeQueueDto()).setStatus(org.sonar.db.ce.CeActivityDto.Status.SUCCESS))); + when(analysisIndexer.supportDiffIndexing()).thenReturn(true); + when(indexDiffResolver.resolve(analysisIndexer.getClass())).thenReturn(Set.of("foo", "bar")); + + underTest.execute(testComputationStepContext); + + verify(analysisIndexer).indexOnAnalysis(PROJECT_UUID, Set.of("foo", "bar")); + } + + @Test + public void execute_whenFailedCETask_shouldReindexFully() { + Component project = ReportComponent.builder(PROJECT, 1).setUuid(PROJECT_UUID).setKey(PROJECT_KEY).build(); + treeRootHolder.setRoot(project); + when(ceActivityDao.selectLastByComponentUuidAndTaskType(dbSession, PROJECT_UUID, CeTaskTypes.REPORT)) + .thenReturn(Optional.of(new CeActivityDto(new CeQueueDto()).setStatus(CeActivityDto.Status.FAILED))); + when(analysisIndexer.supportDiffIndexing()).thenReturn(true); + when(indexDiffResolver.resolve(analysisIndexer.getClass())).thenReturn(Set.of("foo", "bar")); + + underTest.execute(testComputationStepContext); + + verify(analysisIndexer).indexOnAnalysis(PROJECT_UUID); + } + + @Test + public void execute_whenNoCETask_shouldReindexFully() { + Component project = ReportComponent.builder(PROJECT, 1).setUuid(PROJECT_UUID).setKey(PROJECT_KEY).build(); + treeRootHolder.setRoot(project); + when(ceActivityDao.selectLastByComponentUuidAndTaskType(dbSession, PROJECT_UUID, CeTaskTypes.REPORT)) + .thenReturn(Optional.empty()); + when(analysisIndexer.supportDiffIndexing()).thenReturn(true); + when(indexDiffResolver.resolve(analysisIndexer.getClass())).thenReturn(Set.of("foo", "bar")); underTest.execute(testComputationStepContext); diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/FileStatuses.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/FileStatuses.java index fd90e78737e..5df76c4bab3 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/FileStatuses.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/FileStatuses.java @@ -19,8 +19,6 @@ */ package org.sonar.ce.task.projectanalysis.component; -import java.util.Set; - public interface FileStatuses { /** * A file is unchanged compared to the last analysis if it was detected as unchanged by the scanner and @@ -29,7 +27,4 @@ public interface FileStatuses { boolean isUnchanged(Component component); boolean isDataUnchanged(Component component); - - Set<String> getFileUuidsMarkedAsUnchanged(); - } diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/FileStatusesImpl.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/FileStatusesImpl.java index 6707d0f43ed..5f0e34472ba 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/FileStatusesImpl.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/FileStatusesImpl.java @@ -27,7 +27,6 @@ import org.sonar.ce.task.projectanalysis.source.SourceHashRepository; import org.sonar.db.source.FileHashesDto; import static com.google.common.base.Preconditions.checkState; -import static java.util.Collections.unmodifiableSet; import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER; public class FileStatusesImpl implements FileStatuses { @@ -88,12 +87,6 @@ public class FileStatusesImpl implements FileStatuses { return fileUuidsMarkedAsUnchanged.contains(component.getUuid()); } - @Override - public Set<String> getFileUuidsMarkedAsUnchanged() { - failIfNotInitialized(); - return unmodifiableSet(fileUuidsMarkedAsUnchanged); - } - private boolean hashEquals(Component component) { Optional<String> dbHash = previousSourceHashRepository.getDbFile(component).map(FileHashesDto::getSrcHash); return dbHash.map(hash -> hash.equals(sourceHashRepository.getRawSourceHash(component))).orElse(false); diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java index d8ae9abb6f7..1f8615159df 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java @@ -52,7 +52,9 @@ import org.sonar.ce.task.projectanalysis.filemove.ScoreMatrixDumperImpl; import org.sonar.ce.task.projectanalysis.filemove.SourceSimilarityImpl; import org.sonar.ce.task.projectanalysis.filesystem.ComputationTempFolderProvider; import org.sonar.ce.task.projectanalysis.issue.AnticipatedTransitionRepositoryImpl; +import org.sonar.ce.task.projectanalysis.index.IndexDiffResolverImpl; import org.sonar.ce.task.projectanalysis.issue.BaseIssuesLoader; +import org.sonar.ce.task.projectanalysis.issue.ChangedIssuesRepository; import org.sonar.ce.task.projectanalysis.issue.CloseIssuesOnRemovedComponentsVisitor; import org.sonar.ce.task.projectanalysis.issue.ClosedIssuesInputFactory; import org.sonar.ce.task.projectanalysis.issue.ComponentIssuesLoader; @@ -194,6 +196,7 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop new ComputationTempFolderProvider(), FileStatusesImpl.class, + IndexDiffResolverImpl.class, new MetricModule(), // holders @@ -271,6 +274,7 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop IssuesRepositoryVisitor.class, RemoveProcessedComponentsVisitor.class, IssueOnReferenceBranchVisitor.class, + ChangedIssuesRepository.class, // visitors : order is important, measure computers must be executed at the end in order to access to every measures / issues AnalysisFromSonarQube94Visitor.class, diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/index/IndexDiffResolver.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/index/IndexDiffResolver.java new file mode 100644 index 00000000000..5c6ad50f541 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/index/IndexDiffResolver.java @@ -0,0 +1,27 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.index; + +import java.util.Collection; +import org.sonar.server.es.AnalysisIndexer; + +public interface IndexDiffResolver { + Collection<String> resolve(Class<? extends AnalysisIndexer> clazz); +} diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/index/IndexDiffResolverImpl.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/index/IndexDiffResolverImpl.java new file mode 100644 index 00000000000..33d2878fc78 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/index/IndexDiffResolverImpl.java @@ -0,0 +1,41 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.index; + +import java.util.Collection; +import org.sonar.ce.task.projectanalysis.issue.ChangedIssuesRepository; +import org.sonar.server.es.AnalysisIndexer; +import org.sonar.server.issue.index.IssueIndexer; + +public class IndexDiffResolverImpl implements IndexDiffResolver { + private final ChangedIssuesRepository changedIssuesRepository; + + public IndexDiffResolverImpl(ChangedIssuesRepository changedIssuesRepository) { + this.changedIssuesRepository = changedIssuesRepository; + } + + @Override + public Collection<String> resolve(Class<? extends AnalysisIndexer> clazz) { + if (clazz.isAssignableFrom(IssueIndexer.class)) { + return changedIssuesRepository.getChangedIssuesKeys(); + } + throw new UnsupportedOperationException("Unsupported indexer: " + clazz); + } +} diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/index/NopDiffResolverImpl.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/index/NopDiffResolverImpl.java new file mode 100644 index 00000000000..3611b6d6246 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/index/NopDiffResolverImpl.java @@ -0,0 +1,36 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.index; + +import java.util.Collection; +import java.util.Collections; +import org.sonar.server.es.AnalysisIndexer; + +public class NopDiffResolverImpl implements IndexDiffResolver { + + public NopDiffResolverImpl() { + //nothing to do + } + + @Override + public Collection<String> resolve(Class<? extends AnalysisIndexer> clazz) { + return Collections.emptyList(); + } +} diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/index/package-info.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/index/package-info.java new file mode 100644 index 00000000000..459ee2c2949 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/index/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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. + */ +@ParametersAreNonnullByDefault +package org.sonar.ce.task.projectanalysis.index; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/NopFileStatuses.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ChangedIssuesRepository.java index 5740b312fe5..0138f09bccf 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/NopFileStatuses.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ChangedIssuesRepository.java @@ -17,29 +17,19 @@ * 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; - +package org.sonar.ce.task.projectanalysis.issue; +import java.util.HashSet; import java.util.Set; -/** - * No operation implementation of {@link FileStatuses} - */ -public final class NopFileStatuses implements FileStatuses { - - - @Override - public boolean isUnchanged(Component component) { - return false; - } +public class ChangedIssuesRepository { + private final Set<String> changedIssuesKeys = new HashSet<>(); - @Override - public boolean isDataUnchanged(Component component) { - return false; + public void addIssueKey(String issueKey) { + changedIssuesKeys.add(issueKey); } - @Override - public Set<String> getFileUuidsMarkedAsUnchanged() { - return Set.of(); + public Set<String> getChangedIssuesKeys() { + return changedIssuesKeys; } } diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/IndexAnalysisStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/IndexAnalysisStep.java index 21967541467..33ac38cbd26 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/IndexAnalysisStep.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/IndexAnalysisStep.java @@ -19,48 +19,59 @@ */ package org.sonar.ce.task.projectanalysis.step; -import java.util.Set; -import java.util.function.Consumer; +import java.util.Collection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.sonar.ce.task.projectanalysis.component.FileStatuses; +import org.sonar.ce.task.projectanalysis.component.Component; import org.sonar.ce.task.projectanalysis.component.TreeRootHolder; +import org.sonar.ce.task.projectanalysis.index.IndexDiffResolver; import org.sonar.ce.task.step.ComputationStep; import org.sonar.db.DbClient; import org.sonar.db.DbSession; +import org.sonar.db.ce.CeActivityDto; +import org.sonar.db.ce.CeTaskTypes; import org.sonar.server.es.AnalysisIndexer; public class IndexAnalysisStep implements ComputationStep { - private static final Logger LOGGER = LoggerFactory.getLogger(IndexAnalysisStep.class); - private final TreeRootHolder treeRootHolder; - private final FileStatuses fileStatuses; + private final IndexDiffResolver indexDiffResolver; private final AnalysisIndexer[] indexers; private final DbClient dbClient; - public IndexAnalysisStep(TreeRootHolder treeRootHolder, FileStatuses fileStatuses, DbClient dbClient, AnalysisIndexer... indexers) { + public IndexAnalysisStep(TreeRootHolder treeRootHolder, IndexDiffResolver indexDiffResolver, DbClient dbClient, AnalysisIndexer... indexers) { this.treeRootHolder = treeRootHolder; - this.fileStatuses = fileStatuses; + this.indexDiffResolver = indexDiffResolver; this.indexers = indexers; this.dbClient = dbClient; } @Override public void execute(ComputationStep.Context context) { - String branchUuid = treeRootHolder.getRoot().getUuid(); - Consumer<AnalysisIndexer> analysisIndexerConsumer = getAnalysisIndexerConsumer(branchUuid); + Component root = treeRootHolder.getRoot(); + String branchUuid = root.getUuid(); + for (AnalysisIndexer indexer : indexers) { LOGGER.debug("Call {}", indexer); - analysisIndexerConsumer.accept(indexer); + if (isDiffIndexingSupported(root, indexer) && hasPreviousAnalysisSucceeded(branchUuid) && !isBranchNeedIssueSync(branchUuid)) { + Collection<String> diffSet = indexDiffResolver.resolve(indexer.getClass()); + indexer.indexOnAnalysis(branchUuid, diffSet); + } else { + indexer.indexOnAnalysis(branchUuid); + } + } + } + + private boolean hasPreviousAnalysisSucceeded(String branchUuid) { + try (DbSession dbSession = dbClient.openSession(false)) { + return dbClient.ceActivityDao().selectLastByComponentUuidAndTaskType(dbSession, branchUuid, CeTaskTypes.REPORT) + .filter(activityDto -> CeActivityDto.Status.SUCCESS.equals(activityDto.getStatus())) + .isPresent(); } } - private Consumer<AnalysisIndexer> getAnalysisIndexerConsumer(String branchUuid) { - Set<String> fileUuidsMarkedAsUnchanged = fileStatuses.getFileUuidsMarkedAsUnchanged(); - return isBranchNeedIssueSync(branchUuid) - ? (indexer -> indexer.indexOnAnalysis(branchUuid)) - : (indexer -> indexer.indexOnAnalysis(branchUuid, fileUuidsMarkedAsUnchanged)); + private static boolean isDiffIndexingSupported(Component root, AnalysisIndexer indexer) { + return Component.Type.PROJECT.equals(root.getType()) && indexer.supportDiffIndexing(); } private boolean isBranchNeedIssueSync(String branchUuid) { diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadChangedIssuesStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadChangedIssuesStep.java new file mode 100644 index 00000000000..483688ab950 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadChangedIssuesStep.java @@ -0,0 +1,70 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.step; + +import org.sonar.ce.task.projectanalysis.issue.ChangedIssuesRepository; +import org.sonar.ce.task.projectanalysis.issue.ProtoIssueCache; +import org.sonar.ce.task.projectanalysis.period.PeriodHolder; +import org.sonar.ce.task.step.ComputationStep; +import org.sonar.core.issue.DefaultIssue; +import org.sonar.core.util.CloseableIterator; +import org.sonar.db.newcodeperiod.NewCodePeriodType; + +public class LoadChangedIssuesStep implements ComputationStep { + private final PeriodHolder periodHolder; + private final ProtoIssueCache protoIssueCache; + private final ChangedIssuesRepository changedIssuesRepository; + + public LoadChangedIssuesStep(PeriodHolder periodHolder, ProtoIssueCache protoIssueCache, ChangedIssuesRepository changedIssuesRepository) { + this.periodHolder = periodHolder; + this.protoIssueCache = protoIssueCache; + this.changedIssuesRepository = changedIssuesRepository; + } + + @Override + public void execute(Context context) { + try (CloseableIterator<DefaultIssue> issues = protoIssueCache.traverse()) { + while (issues.hasNext()) { + DefaultIssue issue = issues.next(); + if (shouldUpdateIndexForIssue(issue)) { + changedIssuesRepository.addIssueKey(issue.key()); + } + } + } + } + + private boolean shouldUpdateIndexForIssue(DefaultIssue issue) { + return issue.isNew() || issue.isCopied() || issue.isChanged() + || (isOnBranchUsingReferenceBranch() && (issue.isNoLongerNewCodeReferenceIssue() || issue.isToBeMigratedAsNewCodeReferenceIssue())); + } + + private boolean isOnBranchUsingReferenceBranch() { + if (periodHolder.hasPeriod()) { + return NewCodePeriodType.REFERENCE_BRANCH.name().equals(periodHolder.getPeriod().getMode()); + } + return false; + } + + @Override + public String getDescription() { + return "Load changed issues for indexation"; + } + +} diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java index d5d3f4cb578..d0783b2708f 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java @@ -114,6 +114,7 @@ public class ReportComputationSteps extends AbstractComputationSteps { UpdateQualityProfilesLastUsedDateStep.class, PurgeDatastoresStep.class, + LoadChangedIssuesStep.class, IndexAnalysisStep.class, UpdateNeedIssueSyncStep.class, ProjectNclocComputationStep.class, diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/FileStatusesImplTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/FileStatusesImplTest.java index 0288d225d2e..e6e936084f4 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/FileStatusesImplTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/FileStatusesImplTest.java @@ -29,7 +29,6 @@ import org.sonar.ce.task.projectanalysis.source.SourceHashRepository; import org.sonar.db.source.FileHashesDto; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -174,33 +173,6 @@ public class FileStatusesImplTest { verify(previousSourceHashRepository).getDbFile(file1); } - @Test - public void getFileUuidsMarkedAsUnchanged_whenNotInitialized_shouldFail() { - assertThatThrownBy(fileStatuses::getFileUuidsMarkedAsUnchanged) - .isInstanceOf(IllegalStateException.class) - .hasMessage("Not initialized"); - } - - @Test - public void getFileUuidsMarkedAsUnchanged_shouldReturnMarkAsUnchangedFileUuids() { - Component file1 = ReportComponent.builder(Component.Type.FILE, 2, "FILE1_KEY").setStatus(Component.Status.SAME) - .setFileAttributes(new FileAttributes(false, null, 10, true, null)).build(); - Component file2 = ReportComponent.builder(Component.Type.FILE, 3, "FILE2_KEY").setStatus(Component.Status.SAME).build(); - addDbFileHash(file1, "hash1"); - addDbFileHash(file2, "hash2"); - addReportFileHash(file1, "hash1"); - addReportFileHash(file2, "hash2"); - Component project = ReportComponent.builder(Component.Type.PROJECT, 1) - .setUuid(PROJECT_UUID) - .setKey(PROJECT_KEY) - .addChildren(file1, file2) - .build(); - treeRootHolder.setRoot(project); - fileStatuses.initialize(); - - assertThat(fileStatuses.getFileUuidsMarkedAsUnchanged()).contains(file1.getUuid()); - } - private void addDbFileHash(Component file, String hash) { FileHashesDto fileHashesDto = new FileHashesDto().setSrcHash(hash); when(previousSourceHashRepository.getDbFile(file)).thenReturn(Optional.of(fileHashesDto)); diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/index/IndexDiffResolverImplTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/index/IndexDiffResolverImplTest.java new file mode 100644 index 00000000000..ea521426a6e --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/index/IndexDiffResolverImplTest.java @@ -0,0 +1,56 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.index; + +import java.util.Collection; +import java.util.Set; +import org.junit.Test; +import org.sonar.ce.task.projectanalysis.issue.ChangedIssuesRepository; +import org.sonar.server.issue.index.IssueIndexer; +import org.sonar.server.measure.index.ProjectMeasuresIndexer; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class IndexDiffResolverImplTest { + + private final ChangedIssuesRepository changedIssuesRepository = mock(ChangedIssuesRepository.class); + + private final IndexDiffResolverImpl underTest = new IndexDiffResolverImpl(changedIssuesRepository); + + @Test + public void resolve_whenIssueIndexer_shouldReturnChangedIssueKeys() { + when(changedIssuesRepository.getChangedIssuesKeys()).thenReturn(Set.of("key1", "key2","key3")); + Collection<String> resolvedDiff = underTest.resolve(IssueIndexer.class); + + assertThat(resolvedDiff) + .containsExactlyInAnyOrder("key1", "key2","key3"); + } + + @Test + public void resolve_whenUnsupportedIndexer_shouldThrowUPE() { + when(changedIssuesRepository.getChangedIssuesKeys()).thenReturn(Set.of("key1", "key2","key3")); + assertThatThrownBy(() ->underTest.resolve(ProjectMeasuresIndexer.class)) + .isInstanceOf(UnsupportedOperationException.class) + .hasMessage("Unsupported indexer: class org.sonar.server.measure.index.ProjectMeasuresIndexer"); + } +} diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/index/NopDiffResolverImplTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/index/NopDiffResolverImplTest.java new file mode 100644 index 00000000000..5e1e1126b52 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/index/NopDiffResolverImplTest.java @@ -0,0 +1,37 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.index; + +import org.junit.Test; +import org.sonar.server.issue.index.IssueIndexer; + +import static org.assertj.core.api.Assertions.assertThat; + +public class NopDiffResolverImplTest { + + private final NopDiffResolverImpl underTest = new NopDiffResolverImpl(); + + @Test + public void resolve_shouldDoNothing() { + assertThat(underTest.resolve(IssueIndexer.class)) + .isEmpty(); + } + +} diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/NopFileStatusesTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/ChangedIssuesRepositoryTest.java index 8040f40b923..40063f362f5 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/NopFileStatusesTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/ChangedIssuesRepositoryTest.java @@ -17,31 +17,24 @@ * 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; +package org.sonar.ce.task.projectanalysis.issue; import org.junit.Test; -import org.mockito.Mockito; import static org.assertj.core.api.Assertions.assertThat; -public class NopFileStatusesTest { +public class ChangedIssuesRepositoryTest { - private final NopFileStatuses nopFileStatuses = new NopFileStatuses(); - - private final Component component = Mockito.mock(Component.class); + private final ChangedIssuesRepository underTest = new ChangedIssuesRepository(); @Test - public void isUnchanged() { - assertThat(nopFileStatuses.isUnchanged(component)).isFalse(); - } + public void addIssueKey_shouldAddKeysToRepository() { + underTest.addIssueKey("key1"); + underTest.addIssueKey("key2"); + underTest.addIssueKey("key3"); - @Test - public void isDataUnchanged() { - assertThat(nopFileStatuses.isDataUnchanged(component)).isFalse(); + assertThat(underTest.getChangedIssuesKeys()) + .containsExactlyInAnyOrder("key1", "key2", "key3"); } - @Test - public void getFileUuidsMarkedAsUnchanged() { - assertThat(nopFileStatuses.getFileUuidsMarkedAsUnchanged()).isEmpty(); - } } diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadChangedIssuesStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadChangedIssuesStepTest.java new file mode 100644 index 00000000000..ae7d40a51ac --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadChangedIssuesStepTest.java @@ -0,0 +1,157 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.step; + +import java.io.IOException; +import java.util.Date; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.issue.impact.Severity; +import org.sonar.api.issue.impact.SoftwareQuality; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rules.RuleType; +import org.sonar.api.utils.System2; +import org.sonar.ce.task.projectanalysis.issue.ChangedIssuesRepository; +import org.sonar.ce.task.projectanalysis.issue.ProtoIssueCache; +import org.sonar.ce.task.projectanalysis.period.Period; +import org.sonar.ce.task.projectanalysis.period.PeriodHolder; +import org.sonar.ce.task.step.ComputationStep; +import org.sonar.core.issue.DefaultIssue; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.sonar.api.issue.Issue.STATUS_OPEN; +import static org.sonar.api.rule.Severity.BLOCKER; + +public class LoadChangedIssuesStepTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + private final PeriodHolder periodHolder = mock(PeriodHolder.class); + private ProtoIssueCache protoIssueCache; + + private final ChangedIssuesRepository changedIssuesRepository = mock(ChangedIssuesRepository.class); + + private LoadChangedIssuesStep underTest; + + @Before + public void before() throws IOException { + protoIssueCache = new ProtoIssueCache(temp.newFile(), System2.INSTANCE); + underTest = new LoadChangedIssuesStep(periodHolder, protoIssueCache, changedIssuesRepository); + } + + @Test + public void getDescription_shouldReturnDescription() { + assertThat(underTest.getDescription()).isEqualTo("Load changed issues for indexation"); + } + + @Test + public void execute_whenIssueIsNew_shouldLoadIssue() { + protoIssueCache.newAppender() + .append(newDefaultIssue().setNew(true)) + .close(); + + underTest.execute(mock(ComputationStep.Context.class)); + + verify(changedIssuesRepository).addIssueKey("issueKey1"); + } + + @Test + public void execute_whenIssueIssCopied_shouldLoadIssue() { + protoIssueCache.newAppender() + .append(newDefaultIssue().setCopied(true)) + .close(); + + underTest.execute(mock(ComputationStep.Context.class)); + + verify(changedIssuesRepository).addIssueKey("issueKey1"); + } + + @Test + public void execute_whenIssueIsChanged_shouldLoadIssue() { + protoIssueCache.newAppender() + .append(newDefaultIssue().setChanged(true)) + .close(); + + underTest.execute(mock(ComputationStep.Context.class)); + + verify(changedIssuesRepository).addIssueKey("issueKey1"); + } + + @Test + public void execute_whenIssueIsNoLongerNewCodeReferenceIssue_shouldLoadIssue() { + when(periodHolder.hasPeriod()).thenReturn(true); + when(periodHolder.getPeriod()).thenReturn(new Period("REFERENCE_BRANCH", null, null)); + + protoIssueCache.newAppender() + .append(newDefaultIssue() + .setIsNoLongerNewCodeReferenceIssue(true) + .setNew(false) + .setCopied(false) + .setChanged(false)) + .close(); + + underTest.execute(mock(ComputationStep.Context.class)); + + verify(changedIssuesRepository).addIssueKey("issueKey1"); + } + + @Test + public void execute_whenIssueIsToBeMigratedAsNewCodeReferenceIssue_shouldLoadIssue() { + when(periodHolder.hasPeriod()).thenReturn(true); + when(periodHolder.getPeriod()).thenReturn(new Period("REFERENCE_BRANCH", null, null)); + + protoIssueCache.newAppender() + .append(newDefaultIssue() + .setIsOnChangedLine(true) + .setIsNewCodeReferenceIssue(false) + .setIsNoLongerNewCodeReferenceIssue(false) + .setNew(false) + .setCopied(false) + .setChanged(false)) + .close(); + + underTest.execute(mock(ComputationStep.Context.class)); + + verify(changedIssuesRepository).addIssueKey("issueKey1"); + } + + private static DefaultIssue newDefaultIssue() { + return new DefaultIssue() + .setKey("issueKey1") + .setType(RuleType.CODE_SMELL) + .setRuleKey(RuleKey.of("repo", "ruleKey1")) + .setComponentUuid("fileUuid") + .setComponentKey("fileKey") + .setProjectUuid("projectUuid") + .setProjectKey("projectKey") + .setSeverity(BLOCKER) + .setStatus(STATUS_OPEN) + .setCreationDate(new Date()) + .setSelectedAt(1L) + .addImpact(SoftwareQuality.SECURITY, Severity.MEDIUM); + } + +} |