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;
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;
ComponentIssuesRepositoryImpl.class,
IssueFilter.class,
+ FlowGenerator.class,
// push events
PushEventFactory.class,
@CheckForNull
String getPluginKey();
+
+ String getDefaultRuleDescription();
+
+ String getSeverity();
}
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;
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;
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();
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
return pluginKey;
}
+ public String getDefaultRuleDescription() {
+ return defaultRuleDescription;
+ }
+
+ @Override
+ public String getSeverity() {
+ return severity;
+ }
+
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
public String getPluginKey() {
return null;
}
+
+ @Override
+ public String getDefaultRuleDescription() {
+ return addHocRule.getDescription();
+ }
+
+ @Override
+ public String getSeverity() {
+ return addHocRule.getSeverity();
+ }
}
}
--- /dev/null
+/*
+ * 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;
+
+public class Flow {
+ private List<Location> locations;
+
+ public Flow() {
+ // nothing to do
+ }
+
+ public List<Location> getLocations() {
+ return locations;
+ }
+
+ public void setLocations(List<Location> locations) {
+ this.locations = locations;
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+
+public class Location {
+ private String filePath;
+ private String message;
+ private TextRange textRange;
+
+ public Location() {
+ // nothing to do
+ }
+
+ public String getFilePath() {
+ return filePath;
+ }
+
+ public void setFilePath(String filePath) {
+ this.filePath = filePath;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public TextRange getTextRange() {
+ return textRange;
+ }
+
+ public void setTextRange(TextRange textRange) {
+ this.textRange = textRange;
+ }
+}
--- /dev/null
+/*
+ * 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.Objects;
+
+public class TextRange {
+ private int startLine;
+ private int startLineOffset;
+ private int endLine;
+ private int endLineOffset;
+ private String hash;
+
+ public TextRange() {
+ // nothing to do
+ }
+
+ public int getStartLine() {
+ return startLine;
+ }
+
+ public void setStartLine(int startLine) {
+ this.startLine = startLine;
+ }
+
+ public int getStartLineOffset() {
+ return startLineOffset;
+ }
+
+ public void setStartLineOffset(int startLineOffset) {
+ this.startLineOffset = startLineOffset;
+ }
+
+ public int getEndLine() {
+ return endLine;
+ }
+
+ public void setEndLine(int endLine) {
+ this.endLine = endLine;
+ }
+
+ public int getEndLineOffset() {
+ return endLineOffset;
+ }
+
+ public void setEndLineOffset(int endLineOffset) {
+ this.endLineOffset = endLineOffset;
+ }
+
+ public String getHash() {
+ return hash;
+ }
+
+ 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());
+ }
+
+}
+++ /dev/null
-/*
- * 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.pushevent;
-
-import java.util.List;
-
-public class Flow {
- private List<Location> locations;
-
- public Flow() {
- // nothing to do
- }
-
- public List<Location> getLocations() {
- return locations;
- }
-
- public void setLocations(List<Location> locations) {
- this.locations = locations;
- }
-}
+++ /dev/null
-/*
- * 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.pushevent;
-
-public class Location {
- private String filePath;
- private String message;
- private TextRange textRange;
-
- public Location() {
- // nothing to do
- }
-
- public String getFilePath() {
- return filePath;
- }
-
- public void setFilePath(String filePath) {
- this.filePath = filePath;
- }
-
- public String getMessage() {
- return message;
- }
-
- public void setMessage(String message) {
- this.message = message;
- }
-
- public TextRange getTextRange() {
- return textRange;
- }
-
- public void setTextRange(TextRange textRange) {
- this.textRange = textRange;
- }
-}
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;
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;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
-import static java.util.Objects.requireNonNullElse;
@ComputeEngineSide
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) {
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;
}
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 {
+++ /dev/null
-/*
- * 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.pushevent;
-
-public class TextRange {
- private int startLine;
- private int startLineOffset;
- private int endLine;
- private int endLineOffset;
- private String hash;
-
- public TextRange() {
- // nothing to do
- }
-
- public int getStartLine() {
- return startLine;
- }
-
- public void setStartLine(int startLine) {
- this.startLine = startLine;
- }
-
- public int getStartLineOffset() {
- return startLineOffset;
- }
-
- public void setStartLineOffset(int startLineOffset) {
- this.startLineOffset = startLineOffset;
- }
-
- public int getEndLine() {
- return endLine;
- }
-
- public void setEndLine(int endLine) {
- this.endLine = endLine;
- }
-
- public int getEndLineOffset() {
- return endLineOffset;
- }
-
- public void setEndLineOffset(int endLineOffset) {
- this.endLineOffset = endLineOffset;
- }
-
- public String getHash() {
- return hash;
- }
-
- public void setHash(String hash) {
- this.hash = hash;
- }
-}
return pluginKey;
}
+ @Override
+ public String getDefaultRuleDescription() {
+ return null;
+ }
+
+ @Override
+ public String getSeverity() {
+ return null;
+ }
+
@Override
public boolean isExternal() {
return isExternal;
--- /dev/null
+/*
+ * 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());
+ }
+
+}
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;
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() {