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}