@@ -19,14 +19,13 @@ | |||
*/ | |||
package org.sonar.batch.rule; | |||
import org.sonar.api.batch.rules.QProfile; | |||
import com.google.common.collect.ArrayListMultimap; | |||
import com.google.common.collect.ListMultimap; | |||
import org.picocontainer.injectors.ProviderAdapter; | |||
import org.sonar.api.batch.rule.ActiveRules; | |||
import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; | |||
import org.sonar.api.batch.rule.internal.NewActiveRule; | |||
import org.sonar.api.batch.rules.QProfile; | |||
import org.sonar.api.rules.Rule; | |||
import org.sonar.api.rules.RuleFinder; | |||
import org.sonar.api.rules.RuleParam; | |||
@@ -62,7 +61,7 @@ public class ActiveRulesProvider extends ProviderAdapter { | |||
for (ActiveRuleDto activeDto : dao.selectByProfileId(qProfileWithId.id())) { | |||
Rule rule = ruleFinder.findById(activeDto.getRulId()); | |||
if (rule != null) { | |||
NewActiveRule newActiveRule = builder.activate(rule.ruleKey()); | |||
NewActiveRule newActiveRule = builder.create(rule.ruleKey()); | |||
newActiveRule.setSeverity(activeDto.getSeverityString()); | |||
newActiveRule.setLanguage(rule.getLanguage()); | |||
Rule template = rule.getTemplate(); | |||
@@ -83,6 +82,7 @@ public class ActiveRulesProvider extends ProviderAdapter { | |||
newActiveRule.setParam(param.getKey(), param.getDefaultValue()); | |||
} | |||
} | |||
newActiveRule.activate(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,52 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.batch.scan2; | |||
import org.sonar.api.BatchComponent; | |||
import org.sonar.api.batch.analyzer.issue.internal.DefaultAnalyzerIssue; | |||
import org.sonar.batch.index.Cache; | |||
import org.sonar.batch.index.Caches; | |||
/** | |||
* Shared issues among all project modules | |||
*/ | |||
public class AnalyzerIssueCache implements BatchComponent { | |||
// component key -> issue key -> issue | |||
private final Cache<DefaultAnalyzerIssue> cache; | |||
public AnalyzerIssueCache(Caches caches) { | |||
cache = caches.createCache("issues"); | |||
} | |||
public Iterable<DefaultAnalyzerIssue> byComponent(String resourceKey) { | |||
return cache.values(resourceKey); | |||
} | |||
public Iterable<DefaultAnalyzerIssue> all() { | |||
return cache.values(); | |||
} | |||
public AnalyzerIssueCache put(String resourceKey, DefaultAnalyzerIssue issue) { | |||
cache.put(resourceKey, issue.key(), issue); | |||
return this; | |||
} | |||
} |
@@ -19,11 +19,10 @@ | |||
*/ | |||
package org.sonar.batch.scan2; | |||
import org.sonar.api.batch.measure.Metric; | |||
import org.sonar.api.batch.analyzer.AnalyzerContext; | |||
import org.sonar.api.batch.analyzer.issue.AnalyzerIssue; | |||
import org.sonar.api.batch.analyzer.issue.AnalyzerIssueBuilder; | |||
import org.sonar.api.batch.analyzer.issue.internal.DefaultAnalyzerIssue; | |||
import org.sonar.api.batch.analyzer.issue.internal.DefaultAnalyzerIssueBuilder; | |||
import org.sonar.api.batch.analyzer.measure.AnalyzerMeasure; | |||
import org.sonar.api.batch.analyzer.measure.AnalyzerMeasureBuilder; | |||
@@ -32,30 +31,27 @@ import org.sonar.api.batch.analyzer.measure.internal.DefaultAnalyzerMeasureBuild | |||
import org.sonar.api.batch.bootstrap.ProjectDefinition; | |||
import org.sonar.api.batch.fs.FileSystem; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.measure.Metric; | |||
import org.sonar.api.batch.rule.ActiveRules; | |||
import org.sonar.api.config.Settings; | |||
import org.sonar.api.issue.internal.DefaultIssue; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.batch.issue.ModuleIssues; | |||
import org.sonar.core.component.ComponentKeys; | |||
import org.sonar.core.issue.DefaultIssueBuilder; | |||
import java.io.Serializable; | |||
public class DefaultAnalyzerContext implements AnalyzerContext { | |||
private final AnalyzerMeasureCache measureCache; | |||
private final AnalyzerIssueCache issueCache; | |||
private ProjectDefinition def; | |||
private ModuleIssues moduleIssues; | |||
private Settings settings; | |||
private FileSystem fs; | |||
private ActiveRules activeRules; | |||
public DefaultAnalyzerContext(ProjectDefinition def, AnalyzerMeasureCache measureCache, | |||
ModuleIssues moduleIssues, Settings settings, FileSystem fs, ActiveRules activeRules) { | |||
public DefaultAnalyzerContext(ProjectDefinition def, AnalyzerMeasureCache measureCache, AnalyzerIssueCache issueCache, | |||
Settings settings, FileSystem fs, ActiveRules activeRules) { | |||
this.def = def; | |||
this.measureCache = measureCache; | |||
this.moduleIssues = moduleIssues; | |||
this.issueCache = issueCache; | |||
this.settings = settings; | |||
this.fs = fs; | |||
this.activeRules = activeRules; | |||
@@ -117,20 +113,14 @@ public class DefaultAnalyzerContext implements AnalyzerContext { | |||
@Override | |||
public void addIssue(AnalyzerIssue issue) { | |||
DefaultIssueBuilder builder = new DefaultIssueBuilder() | |||
.projectKey(def.getKey()); | |||
String resourceKey; | |||
if (issue.inputFile() != null) { | |||
builder.componentKey(ComponentKeys.createEffectiveKey(def.getKey(), issue.inputFile())); | |||
resourceKey = ComponentKeys.createEffectiveKey(def.getKey(), issue.inputFile()); | |||
} else { | |||
builder.componentKey(def.getKey()); | |||
resourceKey = def.getKey(); | |||
} | |||
moduleIssues.initAndAddIssue((DefaultIssue) builder | |||
.ruleKey(RuleKey.of(issue.ruleKey().repository(), issue.ruleKey().rule())) | |||
.message(issue.message()) | |||
.line(issue.line()) | |||
.effortToFix(issue.effortToFix()) | |||
.build()); | |||
issueCache.put(resourceKey, (DefaultAnalyzerIssue) issue); | |||
} | |||
} |
@@ -29,38 +29,15 @@ import org.sonar.api.batch.bootstrap.ProjectReactor; | |||
import org.sonar.api.config.Settings; | |||
import org.sonar.api.platform.ComponentContainer; | |||
import org.sonar.api.scan.filesystem.PathResolver; | |||
import org.sonar.api.utils.SonarException; | |||
import org.sonar.batch.bootstrap.ExtensionInstaller; | |||
import org.sonar.batch.bootstrap.ExtensionMatcher; | |||
import org.sonar.batch.bootstrap.ExtensionUtils; | |||
import org.sonar.batch.debt.IssueChangelogDebtCalculator; | |||
import org.sonar.batch.index.Caches; | |||
import org.sonar.batch.index.ComponentDataCache; | |||
import org.sonar.batch.index.ComponentDataPersister; | |||
import org.sonar.batch.index.ResourceCache; | |||
import org.sonar.batch.index.SnapshotCache; | |||
import org.sonar.batch.issue.DefaultProjectIssues; | |||
import org.sonar.batch.issue.DeprecatedViolations; | |||
import org.sonar.batch.issue.IssueCache; | |||
import org.sonar.batch.issue.IssuePersister; | |||
import org.sonar.batch.issue.ScanIssueStorage; | |||
import org.sonar.batch.phases.GraphPersister; | |||
import org.sonar.batch.profiling.PhasesSumUpTimeProfiler; | |||
import org.sonar.batch.scan.ProjectReactorBuilder; | |||
import org.sonar.batch.scan.filesystem.InputFileCache; | |||
import org.sonar.batch.scan.maven.FakeMavenPluginExecutor; | |||
import org.sonar.batch.scan.maven.MavenPluginExecutor; | |||
import org.sonar.batch.source.HighlightableBuilder; | |||
import org.sonar.batch.source.SymbolizableBuilder; | |||
import org.sonar.core.component.ScanGraph; | |||
import org.sonar.core.issue.IssueNotifications; | |||
import org.sonar.core.issue.IssueUpdater; | |||
import org.sonar.core.issue.workflow.FunctionExecutor; | |||
import org.sonar.core.issue.workflow.IssueWorkflow; | |||
import org.sonar.core.test.TestPlanBuilder; | |||
import org.sonar.core.test.TestPlanPerspectiveLoader; | |||
import org.sonar.core.test.TestableBuilder; | |||
import org.sonar.core.test.TestablePerspectiveLoader; | |||
public class ProjectScanContainer extends ComponentContainer { | |||
public ProjectScanContainer(ComponentContainer taskContainer) { | |||
@@ -93,7 +70,7 @@ public class ProjectScanContainer extends ComponentContainer { | |||
reactor = bootstrapper.bootstrap(); | |||
} | |||
if (reactor == null) { | |||
throw new SonarException(bootstrapper + " has returned null as ProjectReactor"); | |||
throw new IllegalStateException(bootstrapper + " has returned null as ProjectReactor"); | |||
} | |||
add(reactor); | |||
} | |||
@@ -101,10 +78,8 @@ public class ProjectScanContainer extends ComponentContainer { | |||
private void addBatchComponents() { | |||
add( | |||
Caches.class, | |||
SnapshotCache.class, | |||
ResourceCache.class, | |||
ComponentDataCache.class, | |||
ComponentDataPersister.class, | |||
// Measures | |||
AnalyzerMeasureCache.class, | |||
// file system | |||
@@ -112,28 +87,7 @@ public class ProjectScanContainer extends ComponentContainer { | |||
PathResolver.class, | |||
// issues | |||
IssueUpdater.class, | |||
FunctionExecutor.class, | |||
IssueWorkflow.class, | |||
DeprecatedViolations.class, | |||
IssueCache.class, | |||
ScanIssueStorage.class, | |||
IssuePersister.class, | |||
IssueNotifications.class, | |||
DefaultProjectIssues.class, | |||
IssueChangelogDebtCalculator.class, | |||
// tests | |||
TestPlanPerspectiveLoader.class, | |||
TestablePerspectiveLoader.class, | |||
TestPlanBuilder.class, | |||
TestableBuilder.class, | |||
ScanGraph.create(), | |||
GraphPersister.class, | |||
// lang | |||
HighlightableBuilder.class, | |||
SymbolizableBuilder.class, | |||
AnalyzerIssueCache.class, | |||
ScanTaskObservers.class); | |||
} |
@@ -47,7 +47,9 @@ import static org.fest.assertions.Assertions.assertThat; | |||
import static org.fest.assertions.Fail.fail; | |||
import static org.mockito.Matchers.any; | |||
import static org.mockito.Matchers.eq; | |||
import static org.mockito.Mockito.*; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.verifyZeroInteractions; | |||
import static org.mockito.Mockito.when; | |||
@RunWith(MockitoJUnitRunner.class) | |||
public class ModuleIssuesTest { | |||
@@ -122,7 +124,7 @@ public class ModuleIssuesTest { | |||
@Test | |||
public void ignore_null_rule_of_active_rule() throws Exception { | |||
ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); | |||
activeRulesBuilder.activate(SQUID_RULE_KEY); | |||
activeRulesBuilder.create(SQUID_RULE_KEY).activate(); | |||
initModuleIssues(); | |||
DefaultIssue issue = new DefaultIssue().setRuleKey(SQUID_RULE_KEY); | |||
@@ -135,7 +137,7 @@ public class ModuleIssuesTest { | |||
@Test | |||
public void add_issue_to_cache() throws Exception { | |||
ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); | |||
activeRulesBuilder.activate(SQUID_RULE_KEY).setSeverity(Severity.INFO); | |||
activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate(); | |||
initModuleIssues(); | |||
Date analysisDate = new Date(); | |||
@@ -159,7 +161,7 @@ public class ModuleIssuesTest { | |||
@Test | |||
public void use_severity_from_active_rule_if_no_severity_on_issue() throws Exception { | |||
ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); | |||
activeRulesBuilder.activate(SQUID_RULE_KEY).setSeverity(Severity.INFO); | |||
activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate(); | |||
initModuleIssues(); | |||
Date analysisDate = new Date(); | |||
@@ -178,7 +180,7 @@ public class ModuleIssuesTest { | |||
@Test | |||
public void use_rule_name_if_no_message() throws Exception { | |||
ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); | |||
activeRulesBuilder.activate(SQUID_RULE_KEY).setSeverity(Severity.INFO); | |||
activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate(); | |||
initModuleIssues(); | |||
Date analysisDate = new Date(); | |||
@@ -202,7 +204,7 @@ public class ModuleIssuesTest { | |||
@Test | |||
public void add_deprecated_violation() throws Exception { | |||
ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); | |||
activeRulesBuilder.activate(SQUID_RULE_KEY).setSeverity(Severity.INFO); | |||
activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate(); | |||
initModuleIssues(); | |||
org.sonar.api.rules.Rule rule = org.sonar.api.rules.Rule.create("squid", "AvoidCycle", "Avoid Cycle"); | |||
@@ -232,7 +234,7 @@ public class ModuleIssuesTest { | |||
@Test | |||
public void filter_issue() throws Exception { | |||
ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); | |||
activeRulesBuilder.activate(SQUID_RULE_KEY).setSeverity(Severity.INFO); | |||
activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate(); | |||
initModuleIssues(); | |||
DefaultIssue issue = new DefaultIssue() | |||
@@ -254,7 +256,7 @@ public class ModuleIssuesTest { | |||
.setName(SQUID_RULE_NAME) | |||
.setDebtSubCharacteristic("COMPILER_RELATED_PORTABILITY") | |||
.setDebtRemediationFunction(DebtRemediationFunction.createLinear(Duration.create(10L))); | |||
activeRulesBuilder.activate(SQUID_RULE_KEY).setSeverity(Severity.INFO); | |||
activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate(); | |||
initModuleIssues(); | |||
Date analysisDate = new Date(); | |||
@@ -280,7 +282,7 @@ public class ModuleIssuesTest { | |||
.setName(SQUID_RULE_NAME) | |||
.setDebtSubCharacteristic("COMPILER_RELATED_PORTABILITY") | |||
.setDebtRemediationFunction(DebtRemediationFunction.createLinearWithOffset(Duration.create(10L), Duration.create(25L))); | |||
activeRulesBuilder.activate(SQUID_RULE_KEY).setSeverity(Severity.INFO); | |||
activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate(); | |||
initModuleIssues(); | |||
Date analysisDate = new Date(); | |||
@@ -306,7 +308,7 @@ public class ModuleIssuesTest { | |||
.setName(SQUID_RULE_NAME) | |||
.setDebtSubCharacteristic("COMPILER_RELATED_PORTABILITY") | |||
.setDebtRemediationFunction(DebtRemediationFunction.createConstantPerIssue(Duration.create(10L))); | |||
activeRulesBuilder.activate(SQUID_RULE_KEY).setSeverity(Severity.INFO); | |||
activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate(); | |||
initModuleIssues(); | |||
Date analysisDate = new Date(); | |||
@@ -332,7 +334,7 @@ public class ModuleIssuesTest { | |||
.setName(SQUID_RULE_NAME) | |||
.setDebtSubCharacteristic("COMPILER_RELATED_PORTABILITY") | |||
.setDebtRemediationFunction(DebtRemediationFunction.createConstantPerIssue(Duration.create(25L))); | |||
activeRulesBuilder.activate(SQUID_RULE_KEY).setSeverity(Severity.INFO); | |||
activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate(); | |||
initModuleIssues(); | |||
DefaultIssue issue = new DefaultIssue() |
@@ -0,0 +1,417 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.batch.mediumtest; | |||
import com.google.common.collect.BiMap; | |||
import com.google.common.collect.HashBiMap; | |||
import com.google.common.collect.Lists; | |||
import com.google.common.collect.Maps; | |||
import org.apache.commons.io.IOUtils; | |||
import org.junit.rules.ExternalResource; | |||
import org.sonar.api.SonarPlugin; | |||
import org.sonar.api.batch.analyzer.issue.AnalyzerIssue; | |||
import org.sonar.api.batch.analyzer.measure.AnalyzerMeasure; | |||
import org.sonar.api.batch.debt.internal.DefaultDebtModel; | |||
import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; | |||
import org.sonar.api.batch.rule.internal.RulesBuilder; | |||
import org.sonar.api.batch.rules.QProfile; | |||
import org.sonar.api.measures.CoreMetrics; | |||
import org.sonar.api.measures.Metric; | |||
import org.sonar.api.measures.MetricFinder; | |||
import org.sonar.api.platform.PluginMetadata; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.rules.Rule; | |||
import org.sonar.api.rules.RuleFinder; | |||
import org.sonar.api.rules.RuleQuery; | |||
import org.sonar.batch.bootstrap.PluginsReferential; | |||
import org.sonar.batch.bootstrapper.Batch; | |||
import org.sonar.batch.bootstrapper.EnvironmentInformation; | |||
import org.sonar.batch.languages.Language; | |||
import org.sonar.batch.languages.LanguagesReferential; | |||
import org.sonar.batch.rules.QProfilesReferential; | |||
import org.sonar.batch.scan2.AnalyzerIssueCache; | |||
import org.sonar.batch.scan2.AnalyzerMeasureCache; | |||
import org.sonar.batch.scan2.ProjectScanContainer; | |||
import org.sonar.batch.scan2.ScanTaskObserver; | |||
import org.sonar.batch.settings.SettingsReferential; | |||
import org.sonar.core.plugins.DefaultPluginMetadata; | |||
import org.sonar.core.plugins.RemotePlugin; | |||
import java.io.File; | |||
import java.io.FileReader; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Properties; | |||
public class AnalyzerMediumTester extends ExternalResource { | |||
private Batch batch; | |||
public static AnalyzerMediumTesterBuilder builder() { | |||
return new AnalyzerMediumTesterBuilder().registerCoreMetrics(); | |||
} | |||
public static class AnalyzerMediumTesterBuilder { | |||
private final FakeSettingsReferential settingsReferential = new FakeSettingsReferential(); | |||
private final FackPluginsReferential pluginsReferential = new FackPluginsReferential(); | |||
private final FakeMetricFinder metricFinder = new FakeMetricFinder(); | |||
private final FakeRuleFinder ruleFinder = new FakeRuleFinder(); | |||
private final FakeQProfileReferential qProfileReferential = new FakeQProfileReferential(); | |||
private final FakeLanguageReferential languageReferential = new FakeLanguageReferential(); | |||
private final Map<String, String> bootstrapProperties = new HashMap<String, String>(); | |||
private final RulesBuilder rulesBuilder = new RulesBuilder(); | |||
private final ActiveRulesBuilder activeRulesBuilder = new ActiveRulesBuilder(); | |||
private int metricId = 1; | |||
public AnalyzerMediumTester build() { | |||
return new AnalyzerMediumTester(this); | |||
} | |||
public AnalyzerMediumTesterBuilder registerPlugin(String pluginKey, File location) { | |||
pluginsReferential.addPlugin(pluginKey, location); | |||
return this; | |||
} | |||
public AnalyzerMediumTesterBuilder registerPlugin(String pluginKey, SonarPlugin instance) { | |||
pluginsReferential.addPlugin(pluginKey, instance); | |||
return this; | |||
} | |||
public AnalyzerMediumTesterBuilder registerCoreMetrics() { | |||
for (Metric<?> m : CoreMetrics.getMetrics()) { | |||
registerMetric(m); | |||
} | |||
return this; | |||
} | |||
public AnalyzerMediumTesterBuilder registerMetric(Metric<?> metric) { | |||
metricFinder.add(metricId++, metric); | |||
return this; | |||
} | |||
public AnalyzerMediumTesterBuilder addQProfile(String language, String name) { | |||
qProfileReferential.add(new QProfile(name, language, 1)); | |||
return this; | |||
} | |||
public AnalyzerMediumTesterBuilder addDefaultQProfile(String language, String name) { | |||
qProfileReferential.add(new QProfile(name, language, 1)); | |||
settingsReferential.globalSettings().put("sonar.profile." + language, name); | |||
return this; | |||
} | |||
public AnalyzerMediumTesterBuilder registerLanguage(org.sonar.api.resources.Language... languages) { | |||
languageReferential.register(languages); | |||
return this; | |||
} | |||
public AnalyzerMediumTesterBuilder bootstrapProperties(Map<String, String> props) { | |||
bootstrapProperties.putAll(props); | |||
return this; | |||
} | |||
public AnalyzerMediumTesterBuilder activateRule(RuleKey key) { | |||
rulesBuilder.add(key); | |||
activeRulesBuilder.create(key).activate(); | |||
return this; | |||
} | |||
public AnalyzerMediumTesterBuilder registerInactiveRule(RuleKey key) { | |||
rulesBuilder.add(key); | |||
return this; | |||
} | |||
} | |||
@Override | |||
protected void before() throws Throwable { | |||
batch.start(); | |||
} | |||
@Override | |||
protected void after() { | |||
batch.stop(); | |||
} | |||
private AnalyzerMediumTester(AnalyzerMediumTesterBuilder builder) { | |||
batch = Batch.builder() | |||
.setEnableLoggingConfiguration(true) | |||
.addComponents( | |||
new EnvironmentInformation("mediumTest", "1.0"), | |||
builder.settingsReferential, | |||
builder.pluginsReferential, | |||
builder.metricFinder, | |||
builder.ruleFinder, | |||
builder.qProfileReferential, | |||
builder.rulesBuilder.build(), | |||
builder.activeRulesBuilder.build(), | |||
new DefaultDebtModel(), | |||
builder.languageReferential) | |||
.setBootstrapProperties(builder.bootstrapProperties) | |||
.build(); | |||
} | |||
public TaskBuilder newTask() { | |||
return new TaskBuilder(this); | |||
} | |||
public TaskBuilder newScanTask(File sonarProps) { | |||
Properties prop = new Properties(); | |||
FileReader reader = null; | |||
try { | |||
reader = new FileReader(sonarProps); | |||
prop.load(reader); | |||
} catch (Exception e) { | |||
throw new IllegalStateException("Unable to read configuration file", e); | |||
} finally { | |||
if (reader != null) { | |||
IOUtils.closeQuietly(reader); | |||
} | |||
} | |||
TaskBuilder builder = new TaskBuilder(this); | |||
builder.property("sonar.task", "scan"); | |||
builder.property("sonar.projectBaseDir", sonarProps.getParentFile().getAbsolutePath()); | |||
for (Map.Entry entry : prop.entrySet()) { | |||
builder.property(entry.getKey().toString(), entry.getValue().toString()); | |||
} | |||
return builder; | |||
} | |||
public static class TaskBuilder { | |||
private final Map<String, String> taskProperties = new HashMap<String, String>(); | |||
private AnalyzerMediumTester tester; | |||
public TaskBuilder(AnalyzerMediumTester tester) { | |||
this.tester = tester; | |||
} | |||
public TaskResult start() { | |||
TaskResult result = new TaskResult(); | |||
tester.batch.executeTask(taskProperties, | |||
result | |||
); | |||
return result; | |||
} | |||
public TaskBuilder properties(Map<String, String> props) { | |||
taskProperties.putAll(props); | |||
return this; | |||
} | |||
public TaskBuilder property(String key, String value) { | |||
taskProperties.put(key, value); | |||
return this; | |||
} | |||
} | |||
public static class TaskResult implements ScanTaskObserver { | |||
private List<AnalyzerIssue> issues = new ArrayList<AnalyzerIssue>(); | |||
private List<AnalyzerMeasure> measures = new ArrayList<AnalyzerMeasure>(); | |||
@Override | |||
public void scanTaskCompleted(ProjectScanContainer container) { | |||
for (AnalyzerIssue issue : container.getComponentByType(AnalyzerIssueCache.class).all()) { | |||
issues.add(issue); | |||
} | |||
for (AnalyzerMeasure<?> measure : container.getComponentByType(AnalyzerMeasureCache.class).all()) { | |||
measures.add(measure); | |||
} | |||
} | |||
public List<AnalyzerIssue> issues() { | |||
return issues; | |||
} | |||
public List<AnalyzerMeasure> measures() { | |||
return measures; | |||
} | |||
} | |||
private static class FakeSettingsReferential implements SettingsReferential { | |||
private Map<String, String> globalSettings = new HashMap<String, String>(); | |||
private Map<String, Map<String, String>> projectSettings = new HashMap<String, Map<String, String>>(); | |||
@Override | |||
public Map<String, String> globalSettings() { | |||
return globalSettings; | |||
} | |||
@Override | |||
public Map<String, String> projectSettings(String projectKey) { | |||
return projectSettings.containsKey(projectKey) ? projectSettings.get(projectKey) : Collections.<String, String>emptyMap(); | |||
} | |||
} | |||
private static class FackPluginsReferential implements PluginsReferential { | |||
private List<RemotePlugin> pluginList = new ArrayList<RemotePlugin>(); | |||
private Map<RemotePlugin, File> pluginFiles = new HashMap<RemotePlugin, File>(); | |||
Map<PluginMetadata, SonarPlugin> localPlugins = new HashMap<PluginMetadata, SonarPlugin>(); | |||
@Override | |||
public List<RemotePlugin> pluginList() { | |||
return pluginList; | |||
} | |||
@Override | |||
public File pluginFile(RemotePlugin remote) { | |||
return pluginFiles.get(remote); | |||
} | |||
public FackPluginsReferential addPlugin(String pluginKey, File location) { | |||
RemotePlugin plugin = new RemotePlugin(pluginKey, false); | |||
pluginList.add(plugin); | |||
pluginFiles.put(plugin, location); | |||
return this; | |||
} | |||
public FackPluginsReferential addPlugin(String pluginKey, SonarPlugin pluginInstance) { | |||
localPlugins.put(DefaultPluginMetadata.create(null).setKey(pluginKey), pluginInstance); | |||
return this; | |||
} | |||
@Override | |||
public Map<PluginMetadata, SonarPlugin> localPlugins() { | |||
return localPlugins; | |||
} | |||
} | |||
private static class FakeMetricFinder implements MetricFinder { | |||
private Map<String, Metric> metricsByKey = Maps.newLinkedHashMap(); | |||
private Map<Integer, Metric> metricsById = Maps.newLinkedHashMap(); | |||
public FakeMetricFinder add(int id, Metric metric) { | |||
metricsByKey.put(metric.getKey(), metric); | |||
metricsById.put(id, metric); | |||
return this; | |||
} | |||
@Override | |||
public Metric findById(int metricId) { | |||
return metricsById.get(metricId); | |||
} | |||
@Override | |||
public Metric findByKey(String key) { | |||
return metricsByKey.get(key); | |||
} | |||
@Override | |||
public Collection<Metric> findAll(List<String> metricKeys) { | |||
List<Metric> result = Lists.newLinkedList(); | |||
for (String metricKey : metricKeys) { | |||
Metric metric = findByKey(metricKey); | |||
if (metric != null) { | |||
result.add(metric); | |||
} | |||
} | |||
return result; | |||
} | |||
@Override | |||
public Collection<Metric> findAll() { | |||
return metricsByKey.values(); | |||
} | |||
} | |||
private static class FakeRuleFinder implements RuleFinder { | |||
private BiMap<Integer, Rule> rulesById = HashBiMap.create(); | |||
private Map<String, Map<String, Rule>> rulesByRepoKeyAndRuleKey = Maps.newHashMap(); | |||
@Override | |||
public Rule findById(int ruleId) { | |||
return rulesById.get(ruleId); | |||
} | |||
@Override | |||
public Rule findByKey(String repositoryKey, String ruleKey) { | |||
Map<String, Rule> repository = rulesByRepoKeyAndRuleKey.get(repositoryKey); | |||
return repository != null ? repository.get(ruleKey) : null; | |||
} | |||
@Override | |||
public Rule findByKey(RuleKey key) { | |||
return findByKey(key.repository(), key.rule()); | |||
} | |||
@Override | |||
public Rule find(RuleQuery query) { | |||
throw new UnsupportedOperationException(); | |||
} | |||
@Override | |||
public Collection<Rule> findAll(RuleQuery query) { | |||
throw new UnsupportedOperationException(); | |||
} | |||
} | |||
private static class FakeQProfileReferential implements QProfilesReferential { | |||
private Map<String, Map<String, QProfile>> profiles = new HashMap<String, Map<String, QProfile>>(); | |||
@Override | |||
public QProfile get(String language, String name) { | |||
return profiles.get(language).get(name); | |||
} | |||
public void add(QProfile qprofile) { | |||
if (!profiles.containsKey(qprofile.language())) { | |||
profiles.put(qprofile.language(), new HashMap<String, QProfile>()); | |||
} | |||
profiles.get(qprofile.language()).put(qprofile.name(), qprofile); | |||
} | |||
} | |||
private static class FakeLanguageReferential implements LanguagesReferential { | |||
private Map<String, Language> languages = new HashMap<String, Language>(); | |||
public FakeLanguageReferential register(org.sonar.api.resources.Language... languages) { | |||
for (org.sonar.api.resources.Language language : languages) { | |||
this.languages.put(language.getKey(), new Language(language.getKey(), language.getName(), language.getFileSuffixes())); | |||
} | |||
return this; | |||
} | |||
@Override | |||
public Language get(String languageKey) { | |||
return languages.get(languageKey); | |||
} | |||
@Override | |||
public Collection<Language> all() { | |||
return languages.values(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,113 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.batch.mediumtest.xoo; | |||
import com.google.common.collect.ImmutableMap; | |||
import org.apache.commons.io.FileUtils; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.api.batch.analyzer.issue.AnalyzerIssue; | |||
import org.sonar.api.batch.analyzer.measure.internal.DefaultAnalyzerMeasureBuilder; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.measures.CoreMetrics; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.batch.mediumtest.AnalyzerMediumTester; | |||
import org.sonar.batch.mediumtest.AnalyzerMediumTester.TaskResult; | |||
import org.sonar.batch.mediumtest.xoo.plugin.XooPlugin; | |||
import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
public class XooMediumTest { | |||
@org.junit.Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@org.junit.Rule | |||
public AnalyzerMediumTester tester = AnalyzerMediumTester.builder() | |||
// .registerPlugin("xoo", new File("target/sonar-xoo-plugin-2.0-SNAPSHOT.jar")) | |||
.registerPlugin("xoo", new XooPlugin()) | |||
.registerLanguage(new Xoo()) | |||
.addDefaultQProfile("xoo", "Sonar Way") | |||
.activateRule(RuleKey.of("xoo", "OneIssuePerLine")) | |||
.bootstrapProperties(ImmutableMap.of("sonar.analysis.mode", "sensor")) | |||
.build(); | |||
@Test | |||
public void mediumTestOfSample() throws Exception { | |||
File projectDir = new File(XooMediumTest.class.getResource("/org/sonar/batch/mediumtest/xoo/sample").toURI()); | |||
TaskResult result = tester | |||
.newScanTask(new File(projectDir, "sonar-project.properties")) | |||
.start(); | |||
assertThat(result.measures()).hasSize(13); | |||
assertThat(result.issues()).hasSize(24); | |||
} | |||
@Test | |||
public void mediumTest() throws IOException { | |||
File baseDir = temp.newFolder(); | |||
File srcDir = new File(baseDir, "src"); | |||
srcDir.mkdir(); | |||
File xooFile = new File(srcDir, "sample.xoo"); | |||
File xooMeasureFile = new File(srcDir, "sample.xoo.measures"); | |||
FileUtils.write(xooFile, "Sample xoo\ncontent"); | |||
FileUtils.write(xooMeasureFile, "lines:20"); | |||
TaskResult result = tester.newTask() | |||
.properties(ImmutableMap.<String, String>builder() | |||
.put("sonar.task", "scan") | |||
.put("sonar.projectBaseDir", baseDir.getAbsolutePath()) | |||
.put("sonar.projectKey", "com.foo.project") | |||
.put("sonar.projectName", "Foo Project") | |||
.put("sonar.projectVersion", "1.0-SNAPSHOT") | |||
.put("sonar.projectDescription", "Description of Foo Project") | |||
.put("sonar.sources", "src") | |||
.build()) | |||
.start(); | |||
assertThat(result.measures()).hasSize(1); | |||
assertThat(result.issues()).hasSize(20); | |||
assertThat(result.measures()).contains(new DefaultAnalyzerMeasureBuilder<Integer>() | |||
.forMetric(CoreMetrics.LINES) | |||
.onFile(new DefaultInputFile("src/sample.xoo")) | |||
.withValue(20) | |||
.build()); | |||
boolean foundIssueAtLine1 = false; | |||
for (AnalyzerIssue issue : result.issues()) { | |||
if (issue.line() == 1) { | |||
foundIssueAtLine1 = true; | |||
assertThat(issue.inputFile()).isEqualTo(new DefaultInputFile("src/sample.xoo")); | |||
assertThat(issue.message()).isEqualTo("This issue is generated on each line"); | |||
assertThat(issue.effortToFix()).isNull(); | |||
} | |||
} | |||
assertThat(foundIssueAtLine1).isTrue(); | |||
} | |||
} |
@@ -0,0 +1,43 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.batch.mediumtest.xoo.plugin; | |||
import org.sonar.api.SonarPlugin; | |||
import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo; | |||
import org.sonar.batch.mediumtest.xoo.plugin.lang.MeasureAnalyzer; | |||
import org.sonar.batch.mediumtest.xoo.plugin.rule.OneIssuePerLineAnalyzer; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
public final class XooPlugin extends SonarPlugin { | |||
@Override | |||
public List getExtensions() { | |||
return Arrays.asList( | |||
// language | |||
MeasureAnalyzer.class, | |||
Xoo.class, | |||
// rules | |||
OneIssuePerLineAnalyzer.class | |||
); | |||
} | |||
} |
@@ -0,0 +1,40 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.batch.mediumtest.xoo.plugin.base; | |||
import org.sonar.api.resources.AbstractLanguage; | |||
public class Xoo extends AbstractLanguage { | |||
public static final String KEY = "xoo"; | |||
public static final String NAME = "Xoo"; | |||
public Xoo() { | |||
super(KEY, NAME); | |||
} | |||
/** | |||
* ${@inheritDoc} | |||
*/ | |||
public String[] getFileSuffixes() { | |||
return XooConstants.FILE_SUFFIXES; | |||
} | |||
} |
@@ -0,0 +1,36 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.batch.mediumtest.xoo.plugin.base; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
public interface XooConstants { | |||
String PLUGIN_KEY = "xoo"; | |||
String PLUGIN_NAME = "Xoo"; | |||
String REPOSITORY_KEY = PLUGIN_KEY; | |||
String REPOSITORY_NAME = PLUGIN_NAME; | |||
String[] FILE_SUFFIXES = {"xoo"}; | |||
Logger LOG = LoggerFactory.getLogger("xoo"); | |||
} |
@@ -0,0 +1,138 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.batch.mediumtest.xoo.plugin.lang; | |||
import org.apache.commons.io.FileUtils; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.sonar.api.batch.analyzer.Analyzer; | |||
import org.sonar.api.batch.analyzer.AnalyzerContext; | |||
import org.sonar.api.batch.analyzer.AnalyzerDescriptor; | |||
import org.sonar.api.batch.analyzer.measure.AnalyzerMeasure; | |||
import org.sonar.api.batch.analyzer.measure.AnalyzerMeasureBuilder; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.measures.CoreMetrics; | |||
import org.sonar.api.measures.Metric; | |||
import org.sonar.api.measures.MetricFinder; | |||
import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo; | |||
import org.sonar.batch.mediumtest.xoo.plugin.base.XooConstants; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.io.Serializable; | |||
import java.util.List; | |||
/** | |||
* Parse files *.xoo.measures | |||
*/ | |||
public class MeasureAnalyzer implements Analyzer { | |||
private static final String MEASURES_EXTENSION = ".measures"; | |||
private MetricFinder metricFinder; | |||
public MeasureAnalyzer(MetricFinder metricFinder) { | |||
this.metricFinder = metricFinder; | |||
} | |||
private void processFileMeasures(InputFile inputFile, AnalyzerContext context) { | |||
File ioFile = inputFile.file(); | |||
File measureFile = new File(ioFile.getParentFile(), ioFile.getName() + MEASURES_EXTENSION); | |||
if (measureFile.exists()) { | |||
XooConstants.LOG.debug("Processing " + measureFile.getAbsolutePath()); | |||
try { | |||
List<String> lines = FileUtils.readLines(measureFile, context.fileSystem().encoding().name()); | |||
int lineNumber = 0; | |||
for (String line : lines) { | |||
lineNumber++; | |||
if (StringUtils.isBlank(line)) { | |||
continue; | |||
} | |||
if (line.startsWith("#")) { | |||
continue; | |||
} | |||
try { | |||
String metricKey = StringUtils.substringBefore(line, ":"); | |||
String value = line.substring(metricKey.length() + 1); | |||
context.addMeasure(createMeasure(context, inputFile, metricKey, value)); | |||
} catch (Exception e) { | |||
throw new IllegalStateException("Error processing line " + lineNumber + " of file " + measureFile.getAbsolutePath(), e); | |||
} | |||
} | |||
} catch (IOException e) { | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
} | |||
private AnalyzerMeasure<?> createMeasure(AnalyzerContext context, InputFile xooFile, String metricKey, String value) { | |||
Metric metric = metricFinder.findByKey(metricKey); | |||
AnalyzerMeasureBuilder<Serializable> builder = context.measureBuilder() | |||
.forMetric(metric) | |||
.onFile(xooFile); | |||
switch (metric.getType()) { | |||
case BOOL: | |||
builder.withValue(Boolean.parseBoolean(value)); | |||
break; | |||
case INT: | |||
case MILLISEC: | |||
builder.withValue(Integer.valueOf(value)); | |||
break; | |||
case FLOAT: | |||
case PERCENT: | |||
case RATING: | |||
builder.withValue(Double.valueOf(value)); | |||
break; | |||
case STRING: | |||
case LEVEL: | |||
case DATA: | |||
case DISTRIB: | |||
builder.withValue(value); | |||
break; | |||
case WORK_DUR: | |||
builder.withValue(Long.valueOf(value)); | |||
break; | |||
default: | |||
if (metric.isNumericType()) { | |||
builder.withValue(Double.valueOf(value)); | |||
} else if (metric.isDataType()) { | |||
builder.withValue(value); | |||
} else { | |||
throw new UnsupportedOperationException("Unsupported type :" + metric.getType()); | |||
} | |||
} | |||
return builder.build(); | |||
} | |||
@Override | |||
public void describe(AnalyzerDescriptor descriptor) { | |||
descriptor | |||
.name("Xoo Measure Analyzer") | |||
.provides(CoreMetrics.LINES) | |||
.workOnLanguages(Xoo.KEY) | |||
.workOnFileTypes(InputFile.Type.MAIN, InputFile.Type.TEST); | |||
} | |||
@Override | |||
public void analyse(AnalyzerContext context) { | |||
for (InputFile file : context.fileSystem().inputFiles(context.fileSystem().predicates().hasLanguages(Xoo.KEY))) { | |||
processFileMeasures(file, context); | |||
} | |||
} | |||
} |
@@ -0,0 +1,71 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.batch.mediumtest.xoo.plugin.rule; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.batch.analyzer.Analyzer; | |||
import org.sonar.api.batch.analyzer.AnalyzerContext; | |||
import org.sonar.api.batch.analyzer.AnalyzerDescriptor; | |||
import org.sonar.api.batch.analyzer.measure.AnalyzerMeasure; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.measures.CoreMetrics; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo; | |||
import org.sonar.batch.mediumtest.xoo.plugin.base.XooConstants; | |||
public class OneIssuePerLineAnalyzer implements Analyzer { | |||
public static final String RULE_KEY = "OneIssuePerLine"; | |||
private static final String EFFORT_TO_FIX_PROPERTY = "sonar.oneIssuePerLine.effortToFix"; | |||
@Override | |||
public void describe(AnalyzerDescriptor descriptor) { | |||
descriptor | |||
.name("One Issue Per Line") | |||
.dependsOn(CoreMetrics.LINES) | |||
.workOnLanguages(Xoo.KEY) | |||
.workOnFileTypes(InputFile.Type.MAIN, InputFile.Type.TEST); | |||
} | |||
@Override | |||
public void analyse(AnalyzerContext context) { | |||
for (InputFile file : context.fileSystem().inputFiles(context.fileSystem().predicates().hasLanguages(Xoo.KEY))) { | |||
createIssues(file, context); | |||
} | |||
} | |||
private void createIssues(InputFile file, AnalyzerContext context) { | |||
RuleKey ruleKey = RuleKey.of(XooConstants.REPOSITORY_KEY, RULE_KEY); | |||
AnalyzerMeasure<Integer> linesMeasure = context.getMeasure(file, CoreMetrics.LINES); | |||
if (linesMeasure == null) { | |||
LoggerFactory.getLogger(getClass()).warn("Missing measure " + CoreMetrics.LINES_KEY + " on " + file); | |||
} else { | |||
for (int line = 1; line <= (Integer) linesMeasure.value(); line++) { | |||
context.addIssue(context.issueBuilder() | |||
.ruleKey(ruleKey) | |||
.onFile(file) | |||
.atLine(line) | |||
.effortToFix(context.settings().getDouble(EFFORT_TO_FIX_PROPERTY)) | |||
.message("This issue is generated on each line") | |||
.build()); | |||
} | |||
} | |||
} | |||
} |
@@ -98,18 +98,18 @@ public class AnalyzerOptimizerTest { | |||
assertThat(optimizer.shouldExecute(descriptor)).isFalse(); | |||
ActiveRules activeRules = new ActiveRulesBuilder() | |||
.activate(RuleKey.of("repo1", "foo")) | |||
.end() | |||
.create(RuleKey.of("repo1", "foo")) | |||
.activate() | |||
.build(); | |||
optimizer = new AnalyzerOptimizer(fs, activeRules); | |||
assertThat(optimizer.shouldExecute(descriptor)).isFalse(); | |||
activeRules = new ActiveRulesBuilder() | |||
.activate(RuleKey.of("repo1", "foo")) | |||
.end() | |||
.activate(RuleKey.of("squid", "rule")) | |||
.end() | |||
.create(RuleKey.of("repo1", "foo")) | |||
.activate() | |||
.create(RuleKey.of("squid", "rule")) | |||
.activate() | |||
.build(); | |||
optimizer = new AnalyzerOptimizer(fs, activeRules); | |||
assertThat(optimizer.shouldExecute(descriptor)).isTrue(); |
@@ -0,0 +1,6 @@ | |||
sonar.projectKey=sample | |||
sonar.projectName=Sample | |||
sonar.projectVersion=0.1-SNAPSHOT | |||
sonar.sources=xources | |||
sonar.tests=testx | |||
sonar.language=xoo |
@@ -0,0 +1,11 @@ | |||
package org.sonar.tests; | |||
import org.junit.Test; | |||
public class ClassOneTest { | |||
@Test | |||
public void nothing() { | |||
} | |||
} |
@@ -0,0 +1,7 @@ | |||
lines:11 | |||
ncloc:7 | |||
tests:1 | |||
test_execution_time:1 | |||
skipped_tests:0 | |||
test_errors:0 | |||
test_failures:0 |
@@ -0,0 +1,11 @@ | |||
1,user1,2013-01-04 | |||
1,user1,2013-01-04 | |||
1,user1,2013-01-04 | |||
1,user1,2013-01-04 | |||
2,user2,2013-01-05 | |||
2,user2,2013-01-05 | |||
3,user3,2013-01-06 | |||
4,user4,2013-01-07 | |||
4,user4,2013-01-07 | |||
4,user4,2013-01-07 | |||
4,user4,2013-01-07 |
@@ -0,0 +1,8 @@ | |||
package hello; | |||
public class HelloJava { | |||
public static void main(String[] args) { | |||
System.out.println("Hello"); | |||
} | |||
} |
@@ -0,0 +1,3 @@ | |||
lines:8 | |||
ncloc:3 | |||
complexity:1 |
@@ -0,0 +1,8 @@ | |||
1,user1,2013-01-04 | |||
1,user1,2013-01-04 | |||
1,user1,2013-01-04 | |||
1,user1,2013-01-04 | |||
2,user2,2013-01-05 | |||
2,user2,2013-01-05 | |||
3,user3,2013-01-06 | |||
4,user4,2013-01-07 |
@@ -0,0 +1,6 @@ | |||
object HelloWorld { | |||
def main(args: Array[String]) { | |||
println("Hello, world of xoo!") | |||
} | |||
} | |||
@@ -0,0 +1,3 @@ | |||
lines:5 | |||
ncloc:5 | |||
complexity:2 |
@@ -20,6 +20,9 @@ | |||
package org.sonar.api.batch.analyzer.issue.internal; | |||
import com.google.common.base.Preconditions; | |||
import com.google.common.base.Strings; | |||
import org.apache.commons.lang.builder.ToStringBuilder; | |||
import org.apache.commons.lang.builder.ToStringStyle; | |||
import org.sonar.api.batch.analyzer.issue.AnalyzerIssue; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.rule.RuleKey; | |||
@@ -27,9 +30,11 @@ import org.sonar.api.rule.RuleKey; | |||
import javax.annotation.Nullable; | |||
import java.io.Serializable; | |||
import java.util.UUID; | |||
public class DefaultAnalyzerIssue implements AnalyzerIssue, Serializable { | |||
private final String key; | |||
private final InputFile inputFile; | |||
private final RuleKey ruleKey; | |||
private final String message; | |||
@@ -43,6 +48,12 @@ public class DefaultAnalyzerIssue implements AnalyzerIssue, Serializable { | |||
this.message = builder.message; | |||
this.line = builder.line; | |||
this.effortToFix = builder.effortToFix; | |||
this.key = builder.key == null ? UUID.randomUUID().toString() : builder.key; | |||
Preconditions.checkState(!Strings.isNullOrEmpty(key), "Fail to generate issue key"); | |||
} | |||
public String key() { | |||
return key; | |||
} | |||
@Override | |||
@@ -72,4 +83,26 @@ public class DefaultAnalyzerIssue implements AnalyzerIssue, Serializable { | |||
return effortToFix; | |||
} | |||
@Override | |||
public boolean equals(Object o) { | |||
if (this == o) { | |||
return true; | |||
} | |||
if (o == null || getClass() != o.getClass()) { | |||
return false; | |||
} | |||
DefaultAnalyzerIssue that = (DefaultAnalyzerIssue) o; | |||
return !key.equals(that.key); | |||
} | |||
@Override | |||
public int hashCode() { | |||
return key.hashCode(); | |||
} | |||
@Override | |||
public String toString() { | |||
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); | |||
} | |||
} |
@@ -29,6 +29,7 @@ import javax.annotation.Nullable; | |||
public class DefaultAnalyzerIssueBuilder implements AnalyzerIssueBuilder { | |||
String key; | |||
Boolean onProject = null; | |||
InputFile file; | |||
RuleKey ruleKey; | |||
@@ -80,6 +81,14 @@ public class DefaultAnalyzerIssueBuilder implements AnalyzerIssueBuilder { | |||
return this; | |||
} | |||
/** | |||
* For testing only. | |||
*/ | |||
public DefaultAnalyzerIssueBuilder withKey(String key) { | |||
this.key = key; | |||
return this; | |||
} | |||
@Override | |||
public AnalyzerIssue build() { | |||
return new DefaultAnalyzerIssue(this); |
@@ -19,11 +19,14 @@ | |||
*/ | |||
package org.sonar.api.batch.analyzer.measure.internal; | |||
import org.sonar.api.batch.measure.Metric; | |||
import com.google.common.base.Preconditions; | |||
import org.apache.commons.lang.builder.EqualsBuilder; | |||
import org.apache.commons.lang.builder.HashCodeBuilder; | |||
import org.apache.commons.lang.builder.ToStringBuilder; | |||
import org.apache.commons.lang.builder.ToStringStyle; | |||
import org.sonar.api.batch.analyzer.measure.AnalyzerMeasure; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.measure.Metric; | |||
import javax.annotation.Nullable; | |||
@@ -62,26 +65,35 @@ public class DefaultAnalyzerMeasure<G extends Serializable> implements AnalyzerM | |||
@Override | |||
public boolean equals(Object obj) { | |||
if (!(obj instanceof DefaultAnalyzerMeasure)) { | |||
if (obj == null) { | |||
return false; | |||
} | |||
if (obj == this) { | |||
return true; | |||
} | |||
if (obj.getClass() != getClass()) { | |||
return false; | |||
} | |||
DefaultAnalyzerMeasure<?> other = (DefaultAnalyzerMeasure<?>) obj; | |||
return metric.equals(other.metric) | |||
&& value.equals(other.value) | |||
&& (inputFile == null ? other.inputFile == null : inputFile.equals(other.inputFile)); | |||
DefaultAnalyzerMeasure rhs = (DefaultAnalyzerMeasure) obj; | |||
return new EqualsBuilder() | |||
.append(inputFile, rhs.inputFile) | |||
.append(metric, rhs.metric) | |||
.append(value, rhs.value) | |||
.isEquals(); | |||
} | |||
@Override | |||
public int hashCode() { | |||
return metric.hashCode() | |||
+ value.hashCode() | |||
+ (inputFile != null ? inputFile.hashCode() : 0); | |||
return new HashCodeBuilder(27, 45). | |||
append(inputFile). | |||
append(metric). | |||
append(value). | |||
toHashCode(); | |||
} | |||
@Override | |||
public String toString() { | |||
return "AnalyzerMeasure[" + (inputFile != null ? "inputFile=" + inputFile.toString() : "onProject") | |||
+ ",metric=" + metric + ",value=" + value + "]"; | |||
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); | |||
} | |||
} |
@@ -35,13 +35,15 @@ public class ActiveRulesBuilder { | |||
private final Map<RuleKey, NewActiveRule> map = Maps.newHashMap(); | |||
public NewActiveRule activate(RuleKey ruleKey) { | |||
if (map.containsKey(ruleKey)) { | |||
throw new IllegalStateException(String.format("Rule '%s' is already activated", ruleKey)); | |||
public NewActiveRule create(RuleKey ruleKey) { | |||
return new NewActiveRule(this, ruleKey); | |||
} | |||
void activate(NewActiveRule newActiveRule) { | |||
if (map.containsKey(newActiveRule.ruleKey)) { | |||
throw new IllegalStateException(String.format("Rule '%s' is already activated", newActiveRule.ruleKey)); | |||
} | |||
NewActiveRule newActiveRule = new NewActiveRule(this, ruleKey); | |||
map.put(ruleKey, newActiveRule); | |||
return newActiveRule; | |||
map.put(newActiveRule.ruleKey, newActiveRule); | |||
} | |||
public ActiveRules build() { |
@@ -72,7 +72,8 @@ public class NewActiveRule { | |||
return params; | |||
} | |||
public ActiveRulesBuilder end() { | |||
public ActiveRulesBuilder activate() { | |||
builder.activate(this); | |||
return builder; | |||
} | |||
} |
@@ -46,7 +46,7 @@ public class CheckFactoryTest { | |||
@Test | |||
public void class_name_as_check_key() { | |||
RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithoutProperties"); | |||
builder.activate(ruleKey); | |||
builder.create(ruleKey).activate(); | |||
CheckFactory checkFactory = new CheckFactory(builder.build()); | |||
Checks checks = checkFactory.create("squid").addAnnotatedChecks(CheckWithoutProperties.class); | |||
@@ -60,7 +60,7 @@ public class CheckFactoryTest { | |||
@Test | |||
public void param_as_string_field() { | |||
RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithStringProperty"); | |||
builder.activate(ruleKey).setParam("pattern", "foo"); | |||
builder.create(ruleKey).setParam("pattern", "foo").activate(); | |||
CheckFactory checkFactory = new CheckFactory(builder.build()); | |||
Checks checks = checkFactory.create("squid").addAnnotatedChecks(CheckWithStringProperty.class); | |||
@@ -77,7 +77,7 @@ public class CheckFactoryTest { | |||
thrown.expectMessage("The field 'unknown' does not exist or is not annotated with @RuleProperty in the class org.sonar.api.batch.rule.CheckWithStringProperty"); | |||
RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithStringProperty"); | |||
builder.activate(ruleKey).setParam("unknown", "foo"); | |||
builder.create(ruleKey).setParam("unknown", "foo").activate(); | |||
CheckFactory checkFactory = new CheckFactory(builder.build()); | |||
checkFactory.create("squid").addAnnotatedChecks(CheckWithStringProperty.class); | |||
@@ -86,7 +86,7 @@ public class CheckFactoryTest { | |||
@Test | |||
public void param_as_primitive_fields() { | |||
RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithPrimitiveProperties"); | |||
builder.activate(ruleKey).setParam("max", "300").setParam("ignore", "true"); | |||
builder.create(ruleKey).setParam("max", "300").setParam("ignore", "true").activate(); | |||
CheckFactory checkFactory = new CheckFactory(builder.build()); | |||
Checks checks = checkFactory.create("squid").addAnnotatedChecks(CheckWithPrimitiveProperties.class); | |||
@@ -103,7 +103,7 @@ public class CheckFactoryTest { | |||
@Test | |||
public void param_as_inherited_field() { | |||
RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithPrimitiveProperties"); | |||
builder.activate(ruleKey).setParam("max", "300"); | |||
builder.create(ruleKey).setParam("max", "300").activate(); | |||
CheckFactory checkFactory = new CheckFactory(builder.build()); | |||
Checks checks = checkFactory.create("squid").addAnnotatedChecks(CheckWithPrimitiveProperties.class); | |||
@@ -116,7 +116,7 @@ public class CheckFactoryTest { | |||
@Test | |||
public void use_engine_key() { | |||
RuleKey ruleKey = RuleKey.of("squid", "One"); | |||
builder.activate(ruleKey).setInternalKey("S0001"); | |||
builder.create(ruleKey).setInternalKey("S0001").activate(); | |||
CheckFactory checkFactory = new CheckFactory(builder.build()); | |||
Checks checks = checkFactory.create("squid").addAnnotatedChecks(CheckWithKey.class); | |||
@@ -133,7 +133,7 @@ public class CheckFactoryTest { | |||
thrown.expect(SonarException.class); | |||
RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithUnsupportedPropertyType"); | |||
builder.activate(ruleKey).setParam("max", "300"); | |||
builder.create(ruleKey).setParam("max", "300").activate(); | |||
CheckFactory checkFactory = new CheckFactory(builder.build()); | |||
checkFactory.create("squid").addAnnotatedChecks(CheckWithUnsupportedPropertyType.class); | |||
@@ -142,7 +142,7 @@ public class CheckFactoryTest { | |||
@Test | |||
public void override_field_key() { | |||
RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithOverriddenPropertyKey"); | |||
builder.activate(ruleKey).setParam("maximum", "300"); | |||
builder.create(ruleKey).setParam("maximum", "300").activate(); | |||
CheckFactory checkFactory = new CheckFactory(builder.build()); | |||
Checks checks = checkFactory.create("squid").addAnnotatedChecks(CheckWithOverriddenPropertyKey.class); | |||
@@ -158,7 +158,7 @@ public class CheckFactoryTest { | |||
@Test | |||
public void checks_as_objects() { | |||
RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithStringProperty"); | |||
builder.activate(ruleKey).setParam("pattern", "foo"); | |||
builder.create(ruleKey).setParam("pattern", "foo").activate(); | |||
CheckFactory checkFactory = new CheckFactory(builder.build()); | |||
CheckWithStringProperty check = new CheckWithStringProperty(); |
@@ -39,14 +39,14 @@ public class ActiveRulesBuilderTest { | |||
@Test | |||
public void build_rules() throws Exception { | |||
ActiveRules activeRules = new ActiveRulesBuilder() | |||
.activate(RuleKey.of("squid", "S0001")) | |||
.create(RuleKey.of("squid", "S0001")) | |||
.setSeverity(Severity.CRITICAL) | |||
.setInternalKey("__S0001__") | |||
.setParam("min", "20") | |||
.end() | |||
.activate() | |||
// most simple rule | |||
.activate(RuleKey.of("squid", "S0002")).end() | |||
.activate(RuleKey.of("findbugs", "NPE")).setInternalKey(null).setSeverity(null).setParam("foo", null).end() | |||
.create(RuleKey.of("squid", "S0002")).activate() | |||
.create(RuleKey.of("findbugs", "NPE")).setInternalKey(null).setSeverity(null).setParam("foo", null).activate() | |||
.build(); | |||
assertThat(activeRules.findAll()).hasSize(3); | |||
@@ -77,9 +77,9 @@ public class ActiveRulesBuilderTest { | |||
@Test | |||
public void fail_to_add_twice_the_same_rule() throws Exception { | |||
ActiveRulesBuilder builder = new ActiveRulesBuilder(); | |||
builder.activate(RuleKey.of("squid", "S0001")); | |||
builder.create(RuleKey.of("squid", "S0001")).activate(); | |||
try { | |||
builder.activate(RuleKey.of("squid", "S0001")); | |||
builder.create(RuleKey.of("squid", "S0001")).activate(); | |||
fail(); | |||
} catch (IllegalStateException e) { | |||
assertThat(e).hasMessage("Rule 'squid:S0001' is already activated"); |