Просмотр исходного кода

SONAR-11513 backdate issues on new files

tags/7.5
Sébastien Lesaint 5 лет назад
Родитель
Сommit
e96e6304d1

+ 2
- 0
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java Просмотреть файл

@@ -43,6 +43,7 @@ import org.sonar.ce.task.projectanalysis.duplication.DuplicationMeasures;
import org.sonar.ce.task.projectanalysis.duplication.DuplicationRepositoryImpl;
import org.sonar.ce.task.projectanalysis.duplication.IntegrateCrossProjectDuplications;
import org.sonar.ce.task.projectanalysis.event.EventRepositoryImpl;
import org.sonar.ce.task.projectanalysis.filemove.AddedFileRepositoryImpl;
import org.sonar.ce.task.projectanalysis.filemove.FileSimilarityImpl;
import org.sonar.ce.task.projectanalysis.filemove.MutableMovedFilesRepositoryImpl;
import org.sonar.ce.task.projectanalysis.filemove.ScoreMatrixDumperImpl;
@@ -284,6 +285,7 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop
SourceSimilarityImpl.class,
FileSimilarityImpl.class,
MutableMovedFilesRepositoryImpl.class,
AddedFileRepositoryImpl.class,

// duplication
IntegrateCrossProjectDuplications.class,

+ 30
- 0
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/filemove/AddedFileRepository.java Просмотреть файл

@@ -0,0 +1,30 @@
/*
* SonarQube
* Copyright (C) 2009-2018 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.ce.task.projectanalysis.filemove;

import org.sonar.ce.task.projectanalysis.component.Component;

public interface AddedFileRepository {
/**
* @return {@code true} for any component on first analysis, otherwise {@code true} only if the specified component is
* a {@link Component.Type#FILE file} registered to the repository.
*/
boolean isAdded(Component component);
}

+ 60
- 0
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/filemove/AddedFileRepositoryImpl.java Просмотреть файл

