From: Léo Geoffroy Date: Wed, 24 Jan 2024 12:58:49 +0000 (+0100) Subject: SONAR-21455 Add reliability_issues, maintainability_issues and security_issues measur... X-Git-Tag: 10.4.0.87286~58 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=3ee3d930830f96499efa61ef457af8cb2b325923;p=sonarqube.git SONAR-21455 Add reliability_issues, maintainability_issues and security_issues measures to application computation --- diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/formula/ImpactSumFormula.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/formula/ImpactSumFormula.java new file mode 100644 index 00000000000..2fdf0ef7a83 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/formula/ImpactSumFormula.java @@ -0,0 +1,91 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.ce.task.projectanalysis.formula; + +import java.util.Optional; +import org.sonar.ce.task.projectanalysis.measure.Measure; + +import static java.util.Objects.requireNonNull; + +public class ImpactSumFormula implements Formula { + + private final String metricKey; + + + private ImpactSumFormula(String metricKey) { + this.metricKey = requireNonNull(metricKey, "Metric key cannot be null"); + } + + @Override + public ImpactSumFormula.ImpactCounter createNewCounter() { + return new ImpactSumFormula.ImpactCounter(); + } + + public static ImpactSumFormula createImpactSumFormula(String metricKey) { + return new ImpactSumFormula(metricKey); + } + + @Override + public Optional createMeasure(ImpactCounter counter, CreateMeasureContext context) { + return counter.getValue().map(v -> Measure.newMeasureBuilder().create(v)); + } + + @Override + public String[] getOutputMetricKeys() { + return new String[]{metricKey}; + } + + class ImpactCounter implements Counter { + + private boolean initialized = false; + private boolean hasEmptyValue = false; + private final MeasureImpactBuilder measureImpactBuilder = new MeasureImpactBuilder(); + + @Override + public void aggregate(ImpactSumFormula.ImpactCounter counter) { + Optional value = counter.getValue(); + if (value.isPresent()) { + initialized = true; + measureImpactBuilder.add(value.get()); + } else { + hasEmptyValue = true; + } + } + + @Override + public void initialize(CounterInitializationContext context) { + Optional measureOptional = context.getMeasure(metricKey); + String data = measureOptional.map(Measure::getData).orElse(null); + if (data != null) { + initialized = true; + measureImpactBuilder.add(data); + } else { + hasEmptyValue = true; + } + } + + public Optional getValue() { + if (initialized && !hasEmptyValue) { + return Optional.ofNullable(measureImpactBuilder.build()); + } + return Optional.empty(); + } + } +} diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/formula/MeasureImpactBuilder.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/formula/MeasureImpactBuilder.java new file mode 100644 index 00000000000..bc194833114 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/formula/MeasureImpactBuilder.java @@ -0,0 +1,42 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.ce.task.projectanalysis.formula; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import java.lang.reflect.Type; +import java.util.LinkedHashMap; +import java.util.Map; + +public class MeasureImpactBuilder { + private static final Type GSON_MAP_TYPE = new TypeToken>() { + }.getType(); + private static final Gson gson = new Gson(); + private final Map map = new LinkedHashMap<>(); + + public void add(String value) { + Map impactMap = gson.fromJson(value, GSON_MAP_TYPE); + impactMap.forEach((key, val) -> map.merge(key, val, Integer::sum)); + } + + public String build() { + return gson.toJson(map); + } +} diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/formula/ImpactSumFormulaTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/formula/ImpactSumFormulaTest.java new file mode 100644 index 00000000000..7c69d94e956 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/formula/ImpactSumFormulaTest.java @@ -0,0 +1,142 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.ce.task.projectanalysis.formula; + +import com.google.gson.Gson; +import java.util.Map; +import java.util.Optional; +import javax.annotation.Nullable; +import org.junit.Test; +import org.sonar.api.issue.impact.Severity; +import org.sonar.ce.task.projectanalysis.measure.Measure; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ImpactSumFormulaTest { + + public static final ImpactSumFormula IMPACT_SUM_FORMULA = ImpactSumFormula.createImpactSumFormula("metricKey"); + private final Gson gson = new Gson(); + + private final CounterInitializationContext counterInitializationContext = mock(CounterInitializationContext.class); + + private final CreateMeasureContext createMeasureContext = mock(CreateMeasureContext.class); + + @Test + public void getOutputMetricKeys_shouldReturnCorrectMetrics() { + assertThat(IMPACT_SUM_FORMULA.getOutputMetricKeys()).containsExactly("metricKey"); + } + + @Test + public void createMeasure_whenCounterReturnsValue_shouldReturnExpectedMeasure() { + ImpactSumFormula.ImpactCounter counter = IMPACT_SUM_FORMULA.createNewCounter(); + String value = newImpactJson(4, 2, 1, 1); + addMeasure("metricKey", value); + counter.initialize(counterInitializationContext); + + assertThat(IMPACT_SUM_FORMULA.createMeasure(counter, createMeasureContext)).get().extracting(Measure::getData) + .isEqualTo(value); + } + + @Test + public void createMeasure_whenCounterReturnsNoValue_shouldReturnNoMeasure() { + ImpactSumFormula.ImpactCounter counter = IMPACT_SUM_FORMULA.createNewCounter(); + assertThat(IMPACT_SUM_FORMULA.createMeasure(counter, createMeasureContext)).isEmpty(); + } + + @Test + public void createNewCounter_shouldReturnExpectedCounter() { + assertThat(IMPACT_SUM_FORMULA.createNewCounter()).isNotNull() + .isInstanceOf(ImpactSumFormula.ImpactCounter.class); + } + + @Test + public void getValue_whenInitialized_shouldReturnExpectedValue() { + ImpactSumFormula.ImpactCounter counter = IMPACT_SUM_FORMULA.createNewCounter(); + String value = newImpactJson(4, 2, 1, 1); + addMeasure("metricKey", value); + counter.initialize(counterInitializationContext); + assertThat(counter.getValue()).get().isEqualTo(value); + } + + @Test + public void getValue_whenAggregatingExistingValue_shouldReturnAggregationResult() { + ImpactSumFormula.ImpactCounter counter = IMPACT_SUM_FORMULA.createNewCounter(); + String value = newImpactJson(4, 2, 1, 1); + addMeasure("metricKey", value); + counter.initialize(counterInitializationContext); + + ImpactSumFormula.ImpactCounter counter2 = IMPACT_SUM_FORMULA.createNewCounter(); + String value2 = newImpactJson(3, 1, 1, 1); + addMeasure("metricKey", value2); + counter2.initialize(counterInitializationContext); + + counter.aggregate(counter2); + assertThat(counter.getValue()).get().isEqualTo(newImpactJson(7, 3, 2, 2)); + } + + @Test + public void getValue_whenAggregatingUninitializedValue_shouldReturnEmptyValue() { + ImpactSumFormula.ImpactCounter counter = IMPACT_SUM_FORMULA.createNewCounter(); + String value = newImpactJson(4, 2, 1, 1); + addMeasure("metricKey", value); + counter.initialize(counterInitializationContext); + + ImpactSumFormula.ImpactCounter counter2 = IMPACT_SUM_FORMULA.createNewCounter(); + + counter.aggregate(counter2); + assertThat(counter.getValue()).isEmpty(); + } + + @Test + public void getValue_whenAggregatingEmptyValue_shouldReturnEmptyValue() { + + ImpactSumFormula.ImpactCounter counter = IMPACT_SUM_FORMULA.createNewCounter(); + String value = newImpactJson(4, 2, 1, 1); + addMeasure("metricKey", value); + counter.initialize(counterInitializationContext); + + ImpactSumFormula.ImpactCounter counter2 = IMPACT_SUM_FORMULA.createNewCounter(); + addMeasure("metricKey", null); + counter2.initialize(counterInitializationContext); + + counter.aggregate(counter2); + + assertThat(counter.getValue()).isEmpty(); + } + + @Test + public void getValue_whenNotInitialized_shouldReturnEmpty() { + ImpactSumFormula.ImpactCounter counter = IMPACT_SUM_FORMULA.createNewCounter(); + assertThat(counter.getValue()).isEmpty(); + } + + + public String newImpactJson(Integer total, Integer high, Integer medium, Integer low) { + return gson.toJson(Map.of("total", total, Severity.HIGH.name(), high, Severity.MEDIUM.name(), medium, Severity.LOW.name(), low)); + } + + private void addMeasure(String metricKey, @Nullable String value) { + when(counterInitializationContext.getMeasure(metricKey)).thenReturn(Optional.ofNullable(value).map(v -> Measure.newMeasureBuilder().create(v))); + } + + +} diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/formula/MeasureImpactBuilderTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/formula/MeasureImpactBuilderTest.java new file mode 100644 index 00000000000..7eb308e3740 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/formula/MeasureImpactBuilderTest.java @@ -0,0 +1,57 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.ce.task.projectanalysis.formula; + +import com.google.gson.Gson; +import java.util.Map; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.sonar.api.issue.impact.Severity; + + +public class MeasureImpactBuilderTest { + + Gson gson = new Gson(); + + @Test + public void add_shouldAddExpectedInitialValues() { + MeasureImpactBuilder measureImpactBuilder = new MeasureImpactBuilder(); + Assertions.assertThat(measureImpactBuilder.build()).isEqualTo("{}"); + + measureImpactBuilder = new MeasureImpactBuilder(); + Map map = Map.of("total", 3, Severity.HIGH.name(), 2, Severity.MEDIUM.name(), 1, Severity.LOW.name(), 4); + measureImpactBuilder.add(gson.toJson(map)); + Assertions.assertThat(measureImpactBuilder.build()).isEqualTo(gson.toJson(map)); + } + + @Test + public void add_shouldMergeValuesFromDifferentMaps() { + + MeasureImpactBuilder measureImpactBuilder = new MeasureImpactBuilder(); + Map map = Map.of("total", 3, Severity.HIGH.name(), 2, Severity.MEDIUM.name(), 1, Severity.LOW.name(), 4); + measureImpactBuilder.add(gson.toJson(map)); + + Map map2 = Map.of("total", 6, Severity.HIGH.name(), 4, Severity.MEDIUM.name(), 2, Severity.LOW.name(), 1); + measureImpactBuilder.add(gson.toJson(map2)); + + Assertions.assertThat(measureImpactBuilder.build()).isEqualTo(gson.toJson(Map.of("total", 9, Severity.HIGH.name(), 6, Severity.MEDIUM.name(), 3, Severity.LOW.name(), 5))); + } + +}