3 * Copyright (C) 2009-2024 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.server.issue.notification;
22 import com.google.common.collect.ImmutableSet;
23 import com.google.common.collect.Lists;
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.Optional;
27 import java.util.Random;
28 import java.util.stream.IntStream;
29 import java.util.stream.Stream;
30 import javax.annotation.CheckForNull;
31 import org.junit.jupiter.api.Test;
32 import org.sonar.api.issue.Issue;
33 import org.sonar.api.rule.RuleKey;
34 import org.sonar.api.rules.RuleType;
35 import org.sonar.api.utils.Duration;
36 import org.sonar.core.issue.DefaultIssue;
37 import org.sonar.server.issue.notification.NewIssuesStatistics.Metric;
39 import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
40 import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric;
41 import static org.assertj.core.api.Assertions.assertThat;
43 class NewIssuesStatisticsTest {
45 private final Random random = new Random();
46 private RuleType randomRuleTypeExceptHotspot = RuleType.values()[random.nextInt(RuleType.values().length - 1)];
47 private NewIssuesStatistics underTest = new NewIssuesStatistics(Issue::isNew);
50 void add_issues_with_correct_global_statistics() {
51 DefaultIssue issue = new DefaultIssue()
52 .setAssigneeUuid("maynard")
53 .setComponentUuid("file-uuid")
55 .setRuleKey(RuleKey.of("SonarQube", "rule-the-world"))
56 .setTags(Lists.newArrayList("bug", "owasp"))
57 .setEffort(Duration.create(5L));
60 underTest.add(issue.setAssigneeUuid("james"));
61 underTest.add(issue.setAssigneeUuid("keenan"));
63 assertThat(countDistributionTotal(Metric.ASSIGNEE, "maynard")).isOne();
64 assertThat(countDistributionTotal(Metric.ASSIGNEE, "james")).isOne();
65 assertThat(countDistributionTotal(Metric.ASSIGNEE, "keenan")).isOne();
66 assertThat(countDistributionTotal(Metric.ASSIGNEE, "wrong.login")).isNull();
67 assertThat(countDistributionTotal(Metric.COMPONENT, "file-uuid")).isEqualTo(3);
68 assertThat(countDistributionTotal(Metric.COMPONENT, "wrong-uuid")).isNull();
69 assertThat(countDistributionTotal(Metric.TAG, "owasp")).isEqualTo(3);
70 assertThat(countDistributionTotal(Metric.TAG, "wrong-tag")).isNull();
71 assertThat(countDistributionTotal(Metric.RULE, "SonarQube:rule-the-world")).isEqualTo(3);
72 assertThat(countDistributionTotal(Metric.RULE, "SonarQube:has-a-fake-rule")).isNull();
73 assertThat(underTest.globalStatistics().getIssueCount().getTotal()).isEqualTo(3);
74 assertThat(underTest.globalStatistics().hasIssues()).isTrue();
75 assertThat(underTest.hasIssues()).isTrue();
76 assertThat(underTest.getAssigneesStatistics().get("maynard").hasIssues()).isTrue();
80 void add_counts_issues_on_current_analysis_globally_and_per_assignee() {
81 String assignee = randomAlphanumeric(10);
82 IntStream.range(0, 10)
83 .mapToObj(i -> new DefaultIssue().setAssigneeUuid(assignee).setNew(true))
84 .forEach(underTest::add);
86 MetricStatsInt globalIssueCount = underTest.globalStatistics().getIssueCount();
87 MetricStatsInt assigneeIssueCount = underTest.getAssigneesStatistics().get(assignee).getIssueCount();
88 assertThat(globalIssueCount.getOnCurrentAnalysis()).isEqualTo(10);
89 assertThat(globalIssueCount.getTotal()).isEqualTo(10);
90 assertThat(assigneeIssueCount.getOnCurrentAnalysis()).isEqualTo(10);
91 assertThat(assigneeIssueCount.getTotal()).isEqualTo(10);
95 void add_counts_issues_off_current_analysis_globally_and_per_assignee() {
96 String assignee = randomAlphanumeric(10);
97 IntStream.range(0, 10)
98 .mapToObj(i -> new DefaultIssue().setAssigneeUuid(assignee).setNew(false))
99 .forEach(underTest::add);
101 MetricStatsInt globalIssueCount = underTest.globalStatistics().getIssueCount();
102 MetricStatsInt assigneeIssueCount = underTest.getAssigneesStatistics().get(assignee).getIssueCount();
103 assertThat(globalIssueCount.getOnCurrentAnalysis()).isZero();
104 assertThat(globalIssueCount.getTotal()).isEqualTo(10);
105 assertThat(assigneeIssueCount.getOnCurrentAnalysis()).isZero();
106 assertThat(assigneeIssueCount.getTotal()).isEqualTo(10);
110 void add_counts_issue_per_component_on_current_analysis_globally_and_per_assignee() {
111 List<String> componentUuids = IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> randomAlphabetic(3)).toList();
112 String assignee = randomAlphanumeric(10);
113 componentUuids.stream()
114 .map(componentUuid -> new DefaultIssue().setType(randomRuleTypeExceptHotspot).setComponentUuid(componentUuid).setAssigneeUuid(assignee).setNew(true))
115 .forEach(underTest::add);
117 DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.COMPONENT);
118 DistributedMetricStatsInt assigneeDistribution =
119 underTest.getAssigneesStatistics().get(assignee).getDistributedMetricStats(Metric.COMPONENT);
120 Stream.of(globalDistribution, assigneeDistribution)
121 .forEach(distribution -> componentUuids.forEach(componentUuid -> assertStats(distribution, componentUuid, 1, 1)));
125 void add_counts_issue_per_component_off_current_analysis_globally_and_per_assignee() {
126 List<String> componentUuids = IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> randomAlphabetic(3)).toList();
127 String assignee = randomAlphanumeric(10);
128 componentUuids.stream()
129 .map(componentUuid -> new DefaultIssue().setType(randomRuleTypeExceptHotspot).setComponentUuid(componentUuid).setAssigneeUuid(assignee).setNew(false))
130 .forEach(underTest::add);
132 DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.COMPONENT);
133 NewIssuesStatistics.Stats stats = underTest.getAssigneesStatistics().get(assignee);
134 DistributedMetricStatsInt assigneeDistribution = stats.getDistributedMetricStats(Metric.COMPONENT);
135 Stream.of(globalDistribution, assigneeDistribution)
136 .forEach(distribution -> componentUuids.forEach(componentUuid -> assertStats(distribution, componentUuid, 0, 1)));
140 void add_does_not_count_component_if_null_neither_globally_nor_per_assignee() {
141 String assignee = randomAlphanumeric(10);
142 underTest.add(new DefaultIssue().setType(randomRuleTypeExceptHotspot).setComponentUuid(null).setAssigneeUuid(assignee).setNew(new Random().nextBoolean()));
144 DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.COMPONENT);
145 DistributedMetricStatsInt assigneeDistribution =
146 underTest.getAssigneesStatistics().get(assignee).getDistributedMetricStats(Metric.COMPONENT);
147 Stream.of(globalDistribution, assigneeDistribution)
148 .forEach(distribution -> {
149 assertThat(distribution.getTotal()).isZero();
150 assertThat(distribution.getForLabel(null)).isEmpty();
155 void add_counts_issue_per_ruleKey_on_current_analysis_globally_and_per_assignee() {
156 String repository = randomAlphanumeric(3);
157 List<String> ruleKeys = IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> randomAlphabetic(3)).toList();
158 String assignee = randomAlphanumeric(10);
160 .map(ruleKey -> new DefaultIssue().setType(randomRuleTypeExceptHotspot).setRuleKey(RuleKey.of(repository, ruleKey)).setAssigneeUuid(assignee).setNew(true))
161 .forEach(underTest::add);
163 DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.RULE);
164 NewIssuesStatistics.Stats stats = underTest.getAssigneesStatistics().get(assignee);
165 DistributedMetricStatsInt assigneeDistribution = stats.getDistributedMetricStats(Metric.RULE);
166 Stream.of(globalDistribution, assigneeDistribution)
167 .forEach(distribution -> ruleKeys.forEach(ruleKey -> assertStats(distribution, RuleKey.of(repository, ruleKey).toString(), 1, 1)));
171 void add_counts_issue_per_ruleKey_off_current_analysis_globally_and_per_assignee() {
172 String repository = randomAlphanumeric(3);
173 List<String> ruleKeys = IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> randomAlphabetic(3)).toList();
174 String assignee = randomAlphanumeric(10);
176 .map(ruleKey -> new DefaultIssue().setType(randomRuleTypeExceptHotspot).setRuleKey(RuleKey.of(repository, ruleKey)).setAssigneeUuid(assignee).setNew(false))
177 .forEach(underTest::add);
179 DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.RULE);
180 DistributedMetricStatsInt assigneeDistribution =
181 underTest.getAssigneesStatistics().get(assignee).getDistributedMetricStats(Metric.RULE);
182 Stream.of(globalDistribution, assigneeDistribution)
183 .forEach(distribution -> ruleKeys.forEach(ruleKey -> assertStats(distribution, RuleKey.of(repository, ruleKey).toString(), 0, 1)));
187 void add_does_not_count_ruleKey_if_null_neither_globally_nor_per_assignee() {
188 String assignee = randomAlphanumeric(10);
189 underTest.add(new DefaultIssue().setType(randomRuleTypeExceptHotspot).setRuleKey(null).setAssigneeUuid(assignee).setNew(new Random().nextBoolean()));
191 DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.RULE);
192 DistributedMetricStatsInt assigneeDistribution =
193 underTest.getAssigneesStatistics().get(assignee).getDistributedMetricStats(Metric.RULE);
194 Stream.of(globalDistribution, assigneeDistribution)
195 .forEach(distribution -> {
196 assertThat(distribution.getTotal()).isZero();
197 assertThat(distribution.getForLabel(null)).isEmpty();
202 void add_counts_issue_per_assignee_on_current_analysis_globally_and_per_assignee() {
203 List<String> assignees = IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> randomAlphabetic(3)).toList();
205 .map(assignee -> new DefaultIssue().setType(randomRuleTypeExceptHotspot).setAssigneeUuid(assignee).setNew(true))
206 .forEach(underTest::add);
208 DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.ASSIGNEE);
209 assignees.forEach(assignee -> assertStats(globalDistribution, assignee, 1, 1));
210 assignees.forEach(assignee -> {
211 NewIssuesStatistics.Stats stats = underTest.getAssigneesStatistics().get(assignee);
212 DistributedMetricStatsInt assigneeStats = stats.getDistributedMetricStats(Metric.ASSIGNEE);
213 assertThat(assigneeStats.getOnCurrentAnalysis()).isOne();
214 assertThat(assigneeStats.getTotal()).isOne();
215 assignees.forEach(s -> {
216 Optional<MetricStatsInt> forLabelOpts = assigneeStats.getForLabel(s);
217 if (s.equals(assignee)) {
218 assertThat(forLabelOpts).isPresent();
219 MetricStatsInt forLabel = forLabelOpts.get();
220 assertThat(forLabel.getOnCurrentAnalysis()).isOne();
221 assertThat(forLabel.getTotal()).isOne();
223 assertThat(forLabelOpts).isEmpty();
230 void add_counts_issue_per_assignee_off_current_analysis_globally_and_per_assignee() {
231 List<String> assignees = IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> randomAlphabetic(3)).toList();
233 .map(assignee -> new DefaultIssue().setType(randomRuleTypeExceptHotspot).setAssigneeUuid(assignee).setNew(false))
234 .forEach(underTest::add);
236 DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.ASSIGNEE);
237 assignees.forEach(assignee -> assertStats(globalDistribution, assignee, 0, 1));
238 assignees.forEach(assignee -> {
239 NewIssuesStatistics.Stats stats = underTest.getAssigneesStatistics().get(assignee);
240 DistributedMetricStatsInt assigneeStats = stats.getDistributedMetricStats(Metric.ASSIGNEE);
241 assertThat(assigneeStats.getOnCurrentAnalysis()).isZero();
242 assertThat(assigneeStats.getTotal()).isOne();
243 assignees.forEach(s -> {
244 Optional<MetricStatsInt> forLabelOpts = assigneeStats.getForLabel(s);
245 if (s.equals(assignee)) {
246 assertThat(forLabelOpts).isPresent();
247 MetricStatsInt forLabel = forLabelOpts.get();
248 assertThat(forLabel.getOnCurrentAnalysis()).isZero();
249 assertThat(forLabel.getTotal()).isOne();
251 assertThat(forLabelOpts).isEmpty();
258 void add_does_not_assignee_if_empty_neither_globally_nor_per_assignee() {
259 underTest.add(new DefaultIssue().setType(randomRuleTypeExceptHotspot).setAssigneeUuid(null).setNew(new Random().nextBoolean()));
261 DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.ASSIGNEE);
262 assertThat(globalDistribution.getTotal()).isZero();
263 assertThat(globalDistribution.getForLabel(null)).isEmpty();
264 assertThat(underTest.getAssigneesStatistics()).isEmpty();
268 void add_counts_issue_per_tags_on_current_analysis_globally_and_per_assignee() {
269 List<String> tags = IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> randomAlphabetic(3)).toList();
270 String assignee = randomAlphanumeric(10);
271 underTest.add(new DefaultIssue().setType(randomRuleTypeExceptHotspot).setTags(tags).setAssigneeUuid(assignee).setNew(true));
273 DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.TAG);
274 DistributedMetricStatsInt assigneeDistribution = underTest.getAssigneesStatistics().get(assignee).getDistributedMetricStats(Metric.TAG);
275 Stream.of(globalDistribution, assigneeDistribution)
276 .forEach(distribution -> tags.forEach(tag -> assertStats(distribution, tag, 1, 1)));
280 void add_counts_issue_per_tags_off_current_analysis_globally_and_per_assignee() {
281 List<String> tags = IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> randomAlphabetic(3)).toList();
282 String assignee = randomAlphanumeric(10);
283 underTest.add(new DefaultIssue().setType(randomRuleTypeExceptHotspot).setTags(tags).setAssigneeUuid(assignee).setNew(false));
285 DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.TAG);
286 DistributedMetricStatsInt assigneeDistribution = underTest.getAssigneesStatistics().get(assignee).getDistributedMetricStats(Metric.TAG);
287 Stream.of(globalDistribution, assigneeDistribution)
288 .forEach(distribution -> tags.forEach(tag -> assertStats(distribution, tag, 0, 1)));
292 void add_does_not_count_tags_if_empty_neither_globally_nor_per_assignee() {
293 String assignee = randomAlphanumeric(10);
294 underTest.add(new DefaultIssue().setType(randomRuleTypeExceptHotspot).setTags(Collections.emptyList()).setAssigneeUuid(assignee).setNew(new Random().nextBoolean()));
296 DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.TAG);
297 DistributedMetricStatsInt assigneeDistribution = underTest.getAssigneesStatistics().get(assignee).getDistributedMetricStats(Metric.TAG);
298 Stream.of(globalDistribution, assigneeDistribution)
299 .forEach(distribution -> {
300 assertThat(distribution.getTotal()).isZero();
301 assertThat(distribution.getForLabel(null)).isEmpty();
306 void do_not_have_issues_when_no_issue_added() {
307 assertThat(underTest.globalStatistics().hasIssues()).isFalse();
311 void verify_toString() {
312 String componentUuid = randomAlphanumeric(2);
313 String tag = randomAlphanumeric(3);
314 String assignee = randomAlphanumeric(4);
315 int effort = 10 + new Random().nextInt(5);
316 RuleKey ruleKey = RuleKey.of(randomAlphanumeric(5), randomAlphanumeric(6));
317 underTest.add(new DefaultIssue()
318 .setType(randomRuleTypeExceptHotspot)
319 .setComponentUuid(componentUuid)
320 .setTags(ImmutableSet.of(tag))
321 .setAssigneeUuid(assignee)
323 .setEffort(Duration.create(effort)));
325 assertThat(underTest)
326 .hasToString("NewIssuesStatistics{" +
327 "assigneesStatistics={" + assignee + "=" +
328 "Stats{distributions={" +
329 "TAG=DistributedMetricStatsInt{globalStats=MetricStatsInt{on=1, off=0}, " +
330 "statsPerLabel={" + tag + "=MetricStatsInt{on=1, off=0}}}, " +
331 "COMPONENT=DistributedMetricStatsInt{globalStats=MetricStatsInt{on=1, off=0}, " +
332 "statsPerLabel={" + componentUuid + "=MetricStatsInt{on=1, off=0}}}, " +
333 "ASSIGNEE=DistributedMetricStatsInt{globalStats=MetricStatsInt{on=1, off=0}, " +
334 "statsPerLabel={" + assignee + "=MetricStatsInt{on=1, off=0}}}, " +
335 "RULE=DistributedMetricStatsInt{globalStats=MetricStatsInt{on=1, off=0}, " +
336 "statsPerLabel={" + ruleKey.toString() + "=MetricStatsInt{on=1, off=0}}}}, " +
337 "issueCount=MetricStatsInt{on=1, off=0}}}, " +
338 "globalStatistics=Stats{distributions={" +
339 "TAG=DistributedMetricStatsInt{globalStats=MetricStatsInt{on=1, off=0}, " +
340 "statsPerLabel={" + tag + "=MetricStatsInt{on=1, off=0}}}, " +
341 "COMPONENT=DistributedMetricStatsInt{globalStats=MetricStatsInt{on=1, off=0}, " +
342 "statsPerLabel={" + componentUuid + "=MetricStatsInt{on=1, off=0}}}, " +
343 "ASSIGNEE=DistributedMetricStatsInt{globalStats=MetricStatsInt{on=1, off=0}, " +
344 "statsPerLabel={" + assignee + "=MetricStatsInt{on=1, off=0}}}, " +
345 "RULE=DistributedMetricStatsInt{globalStats=MetricStatsInt{on=1, off=0}, " +
346 "statsPerLabel={" + ruleKey.toString() + "=MetricStatsInt{on=1, off=0}}}}, " +
347 "issueCount=MetricStatsInt{on=1, off=0}}}");
351 private Integer countDistributionTotal(Metric metric, String label) {
352 return underTest.globalStatistics()
353 .getDistributedMetricStats(metric)
355 .map(MetricStatsInt::getTotal)
359 private void assertStats(DistributedMetricStatsInt distribution, String label, int onCurrentAnalysis, int total) {
360 Optional<MetricStatsInt> statsOption = distribution.getForLabel(label);
361 assertThat(statsOption.isPresent()).describedAs("distribution for label %s not found", label).isTrue();
362 MetricStatsInt stats = statsOption.get();
363 assertThat(stats.getOnCurrentAnalysis()).isEqualTo(onCurrentAnalysis);
364 assertThat(stats.getTotal()).isEqualTo(total);