@@ -0,0 +1,60 @@
/*
* SonarQube
* Copyright (C) 2009-2018 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.ce.task.projectanalysis.filemove;

import java.util.HashSet;
import java.util.Set;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
import org.sonar.ce.task.projectanalysis.component.Component;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

public class AddedFileRepositoryImpl implements MutableAddedFileRepository {
private final Set<Component> addedComponents = new HashSet<>();
private final AnalysisMetadataHolder analysisMetadataHolder;

public AddedFileRepositoryImpl(AnalysisMetadataHolder analysisMetadataHolder) {
this.analysisMetadataHolder = analysisMetadataHolder;
}

@Override
public boolean isAdded(Component component) {
checkComponent(component);
if (analysisMetadataHolder.isFirstAnalysis()) {
return true;
}
return addedComponents.contains(component);
}

@Override
public void register(Component component) {
checkComponent(component);
checkArgument(component.getType() == Component.Type.FILE, "component must be a file");
checkState(!analysisMetadataHolder.isFirstAnalysis(), "No file can be registered on first analysis");

addedComponents.add(component);
}

private static void checkComponent(Component component) {
checkNotNull(component, "component can't be null");
}
}

+ 50
- 13
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/filemove/FileMoveDetectionStep.java Просмотреть файл

@@ -22,7 +22,6 @@ package org.sonar.ce.task.projectanalysis.filemove;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import java.util.ArrayList;
@@ -62,7 +61,7 @@ import static com.google.common.collect.FluentIterable.from;
import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER;

public class FileMoveDetectionStep implements ComputationStep {
protected static final int MIN_REQUIRED_SCORE = 85;
static final int MIN_REQUIRED_SCORE = 85;
private static final Logger LOG = Loggers.get(FileMoveDetectionStep.class);
private static final Comparator<ScoreMatrix.ScoreFile> SCORE_FILE_COMPARATOR = (o1, o2) -> -1 * Integer.compare(o1.getLineCount(), o2.getLineCount());
private static final double LOWER_BOUND_RATIO = 0.84;
@@ -75,10 +74,11 @@ public class FileMoveDetectionStep implements ComputationStep {
private final MutableMovedFilesRepository movedFilesRepository;
private final SourceLinesHashRepository sourceLinesHash;
private final ScoreMatrixDumper scoreMatrixDumper;
private final MutableAddedFileRepository addedFileRepository;

public FileMoveDetectionStep(AnalysisMetadataHolder analysisMetadataHolder, TreeRootHolder rootHolder, DbClient dbClient,
FileSimilarity fileSimilarity, MutableMovedFilesRepository movedFilesRepository, SourceLinesHashRepository sourceLinesHash,
ScoreMatrixDumper scoreMatrixDumper) {
ScoreMatrixDumper scoreMatrixDumper, MutableAddedFileRepository addedFileRepository) {
this.analysisMetadataHolder = analysisMetadataHolder;
this.rootHolder = rootHolder;
this.dbClient = dbClient;
@@ -86,6 +86,7 @@ public class FileMoveDetectionStep implements ComputationStep {
this.movedFilesRepository = movedFilesRepository;
this.sourceLinesHash = sourceLinesHash;
this.scoreMatrixDumper = scoreMatrixDumper;
this.addedFileRepository = addedFileRepository;
}

@Override
@@ -103,25 +104,30 @@ public class FileMoveDetectionStep implements ComputationStep {
Profiler p = Profiler.createIfTrace(LOG);

p.start();
Map<String, DbComponent> dbFilesByKey = getDbFilesByKey();
context.getStatistics().add("dbFiles", dbFilesByKey.size());
if (dbFilesByKey.isEmpty()) {
LOG.debug("Previous snapshot has no file. Do nothing.");
return;
}

Map<String, Component> reportFilesByKey = getReportFilesByKey(this.rootHolder.getRoot());
context.getStatistics().add("reportFiles", reportFilesByKey.size());
if (reportFilesByKey.isEmpty()) {
LOG.debug("No files in report. Do nothing.");
LOG.debug("No files in report. No file move detection.");
return;
}

Set<String> addedFileKeys = ImmutableSet.copyOf(Sets.difference(reportFilesByKey.keySet(), dbFilesByKey.keySet()));
Map<String, DbComponent> dbFilesByKey = getDbFilesByKey();
context.getStatistics().add("dbFiles", dbFilesByKey.size());

Set<String> addedFileKeys = difference(reportFilesByKey.keySet(), dbFilesByKey.keySet());
context.getStatistics().add("addedFiles", addedFileKeys.size());
Set<String> removedFileKeys = ImmutableSet.copyOf(Sets.difference(dbFilesByKey.keySet(), reportFilesByKey.keySet()));

if (dbFilesByKey.isEmpty()) {
registerAddedFiles(addedFileKeys, reportFilesByKey, null);
LOG.debug("Previous snapshot has no file. No file move detection.");
return;
}

Set<String> removedFileKeys = difference(dbFilesByKey.keySet(), reportFilesByKey.keySet());

// can find matches if at least one of the added or removed files groups is empty => abort
if (addedFileKeys.isEmpty() || removedFileKeys.isEmpty()) {
registerAddedFiles(addedFileKeys, reportFilesByKey, null);
LOG.debug("Either no files added or no files removed. Do nothing.");
return;
}
@@ -138,6 +144,8 @@ public class FileMoveDetectionStep implements ComputationStep {

// not a single match with score higher than MIN_REQUIRED_SCORE => abort
if (scoreMatrix.getMaxScore() < MIN_REQUIRED_SCORE) {
context.getStatistics().add("movedFiles", 0);
registerAddedFiles(addedFileKeys, reportFilesByKey, null);
LOG.debug("max score in matrix is less than min required score ({}). Do nothing.", MIN_REQUIRED_SCORE);
return;
}
@@ -148,7 +156,16 @@ public class FileMoveDetectionStep implements ComputationStep {
ElectedMatches electedMatches = electMatches(removedFileKeys, reportFileSourcesByKey, matchesByScore);
p.stopTrace("Matches elected");

context.getStatistics().add("movedFiles", electedMatches.size());
registerMatches(dbFilesByKey, reportFilesByKey, electedMatches);
registerAddedFiles(addedFileKeys, reportFilesByKey, electedMatches);
}

public Set<String> difference(Set<String> set1, Set<String> set2) {
if (set1.isEmpty() || set2.isEmpty()) {
return set1;
}
return Sets.difference(set1, set2).immutableCopy();
}

private void registerMatches(Map<String, DbComponent> dbFilesByKey, Map<String, Component> reportFilesByKey, ElectedMatches electedMatches) {
@@ -161,6 +178,22 @@ public class FileMoveDetectionStep implements ComputationStep {
}
}

private void registerAddedFiles(Set<String> addedFileKeys, Map<String, Component> reportFilesByKey, @Nullable ElectedMatches electedMatches) {
if (electedMatches == null || electedMatches.isEmpty()) {
addedFileKeys.stream()
.map(reportFilesByKey::get)
.forEach(addedFileRepository::register);
} else {
Set<String> reallyAddedFileKeys = new HashSet<>(addedFileKeys);
for (Match electedMatch : electedMatches) {
reallyAddedFileKeys.remove(electedMatch.getReportKey());
}
reallyAddedFileKeys.stream()
.map(reportFilesByKey::get)
.forEach(addedFileRepository::register);
}
}

private Map<String, DbComponent> getDbFilesByKey() {
try (DbSession dbSession = dbClient.openSession(false)) {
ImmutableList.Builder<DbComponent> builder = ImmutableList.builder();
@@ -402,5 +435,9 @@ public class FileMoveDetectionStep implements ComputationStep {
public int size() {
return matches.size();
}

public boolean isEmpty() {
return matches.isEmpty();
}
}
}

+ 32
- 0
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/filemove/MutableAddedFileRepository.java Просмотреть файл

@@ -0,0 +1,32 @@
/*
* SonarQube
* Copyright (C) 2009-2018 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.ce.task.projectanalysis.filemove;

import org.sonar.ce.task.projectanalysis.component.Component;

public interface MutableAddedFileRepository extends AddedFileRepository {

/**
* @throws IllegalArgumentException if the specified component is not a {@link Component.Type#FILE File}
* @throws IllegalStateException on first analysis as all components are added on first analysis, none should be
* registered for performance reasons.
*/
void register(Component file);
}

+ 22
- 5
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueCreationDateCalculator.java Просмотреть файл

