import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.regex.Pattern;
import javax.annotation.Nullable;
-import org.apache.commons.codec.digest.DigestUtils;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.Duration;
import org.sonar.ce.task.projectanalysis.issue.filter.IssueFilter;
import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRulesHolder;
import org.sonar.ce.task.projectanalysis.source.SourceLinesHashRepository;
-import org.sonar.ce.task.projectanalysis.source.SourceLinesRepository;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.tracking.Input;
import org.sonar.core.issue.tracking.LazyInput;
import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;
public class TrackerRawInputFactory {
- private static final Pattern MATCH_ALL_WHITESPACES = Pattern.compile("\\s");
private static final long DEFAULT_EXTERNAL_ISSUE_EFFORT = 0L;
private final TreeRootHolder treeRootHolder;
private final BatchReportReader reportReader;
private final CommonRuleEngine commonRuleEngine;
private final IssueFilter issueFilter;
private final SourceLinesHashRepository sourceLinesHash;
- private final SourceLinesRepository sourceLinesRepository;
private final RuleRepository ruleRepository;
private final ActiveRulesHolder activeRulesHolder;
- public TrackerRawInputFactory(TreeRootHolder treeRootHolder, BatchReportReader reportReader, SourceLinesHashRepository sourceLinesHash,
- SourceLinesRepository sourceLinesRepository, CommonRuleEngine commonRuleEngine, IssueFilter issueFilter, RuleRepository ruleRepository,
+ public TrackerRawInputFactory(TreeRootHolder treeRootHolder, BatchReportReader reportReader,
+ SourceLinesHashRepository sourceLinesHash, CommonRuleEngine commonRuleEngine, IssueFilter issueFilter, RuleRepository ruleRepository,
ActiveRulesHolder activeRulesHolder) {
this.treeRootHolder = treeRootHolder;
this.reportReader = reportReader;
this.sourceLinesHash = sourceLinesHash;
- this.sourceLinesRepository = sourceLinesRepository;
this.commonRuleEngine = commonRuleEngine;
this.issueFilter = issueFilter;
this.ruleRepository = ruleRepository;
}
DbIssues.Locations.Builder dbLocationsBuilder = DbIssues.Locations.newBuilder();
if (reportIssue.hasTextRange()) {
- DbCommons.TextRange.Builder textRange = convertTextRange(reportIssue.getTextRange());
- dbLocationsBuilder.setTextRange(textRange);
- dbLocationsBuilder.setChecksum(calculateLocationHash(textRange));
+ dbLocationsBuilder.setTextRange(convertTextRange(reportIssue.getTextRange()));
}
for (ScannerReport.Flow flow : reportIssue.getFlowList()) {
if (flow.getLocationCount() > 0) {
ScannerReport.TextRange sourceRange = source.getTextRange();
DbCommons.TextRange.Builder targetRange = convertTextRange(sourceRange);
target.setTextRange(targetRange);
- target.setChecksum(calculateLocationHash(targetRange));
}
return Optional.of(target.build());
}
- private String calculateLocationHash(DbCommons.TextRange.Builder textRange) {
- try (CloseableIterator<String> linesIterator = sourceLinesRepository.readLines(component)) {
- StringBuilder toHash = new StringBuilder();
- int lineNo = 1;
- while (linesIterator.hasNext()) {
- String line = linesIterator.next();
- if (lineNo == textRange.getStartLine() && lineNo == textRange.getEndLine()) {
- toHash.append(line, textRange.getStartOffset(), textRange.getEndOffset());
- } else if (lineNo == textRange.getStartLine()) {
- toHash.append(line, textRange.getStartOffset(), line.length());
- } else if (lineNo > textRange.getStartLine() && lineNo < textRange.getEndLine()) {
- toHash.append(line);
- } else if (lineNo == textRange.getEndLine()) {
- toHash.append(line, 0, textRange.getEndOffset());
- } else if (lineNo > textRange.getEndLine()) {
- break;
- }
- lineNo++;
- }
- String issueContentWithoutWhitespaces = MATCH_ALL_WHITESPACES.matcher(toHash.toString()).replaceAll("");
- return DigestUtils.md5Hex(issueContentWithoutWhitespaces);
- }
-
- }
-
private DbCommons.TextRange.Builder convertTextRange(ScannerReport.TextRange sourceRange) {
DbCommons.TextRange.Builder targetRange = DbCommons.TextRange.newBuilder();
targetRange.setStartLine(sourceRange.getStartLine());
import com.tngtech.java.junit.dataprovider.UseDataProvider;
import java.util.Collection;
import java.util.Collections;
-import java.util.Iterator;
-import java.util.stream.IntStream;
-import org.apache.commons.codec.digest.DigestUtils;
-import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRule;
import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRulesHolderRule;
import org.sonar.ce.task.projectanalysis.source.SourceLinesHashRepository;
-import org.sonar.ce.task.projectanalysis.source.SourceLinesRepository;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.tracking.Input;
-import org.sonar.core.util.CloseableIterator;
import org.sonar.db.protobuf.DbIssues;
import org.sonar.scanner.protocol.Constants;
import org.sonar.scanner.protocol.output.ScannerReport;
private static final String FILE_UUID = "fake_uuid";
private static final String ANOTHER_FILE_UUID = "another_fake_uuid";
- private static final String EXAMPLE_LINE_OF_CODE_FORMAT = "int example = line + of + code + %d; ";
- private static final int FILE_REF = 2;
- private static final int NOT_IN_REPORT_FILE_REF = 3;
- private static final int ANOTHER_FILE_REF = 4;
+ private static int FILE_REF = 2;
+ private static int NOT_IN_REPORT_FILE_REF = 3;
+ private static int ANOTHER_FILE_REF = 4;
+
+ private static ReportComponent FILE = ReportComponent.builder(Component.Type.FILE, FILE_REF).setUuid(FILE_UUID).build();
+ private static ReportComponent ANOTHER_FILE = ReportComponent.builder(Component.Type.FILE, ANOTHER_FILE_REF).setUuid(ANOTHER_FILE_UUID).build();
+ private static ReportComponent PROJECT = ReportComponent.builder(Component.Type.PROJECT, 1).addChildren(FILE, ANOTHER_FILE).build();
@Rule
public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule().setRoot(PROJECT);
@Rule
public RuleRepositoryRule ruleRepository = new RuleRepositoryRule();
- private static final ReportComponent FILE = ReportComponent.builder(Component.Type.FILE, FILE_REF).setUuid(FILE_UUID).build();
- private static final ReportComponent ANOTHER_FILE = ReportComponent.builder(Component.Type.FILE, ANOTHER_FILE_REF).setUuid(ANOTHER_FILE_UUID).build();
- private static final ReportComponent PROJECT = ReportComponent.builder(Component.Type.PROJECT, 1).addChildren(FILE, ANOTHER_FILE).build();
-
- private final SourceLinesHashRepository sourceLinesHash = mock(SourceLinesHashRepository.class);
- private final SourceLinesRepository sourceLinesRepository = mock(SourceLinesRepository.class);
- private final CommonRuleEngine commonRuleEngine = mock(CommonRuleEngine.class);
- private final IssueFilter issueFilter = mock(IssueFilter.class);
- private final TrackerRawInputFactory underTest = new TrackerRawInputFactory(treeRootHolder, reportReader, sourceLinesHash,
- sourceLinesRepository, commonRuleEngine, issueFilter, ruleRepository, activeRulesHolder);
-
- @Before
- public void before() {
- Iterator<String> stringIterator = IntStream.rangeClosed(1, 9)
- .mapToObj(i -> String.format(EXAMPLE_LINE_OF_CODE_FORMAT, i))
- .iterator();
- when(sourceLinesRepository.readLines(any())).thenReturn(CloseableIterator.from(stringIterator));
- }
+ private SourceLinesHashRepository sourceLinesHash = mock(SourceLinesHashRepository.class);
+ private CommonRuleEngine commonRuleEngine = mock(CommonRuleEngine.class);
+ private IssueFilter issueFilter = mock(IssueFilter.class);
+ private TrackerRawInputFactory underTest = new TrackerRawInputFactory(treeRootHolder, reportReader, sourceLinesHash,
+ commonRuleEngine, issueFilter, ruleRepository, activeRulesHolder);
@Test
public void load_source_hash_sequences() {
when(sourceLinesHash.getLineHashesMatchingDBVersion(FILE)).thenReturn(Collections.singletonList("line"));
ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
- .setTextRange(newTextRange(2))
+ .setTextRange(TextRange.newBuilder().setStartLine(2).build())
.setMsg("the message")
.setRuleRepository(ruleKey.repository())
.setRuleKey(ruleKey.rule())
assertThat(issue.tags()).isEmpty();
assertInitializedIssue(issue);
assertThat(issue.effort()).isNull();
-
- assertLocationHashIsMadeOf(input, "intexample=line+of+code+2;");
- }
-
- @Test
- public void calculateLocationHash_givenIssueOn3Lines_calculateHashOn3Lines() {
- RuleKey ruleKey = RuleKey.of("java", "S001");
- markRuleAsActive(ruleKey);
- when(issueFilter.accept(any(), eq(FILE))).thenReturn(true);
-
- ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
- .setTextRange(TextRange.newBuilder()
- .setStartLine(1)
- .setEndLine(3)
- .setStartOffset(0)
- .setEndOffset(EXAMPLE_LINE_OF_CODE_FORMAT.length() - 1)
- .build())
- .setMsg("the message")
- .setRuleRepository(ruleKey.repository())
- .setRuleKey(ruleKey.rule())
- .build();
- reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
-
- Input<DefaultIssue> input = underTest.create(FILE);
-
- assertLocationHashIsMadeOf(input, "intexample=line+of+code+1;intexample=line+of+code+2;intexample=line+of+code+3;");
- }
-
- @Test
- public void calculateLocationHash_givenIssuePartiallyOn1Line_calculateHashOnAPartOfLine() {
- RuleKey ruleKey = RuleKey.of("java", "S001");
- markRuleAsActive(ruleKey);
- when(issueFilter.accept(any(), eq(FILE))).thenReturn(true);
-
- ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
- .setTextRange(TextRange.newBuilder()
- .setStartLine(1)
- .setEndLine(1)
- .setStartOffset(13)
- .setEndOffset(EXAMPLE_LINE_OF_CODE_FORMAT.length() - 1)
- .build())
- .setMsg("the message")
- .setRuleRepository(ruleKey.repository())
- .setRuleKey(ruleKey.rule())
- .build();
- reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
-
- Input<DefaultIssue> input = underTest.create(FILE);
-
- assertLocationHashIsMadeOf(input, "line+of+code+1;");
- }
-
- @Test
- public void calculateLocationHash_givenIssuePartiallyOn1LineAndPartiallyOnThirdLine_calculateHashAccordingly() {
- RuleKey ruleKey = RuleKey.of("java", "S001");
- markRuleAsActive(ruleKey);
- when(issueFilter.accept(any(), eq(FILE))).thenReturn(true);
-
- ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
- .setTextRange(TextRange.newBuilder()
- .setStartLine(1)
- .setEndLine(3)
- .setStartOffset(13)
- .setEndOffset(11)
- .build())
- .setMsg("the message")
- .setRuleRepository(ruleKey.repository())
- .setRuleKey(ruleKey.rule())
- .build();
- reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
-
- Input<DefaultIssue> input = underTest.create(FILE);
-
- assertLocationHashIsMadeOf(input, "line+of+code+1;intexample=line+of+code+2;intexample");
}
@Test
when(sourceLinesHash.getLineHashesMatchingDBVersion(FILE)).thenReturn(Collections.singletonList("line"));
ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
- .setTextRange(newTextRange(2))
+ .setTextRange(TextRange.newBuilder().setStartLine(2).build())
.setMsg("the message")
.setRuleRepository(ruleKey.repository())
.setRuleKey(ruleKey.rule())
.addLocation(ScannerReport.IssueLocation.newBuilder()
.setComponentRef(FILE_REF)
.setMsg("Secondary location in same file")
- .setTextRange(newTextRange(2)))
+ .setTextRange(TextRange.newBuilder().setStartLine(2).build()))
.addLocation(ScannerReport.IssueLocation.newBuilder()
.setComponentRef(NOT_IN_REPORT_FILE_REF)
.setMsg("Secondary location in a missing file")
- .setTextRange(newTextRange(3)))
+ .setTextRange(TextRange.newBuilder().setStartLine(3).build()))
.addLocation(ScannerReport.IssueLocation.newBuilder()
.setComponentRef(ANOTHER_FILE_REF)
.setMsg("Secondary location in another file")
- .setTextRange(newTextRange(3)))
+ .setTextRange(TextRange.newBuilder().setStartLine(3).build()))
.build())
.build();
reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
public void load_external_issues_from_report(IssueType issueType, RuleType expectedRuleType, String expectedStatus) {
when(sourceLinesHash.getLineHashesMatchingDBVersion(FILE)).thenReturn(Collections.singletonList("line"));
ScannerReport.ExternalIssue reportIssue = ScannerReport.ExternalIssue.newBuilder()
- .setTextRange(newTextRange(2))
+ .setTextRange(TextRange.newBuilder().setStartLine(2).build())
.setMsg("the message")
.setEngineId("eslint")
.setRuleId("S001")
public void load_external_issues_from_report_with_default_effort(IssueType issueType, RuleType expectedRuleType, String expectedStatus) {
when(sourceLinesHash.getLineHashesMatchingDBVersion(FILE)).thenReturn(Collections.singletonList("line"));
ScannerReport.ExternalIssue reportIssue = ScannerReport.ExternalIssue.newBuilder()
- .setTextRange(newTextRange(2))
+ .setTextRange(TextRange.newBuilder().setStartLine(2).build())
.setMsg("the message")
.setEngineId("eslint")
.setRuleId("S001")
when(sourceLinesHash.getLineHashesMatchingDBVersion(FILE)).thenReturn(Collections.singletonList("line"));
ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
- .setTextRange(newTextRange(2))
+ .setTextRange(TextRange.newBuilder().setStartLine(2).build())
.setMsg("the message")
.setRuleRepository(ruleKey.repository())
.setRuleKey(ruleKey.rule())
when(issueFilter.accept(any(), eq(FILE))).thenReturn(false);
when(sourceLinesHash.getLineHashesMatchingDBVersion(FILE)).thenReturn(Collections.singletonList("line"));
ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
- .setTextRange(newTextRange(2))
+ .setTextRange(TextRange.newBuilder().setStartLine(2).build())
.setMsg("the message")
.setRuleRepository(ruleKey.repository())
.setRuleKey(ruleKey.rule())
ruleRepository.add(dumbRule);
}
- private TextRange newTextRange(int issueOnLine) {
- return TextRange.newBuilder()
- .setStartLine(issueOnLine)
- .setEndLine(issueOnLine)
- .setStartOffset(0)
- .setEndOffset(EXAMPLE_LINE_OF_CODE_FORMAT.length() - 1)
- .build();
- }
-
- private void assertLocationHashIsMadeOf(Input<DefaultIssue> input, String stringToHash) {
- DefaultIssue defaultIssue = Iterators.getOnlyElement(input.getIssues().iterator());
- String expectedHash = DigestUtils.md5Hex(stringToHash);
- DbIssues.Locations locations = defaultIssue.getLocations();
-
- assertThat(locations.getChecksum()).isEqualTo(expectedHash);
- }
}