Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

ReportFormulaExecutorComponentVisitorTest.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2019 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.formula;
  21. import com.google.common.collect.ImmutableList;
  22. import java.util.Optional;
  23. import org.junit.Rule;
  24. import org.junit.Test;
  25. import org.junit.rules.ExpectedException;
  26. import org.sonar.api.measures.CoreMetrics;
  27. import org.sonar.ce.task.projectanalysis.component.Component;
  28. import org.sonar.ce.task.projectanalysis.component.PathAwareCrawler;
  29. import org.sonar.ce.task.projectanalysis.component.ReportComponent;
  30. import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
  31. import org.sonar.ce.task.projectanalysis.formula.counter.IntValue;
  32. import org.sonar.ce.task.projectanalysis.measure.Measure;
  33. import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryRule;
  34. import org.sonar.ce.task.projectanalysis.metric.Metric;
  35. import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule;
  36. import static org.assertj.core.api.Assertions.assertThat;
  37. import static org.sonar.api.measures.CoreMetrics.LINES_KEY;
  38. import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
  39. import static org.sonar.api.measures.CoreMetrics.NEW_COVERAGE_KEY;
  40. import static org.sonar.api.measures.CoreMetrics.NEW_LINES_TO_COVER_KEY;
  41. import static org.sonar.ce.task.projectanalysis.component.Component.Type.DIRECTORY;
  42. import static org.sonar.ce.task.projectanalysis.component.Component.Type.PROJECT;
  43. import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
  44. import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilder;
  45. import static org.sonar.ce.task.projectanalysis.measure.MeasureRepoEntry.entryOf;
  46. import static org.sonar.ce.task.projectanalysis.measure.MeasureRepoEntry.toEntries;
  47. import static org.sonar.test.ExceptionCauseMatcher.hasType;
  48. public class ReportFormulaExecutorComponentVisitorTest {
  49. private static final int ROOT_REF = 1;
  50. private static final int DIRECTORY_1_REF = 111;
  51. private static final int FILE_1_REF = 1111;
  52. private static final int FILE_2_REF = 1112;
  53. private static final int DIRECTORY_2_REF = 121;
  54. private static final int FILE_3_REF = 1211;
  55. private static final ReportComponent BALANCED_COMPONENT_TREE = ReportComponent.builder(PROJECT, ROOT_REF)
  56. .addChildren(
  57. ReportComponent.builder(DIRECTORY, DIRECTORY_1_REF)
  58. .addChildren(
  59. builder(Component.Type.FILE, FILE_1_REF).build(),
  60. builder(Component.Type.FILE, FILE_2_REF).build())
  61. .build(),
  62. ReportComponent.builder(DIRECTORY, DIRECTORY_2_REF)
  63. .addChildren(
  64. builder(Component.Type.FILE, FILE_3_REF).build())
  65. .build())
  66. .build();
  67. @Rule
  68. public ExpectedException expectedException = ExpectedException.none();
  69. @Rule
  70. public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
  71. @Rule
  72. public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
  73. .add(CoreMetrics.LINES)
  74. .add(CoreMetrics.NCLOC)
  75. .add(CoreMetrics.NEW_LINES_TO_COVER)
  76. .add(CoreMetrics.NEW_COVERAGE);
  77. @Rule
  78. public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
  79. @Test
  80. public void verify_aggregation_on_value() {
  81. treeRootHolder.setRoot(BALANCED_COMPONENT_TREE);
  82. measureRepository.addRawMeasure(FILE_1_REF, LINES_KEY, newMeasureBuilder().create(10));
  83. measureRepository.addRawMeasure(FILE_2_REF, LINES_KEY, newMeasureBuilder().create(8));
  84. measureRepository.addRawMeasure(FILE_3_REF, LINES_KEY, newMeasureBuilder().create(2));
  85. new PathAwareCrawler<>(formulaExecutorComponentVisitor(new FakeFormula()))
  86. .visit(BALANCED_COMPONENT_TREE);
  87. assertAddedRawMeasure(ROOT_REF, 20);
  88. assertAddedRawMeasure(111, 18);
  89. assertAddedRawMeasure(FILE_1_REF, 10);
  90. assertAddedRawMeasure(FILE_2_REF, 8);
  91. assertAddedRawMeasure(DIRECTORY_2_REF, 2);
  92. assertAddedRawMeasure(FILE_3_REF, 2);
  93. }
  94. @Test
  95. public void verify_multi_metric_formula_support_and_aggregation() {
  96. treeRootHolder.setRoot(BALANCED_COMPONENT_TREE);
  97. measureRepository.addRawMeasure(FILE_1_REF, LINES_KEY, newMeasureBuilder().create(10));
  98. measureRepository.addRawMeasure(FILE_2_REF, LINES_KEY, newMeasureBuilder().create(8));
  99. measureRepository.addRawMeasure(FILE_3_REF, LINES_KEY, newMeasureBuilder().create(2));
  100. new PathAwareCrawler<>(formulaExecutorComponentVisitor(new FakeMultiMetricFormula()))
  101. .visit(BALANCED_COMPONENT_TREE);
  102. assertThat(toEntries(measureRepository.getAddedRawMeasures(ROOT_REF))).containsOnly(
  103. entryOf(NEW_LINES_TO_COVER_KEY, newMeasureBuilder().create(30)),
  104. entryOf(NEW_COVERAGE_KEY, newMeasureBuilder().create(120)));
  105. assertThat(toEntries(measureRepository.getAddedRawMeasures(111))).containsOnly(
  106. entryOf(NEW_LINES_TO_COVER_KEY, newMeasureBuilder().create(28)),
  107. entryOf(NEW_COVERAGE_KEY, newMeasureBuilder().create(118)));
  108. assertThat(toEntries(measureRepository.getAddedRawMeasures(FILE_1_REF))).containsOnly(
  109. entryOf(NEW_LINES_TO_COVER_KEY, newMeasureBuilder().create(20)),
  110. entryOf(NEW_COVERAGE_KEY, newMeasureBuilder().create(110)));
  111. assertThat(toEntries(measureRepository.getAddedRawMeasures(FILE_2_REF))).containsOnly(
  112. entryOf(NEW_LINES_TO_COVER_KEY, newMeasureBuilder().create(18)),
  113. entryOf(NEW_COVERAGE_KEY, newMeasureBuilder().create(108)));
  114. assertThat(toEntries(measureRepository.getAddedRawMeasures(DIRECTORY_2_REF))).containsOnly(
  115. entryOf(NEW_LINES_TO_COVER_KEY, newMeasureBuilder().create(12)),
  116. entryOf(NEW_COVERAGE_KEY, newMeasureBuilder().create(102)));
  117. assertThat(toEntries(measureRepository.getAddedRawMeasures(FILE_3_REF))).containsOnly(
  118. entryOf(NEW_LINES_TO_COVER_KEY, newMeasureBuilder().create(12)),
  119. entryOf(NEW_COVERAGE_KEY, newMeasureBuilder().create(102)));
  120. }
  121. @Test
  122. public void verify_aggregation_on_variation() {
  123. treeRootHolder.setRoot(BALANCED_COMPONENT_TREE);
  124. measureRepository.addRawMeasure(FILE_1_REF, NEW_LINES_TO_COVER_KEY, createMeasureWithVariation(10));
  125. measureRepository.addRawMeasure(FILE_2_REF, NEW_LINES_TO_COVER_KEY, createMeasureWithVariation(8));
  126. measureRepository.addRawMeasure(FILE_3_REF, NEW_LINES_TO_COVER_KEY, createMeasureWithVariation(2));
  127. new PathAwareCrawler<>(formulaExecutorComponentVisitor(new FakeVariationFormula()))
  128. .visit(BALANCED_COMPONENT_TREE);
  129. assertAddedRawMeasureVariation(ROOT_REF, 20);
  130. assertAddedRawMeasureVariation(DIRECTORY_1_REF, 18);
  131. assertAddedRawMeasureVariation(FILE_1_REF, 10);
  132. assertAddedRawMeasureVariation(FILE_2_REF, 8);
  133. assertAddedRawMeasureVariation(DIRECTORY_2_REF, 2);
  134. assertAddedRawMeasureVariation(FILE_3_REF, 2);
  135. }
  136. @Test
  137. public void measures_are_0_when_there_is_no_input_measure() {
  138. ReportComponent project = ReportComponent.builder(PROJECT, ROOT_REF)
  139. .addChildren(
  140. ReportComponent.builder(DIRECTORY, DIRECTORY_1_REF)
  141. .addChildren(
  142. builder(Component.Type.FILE, FILE_1_REF).build())
  143. .build())
  144. .build();
  145. treeRootHolder.setRoot(project);
  146. new PathAwareCrawler<>(formulaExecutorComponentVisitor(new FakeFormula()))
  147. .visit(project);
  148. assertAddedRawMeasure(ROOT_REF, 0);
  149. assertAddedRawMeasure(DIRECTORY_1_REF, 0);
  150. assertAddedRawMeasure(FILE_1_REF, 0);
  151. }
  152. @Test
  153. public void add_measure_even_when_leaf_is_not_FILE() {
  154. ReportComponent project = ReportComponent.builder(PROJECT, ROOT_REF)
  155. .addChildren(
  156. ReportComponent.builder(DIRECTORY, 111).build())
  157. .build();
  158. treeRootHolder.setRoot(project);
  159. new PathAwareCrawler<>(formulaExecutorComponentVisitor(new FakeFormula()))
  160. .visit(project);
  161. assertAddedRawMeasure(DIRECTORY_1_REF, 0);
  162. }
  163. @Test
  164. public void compute_measure_on_project_without_children() {
  165. ReportComponent root = builder(PROJECT, ROOT_REF).build();
  166. treeRootHolder.setRoot(root);
  167. measureRepository.addRawMeasure(ROOT_REF, LINES_KEY, newMeasureBuilder().create(10));
  168. new PathAwareCrawler<>(formulaExecutorComponentVisitor(new FakeFormula()))
  169. .visit(root);
  170. assertThat(toEntries(measureRepository.getAddedRawMeasures(ROOT_REF))).containsOnly(entryOf(NCLOC_KEY, newMeasureBuilder().create(10)));
  171. }
  172. @Test
  173. public void ignore_measure_defined_on_project_when_measure_is_defined_on_leaf() {
  174. ReportComponent root = builder(PROJECT, ROOT_REF)
  175. .addChildren(
  176. builder(Component.Type.FILE, FILE_1_REF).build())
  177. .build();
  178. treeRootHolder.setRoot(root);
  179. measureRepository.addRawMeasure(ROOT_REF, LINES_KEY, newMeasureBuilder().create(10));
  180. measureRepository.addRawMeasure(FILE_1_REF, LINES_KEY, newMeasureBuilder().create(2));
  181. new PathAwareCrawler<>(formulaExecutorComponentVisitor(new FakeFormula()))
  182. .visit(root);
  183. assertAddedRawMeasure(ROOT_REF, 2);
  184. assertAddedRawMeasure(FILE_1_REF, 2);
  185. }
  186. @Test
  187. public void fail_when_trying_to_compute_file_measure_already_existing_in_report() {
  188. ReportComponent root = builder(PROJECT, ROOT_REF)
  189. .addChildren(
  190. builder(Component.Type.FILE, FILE_1_REF).build())
  191. .build();
  192. treeRootHolder.setRoot(root);
  193. measureRepository.addRawMeasure(FILE_1_REF, NCLOC_KEY, newMeasureBuilder().create(2));
  194. expectedException.expectCause(hasType(UnsupportedOperationException.class)
  195. .andMessage(String.format("A measure can only be set once for Component (ref=%s), Metric (key=%s)", FILE_1_REF, NCLOC_KEY)));
  196. new PathAwareCrawler<>(formulaExecutorComponentVisitor(new FakeFormula()))
  197. .visit(root);
  198. }
  199. @Test
  200. public void fail_on_project_without_children_already_having_computed_measure() {
  201. ReportComponent root = builder(PROJECT, ROOT_REF).build();
  202. treeRootHolder.setRoot(root);
  203. measureRepository.addRawMeasure(ROOT_REF, NCLOC_KEY, newMeasureBuilder().create(10));
  204. expectedException.expectCause(hasType(UnsupportedOperationException.class)
  205. .andMessage(String.format("A measure can only be set once for Component (ref=%s), Metric (key=%s)", ROOT_REF, NCLOC_KEY)));
  206. new PathAwareCrawler<>(formulaExecutorComponentVisitor(new FakeFormula()))
  207. .visit(root);
  208. }
  209. private FormulaExecutorComponentVisitor formulaExecutorComponentVisitor(Formula formula) {
  210. return FormulaExecutorComponentVisitor.newBuilder(metricRepository, measureRepository)
  211. .buildFor(ImmutableList.of(formula));
  212. }
  213. private static Measure createMeasureWithVariation(double variation) {
  214. return newMeasureBuilder().setVariation(variation).createNoValue();
  215. }
  216. private void assertAddedRawMeasure(int componentRef, int expectedValue) {
  217. assertThat(toEntries(measureRepository.getAddedRawMeasures(componentRef))).containsOnly(entryOf(NCLOC_KEY, newMeasureBuilder().create(expectedValue)));
  218. }
  219. private void assertAddedRawMeasureVariation(int componentRef, int variation) {
  220. assertThat(toEntries(measureRepository.getAddedRawMeasures(componentRef)))
  221. .containsOnly(entryOf(NEW_COVERAGE_KEY, createMeasureWithVariation(variation)));
  222. }
  223. private class FakeFormula implements Formula<FakeCounter> {
  224. @Override
  225. public FakeCounter createNewCounter() {
  226. return new FakeCounter();
  227. }
  228. @Override
  229. public Optional<Measure> createMeasure(FakeCounter counter, CreateMeasureContext context) {
  230. // verify the context which is passed to the method
  231. assertThat(context.getComponent()).isNotNull();
  232. assertThat(context.getMetric()).isSameAs(metricRepository.getByKey(NCLOC_KEY));
  233. return Optional.of(Measure.newMeasureBuilder().create(counter.value));
  234. }
  235. @Override
  236. public String[] getOutputMetricKeys() {
  237. return new String[] {NCLOC_KEY};
  238. }
  239. }
  240. private class FakeMultiMetricFormula implements Formula<FakeCounter> {
  241. @Override
  242. public FakeCounter createNewCounter() {
  243. return new FakeCounter();
  244. }
  245. @Override
  246. public Optional<Measure> createMeasure(FakeCounter counter, CreateMeasureContext context) {
  247. // verify the context which is passed to the method
  248. assertThat(context.getComponent()).isNotNull();
  249. assertThat(context.getMetric())
  250. .isIn(metricRepository.getByKey(NEW_LINES_TO_COVER_KEY), metricRepository.getByKey(NEW_COVERAGE_KEY));
  251. return Optional.of(Measure.newMeasureBuilder().create(counter.value + metricOffset(context.getMetric())));
  252. }
  253. private int metricOffset(Metric metric) {
  254. if (metric.getKey().equals(NEW_LINES_TO_COVER_KEY)) {
  255. return 10;
  256. }
  257. if (metric.getKey().equals(NEW_COVERAGE_KEY)) {
  258. return 100;
  259. }
  260. throw new IllegalArgumentException("Unsupported metric " + metric);
  261. }
  262. @Override
  263. public String[] getOutputMetricKeys() {
  264. return new String[] {NEW_LINES_TO_COVER_KEY, NEW_COVERAGE_KEY};
  265. }
  266. }
  267. private static class FakeCounter implements Counter<FakeCounter> {
  268. private int value = 0;
  269. @Override
  270. public void aggregate(FakeCounter counter) {
  271. this.value += counter.value;
  272. }
  273. @Override
  274. public void initialize(CounterInitializationContext context) {
  275. // verify the context which is passed to the method
  276. assertThat(context.getLeaf().getChildren()).isEmpty();
  277. Optional<Measure> measureOptional = context.getMeasure(LINES_KEY);
  278. if (measureOptional.isPresent()) {
  279. value += measureOptional.get().getIntValue();
  280. }
  281. }
  282. }
  283. private class FakeVariationFormula implements Formula<FakeVariationCounter> {
  284. @Override
  285. public FakeVariationCounter createNewCounter() {
  286. return new FakeVariationCounter();
  287. }
  288. @Override
  289. public Optional<Measure> createMeasure(FakeVariationCounter counter, CreateMeasureContext context) {
  290. // verify the context which is passed to the method
  291. assertThat(context.getComponent()).isNotNull();
  292. assertThat(context.getMetric()).isSameAs(metricRepository.getByKey(NEW_COVERAGE_KEY));
  293. IntValue measureVariations = counter.values;
  294. if (measureVariations.isSet()) {
  295. return Optional.of(
  296. newMeasureBuilder()
  297. .setVariation(measureVariations.getValue())
  298. .createNoValue());
  299. }
  300. return Optional.empty();
  301. }
  302. @Override
  303. public String[] getOutputMetricKeys() {
  304. return new String[] {NEW_COVERAGE_KEY};
  305. }
  306. }
  307. private static class FakeVariationCounter implements Counter<FakeVariationCounter> {
  308. private final IntValue values = new IntValue();
  309. @Override
  310. public void aggregate(FakeVariationCounter counter) {
  311. values.increment(counter.values);
  312. }
  313. @Override
  314. public void initialize(CounterInitializationContext context) {
  315. // verify the context which is passed to the method
  316. assertThat(context.getLeaf().getChildren()).isEmpty();
  317. Optional<Measure> measureOptional = context.getMeasure(NEW_LINES_TO_COVER_KEY);
  318. if (!measureOptional.isPresent()) {
  319. return;
  320. }
  321. this.values.increment((int) measureOptional.get().getVariation());
  322. }
  323. }
  324. }