You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

IssueCounterTest.java 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  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.
  10. *
  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.
  15. *
  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.
  19. */
  20. package org.sonar.ce.task.projectanalysis.issue;
  21. import com.google.gson.Gson;
  22. import java.util.Arrays;
  23. import java.util.LinkedHashMap;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.Set;
  27. import javax.annotation.Nullable;
  28. import org.assertj.core.data.MapEntry;
  29. import org.junit.jupiter.api.Test;
  30. import org.junit.jupiter.api.extension.RegisterExtension;
  31. import org.sonar.api.issue.impact.Severity;
  32. import org.sonar.api.issue.impact.SoftwareQuality;
  33. import org.sonar.api.rules.RuleType;
  34. import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
  35. import org.sonar.ce.task.projectanalysis.component.Component;
  36. import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
  37. import org.sonar.ce.task.projectanalysis.measure.Measure;
  38. import org.sonar.ce.task.projectanalysis.measure.MeasureRepoEntry;
  39. import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryRule;
  40. import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule;
  41. import org.sonar.core.issue.DefaultIssue;
  42. import org.sonar.db.rule.RuleTesting;
  43. import org.sonar.server.measure.ImpactMeasureBuilder;
  44. import static java.util.Arrays.stream;
  45. import static org.assertj.core.api.Assertions.assertThat;
  46. import static org.assertj.core.api.Assertions.entry;
  47. import static org.mockito.ArgumentMatchers.any;
  48. import static org.mockito.ArgumentMatchers.eq;
  49. import static org.mockito.Mockito.mock;
  50. import static org.mockito.Mockito.when;
  51. import static org.sonar.api.issue.Issue.RESOLUTION_FALSE_POSITIVE;
  52. import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
  53. import static org.sonar.api.issue.Issue.RESOLUTION_WONT_FIX;
  54. import static org.sonar.api.issue.Issue.STATUS_CLOSED;
  55. import static org.sonar.api.issue.Issue.STATUS_CONFIRMED;
  56. import static org.sonar.api.issue.Issue.STATUS_OPEN;
  57. import static org.sonar.api.issue.Issue.STATUS_RESOLVED;
  58. import static org.sonar.api.issue.impact.Severity.HIGH;
  59. import static org.sonar.api.issue.impact.Severity.LOW;
  60. import static org.sonar.api.issue.impact.Severity.MEDIUM;
  61. import static org.sonar.api.measures.CoreMetrics.ACCEPTED_ISSUES;
  62. import static org.sonar.api.measures.CoreMetrics.ACCEPTED_ISSUES_KEY;
  63. import static org.sonar.api.measures.CoreMetrics.BLOCKER_VIOLATIONS;
  64. import static org.sonar.api.measures.CoreMetrics.BLOCKER_VIOLATIONS_KEY;
  65. import static org.sonar.api.measures.CoreMetrics.BUGS;
  66. import static org.sonar.api.measures.CoreMetrics.BUGS_KEY;
  67. import static org.sonar.api.measures.CoreMetrics.CODE_SMELLS;
  68. import static org.sonar.api.measures.CoreMetrics.CODE_SMELLS_KEY;
  69. import static org.sonar.api.measures.CoreMetrics.CONFIRMED_ISSUES;
  70. import static org.sonar.api.measures.CoreMetrics.CONFIRMED_ISSUES_KEY;
  71. import static org.sonar.api.measures.CoreMetrics.CRITICAL_VIOLATIONS;
  72. import static org.sonar.api.measures.CoreMetrics.CRITICAL_VIOLATIONS_KEY;
  73. import static org.sonar.api.measures.CoreMetrics.FALSE_POSITIVE_ISSUES;
  74. import static org.sonar.api.measures.CoreMetrics.FALSE_POSITIVE_ISSUES_KEY;
  75. import static org.sonar.api.measures.CoreMetrics.HIGH_IMPACT_ACCEPTED_ISSUES;
  76. import static org.sonar.api.measures.CoreMetrics.HIGH_IMPACT_ACCEPTED_ISSUES_KEY;
  77. import static org.sonar.api.measures.CoreMetrics.INFO_VIOLATIONS;
  78. import static org.sonar.api.measures.CoreMetrics.MAINTAINABILITY_ISSUES;
  79. import static org.sonar.api.measures.CoreMetrics.MAJOR_VIOLATIONS;
  80. import static org.sonar.api.measures.CoreMetrics.MAJOR_VIOLATIONS_KEY;
  81. import static org.sonar.api.measures.CoreMetrics.MINOR_VIOLATIONS;
  82. import static org.sonar.api.measures.CoreMetrics.NEW_ACCEPTED_ISSUES;
  83. import static org.sonar.api.measures.CoreMetrics.NEW_ACCEPTED_ISSUES_KEY;
  84. import static org.sonar.api.measures.CoreMetrics.NEW_BLOCKER_VIOLATIONS;
  85. import static org.sonar.api.measures.CoreMetrics.NEW_BLOCKER_VIOLATIONS_KEY;
  86. import static org.sonar.api.measures.CoreMetrics.NEW_BUGS;
  87. import static org.sonar.api.measures.CoreMetrics.NEW_BUGS_KEY;
  88. import static org.sonar.api.measures.CoreMetrics.NEW_CODE_SMELLS;
  89. import static org.sonar.api.measures.CoreMetrics.NEW_CODE_SMELLS_KEY;
  90. import static org.sonar.api.measures.CoreMetrics.NEW_CRITICAL_VIOLATIONS;
  91. import static org.sonar.api.measures.CoreMetrics.NEW_CRITICAL_VIOLATIONS_KEY;
  92. import static org.sonar.api.measures.CoreMetrics.NEW_INFO_VIOLATIONS;
  93. import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_ISSUES;
  94. import static org.sonar.api.measures.CoreMetrics.NEW_MAJOR_VIOLATIONS;
  95. import static org.sonar.api.measures.CoreMetrics.NEW_MAJOR_VIOLATIONS_KEY;
  96. import static org.sonar.api.measures.CoreMetrics.NEW_MINOR_VIOLATIONS;
  97. import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_ISSUES;
  98. import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS;
  99. import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_KEY;
  100. import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_ISSUES;
  101. import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS;
  102. import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS_KEY;
  103. import static org.sonar.api.measures.CoreMetrics.NEW_VULNERABILITIES;
  104. import static org.sonar.api.measures.CoreMetrics.NEW_VULNERABILITIES_KEY;
  105. import static org.sonar.api.measures.CoreMetrics.OPEN_ISSUES;
  106. import static org.sonar.api.measures.CoreMetrics.OPEN_ISSUES_KEY;
  107. import static org.sonar.api.measures.CoreMetrics.RELIABILITY_ISSUES;
  108. import static org.sonar.api.measures.CoreMetrics.REOPENED_ISSUES;
  109. import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS;
  110. import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_KEY;
  111. import static org.sonar.api.measures.CoreMetrics.SECURITY_ISSUES;
  112. import static org.sonar.api.measures.CoreMetrics.VIOLATIONS;
  113. import static org.sonar.api.measures.CoreMetrics.VIOLATIONS_KEY;
  114. import static org.sonar.api.measures.CoreMetrics.VULNERABILITIES;
  115. import static org.sonar.api.measures.CoreMetrics.VULNERABILITIES_KEY;
  116. import static org.sonar.api.rule.Severity.BLOCKER;
  117. import static org.sonar.api.rule.Severity.CRITICAL;
  118. import static org.sonar.api.rule.Severity.MAJOR;
  119. import static org.sonar.api.rules.RuleType.BUG;
  120. import static org.sonar.api.rules.RuleType.CODE_SMELL;
  121. import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
  122. import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
  123. import static org.sonar.ce.task.projectanalysis.issue.IssueCounter.IMPACT_TO_METRIC_KEY;
  124. import static org.sonar.ce.task.projectanalysis.issue.IssueCounter.IMPACT_TO_NEW_METRIC_KEY;
  125. import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilder;
  126. import static org.sonar.ce.task.projectanalysis.measure.MeasureRepoEntry.entryOf;
  127. class IssueCounterTest {
  128. private static final Component FILE1 = builder(Component.Type.FILE, 1).build();
  129. private static final Component FILE2 = builder(Component.Type.FILE, 2).build();
  130. private static final Component FILE3 = builder(Component.Type.FILE, 3).build();
  131. private static final Component PROJECT = builder(Component.Type.PROJECT, 4).addChildren(FILE1, FILE2, FILE3).build();
  132. @RegisterExtension
  133. private final BatchReportReaderRule reportReader = new BatchReportReaderRule();
  134. @RegisterExtension
  135. private final TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
  136. @RegisterExtension
  137. private final MetricRepositoryRule metricRepository = new MetricRepositoryRule()
  138. .add(VIOLATIONS)
  139. .add(OPEN_ISSUES)
  140. .add(REOPENED_ISSUES)
  141. .add(CONFIRMED_ISSUES)
  142. .add(BLOCKER_VIOLATIONS)
  143. .add(CRITICAL_VIOLATIONS)
  144. .add(MAJOR_VIOLATIONS)
  145. .add(MINOR_VIOLATIONS)
  146. .add(INFO_VIOLATIONS)
  147. .add(NEW_VIOLATIONS)
  148. .add(NEW_BLOCKER_VIOLATIONS)
  149. .add(NEW_CRITICAL_VIOLATIONS)
  150. .add(NEW_MAJOR_VIOLATIONS)
  151. .add(NEW_MINOR_VIOLATIONS)
  152. .add(NEW_INFO_VIOLATIONS)
  153. .add(FALSE_POSITIVE_ISSUES)
  154. .add(ACCEPTED_ISSUES)
  155. .add(CODE_SMELLS)
  156. .add(BUGS)
  157. .add(VULNERABILITIES)
  158. .add(SECURITY_HOTSPOTS)
  159. .add(NEW_CODE_SMELLS)
  160. .add(NEW_BUGS)
  161. .add(NEW_VULNERABILITIES)
  162. .add(NEW_SECURITY_HOTSPOTS)
  163. .add(NEW_ACCEPTED_ISSUES)
  164. .add(HIGH_IMPACT_ACCEPTED_ISSUES)
  165. .add(RELIABILITY_ISSUES)
  166. .add(MAINTAINABILITY_ISSUES)
  167. .add(SECURITY_ISSUES)
  168. .add(NEW_RELIABILITY_ISSUES)
  169. .add(NEW_MAINTAINABILITY_ISSUES)
  170. .add(NEW_SECURITY_ISSUES);
  171. @RegisterExtension
  172. private final MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
  173. private final NewIssueClassifier newIssueClassifier = mock(NewIssueClassifier.class);
  174. private final IssueCounter underTest = new IssueCounter(metricRepository, measureRepository, newIssueClassifier);
  175. private static int issueCounter;
  176. @Test
  177. void count_issues_by_status() {
  178. // bottom-up traversal -> from files to project
  179. underTest.beforeComponent(FILE1);
  180. underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER));
  181. underTest.onIssue(FILE1, createIssue(RESOLUTION_FIXED, STATUS_CLOSED, MAJOR));
  182. underTest.onIssue(FILE1, createIssue(RESOLUTION_FALSE_POSITIVE, STATUS_RESOLVED, MAJOR));
  183. underTest.afterComponent(FILE1);
  184. underTest.beforeComponent(FILE2);
  185. underTest.onIssue(FILE2, createIssue(null, STATUS_CONFIRMED, BLOCKER));
  186. underTest.onIssue(FILE2, createIssue(null, STATUS_CONFIRMED, MAJOR));
  187. underTest.afterComponent(FILE2);
  188. underTest.beforeComponent(FILE3);
  189. // Security hotspot should be ignored
  190. underTest.onIssue(FILE3, createSecurityHotspot().setStatus(STATUS_OPEN));
  191. underTest.afterComponent(FILE3);
  192. underTest.beforeComponent(PROJECT);
  193. underTest.afterComponent(PROJECT);
  194. assertMeasures(FILE1, entry(VIOLATIONS_KEY, 1), entry(OPEN_ISSUES_KEY, 1), entry(CONFIRMED_ISSUES_KEY, 0));
  195. assertMeasures(FILE2, entry(VIOLATIONS_KEY, 2), entry(OPEN_ISSUES_KEY, 0), entry(CONFIRMED_ISSUES_KEY, 2));
  196. assertMeasures(FILE3, entry(VIOLATIONS_KEY, 0));
  197. assertMeasures(PROJECT, entry(VIOLATIONS_KEY, 3), entry(OPEN_ISSUES_KEY, 1), entry(CONFIRMED_ISSUES_KEY, 2));
  198. }
  199. @Test
  200. void count_issues_by_resolution() {
  201. // bottom-up traversal -> from files to project
  202. underTest.beforeComponent(FILE1);
  203. underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER));
  204. underTest.onIssue(FILE1, createIssue(RESOLUTION_FIXED, STATUS_CLOSED, MAJOR));
  205. underTest.onIssue(FILE1, createIssue(RESOLUTION_FALSE_POSITIVE, STATUS_RESOLVED, MAJOR));
  206. underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, MAJOR));
  207. underTest.afterComponent(FILE1);
  208. underTest.beforeComponent(FILE2);
  209. underTest.onIssue(FILE2, createIssue(null, STATUS_CONFIRMED, BLOCKER));
  210. underTest.onIssue(FILE2, createIssue(null, STATUS_CONFIRMED, MAJOR));
  211. underTest.onIssue(FILE2, createIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, MAJOR));
  212. underTest.afterComponent(FILE2);
  213. underTest.beforeComponent(FILE3);
  214. // Security hotspot should be ignored
  215. underTest.onIssue(FILE3, createSecurityHotspot().setResolution(RESOLUTION_WONT_FIX));
  216. underTest.afterComponent(FILE3);
  217. underTest.beforeComponent(PROJECT);
  218. underTest.afterComponent(PROJECT);
  219. assertMeasures(FILE1, entry(VIOLATIONS_KEY, 1), entry(FALSE_POSITIVE_ISSUES_KEY, 1), entry(ACCEPTED_ISSUES_KEY, 1));
  220. assertMeasures(FILE2, entry(VIOLATIONS_KEY, 2), entry(FALSE_POSITIVE_ISSUES_KEY, 0), entry(ACCEPTED_ISSUES_KEY, 1));
  221. assertMeasures(FILE3, entry(VIOLATIONS_KEY, 0));
  222. assertMeasures(PROJECT, entry(VIOLATIONS_KEY, 3), entry(FALSE_POSITIVE_ISSUES_KEY, 1), entry(ACCEPTED_ISSUES_KEY, 2));
  223. }
  224. @Test
  225. void count_unresolved_issues_by_severity() {
  226. // bottom-up traversal -> from files to project
  227. underTest.beforeComponent(FILE1);
  228. underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER));
  229. // this resolved issue is ignored
  230. underTest.onIssue(FILE1, createIssue(RESOLUTION_FIXED, STATUS_CLOSED, MAJOR));
  231. underTest.afterComponent(FILE1);
  232. underTest.beforeComponent(FILE2);
  233. underTest.onIssue(FILE2, createIssue(null, STATUS_CONFIRMED, BLOCKER));
  234. underTest.onIssue(FILE2, createIssue(null, STATUS_CONFIRMED, MAJOR));
  235. underTest.afterComponent(FILE2);
  236. underTest.beforeComponent(PROJECT);
  237. // Security hotspot should be ignored
  238. underTest.onIssue(FILE3, createSecurityHotspot().setSeverity(MAJOR));
  239. underTest.afterComponent(PROJECT);
  240. assertMeasures(FILE1, entry(BLOCKER_VIOLATIONS_KEY, 1), entry(CRITICAL_VIOLATIONS_KEY, 0), entry(MAJOR_VIOLATIONS_KEY, 0));
  241. assertMeasures(FILE2, entry(BLOCKER_VIOLATIONS_KEY, 1), entry(CRITICAL_VIOLATIONS_KEY, 0), entry(MAJOR_VIOLATIONS_KEY, 1));
  242. assertMeasures(PROJECT, entry(BLOCKER_VIOLATIONS_KEY, 2), entry(CRITICAL_VIOLATIONS_KEY, 0), entry(MAJOR_VIOLATIONS_KEY, 1));
  243. }
  244. @Test
  245. void count_unresolved_issues_by_type() {
  246. // bottom-up traversal -> from files to project
  247. // file1 : one open code smell, one closed code smell (which will be excluded from metric)
  248. underTest.beforeComponent(FILE1);
  249. underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER).setType(CODE_SMELL));
  250. underTest.onIssue(FILE1, createIssue(RESOLUTION_FIXED, STATUS_CLOSED, MAJOR).setType(CODE_SMELL));
  251. underTest.afterComponent(FILE1);
  252. // file2 : one bug
  253. underTest.beforeComponent(FILE2);
  254. underTest.onIssue(FILE2, createIssue(null, STATUS_CONFIRMED, BLOCKER).setType(BUG));
  255. underTest.afterComponent(FILE2);
  256. // file3 : one unresolved security hotspot
  257. underTest.beforeComponent(FILE3);
  258. underTest.onIssue(FILE3, createSecurityHotspot());
  259. underTest.onIssue(FILE3, createSecurityHotspot().setResolution(RESOLUTION_WONT_FIX).setStatus(STATUS_CLOSED));
  260. underTest.afterComponent(FILE3);
  261. underTest.beforeComponent(PROJECT);
  262. underTest.afterComponent(PROJECT);
  263. assertMeasures(FILE1, entry(CODE_SMELLS_KEY, 1), entry(BUGS_KEY, 0), entry(VULNERABILITIES_KEY, 0), entry(SECURITY_HOTSPOTS_KEY, 0));
  264. assertMeasures(FILE2, entry(CODE_SMELLS_KEY, 0), entry(BUGS_KEY, 1), entry(VULNERABILITIES_KEY, 0), entry(SECURITY_HOTSPOTS_KEY, 0));
  265. assertMeasures(FILE3, entry(CODE_SMELLS_KEY, 0), entry(BUGS_KEY, 0), entry(VULNERABILITIES_KEY, 0), entry(SECURITY_HOTSPOTS_KEY, 1));
  266. assertMeasures(PROJECT, entry(CODE_SMELLS_KEY, 1), entry(BUGS_KEY, 1), entry(VULNERABILITIES_KEY, 0), entry(SECURITY_HOTSPOTS_KEY, 1));
  267. }
  268. @Test
  269. void count_new_issues() {
  270. when(newIssueClassifier.isEnabled()).thenReturn(true);
  271. underTest.beforeComponent(FILE1);
  272. // created before -> existing issues (so ignored)
  273. underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER).setType(CODE_SMELL));
  274. underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER).setType(BUG));
  275. // created after -> 4 new issues but 1 is closed
  276. underTest.onIssue(FILE1, createNewIssue(null, STATUS_OPEN, CRITICAL).setType(CODE_SMELL));
  277. underTest.onIssue(FILE1, createNewIssue(null, STATUS_OPEN, CRITICAL).setType(BUG));
  278. underTest.onIssue(FILE1, createNewIssue(RESOLUTION_FIXED, STATUS_CLOSED, MAJOR).setType(BUG));
  279. underTest.onIssue(FILE1, createNewSecurityHotspot());
  280. underTest.onIssue(FILE1, createNewSecurityHotspot().setResolution(RESOLUTION_WONT_FIX).setStatus(STATUS_CLOSED));
  281. underTest.afterComponent(FILE1);
  282. underTest.beforeComponent(FILE2);
  283. underTest.afterComponent(FILE2);
  284. underTest.beforeComponent(PROJECT);
  285. underTest.afterComponent(PROJECT);
  286. assertIntValue(FILE1, entry(NEW_VIOLATIONS_KEY, 2), entry(NEW_CRITICAL_VIOLATIONS_KEY, 2), entry(NEW_BLOCKER_VIOLATIONS_KEY, 0), entry(NEW_MAJOR_VIOLATIONS_KEY, 0),
  287. entry(NEW_CODE_SMELLS_KEY, 1), entry(NEW_BUGS_KEY, 1), entry(NEW_VULNERABILITIES_KEY, 0), entry(NEW_SECURITY_HOTSPOTS_KEY, 1));
  288. assertIntValue(PROJECT, entry(NEW_VIOLATIONS_KEY, 2), entry(NEW_CRITICAL_VIOLATIONS_KEY, 2), entry(NEW_BLOCKER_VIOLATIONS_KEY, 0), entry(NEW_MAJOR_VIOLATIONS_KEY, 0),
  289. entry(NEW_CODE_SMELLS_KEY, 1), entry(NEW_BUGS_KEY, 1), entry(NEW_VULNERABILITIES_KEY, 0), entry(NEW_SECURITY_HOTSPOTS_KEY, 1));
  290. }
  291. @Test
  292. void count_new_accepted_issues() {
  293. when(newIssueClassifier.isEnabled()).thenReturn(true);
  294. underTest.beforeComponent(FILE1);
  295. // created before -> existing issues (so ignored)
  296. underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, CRITICAL));
  297. underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, CRITICAL));
  298. // created after -> 2 accepted, 1 open, 1 hotspot
  299. underTest.onIssue(FILE1, createNewIssue(null, STATUS_OPEN, CRITICAL));
  300. underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, CRITICAL));
  301. underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, CRITICAL));
  302. underTest.onIssue(FILE1, createNewSecurityHotspot());
  303. underTest.afterComponent(FILE1);
  304. underTest.beforeComponent(PROJECT);
  305. underTest.afterComponent(PROJECT);
  306. assertIntValue(FILE1, entry(NEW_VIOLATIONS_KEY, 1), entry(NEW_ACCEPTED_ISSUES_KEY, 2), entry(NEW_SECURITY_HOTSPOTS_KEY, 1));
  307. assertIntValue(PROJECT, entry(NEW_VIOLATIONS_KEY, 1), entry(NEW_ACCEPTED_ISSUES_KEY, 2), entry(NEW_SECURITY_HOTSPOTS_KEY, 1));
  308. }
  309. @Test
  310. void onIssue_shouldCountOverallSoftwareQualitiesMeasures() {
  311. when(newIssueClassifier.isEnabled()).thenReturn(true);
  312. underTest.beforeComponent(FILE1);
  313. underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.MAINTAINABILITY, HIGH));
  314. underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.MAINTAINABILITY, MEDIUM));
  315. underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.MAINTAINABILITY, HIGH));
  316. underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, SoftwareQuality.MAINTAINABILITY, HIGH));
  317. underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.MAINTAINABILITY, MEDIUM));
  318. underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, HIGH));
  319. underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, MEDIUM));
  320. underTest.onIssue(FILE1, createNewSecurityHotspot());
  321. underTest.afterComponent(FILE1);
  322. underTest.beforeComponent(PROJECT);
  323. underTest.afterComponent(PROJECT);
  324. Set<Map.Entry<String, Measure>> entries = measureRepository.getRawMeasures(FILE1).entrySet();
  325. assertOverallSoftwareQualityMeasures(SoftwareQuality.MAINTAINABILITY, getImpactMeasure(4, 2, 2, 0), entries);
  326. assertOverallSoftwareQualityMeasures(SoftwareQuality.SECURITY, getImpactMeasure(2, 1, 1, 0), entries);
  327. assertOverallSoftwareQualityMeasures(SoftwareQuality.RELIABILITY, getImpactMeasure(0, 0, 0, 0), entries);
  328. }
  329. @Test
  330. void onIssue_shouldCountNewSoftwareQualitiesMeasures() {
  331. when(newIssueClassifier.isEnabled()).thenReturn(true);
  332. underTest.beforeComponent(FILE1);
  333. underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.MAINTAINABILITY, HIGH));
  334. underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.MAINTAINABILITY, HIGH));
  335. underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, SoftwareQuality.MAINTAINABILITY, HIGH));
  336. underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.MAINTAINABILITY, MEDIUM));
  337. underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.RELIABILITY, HIGH));
  338. underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.RELIABILITY, LOW));
  339. underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, SoftwareQuality.RELIABILITY, HIGH));
  340. underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.RELIABILITY, MEDIUM));
  341. underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, MEDIUM));
  342. underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, LOW));
  343. underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, HIGH));
  344. underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, HIGH));
  345. underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, SoftwareQuality.SECURITY, HIGH));
  346. underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, MEDIUM));
  347. underTest.afterComponent(FILE1);
  348. underTest.beforeComponent(PROJECT);
  349. underTest.afterComponent(PROJECT);
  350. Set<Map.Entry<String, Measure>> entries = measureRepository.getRawMeasures(FILE1).entrySet();
  351. assertNewSoftwareQualityMeasures(SoftwareQuality.MAINTAINABILITY, getImpactMeasure(2, 1, 1, 0), entries);
  352. assertNewSoftwareQualityMeasures(SoftwareQuality.RELIABILITY, getImpactMeasure(2, 0, 1, 1), entries);
  353. assertNewSoftwareQualityMeasures(SoftwareQuality.SECURITY, getImpactMeasure(4, 2, 1, 1), entries);
  354. }
  355. private static Map<String, Long> getImpactMeasure(long total, long high, long medium, long low) {
  356. Map<String, Long> map = new LinkedHashMap<>();
  357. map.put(LOW.name(), low);
  358. map.put(MEDIUM.name(), medium);
  359. map.put(HIGH.name(), high);
  360. map.put(ImpactMeasureBuilder.TOTAL_KEY, total);
  361. return map;
  362. }
  363. private void assertOverallSoftwareQualityMeasures(SoftwareQuality softwareQuality, Map<? extends String, Long> expectedMap,
  364. Set<Map.Entry<String, Measure>> actualRaw) {
  365. assertSoftwareQualityMeasures(softwareQuality, expectedMap, actualRaw, IMPACT_TO_METRIC_KEY);
  366. }
  367. private void assertNewSoftwareQualityMeasures(SoftwareQuality softwareQuality, Map<? extends String, Long> expectedMap,
  368. Set<Map.Entry<String, Measure>> actualRaw) {
  369. assertSoftwareQualityMeasures(softwareQuality, expectedMap, actualRaw, IMPACT_TO_NEW_METRIC_KEY);
  370. }
  371. private void assertSoftwareQualityMeasures(SoftwareQuality softwareQuality, Map<? extends String, Long> expectedMap,
  372. Set<Map.Entry<String, Measure>> actualRaw, Map<String, String> impactToMetricMap) {
  373. Map.Entry<String, Measure> softwareQualityMap = actualRaw.stream()
  374. .filter(e -> e.getKey().equals(impactToMetricMap.get(softwareQuality.name())))
  375. .findFirst()
  376. .get();
  377. assertThat(softwareQualityMap.getValue().getData()).isEqualTo(new Gson().toJson(expectedMap));
  378. }
  379. @Test
  380. void count_high_impact_accepted_issues() {
  381. when(newIssueClassifier.isEnabled()).thenReturn(true);
  382. underTest.beforeComponent(FILE1);
  383. // created before -> existing issues with 1 high impact accepted
  384. underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, HIGH));
  385. underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, HIGH));
  386. underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, MEDIUM));
  387. // created after -> 2 high impact accepted
  388. underTest.onIssue(FILE1, createNewIssue(null, STATUS_OPEN, HIGH));
  389. underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, HIGH));
  390. underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, HIGH));
  391. underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, MEDIUM));
  392. underTest.onIssue(FILE1, createNewSecurityHotspot());
  393. underTest.afterComponent(FILE1);
  394. underTest.beforeComponent(PROJECT);
  395. underTest.afterComponent(PROJECT);
  396. assertIntValue(FILE1, entry(VIOLATIONS_KEY, 2), entry(NEW_VIOLATIONS_KEY, 1), entry(NEW_ACCEPTED_ISSUES_KEY, 3),
  397. entry(HIGH_IMPACT_ACCEPTED_ISSUES_KEY, 3));
  398. assertIntValue(PROJECT, entry(VIOLATIONS_KEY, 2), entry(NEW_VIOLATIONS_KEY, 1), entry(NEW_ACCEPTED_ISSUES_KEY, 3),
  399. entry(HIGH_IMPACT_ACCEPTED_ISSUES_KEY, 3));
  400. }
  401. @Test
  402. void exclude_hotspots_from_issue_counts() {
  403. // bottom-up traversal -> from files to project
  404. underTest.beforeComponent(FILE1);
  405. underTest.onIssue(FILE1, createSecurityHotspot());
  406. underTest.onIssue(FILE1, createSecurityHotspot());
  407. underTest.afterComponent(FILE1);
  408. underTest.beforeComponent(FILE2);
  409. underTest.onIssue(FILE2, createSecurityHotspot());
  410. underTest.afterComponent(FILE2);
  411. underTest.beforeComponent(FILE3);
  412. underTest.afterComponent(FILE3);
  413. underTest.beforeComponent(PROJECT);
  414. underTest.afterComponent(PROJECT);
  415. assertMeasures(FILE1, entry(VIOLATIONS_KEY, 0), entry(OPEN_ISSUES_KEY, 0), entry(CONFIRMED_ISSUES_KEY, 0));
  416. assertMeasures(FILE2, entry(VIOLATIONS_KEY, 0), entry(OPEN_ISSUES_KEY, 0), entry(CONFIRMED_ISSUES_KEY, 0));
  417. assertMeasures(FILE3, entry(VIOLATIONS_KEY, 0));
  418. assertMeasures(PROJECT, entry(VIOLATIONS_KEY, 0), entry(OPEN_ISSUES_KEY, 0), entry(CONFIRMED_ISSUES_KEY, 0));
  419. }
  420. @Test
  421. void exclude_new_hotspots_from_issue_counts() {
  422. when(newIssueClassifier.isEnabled()).thenReturn(true);
  423. underTest.beforeComponent(FILE1);
  424. // created before -> existing issues (so ignored)
  425. underTest.onIssue(FILE1, createSecurityHotspot());
  426. underTest.onIssue(FILE1, createSecurityHotspot());
  427. // created after, but closed
  428. underTest.onIssue(FILE1, createNewSecurityHotspot().setStatus(STATUS_RESOLVED).setResolution(RESOLUTION_WONT_FIX));
  429. for (String severity : Arrays.asList(CRITICAL, BLOCKER, MAJOR)) {
  430. DefaultIssue issue = createNewSecurityHotspot();
  431. issue.setSeverity(severity);
  432. underTest.onIssue(FILE1, issue);
  433. }
  434. underTest.afterComponent(FILE1);
  435. underTest.beforeComponent(FILE2);
  436. underTest.afterComponent(FILE2);
  437. underTest.beforeComponent(PROJECT);
  438. underTest.afterComponent(PROJECT);
  439. assertIntValue(FILE1, entry(NEW_VIOLATIONS_KEY, 0), entry(NEW_CRITICAL_VIOLATIONS_KEY, 0), entry(NEW_BLOCKER_VIOLATIONS_KEY, 0), entry(NEW_MAJOR_VIOLATIONS_KEY, 0),
  440. entry(NEW_VULNERABILITIES_KEY, 0));
  441. assertIntValue(PROJECT, entry(NEW_VIOLATIONS_KEY, 0), entry(NEW_CRITICAL_VIOLATIONS_KEY, 0), entry(NEW_BLOCKER_VIOLATIONS_KEY, 0), entry(NEW_MAJOR_VIOLATIONS_KEY, 0),
  442. entry(NEW_VULNERABILITIES_KEY, 0));
  443. }
  444. @SafeVarargs
  445. private void assertIntValue(Component componentRef, MapEntry<String, Integer>... entries) {
  446. assertThat(measureRepository.getRawMeasures(componentRef).entrySet()
  447. .stream()
  448. .filter(e -> e.getValue().getValueType() == Measure.ValueType.INT)
  449. .map(e -> entry(e.getKey(), e.getValue().getIntValue())))
  450. .contains(entries);
  451. }
  452. @SafeVarargs
  453. private void assertMeasures(Component componentRef, Map.Entry<String, Integer>... entries) {
  454. List<MeasureRepoEntry> expected = stream(entries)
  455. .map(e -> entryOf(e.getKey(), newMeasureBuilder().create(e.getValue())))
  456. .toList();
  457. assertThat(measureRepository.getRawMeasures(componentRef).entrySet().stream().map(e -> entryOf(e.getKey(), e.getValue())))
  458. .containsAll(expected);
  459. }
  460. private DefaultIssue createNewIssue(@Nullable String resolution, String status, String severity) {
  461. return createNewIssue(resolution, status, severity, CODE_SMELL);
  462. }
  463. private DefaultIssue createNewIssue(@Nullable String resolution, String status, Severity impactSeverity) {
  464. return createNewIssue(resolution, status, SoftwareQuality.MAINTAINABILITY, impactSeverity);
  465. }
  466. private DefaultIssue createNewIssue(@Nullable String resolution, String status, SoftwareQuality softwareQuality, Severity impactSeverity) {
  467. DefaultIssue issue = createNewIssue(resolution, status, MAJOR, CODE_SMELL);
  468. issue.addImpact(softwareQuality, impactSeverity);
  469. return issue;
  470. }
  471. private DefaultIssue createNewIssue(@Nullable String resolution, String status, String severity, RuleType ruleType) {
  472. DefaultIssue issue = createIssue(resolution, status, severity, ruleType);
  473. when(newIssueClassifier.isNew(any(), eq(issue))).thenReturn(true);
  474. return issue;
  475. }
  476. private static DefaultIssue createIssue(@Nullable String resolution, String status, String severity) {
  477. return createIssue(resolution, status, severity, CODE_SMELL);
  478. }
  479. private static DefaultIssue createIssue(@Nullable String resolution, String status, Severity impactSeverity) {
  480. return createIssue(resolution, status, SoftwareQuality.MAINTAINABILITY, impactSeverity);
  481. }
  482. private static DefaultIssue createIssue(@Nullable String resolution, String status, SoftwareQuality softwareQuality, Severity impactSeverity) {
  483. DefaultIssue issue = createIssue(resolution, status, MAJOR, CODE_SMELL);
  484. issue.addImpact(softwareQuality, impactSeverity);
  485. return issue;
  486. }
  487. private static DefaultIssue createIssue(@Nullable String resolution, String status, String severity, RuleType ruleType) {
  488. return new DefaultIssue()
  489. .setKey(String.valueOf(++issueCounter))
  490. .setResolution(resolution).setStatus(status)
  491. .setSeverity(severity).setRuleKey(RuleTesting.XOO_X1)
  492. .setType(ruleType);
  493. }
  494. private static DefaultIssue createSecurityHotspot() {
  495. return createIssue(null, STATUS_OPEN, "MAJOR", SECURITY_HOTSPOT);
  496. }
  497. private DefaultIssue createNewSecurityHotspot() {
  498. return createNewIssue(null, STATUS_OPEN, "MAJOR", SECURITY_HOTSPOT);
  499. }
  500. }