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