001/*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2014 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * SonarQube is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * SonarQube is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public License
017 * along with this program; if not, write to the Free Software Foundation,
018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019 */
020package org.sonar.api.batch.sensor.internal;
021
022import com.google.common.annotations.Beta;
023import org.sonar.api.batch.AnalysisMode;
024import org.sonar.api.batch.fs.InputDir;
025import org.sonar.api.batch.fs.InputFile;
026import org.sonar.api.batch.fs.InputPath;
027import org.sonar.api.batch.fs.internal.DefaultFileSystem;
028import org.sonar.api.batch.fs.internal.DefaultInputDir;
029import org.sonar.api.batch.fs.internal.DefaultInputFile;
030import org.sonar.api.batch.rule.ActiveRules;
031import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
032import org.sonar.api.batch.sensor.Sensor;
033import org.sonar.api.batch.sensor.SensorContext;
034import org.sonar.api.batch.sensor.dependency.Dependency;
035import org.sonar.api.batch.sensor.dependency.NewDependency;
036import org.sonar.api.batch.sensor.dependency.internal.DefaultDependency;
037import org.sonar.api.batch.sensor.duplication.Duplication;
038import org.sonar.api.batch.sensor.duplication.NewDuplication;
039import org.sonar.api.batch.sensor.duplication.internal.DefaultDuplication;
040import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
041import org.sonar.api.batch.sensor.highlighting.TypeOfText;
042import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
043import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule;
044import org.sonar.api.batch.sensor.issue.Issue;
045import org.sonar.api.batch.sensor.issue.NewIssue;
046import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
047import org.sonar.api.batch.sensor.measure.Measure;
048import org.sonar.api.batch.sensor.measure.NewMeasure;
049import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
050import org.sonar.api.config.Settings;
051import org.sonar.api.measures.Metric;
052
053import javax.annotation.CheckForNull;
054import javax.annotation.Nullable;
055
056import java.io.File;
057import java.io.Serializable;
058import java.util.ArrayList;
059import java.util.Collection;
060import java.util.Collections;
061import java.util.HashMap;
062import java.util.List;
063import java.util.Map;
064
065/**
066 * Utility class to help testing {@link Sensor}.
067 * 
068 * Usage: call {@link #create(File)} to create an "in memory" implementation of {@link SensorContext} then
069 * pass it to your {@link Sensor}. You can then query elements provided by your sensor using methods {@link #allIssues()}, ...
070 * 
071 * @since 5.1
072 */
073@Beta
074public class SensorContextTester implements SensorContext {
075
076  private Settings settings;
077  private DefaultFileSystem fs;
078  private ActiveRules activeRules;
079  private MockAnalysisMode analysisMode;
080  private InMemorySensorStorage sensorStorage;
081
082  private SensorContextTester(File moduleBaseDir) {
083    this.settings = new Settings();
084    this.fs = new DefaultFileSystem(moduleBaseDir);
085    this.activeRules = new ActiveRulesBuilder().build();
086    this.analysisMode = new MockAnalysisMode();
087    this.sensorStorage = new InMemorySensorStorage();
088  }
089
090  public static SensorContextTester create(File moduleBaseDir) {
091    return new SensorContextTester(moduleBaseDir);
092  }
093
094  @Override
095  public Settings settings() {
096    return settings;
097  }
098
099  public void setSettings(Settings settings) {
100    this.settings = settings;
101  }
102
103  @Override
104  public DefaultFileSystem fileSystem() {
105    return fs;
106  }
107
108  public void setFileSystem(DefaultFileSystem fs) {
109    this.fs = fs;
110  }
111
112  @Override
113  public ActiveRules activeRules() {
114    return activeRules;
115  }
116
117  public void setActiveRules(ActiveRules activeRules) {
118    this.activeRules = activeRules;
119  }
120
121  @Override
122  public MockAnalysisMode analysisMode() {
123    return analysisMode;
124  }
125
126  @Override
127  public <G extends Serializable> NewMeasure<G> newMeasure() {
128    return new DefaultMeasure<>(sensorStorage);
129  }
130
131  public Collection<Measure> measures(@Nullable String componetKey) {
132    if (componetKey == null) {
133      return sensorStorage.projectMeasuresByMetric.values();
134    }
135    Map<String, Measure> measures = sensorStorage.measuresByComponentAndMetric.get(componetKey);
136    return measures != null ? measures.values() : Collections.<Measure>emptyList();
137  }
138
139  public <G extends Serializable> Measure<G> measure(String componetKey, Metric<G> metric) {
140    return measure(componetKey, metric.key());
141  }
142
143  public Measure measure(String componetKey, String metricKey) {
144    if (componetKey == null) {
145      return sensorStorage.projectMeasuresByMetric.get(metricKey);
146    }
147    Map<String, Measure> measures = sensorStorage.measuresByComponentAndMetric.get(componetKey);
148    return measures != null ? measures.get(metricKey) : null;
149  }
150
151  @Override
152  public NewIssue newIssue() {
153    return new DefaultIssue(sensorStorage);
154  }
155
156  public Collection<Issue> allIssues() {
157    List<Issue> result = new ArrayList<>();
158    result.addAll(sensorStorage.projectIssues);
159    for (String key : sensorStorage.issuesByComponent.keySet()) {
160      result.addAll(sensorStorage.issuesByComponent.get(key));
161    }
162    return result;
163  }
164
165  /**
166   * @param componentKey null for project issues
167   */
168  public Collection<Issue> issues(@Nullable String componentKey) {
169    if (componentKey != null) {
170      List<Issue> list = sensorStorage.issuesByComponent.get(componentKey);
171      return list != null ? list : Collections.<Issue>emptyList();
172    } else {
173      return sensorStorage.projectIssues;
174    }
175  }
176
177  @Override
178  public NewHighlighting newHighlighting() {
179    return new DefaultHighlighting(sensorStorage);
180  }
181
182  public List<TypeOfText> highlightingTypeFor(String componentKey, int charIndex) {
183    DefaultHighlighting syntaxHighlightingData = sensorStorage.highlightingByComponent.get(componentKey);
184    if (syntaxHighlightingData == null) {
185      return Collections.emptyList();
186    }
187    List<TypeOfText> result = new ArrayList<TypeOfText>();
188    for (SyntaxHighlightingRule sortedRule : syntaxHighlightingData.getSyntaxHighlightingRuleSet()) {
189      if (sortedRule.getStartPosition() <= charIndex && sortedRule.getEndPosition() > charIndex) {
190        result.add(sortedRule.getTextType());
191      }
192    }
193    return result;
194  }
195
196  @Override
197  public NewDuplication newDuplication() {
198    return new DefaultDuplication(sensorStorage);
199  }
200
201  public Collection<Duplication> duplications() {
202    return sensorStorage.duplications;
203  }
204
205  @Override
206  public NewDependency newDependency() {
207    return new DefaultDependency(sensorStorage);
208  }
209
210  public Collection<Dependency> dependencies() {
211    return sensorStorage.dependencies;
212  }
213
214  public static class MockAnalysisMode implements AnalysisMode {
215    private boolean isIncremental = false;
216    private boolean isPreview = false;
217
218    @Override
219    public boolean isIncremental() {
220      return isIncremental;
221    }
222
223    public void setIncremental(boolean value) {
224      this.isIncremental = value;
225    }
226
227    @Override
228    public boolean isPreview() {
229      return isPreview;
230    }
231
232    public void setPreview(boolean value) {
233      this.isPreview = value;
234    }
235  }
236
237  private static class InMemorySensorStorage implements SensorStorage {
238
239    private Map<String, Measure> projectMeasuresByMetric = new HashMap<>();
240    private Map<String, Map<String, Measure>> measuresByComponentAndMetric = new HashMap<>();
241
242    private Collection<Issue> projectIssues = new ArrayList<>();
243    private Map<String, List<Issue>> issuesByComponent = new HashMap<>();
244
245    private Map<String, DefaultHighlighting> highlightingByComponent = new HashMap<>();
246
247    private List<Duplication> duplications = new ArrayList<>();
248    private List<Dependency> dependencies = new ArrayList<>();
249
250    @Override
251    public void store(Measure measure) {
252      String key = getKey(measure.inputFile());
253      if (key == null) {
254        projectMeasuresByMetric.put(measure.metric().key(), measure);
255      } else {
256        if (!measuresByComponentAndMetric.containsKey(key)) {
257          measuresByComponentAndMetric.put(key, new HashMap<String, Measure>());
258        }
259        measuresByComponentAndMetric.get(key).put(measure.metric().key(), measure);
260      }
261    }
262
263    @Override
264    public void store(Issue issue) {
265      String key = getKey(issue.inputPath());
266      if (key == null) {
267        projectIssues.add(issue);
268      } else {
269        if (!issuesByComponent.containsKey(key)) {
270          issuesByComponent.put(key, new ArrayList<Issue>());
271        }
272        issuesByComponent.get(key).add(issue);
273      }
274    }
275
276    @Override
277    public void store(Duplication duplication) {
278      duplications.add(duplication);
279    }
280
281    @Override
282    public void store(Dependency dependency) {
283      dependencies.add(dependency);
284    }
285
286    @Override
287    public void store(DefaultHighlighting highlighting) {
288      highlightingByComponent.put(getKey(highlighting.inputFile()), highlighting);
289    }
290
291    @CheckForNull
292    private String getKey(@Nullable InputPath inputPath) {
293      if (inputPath == null) {
294        return null;
295      }
296      if (inputPath instanceof InputFile) {
297        return ((DefaultInputFile) inputPath).key();
298      }
299      if (inputPath instanceof InputDir) {
300        return ((DefaultInputDir) inputPath).key();
301      }
302      throw new IllegalStateException("Unknow component " + inputPath);
303    }
304
305  }
306
307}