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.

MeasureRepositoryRule.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2021 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.measure;
  21. import com.google.common.base.Function;
  22. import com.google.common.base.Predicate;
  23. import java.util.HashMap;
  24. import java.util.Map;
  25. import java.util.Objects;
  26. import java.util.Optional;
  27. import java.util.stream.Collectors;
  28. import javax.annotation.CheckForNull;
  29. import javax.annotation.Nonnull;
  30. import javax.annotation.Nullable;
  31. import org.junit.rules.ExternalResource;
  32. import org.sonar.ce.task.projectanalysis.component.Component;
  33. import org.sonar.ce.task.projectanalysis.component.ComponentProvider;
  34. import org.sonar.ce.task.projectanalysis.component.NoComponentProvider;
  35. import org.sonar.ce.task.projectanalysis.component.TreeComponentProvider;
  36. import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
  37. import org.sonar.ce.task.projectanalysis.component.TreeRootHolderComponentProvider;
  38. import org.sonar.ce.task.projectanalysis.metric.Metric;
  39. import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule;
  40. import static com.google.common.base.Preconditions.checkState;
  41. import static com.google.common.collect.FluentIterable.from;
  42. import static com.google.common.collect.Maps.filterKeys;
  43. import static java.lang.String.format;
  44. import static java.util.Objects.requireNonNull;
  45. /**
  46. * An implementation of MeasureRepository as a JUnit rule which provides add methods for raw measures and extra add
  47. * methods that takes component ref and metric keys thanks to the integration with various Component and Metric
  48. * providers.
  49. */
  50. public class MeasureRepositoryRule extends ExternalResource implements MeasureRepository {
  51. private final ComponentProvider componentProvider;
  52. @CheckForNull
  53. private final MetricRepositoryRule metricRepositoryRule;
  54. private final Map<InternalKey, Measure> baseMeasures = new HashMap<>();
  55. private final Map<InternalKey, Measure> rawMeasures = new HashMap<>();
  56. private final Map<InternalKey, Measure> initialRawMeasures = new HashMap<>();
  57. private final Predicate<Map.Entry<InternalKey, Measure>> isAddedMeasure = input -> !initialRawMeasures.containsKey(input.getKey())
  58. || !MeasureRepoEntry.deepEquals(input.getValue(), initialRawMeasures.get(input.getKey()));
  59. private MeasureRepositoryRule(ComponentProvider componentProvider, @Nullable MetricRepositoryRule metricRepositoryRule) {
  60. this.componentProvider = componentProvider;
  61. this.metricRepositoryRule = metricRepositoryRule;
  62. }
  63. @Override
  64. protected void after() {
  65. componentProvider.reset();
  66. baseMeasures.clear();
  67. rawMeasures.clear();
  68. }
  69. public static MeasureRepositoryRule create() {
  70. return new MeasureRepositoryRule(NoComponentProvider.INSTANCE, null);
  71. }
  72. public static MeasureRepositoryRule create(TreeRootHolder treeRootHolder, MetricRepositoryRule metricRepositoryRule) {
  73. return new MeasureRepositoryRule(new TreeRootHolderComponentProvider(treeRootHolder), requireNonNull(metricRepositoryRule));
  74. }
  75. public static MeasureRepositoryRule create(Component treeRoot, MetricRepositoryRule metricRepositoryRule) {
  76. return new MeasureRepositoryRule(new TreeComponentProvider(treeRoot), requireNonNull(metricRepositoryRule));
  77. }
  78. public MeasureRepositoryRule addBaseMeasure(int componentRef, String metricKey, Measure measure) {
  79. checkAndInitProvidersState();
  80. InternalKey internalKey = new InternalKey(componentProvider.getByRef(componentRef), metricRepositoryRule.getByKey(metricKey));
  81. checkState(!baseMeasures.containsKey(internalKey), format("Can not add a BaseMeasure twice for a Component (ref=%s) and Metric (key=%s)", componentRef, metricKey));
  82. baseMeasures.put(internalKey, measure);
  83. return this;
  84. }
  85. public Map<String, Measure> getRawMeasures(int componentRef) {
  86. return getRawMeasures(componentProvider.getByRef(componentRef));
  87. }
  88. /**
  89. * Return measures that were added by the step (using {@link #add(Component, Metric, Measure)}).
  90. * It does not contain the one added in the test by {@link #addRawMeasure(int, String, Measure)}
  91. */
  92. public Map<String, Measure> getAddedRawMeasures(int componentRef) {
  93. checkAndInitProvidersState();
  94. return getAddedRawMeasures(componentProvider.getByRef(componentRef));
  95. }
  96. /**
  97. * Return a measure that were added by the step (using {@link #add(Component, Metric, Measure)}).
  98. * It does not contain the one added in the test by {@link #addRawMeasure(int, String, Measure)}
  99. */
  100. public Optional<Measure> getAddedRawMeasure(Component component, String metricKey) {
  101. return getAddedRawMeasure(component.getReportAttributes().getRef(), metricKey);
  102. }
  103. /**
  104. * Return a measure that were added by the step (using {@link #add(Component, Metric, Measure)}).
  105. * It does not contain the one added in the test by {@link #addRawMeasure(int, String, Measure)}
  106. */
  107. public Optional<Measure> getAddedRawMeasure(int componentRef, String metricKey) {
  108. checkAndInitProvidersState();
  109. Measure measure = getAddedRawMeasures(componentProvider.getByRef(componentRef)).get(metricKey);
  110. return Optional.ofNullable(measure);
  111. }
  112. /**
  113. * Return measures that were added by the step (using {@link #add(Component, Metric, Measure)}).
  114. * It does not contain the one added in the test by {@link #addRawMeasure(int, String, Measure)}
  115. */
  116. public Map<String, Measure> getAddedRawMeasures(Component component) {
  117. checkAndInitProvidersState();
  118. Map<String, Measure> builder = new HashMap<>();
  119. for (Map.Entry<InternalKey, Measure> entry : from(filterKeys(rawMeasures, hasComponentRef(component)).entrySet()).filter(isAddedMeasure)) {
  120. builder.put(entry.getKey().getMetricKey(), entry.getValue());
  121. }
  122. return builder;
  123. }
  124. public MeasureRepositoryRule addRawMeasure(int componentRef, String metricKey, Measure measure) {
  125. checkAndInitProvidersState();
  126. InternalKey internalKey = new InternalKey(componentProvider.getByRef(componentRef), metricRepositoryRule.getByKey(metricKey));
  127. checkState(!rawMeasures.containsKey(internalKey), format(
  128. "A measure can only be set once for Component (ref=%s), Metric (key=%s)",
  129. componentRef, metricKey));
  130. rawMeasures.put(internalKey, measure);
  131. initialRawMeasures.put(internalKey, measure);
  132. return this;
  133. }
  134. @Override
  135. public Optional<Measure> getBaseMeasure(Component component, Metric metric) {
  136. return Optional.ofNullable(baseMeasures.get(new InternalKey(component, metric)));
  137. }
  138. @Override
  139. public Optional<Measure> getRawMeasure(Component component, Metric metric) {
  140. return Optional.ofNullable(rawMeasures.get(new InternalKey(component, metric)));
  141. }
  142. @Override
  143. public Map<String, Measure> getRawMeasures(Component component) {
  144. return filterKeys(rawMeasures, hasComponentRef(component)).entrySet().stream()
  145. .collect(Collectors.toMap(k -> k.getKey().getMetricKey(), Map.Entry::getValue));
  146. }
  147. private HasComponentRefPredicate hasComponentRef(Component component) {
  148. return new HasComponentRefPredicate(component);
  149. }
  150. @Override
  151. public void add(Component component, Metric metric, Measure measure) {
  152. String ref = getRef(component);
  153. InternalKey internalKey = new InternalKey(ref, metric.getKey());
  154. if (rawMeasures.containsKey(internalKey)) {
  155. throw new UnsupportedOperationException(format(
  156. "A measure can only be set once for Component (ref=%s), Metric (key=%s)",
  157. ref, metric.getKey()));
  158. }
  159. rawMeasures.put(internalKey, measure);
  160. }
  161. @Override
  162. public void update(Component component, Metric metric, Measure measure) {
  163. String componentRef = getRef(component);
  164. InternalKey internalKey = new InternalKey(componentRef, metric.getKey());
  165. if (!rawMeasures.containsKey(internalKey)) {
  166. throw new UnsupportedOperationException(format(
  167. "A measure can only be updated if it has been added first for Component (ref=%s), Metric (key=%s)",
  168. componentRef, metric.getKey()));
  169. }
  170. rawMeasures.put(internalKey, measure);
  171. }
  172. private void checkAndInitProvidersState() {
  173. checkState(metricRepositoryRule != null, "Can not add a measure by metric key if MeasureRepositoryRule has not been created for a MetricRepository");
  174. componentProvider.ensureInitialized();
  175. }
  176. public boolean isEmpty() {
  177. return rawMeasures.isEmpty();
  178. }
  179. private static final class InternalKey {
  180. private final String componentRef;
  181. private final String metricKey;
  182. public InternalKey(Component component, Metric metric) {
  183. this(getRef(component), metric.getKey());
  184. }
  185. private InternalKey(String componentRef, String metricKey) {
  186. this.componentRef = componentRef;
  187. this.metricKey = metricKey;
  188. }
  189. public String getComponentRef() {
  190. return componentRef;
  191. }
  192. public String getMetricKey() {
  193. return metricKey;
  194. }
  195. @Override
  196. public boolean equals(@Nullable Object o) {
  197. if (this == o) {
  198. return true;
  199. }
  200. if (o == null || getClass() != o.getClass()) {
  201. return false;
  202. }
  203. InternalKey that = (InternalKey) o;
  204. return Objects.equals(componentRef, that.componentRef) &&
  205. Objects.equals(metricKey, that.metricKey);
  206. }
  207. @Override
  208. public int hashCode() {
  209. return Objects.hash(componentRef, metricKey);
  210. }
  211. @Override
  212. public String toString() {
  213. return "InternalKey{" +
  214. "component=" + componentRef +
  215. ", metric='" + metricKey + '\'' +
  216. '}';
  217. }
  218. }
  219. private static class HasComponentRefPredicate implements Predicate<InternalKey> {
  220. private final String componentRef;
  221. public HasComponentRefPredicate(Component component) {
  222. this.componentRef = getRef(component);
  223. }
  224. @Override
  225. public boolean apply(@Nonnull InternalKey input) {
  226. return input.getComponentRef().equals(this.componentRef);
  227. }
  228. }
  229. private static String getRef(Component component) {
  230. return component.getType().isReportType() ? String.valueOf(component.getReportAttributes().getRef()) : component.getDbKey();
  231. }
  232. private static class MatchMetric implements Predicate<Map.Entry<InternalKey, Measure>> {
  233. private final Metric metric;
  234. public MatchMetric(Metric metric) {
  235. this.metric = metric;
  236. }
  237. @Override
  238. public boolean apply(@Nonnull Map.Entry<InternalKey, Measure> input) {
  239. return input.getKey().getMetricKey().equals(metric.getKey());
  240. }
  241. }
  242. private enum ToMeasure implements Function<Map.Entry<InternalKey, Measure>, Measure> {
  243. INSTANCE;
  244. @Nullable
  245. @Override
  246. public Measure apply(@Nonnull Map.Entry<InternalKey, Measure> input) {
  247. return input.getValue();
  248. }
  249. }
  250. }