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