@@ -30,6 +30,7 @@ import org.sonar.ce.task.projectanalysis.analysis.Analysis;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
import org.sonar.ce.task.projectanalysis.analysis.ScannerPlugin;
import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.ce.task.projectanalysis.filemove.AddedFileRepository;
import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRule;
import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRulesHolder;
import org.sonar.ce.task.projectanalysis.scm.Changeset;
@@ -53,15 +54,18 @@ public class IssueCreationDateCalculator extends IssueVisitor {
private final IssueChangeContext changeContext;
private final ActiveRulesHolder activeRulesHolder;
private final RuleRepository ruleRepository;
private final AddedFileRepository addedFileRepository;

public IssueCreationDateCalculator(AnalysisMetadataHolder analysisMetadataHolder, ScmInfoRepository scmInfoRepository,
IssueFieldsSetter issueUpdater, ActiveRulesHolder activeRulesHolder, RuleRepository ruleRepository) {
IssueFieldsSetter issueUpdater, ActiveRulesHolder activeRulesHolder, RuleRepository ruleRepository,
AddedFileRepository addedFileRepository) {
this.scmInfoRepository = scmInfoRepository;
this.issueUpdater = issueUpdater;
this.analysisMetadataHolder = analysisMetadataHolder;
this.ruleRepository = ruleRepository;
this.changeContext = createScan(new Date(analysisMetadataHolder.getAnalysisDate()));
this.activeRulesHolder = activeRulesHolder;
this.addedFileRepository = addedFileRepository;
}

@Override
@@ -69,23 +73,36 @@ public class IssueCreationDateCalculator extends IssueVisitor {
if (!issue.isNew()) {
return;
}

Optional<Long> lastAnalysisOptional = lastAnalysis();
boolean firstAnalysis = !lastAnalysisOptional.isPresent();
if (firstAnalysis || isNewFile(component)) {
backdateIssue(component, issue);
return;
}

Rule rule = ruleRepository.findByKey(issue.getRuleKey())
.orElseThrow(illegalStateException("The rule with key '%s' raised an issue, but no rule with that key was found", issue.getRuleKey()));

if (rule.isExternal()) {
getDateOfLatestChange(component, issue).ifPresent(changeDate -> updateDate(issue, changeDate));
backdateIssue(component, issue);
} else {
// Rule can't be inactive (see contract of IssueVisitor)
ActiveRule activeRule = activeRulesHolder.get(issue.getRuleKey()).get();
if (firstAnalysis || activeRuleIsNew(activeRule, lastAnalysisOptional.get())
if (activeRuleIsNew(activeRule, lastAnalysisOptional.get())
|| ruleImplementationChanged(activeRule.getRuleKey(), activeRule.getPluginKey(), lastAnalysisOptional.get())) {
getDateOfLatestChange(component, issue).ifPresent(changeDate -> updateDate(issue, changeDate));
backdateIssue(component, issue);
}
}
}

private boolean isNewFile(Component component) {
return component.getType() == Component.Type.FILE && addedFileRepository.isAdded(component);
}

private void backdateIssue(Component component, DefaultIssue issue) {
getDateOfLatestChange(component, issue).ifPresent(changeDate -> updateDate(issue, changeDate));
}

private boolean ruleImplementationChanged(RuleKey ruleKey, @Nullable String pluginKey, long lastAnalysisDate) {
if (pluginKey == null) {
return false;

+ 129
- 0
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/filemove/AddedFileRepositoryImplTest.java Просмотреть файл

@@ -0,0 +1,129 @@
/*
* SonarQube
* Copyright (C) 2009-2018 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.ce.task.projectanalysis.filemove;

import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
import java.util.Arrays;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
import org.sonar.ce.task.projectanalysis.component.Component;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@RunWith(DataProviderRunner.class)
public class AddedFileRepositoryImplTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();

private AnalysisMetadataHolder analysisMetadataHolder = mock(AnalysisMetadataHolder.class);
private AddedFileRepositoryImpl underTest = new AddedFileRepositoryImpl(analysisMetadataHolder);

@Test
public void isAdded_fails_with_NPE_if_component_is_null() {
expectedException.expect(NullPointerException.class);
expectedException.expectMessage("component can't be null");

underTest.isAdded(null);
}

@Test
public void isAdded_returns_true_for_any_component_type_on_first_analysis() {
when(analysisMetadataHolder.isFirstAnalysis()).thenReturn(true);

Arrays.stream(Component.Type.values()).forEach(type -> {
Component component = newComponent(type);

assertThat(underTest.isAdded(component)).isTrue();
});
}

@Test
public void isAdded_returns_false_for_unregistered_component_type_when_not_on_first_analysis() {
when(analysisMetadataHolder.isFirstAnalysis()).thenReturn(false);

Arrays.stream(Component.Type.values()).forEach(type -> {
Component component = newComponent(type);

assertThat(underTest.isAdded(component)).isFalse();
});
}

@Test
public void isAdded_returns_true_for_registered_file_when_not_on_first_analysis() {
when(analysisMetadataHolder.isFirstAnalysis()).thenReturn(false);
Component file1 = newComponent(Component.Type.FILE);
Component file2 = newComponent(Component.Type.FILE);
underTest.register(file1);

assertThat(underTest.isAdded(file1)).isTrue();
assertThat(underTest.isAdded(file2)).isFalse();
}

@Test
public void register_fails_with_NPE_if_component_is_null() {
expectedException.expect(NullPointerException.class);
expectedException.expectMessage("component can't be null");

underTest.register(null);
}

@Test
@UseDataProvider("anyTypeButFile")
public void register_fails_with_IAE_if_component_is_not_a_file(Component.Type anyTypeButFile) {
Component component = newComponent(anyTypeButFile);

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("component must be a file");

underTest.register(component);
}

@DataProvider
public static Object[][] anyTypeButFile() {
return Arrays.stream(Component.Type.values())
.filter(t -> t != Component.Type.FILE)
.map(t -> new Object[] {t})
.toArray(Object[][]::new);
}

@Test
public void register_fails_with_ISE_if_called_on_first_analysis() {
when(analysisMetadataHolder.isFirstAnalysis()).thenReturn(true);
Component component = newComponent(Component.Type.FILE);

expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("No file can be registered on first analysis");

underTest.register(component);
}

private static Component newComponent(Component.Type type) {
Component component = mock(Component.class);
when(component.getType()).thenReturn(type);
return component;
}
}

+ 63
- 20
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/filemove/FileMoveDetectionStepTest.java Просмотреть файл

@@ -22,7 +22,9 @@ package org.sonar.ce.task.projectanalysis.filemove;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.IntStream;
@@ -228,9 +230,10 @@ public class FileMoveDetectionStepTest {
private SourceLinesHashRepository sourceLinesHash = mock(SourceLinesHashRepository.class);
private FileSimilarity fileSimilarity = new FileSimilarityImpl(new SourceSimilarityImpl());
private CapturingScoreMatrixDumper scoreMatrixDumper = new CapturingScoreMatrixDumper();
private RecordingMutableAddedFileRepository addedFileRepository = new RecordingMutableAddedFileRepository();

private FileMoveDetectionStep underTest = new FileMoveDetectionStep(analysisMetadataHolder, treeRootHolder, dbClient,
fileSimilarity, movedFilesRepository, sourceLinesHash, scoreMatrixDumper);
fileSimilarity, movedFilesRepository, sourceLinesHash, scoreMatrixDumper, addedFileRepository);

@Before
public void setUp() throws Exception {
@@ -245,23 +248,26 @@ public class FileMoveDetectionStepTest {
}

@Test
public void execute_detects_no_move_if_baseProjectSnapshot_is_null() {
public void execute_detects_no_move_on_first_analysis() {
analysisMetadataHolder.setBaseAnalysis(null);

TestComputationStepContext context = new TestComputationStepContext();
underTest.execute(context);

assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
verifyStatistics(context, null, null);
verifyStatistics(context, null, null, null, null);
}

@Test
public void execute_detects_no_move_if_baseSnapshot_has_no_file_and_report_has_no_file() {
analysisMetadataHolder.setBaseAnalysis(ANALYSIS);

underTest.execute(new TestComputationStepContext());
TestComputationStepContext context = new TestComputationStepContext();
underTest.execute(context);

assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
assertThat(addedFileRepository.getComponents()).isEmpty();
verifyStatistics(context, 0, null, null, null);
}

@Test
@@ -275,7 +281,8 @@ public class FileMoveDetectionStepTest {
underTest.execute(context);

assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
verifyStatistics(context, 0, null);
assertThat(addedFileRepository.getComponents()).containsOnly(file1, file2);
verifyStatistics(context, 2, 0, 2, null);
}

@Test
@@ -288,7 +295,8 @@ public class FileMoveDetectionStepTest {
underTest.execute(context);

assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
verifyStatistics(context, 0, null);
assertThat(addedFileRepository.getComponents()).isEmpty();
verifyStatistics(context, 0, null, null, null);
}

@Test
@@ -297,13 +305,16 @@ public class FileMoveDetectionStepTest {
Component file1 = fileComponent(FILE_1_REF, null);
Component file2 = fileComponent(FILE_2_REF, null);
insertFiles(file1.getDbKey(), file2.getDbKey());
insertContentOfFileInDb(file1.getDbKey(), CONTENT1);
insertContentOfFileInDb(file2.getDbKey(), CONTENT2);
setFilesInReport(file2, file1);

TestComputationStepContext context = new TestComputationStepContext();
underTest.execute(context);

assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
verifyStatistics(context, 0, null);
assertThat(addedFileRepository.getComponents()).isEmpty();
verifyStatistics(context, 2, 2, 0, null);
}

@Test
@@ -323,7 +334,8 @@ public class FileMoveDetectionStepTest {
assertThat(originalFile.getId()).isEqualTo(dtos[0].getId());
assertThat(originalFile.getKey()).isEqualTo(dtos[0].getDbKey());
assertThat(originalFile.getUuid()).isEqualTo(dtos[0].uuid());
verifyStatistics(context, 1, 1);
assertThat(addedFileRepository.getComponents()).isEmpty();
verifyStatistics(context, 1, 1, 1, 1);
}

@Test
@@ -342,7 +354,8 @@ public class FileMoveDetectionStepTest {
assertThat(scoreMatrixDumper.scoreMatrix.getMaxScore())
.isGreaterThan(0)
.isLessThan(MIN_REQUIRED_SCORE);
verifyStatistics(context, 1, 1);
assertThat(addedFileRepository.getComponents()).contains(file2);
verifyStatistics(context, 1, 1, 1, 0);
}

@Test
@@ -359,7 +372,8 @@ public class FileMoveDetectionStepTest {

assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
assertThat(scoreMatrixDumper.scoreMatrix.getMaxScore()).isZero();
verifyStatistics(context, 1, 1);
assertThat(addedFileRepository.getComponents()).contains(file2);
verifyStatistics(context, 1, 1, 1, 0);
}

@Test
@@ -376,7 +390,8 @@ public class FileMoveDetectionStepTest {

assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
assertThat(scoreMatrixDumper.scoreMatrix).isNull();
verifyStatistics(context, 0, null);
assertThat(addedFileRepository.getComponents()).containsOnly(file2);
verifyStatistics(context, 1, 0, 1, null);
}

@Test
@@ -393,7 +408,8 @@ public class FileMoveDetectionStepTest {

assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
assertThat(scoreMatrixDumper.scoreMatrix.getMaxScore()).isZero();
verifyStatistics(context, 1, 1);
assertThat(addedFileRepository.getComponents()).contains(file2);
verifyStatistics(context, 1, 1, 1, 0);
assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("max score in matrix is less than min required score (85). Do nothing.");
}

@@ -412,7 +428,8 @@ public class FileMoveDetectionStepTest {

assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
assertThat(scoreMatrixDumper.scoreMatrix.getMaxScore()).isEqualTo(100);
verifyStatistics(context, 1, 2);
assertThat(addedFileRepository.getComponents()).containsOnly(file2, file3);
verifyStatistics(context, 2, 1, 2, 0);
}

@Test
@@ -431,24 +448,27 @@ public class FileMoveDetectionStepTest {

assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
assertThat(scoreMatrixDumper.scoreMatrix.getMaxScore()).isEqualTo(100);
verifyStatistics(context, 2, 1);
assertThat(addedFileRepository.getComponents()).containsOnly(file3);
verifyStatistics(context, 1, 2, 1, 0);
}

@Test
public void execute_detects_no_move_if_two_files_are_empty() {
public void execute_detects_no_move_if_two_files_are_empty_in_DB() {
analysisMetadataHolder.setBaseAnalysis(ANALYSIS);
Component file1 = fileComponent(FILE_1_REF, null);
Component file2 = fileComponent(FILE_2_REF, null);
insertFiles(file1.getDbKey(), file2.getDbKey());
insertContentOfFileInDb(file1.getDbKey(), null);
insertContentOfFileInDb(file2.getDbKey(), null);
setFilesInReport(file1, file2);

TestComputationStepContext context = new TestComputationStepContext();
underTest.execute(context);

assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
assertThat(scoreMatrixDumper.scoreMatrix).isNull();
verifyStatistics(context, 2, null);
assertThat(addedFileRepository.getComponents()).isEmpty();
verifyStatistics(context, 2, 2, 0, null);
}

@Test
@@ -485,7 +505,8 @@ public class FileMoveDetectionStepTest {
assertThat(originalFile5.getKey()).isEqualTo(dtos[3].getDbKey());
assertThat(originalFile5.getUuid()).isEqualTo(dtos[3].uuid());
assertThat(scoreMatrixDumper.scoreMatrix.getMaxScore()).isGreaterThan(MIN_REQUIRED_SCORE);
verifyStatistics(context, 4, 2);
assertThat(addedFileRepository.getComponents()).isEmpty();
verifyStatistics(context, 3, 4, 2, 2);
}

@Test
@@ -505,7 +526,7 @@ public class FileMoveDetectionStepTest {

assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
assertThat(scoreMatrixDumper.scoreMatrix.getMaxScore()).isZero();
verifyStatistics(context, 2, 2);
verifyStatistics(context, 2, 2, 2, 0);
}

/**
@@ -559,7 +580,7 @@ public class FileMoveDetectionStepTest {
.isEqualTo("1242_make_analysis_uuid_not_null_on_duplications_index.rb");
assertThat(movedFilesRepository.getOriginalFile(addComponentUuidAndAnalysisUuidColumnToDuplicationsIndex).get().getKey())
.isEqualTo("AddComponentUuidColumnToDuplicationsIndex.java");
verifyStatistics(context, 12, 6);
verifyStatistics(context, comps.values().size(), 12, 6, 3);
}

private String[] readLines(File filename) throws IOException {
@@ -640,8 +661,30 @@ public class FileMoveDetectionStepTest {
}
}

private static void verifyStatistics(TestComputationStepContext context, @Nullable Integer expectedDbFiles, @Nullable Integer expectedAddedFiles) {
private static void verifyStatistics(TestComputationStepContext context,
@Nullable Integer expectedReportFiles, @Nullable Integer expectedDbFiles,
@Nullable Integer expectedAddedFiles, @Nullable Integer expectedMovedFiles) {
context.getStatistics().assertValue("reportFiles", expectedReportFiles);
context.getStatistics().assertValue("dbFiles", expectedDbFiles);
context.getStatistics().assertValue("addedFiles", expectedAddedFiles);
context.getStatistics().assertValue("movedFiles", expectedMovedFiles);
}

private static class RecordingMutableAddedFileRepository implements MutableAddedFileRepository {
private final List<Component> components = new ArrayList<>();

@Override
public void register(Component file) {
components.add(file);
}

@Override
public boolean isAdded(Component component) {
throw new UnsupportedOperationException("isAdded should not be called");
}

public List<Component> getComponents() {
return components;
}
}
}

+ 183
- 136
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueCreationDateCalculatorTest.java Просмотреть файл

@@ -19,18 +19,25 @@
*/
package org.sonar.ce.task.projectanalysis.issue;

import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;
import org.junit.Before;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.sonar.api.rule.RuleKey;
import org.sonar.ce.task.projectanalysis.analysis.Analysis;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
import org.sonar.ce.task.projectanalysis.analysis.ScannerPlugin;
import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.ce.task.projectanalysis.filemove.AddedFileRepository;
import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRule;
import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRulesHolder;
import org.sonar.ce.task.projectanalysis.scm.Changeset;
@@ -54,12 +61,12 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;

@RunWith(DataProviderRunner.class)
public class IssueCreationDateCalculatorTest {
private static final String COMPONENT_UUID = "ab12";

@org.junit.Rule
public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();

@org.junit.Rule
public ExpectedException exception = ExpectedException.none();

@@ -70,10 +77,13 @@ public class IssueCreationDateCalculatorTest {
private RuleKey ruleKey = RuleKey.of("reop", "rule");
private DefaultIssue issue = mock(DefaultIssue.class);
private ActiveRule activeRule = mock(ActiveRule.class);
private IssueCreationDateCalculator calculator;

private IssueCreationDateCalculator underTest;

private Analysis baseAnalysis = mock(Analysis.class);
private Map<String, ScannerPlugin> scannerPlugins = new HashMap<>();
private RuleRepository ruleRepository = mock(RuleRepository.class);
private AddedFileRepository addedFileRepository = mock(AddedFileRepository.class);
private ScmInfo scmInfo;
private Rule rule = mock(Rule.class);

@@ -82,9 +92,9 @@ public class IssueCreationDateCalculatorTest {
analysisMetadataHolder.setScannerPluginsByKey(scannerPlugins);
analysisMetadataHolder.setAnalysisDate(new Date());
when(component.getUuid()).thenReturn(COMPONENT_UUID);
calculator = new IssueCreationDateCalculator(analysisMetadataHolder, scmInfoRepository, issueUpdater, activeRulesHolder, ruleRepository);
underTest = new IssueCreationDateCalculator(analysisMetadataHolder, scmInfoRepository, issueUpdater, activeRulesHolder, ruleRepository, addedFileRepository);

when(ruleRepository.findByKey(ruleKey)).thenReturn(java.util.Optional.of(rule));
when(ruleRepository.findByKey(ruleKey)).thenReturn(Optional.of(rule));
when(activeRulesHolder.get(any(RuleKey.class)))
.thenReturn(Optional.empty());
when(activeRulesHolder.get(ruleKey))
@@ -94,13 +104,13 @@ public class IssueCreationDateCalculatorTest {
}

@Test
public void should_not_change_date_if_no_scm_available() {
public void should_not_backdate_if_no_scm_available() {
previousAnalysisWas(2000L);
currentAnalysisIs(3000L);

newIssue();
makeIssueNew();
noScm();
ruleCreatedAt(2800L);
setRuleCreatedAt(2800L);

run();

@@ -108,13 +118,14 @@ public class IssueCreationDateCalculatorTest {
}

@Test
public void should_not_change_date_if_rule_and_plugin_and_base_plugin_are_old() {
@UseDataProvider("backdatingDateCases")
public void should_not_backdate_if_rule_and_plugin_and_base_plugin_are_old(BiConsumer<DefaultIssue, ScmInfo> configure, long expectedDate) {
previousAnalysisWas(2000L);
currentAnalysisIs(3000L);

newIssue();
withScm(1200L);
ruleCreatedAt(1500L);
makeIssueNew();
configure.accept(issue, createMockScmInfo());
setRuleCreatedAt(1500L);
rulePlugin("customjava");
pluginUpdatedAt("customjava", "java", 1700L);
pluginUpdatedAt("java", 1700L);
@@ -125,13 +136,14 @@ public class IssueCreationDateCalculatorTest {
}

@Test
public void should_not_change_date_if_rule_and_plugin_are_old_and_no_base_plugin() {
@UseDataProvider("backdatingDateCases")
public void should_not_backdate_if_rule_and_plugin_are_old_and_no_base_plugin(BiConsumer<DefaultIssue, ScmInfo> configure, long expectedDate) {
previousAnalysisWas(2000L);
currentAnalysisIs(3000L);

newIssue();
withScm(1200L);
ruleCreatedAt(1500L);
makeIssueNew();
configure.accept(issue, createMockScmInfo());
setRuleCreatedAt(1500L);
rulePlugin("java");
pluginUpdatedAt("java", 1700L);

@@ -141,13 +153,14 @@ public class IssueCreationDateCalculatorTest {
}

@Test
public void should_not_change_date_if_issue_existed_before() {
@UseDataProvider("backdatingDateCases")
public void should_not_backdate_if_issue_existed_before(BiConsumer<DefaultIssue, ScmInfo> configure, long expectedDate) {
previousAnalysisWas(2000L);
currentAnalysisIs(3000L);

existingIssue();
withScm(1200L);
ruleCreatedAt(2800L);
makeIssueNotNew();
configure.accept(issue, createMockScmInfo());
setRuleCreatedAt(2800L);

run();

@@ -159,9 +172,8 @@ public class IssueCreationDateCalculatorTest {
previousAnalysisWas(2000L);
currentAnalysisIs(3000L);

existingIssue();
when(issue.getRuleKey())
.thenReturn(RuleKey.of("repo", "disabled"));
makeIssueNotNew();
setIssueBelongToNonExistingRule();

run();

@@ -173,8 +185,8 @@ public class IssueCreationDateCalculatorTest {
previousAnalysisWas(2000L);
currentAnalysisIs(3000L);

when(ruleRepository.findByKey(ruleKey)).thenReturn(java.util.Optional.empty());
newIssue();
when(ruleRepository.findByKey(ruleKey)).thenReturn(Optional.empty());
makeIssueNew();

exception.expect(IllegalStateException.class);
exception.expectMessage("The rule with key 'reop:rule' raised an issue, but no rule with that key was found");
@@ -182,155 +194,159 @@ public class IssueCreationDateCalculatorTest {
}

@Test
public void should_change_date_if_scm_is_available_and_rule_is_new() {
@UseDataProvider("backdatingDateCases")
public void should_backdate_date_if_scm_is_available_and_rule_is_new(BiConsumer<DefaultIssue, ScmInfo> configure, long expectedDate) {
previousAnalysisWas(2000L);
currentAnalysisIs(3000L);

newIssue();
withScm(1200L);
ruleCreatedAt(2800L);
makeIssueNew();
configure.accept(issue, createMockScmInfo());
setRuleCreatedAt(2800L);

run();

assertChangeOfCreationDateTo(1200L);
assertChangeOfCreationDateTo(expectedDate);
}

@Test
public void should_change_date_if_scm_is_available_and_first_analysis() {
analysisMetadataHolder.setBaseAnalysis(null);
@UseDataProvider("backdatingDateCases")
public void should_backdate_date_if_scm_is_available_and_first_analysis(BiConsumer<DefaultIssue, ScmInfo> configure, long expectedDate) {
currentAnalysisIsFirstAnalysis();
currentAnalysisIs(3000L);

newIssue();
withScm(1200L);
makeIssueNew();
configure.accept(issue, createMockScmInfo());

run();

assertChangeOfCreationDateTo(1200L);
assertChangeOfCreationDateTo(expectedDate);
}

@Test
public void should_change_date_if_scm_is_available_and_plugin_is_new() {
@UseDataProvider("backdatingDateCases")
public void should_backdate_date_if_scm_is_available_and_current_component_is_new_file(BiConsumer<DefaultIssue, ScmInfo> configure, long expectedDate) {
previousAnalysisWas(2000L);
currentAnalysisIs(3000L);

newIssue();
withScm(1200L);
ruleCreatedAt(1500L);
rulePlugin("java");
pluginUpdatedAt("java", 2500L);
makeIssueNew();
configure.accept(issue, createMockScmInfo());
currentComponentIsNewFile();

run();

assertChangeOfCreationDateTo(1200L);
assertChangeOfCreationDateTo(expectedDate);
}

@Test
public void should_change_date_if_scm_is_available_and_base_plugin_is_new() {
@UseDataProvider("backdatingDateCases")
public void should_backdate_if_scm_is_available_and_plugin_is_new(BiConsumer<DefaultIssue, ScmInfo> configure, long expectedDate) {
previousAnalysisWas(2000L);
currentAnalysisIs(3000L);

newIssue();
withScm(1200L);
ruleCreatedAt(1500L);
rulePlugin("customjava");
pluginUpdatedAt("customjava", "java", 1000L);
makeIssueNew();
configure.accept(issue, createMockScmInfo());
setRuleCreatedAt(1500L);
rulePlugin("java");
pluginUpdatedAt("java", 2500L);

run();

assertChangeOfCreationDateTo(1200L);
assertChangeOfCreationDateTo(expectedDate);
}

@Test
public void should_backdate_external_issues() {
analysisMetadataHolder.setBaseAnalysis(null);
@UseDataProvider("backdatingDateCases")
public void should_backdate_if_scm_is_available_and_base_plugin_is_new(BiConsumer<DefaultIssue, ScmInfo> configure, long expectedDate) {
previousAnalysisWas(2000L);
currentAnalysisIs(3000L);

newIssue();
when(rule.isExternal()).thenReturn(true);
when(issue.getLocations()).thenReturn(DbIssues.Locations.newBuilder().setTextRange(range(2, 3)).build());
withScmAt(2, 1200L);
withScmAt(3, 1300L);
makeIssueNew();
configure.accept(issue, createMockScmInfo());
setRuleCreatedAt(1500L);
rulePlugin("customjava");
pluginUpdatedAt("customjava", "java", 1000L);
pluginUpdatedAt("java", 2500L);

run();

assertChangeOfCreationDateTo(1300L);
verifyZeroInteractions(activeRulesHolder);
assertChangeOfCreationDateTo(expectedDate);
}

@Test
public void should_use_primary_location_when_backdating() {
analysisMetadataHolder.setBaseAnalysis(null);
@UseDataProvider("backdatingDateCases")
public void should_backdate_external_issues(BiConsumer<DefaultIssue, ScmInfo> configure, long expectedDate) {
currentAnalysisIsFirstAnalysis();
currentAnalysisIs(3000L);

newIssue();
when(issue.getLocations()).thenReturn(DbIssues.Locations.newBuilder().setTextRange(range(2, 3)).build());
withScmAt(2, 1200L);
withScmAt(3, 1300L);
makeIssueNew();
when(rule.isExternal()).thenReturn(true);
configure.accept(issue, createMockScmInfo());

run();

assertChangeOfCreationDateTo(1300L);
assertChangeOfCreationDateTo(expectedDate);
verifyZeroInteractions(activeRulesHolder);
}

@Test
public void should_use_flows_location_when_backdating() {
analysisMetadataHolder.setBaseAnalysis(null);
currentAnalysisIs(3000L);

newIssue();
Builder builder = DbIssues.Locations.newBuilder()
.setTextRange(range(2, 3));
Flow.Builder secondary = Flow.newBuilder().addLocation(Location.newBuilder().setTextRange(range(4, 5)));
builder.addFlow(secondary).build();
Flow.Builder flow = Flow.newBuilder()
.addLocation(Location.newBuilder().setTextRange(range(6, 7)).setComponentId(COMPONENT_UUID))
.addLocation(Location.newBuilder().setTextRange(range(8, 9)).setComponentId(COMPONENT_UUID));
builder.addFlow(flow).build();
when(issue.getLocations()).thenReturn(builder.build());
withScmAt(2, 1200L);
withScmAt(3, 1300L);
withScmAt(4, 1400L);
withScmAt(5, 1500L);
// some lines missing should be ok
withScmAt(9, 1900L);

run();

assertChangeOfCreationDateTo(1900L);
@DataProvider
public static Object[][] backdatingDateCases() {
return new Object[][] {
{new NoIssueLocation(), 1200L},
{new OnlyPrimaryLocation(), 1300L},
{new FlowOnCurrentFileOnly(), 1900L},
{new FlowOnMultipleFiles(), 1700L}
};
}

@Test
public void should_ignore_flows_location_outside_current_file_when_backdating() {
analysisMetadataHolder.setBaseAnalysis(null);
currentAnalysisIs(3000L);

newIssue();
Builder builder = DbIssues.Locations.newBuilder()
.setTextRange(range(2, 3));
Flow.Builder secondary = Flow.newBuilder().addLocation(Location.newBuilder().setTextRange(range(4, 5)));
builder.addFlow(secondary).build();
Flow.Builder flow = Flow.newBuilder()
.addLocation(Location.newBuilder().setTextRange(range(6, 7)).setComponentId(COMPONENT_UUID))
.addLocation(Location.newBuilder().setTextRange(range(8, 9)).setComponentId("another"));
builder.addFlow(flow).build();
when(issue.getLocations()).thenReturn(builder.build());
withScmAt(2, 1200L);
withScmAt(3, 1300L);
withScmAt(4, 1400L);
withScmAt(5, 1500L);
withScmAt(6, 1600L);
withScmAt(7, 1700L);
withScmAt(8, 1800L);
withScmAt(9, 1900L);
private static class NoIssueLocation implements BiConsumer<DefaultIssue, ScmInfo> {
@Override
public void accept(DefaultIssue issue, ScmInfo scmInfo) {
setDateOfLatestScmChangeset(scmInfo, 1200L);
}
}

run();
private static class OnlyPrimaryLocation implements BiConsumer<DefaultIssue, ScmInfo> {
@Override
public void accept(DefaultIssue issue, ScmInfo scmInfo) {
when(issue.getLocations()).thenReturn(DbIssues.Locations.newBuilder().setTextRange(range(2, 3)).build());
setDateOfChangetsetAtLine(scmInfo, 2, 1200L);
setDateOfChangetsetAtLine(scmInfo, 3, 1300L);
}
}

assertChangeOfCreationDateTo(1700L);
private static class FlowOnCurrentFileOnly implements BiConsumer<DefaultIssue, ScmInfo> {
@Override
public void accept(DefaultIssue issue, ScmInfo scmInfo) {
Builder locations = DbIssues.Locations.newBuilder()
.setTextRange(range(2, 3))
.addFlow(newFlow(newLocation(4, 5)))
.addFlow(newFlow(newLocation(6, 7, COMPONENT_UUID), newLocation(8, 9, COMPONENT_UUID)));
when(issue.getLocations()).thenReturn(locations.build());
setDateOfChangetsetAtLine(scmInfo, 2, 1200L);
setDateOfChangetsetAtLine(scmInfo, 3, 1300L);
setDateOfChangetsetAtLine(scmInfo, 4, 1400L);
setDateOfChangetsetAtLine(scmInfo, 5, 1500L);
// some lines missing should be ok
setDateOfChangetsetAtLine(scmInfo, 9, 1900L);
}
}

private org.sonar.db.protobuf.DbCommons.TextRange.Builder range(int startLine, int endLine) {
return TextRange.newBuilder().setStartLine(startLine).setEndLine(endLine);
private static class FlowOnMultipleFiles implements BiConsumer<DefaultIssue, ScmInfo> {
@Override
public void accept(DefaultIssue issue, ScmInfo scmInfo) {
Builder locations = DbIssues.Locations.newBuilder()
.setTextRange(range(2, 3))
.addFlow(newFlow(newLocation(4, 5)))
.addFlow(newFlow(newLocation(6, 7, COMPONENT_UUID), newLocation(8, 9, "another")));
when(issue.getLocations()).thenReturn(locations.build());
setDateOfChangetsetAtLine(scmInfo, 2, 1200L);
setDateOfChangetsetAtLine(scmInfo, 3, 1300L);
setDateOfChangetsetAtLine(scmInfo, 4, 1400L);
setDateOfChangetsetAtLine(scmInfo, 5, 1500L);
setDateOfChangetsetAtLine(scmInfo, 6, 1600L);
setDateOfChangetsetAtLine(scmInfo, 7, 1700L);
setDateOfChangetsetAtLine(scmInfo, 8, 1800L);
setDateOfChangetsetAtLine(scmInfo, 9, 1900L);
}
}

private void previousAnalysisWas(long analysisDate) {
@@ -347,47 +363,60 @@ public class IssueCreationDateCalculatorTest {
scannerPlugins.put(pluginKey, new ScannerPlugin(pluginKey, basePluginKey, updatedAt));
}

private AnalysisMetadataHolderRule currentAnalysisIsFirstAnalysis() {
return analysisMetadataHolder.setBaseAnalysis(null);
}

private void currentAnalysisIs(long analysisDate) {
analysisMetadataHolder.setAnalysisDate(analysisDate);
}

private void newIssue() {
private void currentComponentIsNewFile() {
when(component.getType()).thenReturn(Component.Type.FILE);
when(addedFileRepository.isAdded(component)).thenReturn(true);
}

private void makeIssueNew() {
when(issue.isNew())
.thenReturn(true);
}

private void existingIssue() {
private void makeIssueNotNew() {
when(issue.isNew())
.thenReturn(false);
}

private void setIssueBelongToNonExistingRule() {
when(issue.getRuleKey())
.thenReturn(RuleKey.of("repo", "disabled"));
}

private void noScm() {
when(scmInfoRepository.getScmInfo(component))
.thenReturn(java.util.Optional.empty());
.thenReturn(Optional.empty());
}

private void withScm(long blame) {
createMockScmInfo();
Changeset changeset = Changeset.newChangesetBuilder().setDate(blame).setRevision("1").build();
private static void setDateOfLatestScmChangeset(ScmInfo scmInfo, long date) {
Changeset changeset = Changeset.newChangesetBuilder().setDate(date).setRevision("1").build();
when(scmInfo.getLatestChangeset()).thenReturn(changeset);
}

private void createMockScmInfo() {
private static void setDateOfChangetsetAtLine(ScmInfo scmInfo, int line, long date) {
Changeset changeset = Changeset.newChangesetBuilder().setDate(date).setRevision("1").build();
when(scmInfo.hasChangesetForLine(line)).thenReturn(true);
when(scmInfo.getChangesetForLine(line)).thenReturn(changeset);
}

private ScmInfo createMockScmInfo() {
if (scmInfo == null) {
scmInfo = mock(ScmInfo.class);
when(scmInfoRepository.getScmInfo(component))
.thenReturn(java.util.Optional.of(scmInfo));
.thenReturn(Optional.of(scmInfo));
}
return scmInfo;
}

private void withScmAt(int line, long blame) {
createMockScmInfo();
Changeset changeset = Changeset.newChangesetBuilder().setDate(blame).setRevision("1").build();
when(scmInfo.hasChangesetForLine(line)).thenReturn(true);
when(scmInfo.getChangesetForLine(line)).thenReturn(changeset);
}

private void ruleCreatedAt(long createdAt) {
private void setRuleCreatedAt(long createdAt) {
when(activeRule.getCreatedAt()).thenReturn(createdAt);
}

@@ -395,10 +424,28 @@ public class IssueCreationDateCalculatorTest {
when(activeRule.getPluginKey()).thenReturn(pluginKey);
}

private static Location newLocation(int startLine, int endLine) {
return Location.newBuilder().setTextRange(range(startLine, endLine)).build();
}

private static Location newLocation(int startLine, int endLine, String componentUuid) {
return Location.newBuilder().setTextRange(range(startLine, endLine)).setComponentId(componentUuid).build();
}

private static org.sonar.db.protobuf.DbCommons.TextRange range(int startLine, int endLine) {
return TextRange.newBuilder().setStartLine(startLine).setEndLine(endLine).build();
}

private static Flow newFlow(Location... locations) {
Flow.Builder builder = Flow.newBuilder();
Arrays.stream(locations).forEach(builder::addLocation);
return builder.build();
}

private void run() {
calculator.beforeComponent(component);
calculator.onIssue(component, issue);
calculator.afterComponent(component);
underTest.beforeComponent(component);
underTest.onIssue(component, issue);
underTest.afterComponent(component);
}

private void assertNoChangeOfCreationDate() {

Загрузка…
Отмена
Сохранить