aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-ce-task-projectanalysis
diff options
context:
space:
mode:
authorAurelien Poscia <aurelien.poscia@sonarsource.com>2022-08-19 16:30:53 +0200
committersonartech <sonartech@sonarsource.com>2022-08-30 20:03:14 +0000
commitad1024286aff2d16a3b3aa5f926613d596348f91 (patch)
treefcac137b5542429b2f903748c089320828be4f96 /server/sonar-ce-task-projectanalysis
parent88978d1ee6a8d4a83ca1259d008cf58e89f31d2e (diff)
downloadsonarqube-ad1024286aff2d16a3b3aa5f926613d596348f91.tar.gz
sonarqube-ad1024286aff2d16a3b3aa5f926613d596348f91.zip
SONAR-17210 - Generate the SARIF report
Diffstat (limited to 'server/sonar-ce-task-projectanalysis')
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java4
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/Rule.java4
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleImpl.java21
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleRepositoryImpl.java10
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/Flow.java (renamed from server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/Flow.java)2
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/FlowGenerator.java85
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/Location.java (renamed from server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/Location.java)2
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/TextRange.java (renamed from server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TextRange.java)23
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventFactory.java30
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TaintVulnerabilityRaised.java2
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/DumbRule.java10
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/locations/flow/FlowGeneratorTest.java160
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventFactoryTest.java4
13 files changed, 329 insertions, 28 deletions
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 a90a512cd8a..674b1c3f9c4 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
@@ -55,10 +55,10 @@ import org.sonar.ce.task.projectanalysis.filesystem.ComputationTempFolderProvide
import org.sonar.ce.task.projectanalysis.issue.BaseIssuesLoader;
import org.sonar.ce.task.projectanalysis.issue.CloseIssuesOnRemovedComponentsVisitor;
import org.sonar.ce.task.projectanalysis.issue.ClosedIssuesInputFactory;
-import org.sonar.ce.task.projectanalysis.issue.ComputeLocationHashesVisitor;
import org.sonar.ce.task.projectanalysis.issue.ComponentIssuesLoader;
import org.sonar.ce.task.projectanalysis.issue.ComponentIssuesRepositoryImpl;
import org.sonar.ce.task.projectanalysis.issue.ComponentsWithUnprocessedIssues;
+import org.sonar.ce.task.projectanalysis.issue.ComputeLocationHashesVisitor;
import org.sonar.ce.task.projectanalysis.issue.DebtCalculator;
import org.sonar.ce.task.projectanalysis.issue.DefaultAssignee;
import org.sonar.ce.task.projectanalysis.issue.EffortAggregator;
@@ -105,6 +105,7 @@ import org.sonar.ce.task.projectanalysis.issue.commonrule.SkippedTestRule;
import org.sonar.ce.task.projectanalysis.issue.commonrule.TestErrorRule;
import org.sonar.ce.task.projectanalysis.issue.filter.IssueFilter;
import org.sonar.ce.task.projectanalysis.language.LanguageRepositoryImpl;
+import org.sonar.ce.task.projectanalysis.locations.flow.FlowGenerator;
import org.sonar.ce.task.projectanalysis.measure.MeasureComputersHolderImpl;
import org.sonar.ce.task.projectanalysis.measure.MeasureComputersVisitor;
import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryImpl;
@@ -256,6 +257,7 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop
ComponentIssuesRepositoryImpl.class,
IssueFilter.class,
+ FlowGenerator.class,
// push events
PushEventFactory.class,
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/Rule.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/Rule.java
index 6d70363bb94..a6cf2028e90 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/Rule.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/Rule.java
@@ -58,4 +58,8 @@ public interface Rule {
@CheckForNull
String getPluginKey();
+
+ String getDefaultRuleDescription();
+
+ String getSeverity();
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleImpl.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleImpl.java
index f8600a0f1d3..ee29fda5c08 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleImpl.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleImpl.java
@@ -20,6 +20,7 @@
package org.sonar.ce.task.projectanalysis.issue;
import com.google.common.base.MoreObjects;
+import java.util.Optional;
import java.util.Set;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
@@ -29,6 +30,7 @@ import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rules.RuleType;
import org.sonar.api.server.debt.DebtRemediationFunction;
import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction;
+import org.sonar.db.rule.RuleDescriptionSectionDto;
import org.sonar.db.rule.RuleDto;
import static com.google.common.collect.Sets.union;
@@ -47,6 +49,8 @@ public class RuleImpl implements Rule {
private final String pluginKey;
private final boolean isExternal;
private final boolean isAdHoc;
+ private final String defaultRuleDescription;
+ private final String severity;
public RuleImpl(RuleDto dto) {
this.uuid = dto.getUuid();
@@ -60,6 +64,14 @@ public class RuleImpl implements Rule {
this.pluginKey = dto.getPluginKey();
this.isExternal = dto.isExternal();
this.isAdHoc = dto.isAdHoc();
+ this.defaultRuleDescription = getNonNullDefaultRuleDescription(dto);
+ this.severity = Optional.ofNullable(dto.getSeverityString()).orElse(dto.getAdHocSeverity());
+ }
+
+ private static String getNonNullDefaultRuleDescription(RuleDto dto) {
+ return Optional.ofNullable(dto.getDefaultRuleDescriptionSection())
+ .map(RuleDescriptionSectionDto::getContent)
+ .orElse("");
}
@Override
@@ -109,6 +121,15 @@ public class RuleImpl implements Rule {
return pluginKey;
}
+ public String getDefaultRuleDescription() {
+ return defaultRuleDescription;
+ }
+
+ @Override
+ public String getSeverity() {
+ return severity;
+ }
+
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleRepositoryImpl.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleRepositoryImpl.java
index dcf299a5295..4ada1d7bc2d 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleRepositoryImpl.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleRepositoryImpl.java
@@ -211,5 +211,15 @@ public class RuleRepositoryImpl implements RuleRepository {
public String getPluginKey() {
return null;
}
+
+ @Override
+ public String getDefaultRuleDescription() {
+ return addHocRule.getDescription();
+ }
+
+ @Override
+ public String getSeverity() {
+ return addHocRule.getSeverity();
+ }
}
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/Flow.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/Flow.java
index 75671df34fe..2b7e8447213 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/Flow.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/Flow.java
@@ -17,7 +17,7 @@
* 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.pushevent;
+package org.sonar.ce.task.projectanalysis.locations.flow;
import java.util.List;
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/FlowGenerator.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/FlowGenerator.java
new file mode 100644
index 00000000000..be25776a588
--- /dev/null
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/FlowGenerator.java
@@ -0,0 +1,85 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.locations.flow;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
+import org.sonar.db.protobuf.DbCommons;
+import org.sonar.db.protobuf.DbIssues;
+
+import static java.util.stream.Collectors.toCollection;
+
+public class FlowGenerator {
+ private final TreeRootHolder treeRootHolder;
+
+ public FlowGenerator(TreeRootHolder treeRootHolder) {
+ this.treeRootHolder = treeRootHolder;
+ }
+
+ public List<Flow> convertFlows(String componentName, @Nullable DbIssues.Locations issueLocations) {
+ if (issueLocations == null) {
+ return Collections.emptyList();
+ }
+ return issueLocations.getFlowList().stream()
+ .map(sourceFlow -> toFlow(componentName, sourceFlow))
+ .collect(Collectors.toCollection(LinkedList::new));
+ }
+
+ private Flow toFlow(String componentName, DbIssues.Flow sourceFlow) {
+ Flow flow = new Flow();
+ List<Location> locations = getFlowLocations(componentName, sourceFlow);
+ flow.setLocations(locations);
+ return flow;
+ }
+
+ private List<Location> getFlowLocations(String componentName, DbIssues.Flow sourceFlow) {
+ return sourceFlow.getLocationList().stream()
+ .map(sourceLocation -> toLocation(componentName, sourceLocation))
+ .collect(toCollection(LinkedList::new));
+ }
+
+ private Location toLocation(String componentName, DbIssues.Location sourceLocation) {
+ Location location = new Location();
+ Component locationComponent = treeRootHolder.getComponentByUuid(sourceLocation.getComponentId());
+ String filePath = Optional.ofNullable(locationComponent).map(Component::getName).orElse(componentName);
+ location.setFilePath(filePath);
+ location.setMessage(sourceLocation.getMsg());
+
+ TextRange textRange = getTextRange(sourceLocation.getTextRange(), sourceLocation.getChecksum());
+ location.setTextRange(textRange);
+ return location;
+ }
+
+ private static TextRange getTextRange(DbCommons.TextRange source, String checksum) {
+ TextRange textRange = new TextRange();
+ textRange.setStartLine(source.getStartLine());
+ textRange.setStartLineOffset(source.getStartOffset());
+ textRange.setEndLine(source.getEndLine());
+ textRange.setEndLineOffset(source.getEndOffset());
+ textRange.setHash(checksum);
+ return textRange;
+ }
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/Location.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/Location.java
index 016dbfa71c9..bedae2197f6 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/Location.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/Location.java
@@ -17,7 +17,7 @@
* 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.pushevent;
+package org.sonar.ce.task.projectanalysis.locations.flow;
public class Location {
private String filePath;
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TextRange.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/TextRange.java
index ce7ebc528dd..16e336cf764 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TextRange.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/TextRange.java
@@ -17,7 +17,9 @@
* 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.pushevent;
+package org.sonar.ce.task.projectanalysis.locations.flow;
+
+import java.util.Objects;
public class TextRange {
private int startLine;
@@ -69,4 +71,23 @@ public class TextRange {
public void setHash(String hash) {
this.hash = hash;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ TextRange textRange = (TextRange) o;
+ return getStartLine() == textRange.getStartLine() && getStartLineOffset() == textRange.getStartLineOffset() && getEndLine() == textRange.getEndLine()
+ && getEndLineOffset() == textRange.getEndLineOffset() && Objects.equals(getHash(), textRange.getHash());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getStartLine(), getStartLineOffset(), getEndLine(), getEndLineOffset(), getHash());
+ }
+
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventFactory.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventFactory.java
index 35e68978082..18a8a17b8fb 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventFactory.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventFactory.java
@@ -21,8 +21,6 @@ package org.sonar.ce.task.projectanalysis.pushevent;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Objects;
import java.util.Optional;
import org.jetbrains.annotations.NotNull;
@@ -30,6 +28,9 @@ import org.sonar.api.ce.ComputeEngineSide;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
+import org.sonar.ce.task.projectanalysis.locations.flow.FlowGenerator;
+import org.sonar.ce.task.projectanalysis.locations.flow.Location;
+import org.sonar.ce.task.projectanalysis.locations.flow.TextRange;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.db.protobuf.DbCommons;
import org.sonar.db.protobuf.DbIssues;
@@ -38,7 +39,6 @@ import org.sonar.server.issue.TaintChecker;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
-import static java.util.Objects.requireNonNullElse;
@ComputeEngineSide
public class PushEventFactory {
@@ -47,12 +47,14 @@ public class PushEventFactory {
private final TreeRootHolder treeRootHolder;
private final AnalysisMetadataHolder analysisMetadataHolder;
private final TaintChecker taintChecker;
+ private final FlowGenerator flowGenerator;
public PushEventFactory(TreeRootHolder treeRootHolder,
- AnalysisMetadataHolder analysisMetadataHolder, TaintChecker taintChecker) {
+ AnalysisMetadataHolder analysisMetadataHolder, TaintChecker taintChecker, FlowGenerator flowGenerator) {
this.treeRootHolder = treeRootHolder;
this.analysisMetadataHolder = analysisMetadataHolder;
this.taintChecker = taintChecker;
+ this.flowGenerator = flowGenerator;
}
public Optional<PushEventDto> raiseEventOnIssue(DefaultIssue currentIssue) {
@@ -110,25 +112,7 @@ public class PushEventFactory {
mainLocation.setTextRange(mainLocationTextRange);
event.setMainLocation(mainLocation);
- List<Flow> flows = new LinkedList<>();
- for (DbIssues.Flow sourceFlow : issueLocations.getFlowList()) {
- Flow flow = new Flow();
- List<Location> locations = new LinkedList<>();
- for (DbIssues.Location sourceLocation : sourceFlow.getLocationList()) {
- Location location = new Location();
- var locationComponent = treeRootHolder.getComponentByUuid(sourceLocation.getComponentId());
- location.setFilePath(requireNonNullElse(locationComponent, component).getName());
- location.setMessage(sourceLocation.getMsg());
-
- TextRange textRange = getTextRange(sourceLocation.getTextRange(), sourceLocation.getChecksum());
- location.setTextRange(textRange);
-
- locations.add(location);
- }
- flow.setLocations(locations);
- flows.add(flow);
- }
- event.setFlows(flows);
+ event.setFlows(flowGenerator.convertFlows(component.getName(), issueLocations));
return event;
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TaintVulnerabilityRaised.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TaintVulnerabilityRaised.java
index e9ab37d368f..a3ddd139ef6 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TaintVulnerabilityRaised.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TaintVulnerabilityRaised.java
@@ -20,6 +20,8 @@
package org.sonar.ce.task.projectanalysis.pushevent;
import java.util.List;
+import org.sonar.ce.task.projectanalysis.locations.flow.Flow;
+import org.sonar.ce.task.projectanalysis.locations.flow.Location;
public class TaintVulnerabilityRaised {
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/DumbRule.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/DumbRule.java
index 00a7e960a58..7ce05f22a7c 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/DumbRule.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/DumbRule.java
@@ -96,6 +96,16 @@ public class DumbRule implements Rule {
}
@Override
+ public String getDefaultRuleDescription() {
+ return null;
+ }
+
+ @Override
+ public String getSeverity() {
+ return null;
+ }
+
+ @Override
public boolean isExternal() {
return isExternal;
}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/locations/flow/FlowGeneratorTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/locations/flow/FlowGeneratorTest.java
new file mode 100644
index 00000000000..ddc27950ce2
--- /dev/null
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/locations/flow/FlowGeneratorTest.java
@@ -0,0 +1,160 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.locations.flow;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang.math.RandomUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
+import org.sonar.db.protobuf.DbCommons;
+import org.sonar.db.protobuf.DbIssues;
+
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toMap;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class FlowGeneratorTest {
+
+ private static final String COMPONENT_NAME = "test_comp";
+
+ @Mock
+ private TreeRootHolder treeRootHolder;
+
+ @InjectMocks
+ private FlowGenerator flowGenerator;
+
+ @Test
+ public void convertFlows_withNullDbLocations_returnsEmptyList() {
+ assertThat(flowGenerator.convertFlows(COMPONENT_NAME, null)).isEmpty();
+ }
+
+ @Test
+ public void convertFlows_withEmptyDbLocations_returnsEmptyList() {
+ DbIssues.Locations issueLocations = DbIssues.Locations.newBuilder().build();
+ assertThat(flowGenerator.convertFlows(COMPONENT_NAME, issueLocations)).isEmpty();
+ }
+
+ @Test
+ public void convertFlows_withSingleDbLocations_returnsCorrectFlow() {
+ DbIssues.Location location = createDbLocation("comp_id_1");
+ DbIssues.Locations issueLocations = DbIssues.Locations.newBuilder()
+ .addFlow(createFlow(location))
+ .build();
+
+ List<Flow> flows = flowGenerator.convertFlows(COMPONENT_NAME, issueLocations);
+
+ assertThat(flows).hasSize(1);
+ Flow singleFlow = flows.iterator().next();
+
+ assertThat(singleFlow.getLocations()).hasSize(1);
+ Location singleLocation = singleFlow.getLocations().iterator().next();
+
+ assertLocationMatches(singleLocation, location);
+ }
+
+ @Test
+ public void convertFlows_with2FlowsSingleDbLocations_returnsCorrectFlow() {
+ DbIssues.Location location1 = createDbLocation("comp_id_1");
+ DbIssues.Location location2 = createDbLocation("comp_id_2");
+ DbIssues.Locations issueLocations = DbIssues.Locations.newBuilder()
+ .addFlow(createFlow(location1))
+ .addFlow(createFlow(location2))
+ .build();
+
+ List<Flow> flows = flowGenerator.convertFlows(COMPONENT_NAME, issueLocations);
+
+ assertThat(flows).hasSize(2).extracting(f -> f.getLocations().size()).containsExactly(1, 1);
+ Map<String, DbIssues.Location> toDbLocation = Map.of(
+ "file_path_" + location1.getComponentId(), location1,
+ "file_path_" + location2.getComponentId(), location2);
+ flows.stream()
+ .map(actualFlow -> actualFlow.getLocations().iterator().next())
+ .forEach(l -> assertLocationMatches(l, toDbLocation.get(l.getFilePath())));
+ }
+
+ @Test
+ public void convertFlows_with2DbLocations_returns() {
+ DbIssues.Location location1 = createDbLocation("comp_id_1");
+ DbIssues.Location location2 = createDbLocation("comp_id_2");
+ DbIssues.Locations issueLocations = DbIssues.Locations.newBuilder()
+ .addFlow(createFlow(location1, location2))
+ .build();
+
+ List<Flow> flows = flowGenerator.convertFlows(COMPONENT_NAME, issueLocations);
+
+ assertThat(flows).hasSize(1);
+ Flow singleFlow = flows.iterator().next();
+
+ assertThat(singleFlow.getLocations()).hasSize(2);
+ Map<String, Location> pathToLocations = singleFlow.getLocations()
+ .stream()
+ .collect(toMap(Location::getFilePath, identity()));
+
+ assertLocationMatches(pathToLocations.get("file_path_comp_id_1"), location1);
+ assertLocationMatches(pathToLocations.get("file_path_comp_id_2"), location2);
+
+ }
+
+ private DbIssues.Location createDbLocation(String componentId) {
+ org.sonar.db.protobuf.DbCommons.TextRange textRange = org.sonar.db.protobuf.DbCommons.TextRange.newBuilder()
+ .setStartLine(RandomUtils.nextInt())
+ .setEndLine(RandomUtils.nextInt())
+ .setStartOffset(RandomUtils.nextInt())
+ .setEndOffset(RandomUtils.nextInt())
+ .build();
+
+ Component component = mock(Component.class);
+ when(component.getName()).thenReturn("file_path_" + componentId);
+ when(treeRootHolder.getComponentByUuid(componentId)).thenReturn(component);
+ return DbIssues.Location.newBuilder()
+ .setComponentId(componentId)
+ .setChecksum("hash" + randomAlphanumeric(10))
+ .setTextRange(textRange)
+ .setMsg("msg" + randomAlphanumeric(15))
+ .build();
+ }
+
+ private static DbIssues.Flow createFlow(DbIssues.Location ... locations) {
+ return DbIssues.Flow.newBuilder()
+ .addAllLocation(List.of(locations))
+ .build();
+ }
+
+ private static void assertLocationMatches(Location actualLocation, DbIssues.Location sourceLocation) {
+ assertThat(actualLocation.getMessage()).isEqualTo(sourceLocation.getMsg());
+ DbCommons.TextRange textRange = sourceLocation.getTextRange();
+ assertThat(actualLocation.getTextRange().getStartLine()).isEqualTo(textRange.getStartLine());
+ assertThat(actualLocation.getTextRange().getEndLine()).isEqualTo(textRange.getEndLine());
+ assertThat(actualLocation.getTextRange().getStartLineOffset()).isEqualTo(textRange.getStartOffset());
+ assertThat(actualLocation.getTextRange().getEndLineOffset()).isEqualTo(textRange.getEndOffset());
+ assertThat(actualLocation.getTextRange().getHash()).isEqualTo(sourceLocation.getChecksum());
+ }
+
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventFactoryTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventFactoryTest.java
index 83aa9bf1836..65548362402 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventFactoryTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventFactoryTest.java
@@ -33,6 +33,7 @@ import org.sonar.ce.task.projectanalysis.analysis.TestBranch;
import org.sonar.ce.task.projectanalysis.component.Component.Type;
import org.sonar.ce.task.projectanalysis.component.MutableTreeRootHolderRule;
import org.sonar.ce.task.projectanalysis.component.ReportComponent;
+import org.sonar.ce.task.projectanalysis.locations.flow.FlowGenerator;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.FieldDiffs;
import org.sonar.db.protobuf.DbCommons;
@@ -54,7 +55,8 @@ public class PushEventFactoryTest {
public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule()
.setBranch(new TestBranch("develop"));
- private final PushEventFactory underTest = new PushEventFactory(treeRootHolder, analysisMetadataHolder, taintChecker);
+ private final FlowGenerator flowGenerator = new FlowGenerator(treeRootHolder);
+ private final PushEventFactory underTest = new PushEventFactory(treeRootHolder, analysisMetadataHolder, taintChecker, flowGenerator);
@Before
public void setUp() {