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 12KB

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