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 com.google.common.collect.HashBasedTable; 024import com.google.common.collect.Table; 025import java.io.File; 026import java.io.Serializable; 027import java.util.ArrayList; 028import java.util.Collection; 029import java.util.Collections; 030import java.util.EnumMap; 031import java.util.HashMap; 032import java.util.List; 033import java.util.Map; 034import javax.annotation.CheckForNull; 035import org.sonar.api.batch.AnalysisMode; 036import org.sonar.api.batch.fs.internal.DefaultFileSystem; 037import org.sonar.api.batch.fs.internal.DefaultTextPointer; 038import org.sonar.api.batch.rule.ActiveRules; 039import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; 040import org.sonar.api.batch.sensor.Sensor; 041import org.sonar.api.batch.sensor.SensorContext; 042import org.sonar.api.batch.sensor.coverage.CoverageType; 043import org.sonar.api.batch.sensor.coverage.NewCoverage; 044import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage; 045import org.sonar.api.batch.sensor.duplication.Duplication; 046import org.sonar.api.batch.sensor.duplication.NewDuplication; 047import org.sonar.api.batch.sensor.duplication.internal.DefaultDuplication; 048import org.sonar.api.batch.sensor.highlighting.NewHighlighting; 049import org.sonar.api.batch.sensor.highlighting.TypeOfText; 050import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting; 051import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule; 052import org.sonar.api.batch.sensor.issue.Issue; 053import org.sonar.api.batch.sensor.issue.NewIssue; 054import org.sonar.api.batch.sensor.issue.internal.DefaultIssue; 055import org.sonar.api.batch.sensor.measure.Measure; 056import org.sonar.api.batch.sensor.measure.NewMeasure; 057import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; 058import org.sonar.api.config.Settings; 059import org.sonar.api.measures.Metric; 060 061/** 062 * Utility class to help testing {@link Sensor}. 063 * 064 * Usage: call {@link #create(File)} to create an "in memory" implementation of {@link SensorContext} then 065 * pass it to your {@link Sensor}. You can then query elements provided by your sensor using methods {@link #allIssues()}, ... 066 * 067 * @since 5.1 068 */ 069@Beta 070public class SensorContextTester implements SensorContext { 071 072 private Settings settings; 073 private DefaultFileSystem fs; 074 private ActiveRules activeRules; 075 private MockAnalysisMode analysisMode; 076 private InMemorySensorStorage sensorStorage; 077 078 private SensorContextTester(File moduleBaseDir) { 079 this.settings = new Settings(); 080 this.fs = new DefaultFileSystem(moduleBaseDir); 081 this.activeRules = new ActiveRulesBuilder().build(); 082 this.analysisMode = new MockAnalysisMode(); 083 this.sensorStorage = new InMemorySensorStorage(); 084 } 085 086 public static SensorContextTester create(File moduleBaseDir) { 087 return new SensorContextTester(moduleBaseDir); 088 } 089 090 @Override 091 public Settings settings() { 092 return settings; 093 } 094 095 public void setSettings(Settings settings) { 096 this.settings = settings; 097 } 098 099 @Override 100 public DefaultFileSystem fileSystem() { 101 return fs; 102 } 103 104 public void setFileSystem(DefaultFileSystem fs) { 105 this.fs = fs; 106 } 107 108 @Override 109 public ActiveRules activeRules() { 110 return activeRules; 111 } 112 113 public void setActiveRules(ActiveRules activeRules) { 114 this.activeRules = activeRules; 115 } 116 117 @Override 118 public MockAnalysisMode analysisMode() { 119 return analysisMode; 120 } 121 122 @Override 123 public <G extends Serializable> NewMeasure<G> newMeasure() { 124 return new DefaultMeasure<>(sensorStorage); 125 } 126 127 public Collection<Measure> measures(String componentKey) { 128 return sensorStorage.measuresByComponentAndMetric.row(componentKey).values(); 129 } 130 131 public <G extends Serializable> Measure<G> measure(String componetKey, Metric<G> metric) { 132 return measure(componetKey, metric.key()); 133 } 134 135 public <G extends Serializable> Measure<G> measure(String componentKey, String metricKey) { 136 return sensorStorage.measuresByComponentAndMetric.row(componentKey).get(metricKey); 137 } 138 139 @Override 140 public NewIssue newIssue() { 141 return new DefaultIssue(sensorStorage); 142 } 143 144 public Collection<Issue> allIssues() { 145 return sensorStorage.allIssues; 146 } 147 148 @CheckForNull 149 public Integer lineHits(String fileKey, CoverageType type, int line) { 150 Map<CoverageType, DefaultCoverage> defaultCoverageByType = sensorStorage.coverageByComponent.get(fileKey); 151 if (defaultCoverageByType == null) { 152 return null; 153 } 154 if (defaultCoverageByType.containsKey(type)) { 155 return defaultCoverageByType.get(type).hitsByLine().get(line); 156 } 157 return null; 158 } 159 160 @CheckForNull 161 public Integer conditions(String fileKey, CoverageType type, int line) { 162 Map<CoverageType, DefaultCoverage> defaultCoverageByType = sensorStorage.coverageByComponent.get(fileKey); 163 if (defaultCoverageByType == null) { 164 return null; 165 } 166 if (defaultCoverageByType.containsKey(type)) { 167 return defaultCoverageByType.get(type).conditionsByLine().get(line); 168 } 169 return null; 170 } 171 172 @CheckForNull 173 public Integer coveredConditions(String fileKey, CoverageType type, int line) { 174 Map<CoverageType, DefaultCoverage> defaultCoverageByType = sensorStorage.coverageByComponent.get(fileKey); 175 if (defaultCoverageByType == null) { 176 return null; 177 } 178 if (defaultCoverageByType.containsKey(type)) { 179 return defaultCoverageByType.get(type).coveredConditionsByLine().get(line); 180 } 181 return null; 182 } 183 184 @Override 185 public NewHighlighting newHighlighting() { 186 return new DefaultHighlighting(sensorStorage); 187 } 188 189 @Override 190 public NewCoverage newCoverage() { 191 return new DefaultCoverage(sensorStorage); 192 } 193 194 public List<TypeOfText> highlightingTypeAt(String componentKey, int line, int lineOffset) { 195 DefaultHighlighting syntaxHighlightingData = sensorStorage.highlightingByComponent.get(componentKey); 196 if (syntaxHighlightingData == null) { 197 return Collections.emptyList(); 198 } 199 List<TypeOfText> result = new ArrayList<>(); 200 DefaultTextPointer location = new DefaultTextPointer(line, lineOffset); 201 for (SyntaxHighlightingRule sortedRule : syntaxHighlightingData.getSyntaxHighlightingRuleSet()) { 202 if (sortedRule.range().start().compareTo(location) <= 0 && sortedRule.range().end().compareTo(location) > 0) { 203 result.add(sortedRule.getTextType()); 204 } 205 } 206 return result; 207 } 208 209 @Override 210 public NewDuplication newDuplication() { 211 return new DefaultDuplication(sensorStorage); 212 } 213 214 public Collection<Duplication> duplications() { 215 return sensorStorage.duplications; 216 } 217 218 public static class MockAnalysisMode implements AnalysisMode { 219 private boolean isPreview = false; 220 private boolean isIssues = false; 221 222 @Override 223 public boolean isPreview() { 224 return isPreview; 225 } 226 227 public void setPreview(boolean value) { 228 this.isPreview = value; 229 } 230 231 @Override 232 public boolean isIssues() { 233 return this.isIssues; 234 } 235 236 public void setIssues(boolean issues) { 237 this.isIssues = issues; 238 } 239 240 @Override 241 public boolean isPublish() { 242 return !isPreview && !isIssues; 243 } 244 } 245 246 private static class InMemorySensorStorage implements SensorStorage { 247 248 private Table<String, String, Measure> measuresByComponentAndMetric = HashBasedTable.create(); 249 250 private Collection<Issue> allIssues = new ArrayList<>(); 251 252 private Map<String, DefaultHighlighting> highlightingByComponent = new HashMap<>(); 253 private Map<String, Map<CoverageType, DefaultCoverage>> coverageByComponent = new HashMap<>(); 254 255 private List<Duplication> duplications = new ArrayList<>(); 256 257 @Override 258 public void store(Measure measure) { 259 measuresByComponentAndMetric.row(measure.inputComponent().key()).put(measure.metric().key(), measure); 260 } 261 262 @Override 263 public void store(Issue issue) { 264 allIssues.add(issue); 265 } 266 267 @Override 268 public void store(Duplication duplication) { 269 duplications.add(duplication); 270 } 271 272 @Override 273 public void store(DefaultHighlighting highlighting) { 274 highlightingByComponent.put(highlighting.inputFile().key(), highlighting); 275 } 276 277 @Override 278 public void store(DefaultCoverage defaultCoverage) { 279 String key = defaultCoverage.inputFile().key(); 280 if (!coverageByComponent.containsKey(key)) { 281 coverageByComponent.put(key, new EnumMap<CoverageType, DefaultCoverage>(CoverageType.class)); 282 } 283 coverageByComponent.get(key).put(defaultCoverage.type(), defaultCoverage); 284 } 285 286 } 287 288}