aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-batch
diff options
context:
space:
mode:
authorJulien HENRY <julien.henry@sonarsource.com>2014-06-19 17:10:01 +0200
committerJulien HENRY <julien.henry@sonarsource.com>2014-06-19 17:12:09 +0200
commit3bd4ce6a6c0010e272b55656ae62cc2f0f20b9f7 (patch)
tree7385c15b6edf1f3c768ca22368b116fe6af6517a /sonar-batch
parentba05cbc35820e64d70642615525377c9aa1b3069 (diff)
downloadsonarqube-3bd4ce6a6c0010e272b55656ae62cc2f0f20b9f7.tar.gz
sonarqube-3bd4ce6a6c0010e272b55656ae62cc2f0f20b9f7.zip
SONAR-5389 Add medium tests
Diffstat (limited to 'sonar-batch')
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java6
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerIssueCache.java52
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultAnalyzerContext.java30
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java54
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java24
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/AnalyzerMediumTester.java417
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/XooMediumTest.java113
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/XooPlugin.java43
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/base/Xoo.java40
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/base/XooConstants.java36
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/MeasureAnalyzer.java138
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssuePerLineAnalyzer.java71
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/scan2/AnalyzerOptimizerTest.java12
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/sonar-project.properties6
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/testx/ClassOneTest.xoo11
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/testx/ClassOneTest.xoo.measures7
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/testx/ClassOneTest.xoo.scm11
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/xources/hello/HelloJava.xoo8
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/xources/hello/HelloJava.xoo.measures3
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/xources/hello/HelloJava.xoo.scm8
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/xources/hello/helloscala.xoo6
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/xources/hello/helloscala.xoo.measures3
22 files changed, 1009 insertions, 90 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java b/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java
index 49f762ef811..ca90e38d01c 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java
@@ -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();
}
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerIssueCache.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerIssueCache.java
new file mode 100644
index 00000000000..613507de041
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerIssueCache.java
@@ -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;
+ }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultAnalyzerContext.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultAnalyzerContext.java
index 1ec141ada84..d9f03c17e50 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultAnalyzerContext.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultAnalyzerContext.java
@@ -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);
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java
index f5dd041678e..8dad42fd7e6 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java
@@ -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);
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
index bacfe55be16..ef507f38001 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
@@ -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()
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/AnalyzerMediumTester.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/AnalyzerMediumTester.java
new file mode 100644
index 00000000000..c8dc898b823
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/AnalyzerMediumTester.java
@@ -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();
+ }
+
+ }
+
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/XooMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/XooMediumTest.java
new file mode 100644
index 00000000000..73c2b563639
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/XooMediumTest.java
@@ -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();
+ }
+
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/XooPlugin.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/XooPlugin.java
new file mode 100644
index 00000000000..a93cdbf6b22
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/XooPlugin.java
@@ -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
+ );
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/base/Xoo.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/base/Xoo.java
new file mode 100644
index 00000000000..1922fd7aa35
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/base/Xoo.java
@@ -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;
+ }
+
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/base/XooConstants.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/base/XooConstants.java
new file mode 100644
index 00000000000..c59091fb0f6
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/base/XooConstants.java
@@ -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");
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/MeasureAnalyzer.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/MeasureAnalyzer.java
new file mode 100644
index 00000000000..8da7690333f
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/MeasureAnalyzer.java
@@ -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);
+ }
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssuePerLineAnalyzer.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssuePerLineAnalyzer.java
new file mode 100644
index 00000000000..522ad3b6b53
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssuePerLineAnalyzer.java
@@ -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());
+ }
+ }
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan2/AnalyzerOptimizerTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan2/AnalyzerOptimizerTest.java
index 1384950b99d..8940c3dd2fc 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/scan2/AnalyzerOptimizerTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/scan2/AnalyzerOptimizerTest.java
@@ -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();
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/sonar-project.properties b/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/sonar-project.properties
new file mode 100644
index 00000000000..8810e376701
--- /dev/null
+++ b/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/sonar-project.properties
@@ -0,0 +1,6 @@
+sonar.projectKey=sample
+sonar.projectName=Sample
+sonar.projectVersion=0.1-SNAPSHOT
+sonar.sources=xources
+sonar.tests=testx
+sonar.language=xoo
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/testx/ClassOneTest.xoo b/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/testx/ClassOneTest.xoo
new file mode 100644
index 00000000000..8c0967e496f
--- /dev/null
+++ b/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/testx/ClassOneTest.xoo
@@ -0,0 +1,11 @@
+package org.sonar.tests;
+
+import org.junit.Test;
+
+public class ClassOneTest {
+
+ @Test
+ public void nothing() {
+
+ }
+}
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/testx/ClassOneTest.xoo.measures b/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/testx/ClassOneTest.xoo.measures
new file mode 100644
index 00000000000..23b08dc0e0e
--- /dev/null
+++ b/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/testx/ClassOneTest.xoo.measures
@@ -0,0 +1,7 @@
+lines:11
+ncloc:7
+tests:1
+test_execution_time:1
+skipped_tests:0
+test_errors:0
+test_failures:0
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/testx/ClassOneTest.xoo.scm b/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/testx/ClassOneTest.xoo.scm
new file mode 100644
index 00000000000..2cec35b8a72
--- /dev/null
+++ b/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/testx/ClassOneTest.xoo.scm
@@ -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 \ No newline at end of file
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/xources/hello/HelloJava.xoo b/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/xources/hello/HelloJava.xoo
new file mode 100644
index 00000000000..1d9c60d56b7
--- /dev/null
+++ b/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/xources/hello/HelloJava.xoo
@@ -0,0 +1,8 @@
+package hello;
+
+public class HelloJava {
+
+ public static void main(String[] args) {
+ System.out.println("Hello");
+ }
+} \ No newline at end of file
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/xources/hello/HelloJava.xoo.measures b/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/xources/hello/HelloJava.xoo.measures
new file mode 100644
index 00000000000..388d08b58a8
--- /dev/null
+++ b/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/xources/hello/HelloJava.xoo.measures
@@ -0,0 +1,3 @@
+lines:8
+ncloc:3
+complexity:1
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/xources/hello/HelloJava.xoo.scm b/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/xources/hello/HelloJava.xoo.scm
new file mode 100644
index 00000000000..03a9de2f486
--- /dev/null
+++ b/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/xources/hello/HelloJava.xoo.scm
@@ -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 \ No newline at end of file
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/xources/hello/helloscala.xoo b/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/xources/hello/helloscala.xoo
new file mode 100644
index 00000000000..53cb085156c
--- /dev/null
+++ b/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/xources/hello/helloscala.xoo
@@ -0,0 +1,6 @@
+ object HelloWorld {
+ def main(args: Array[String]) {
+ println("Hello, world of xoo!")
+ }
+ }
+ \ No newline at end of file
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/xources/hello/helloscala.xoo.measures b/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/xources/hello/helloscala.xoo.measures
new file mode 100644
index 00000000000..c47948fc955
--- /dev/null
+++ b/sonar-batch/src/test/resources/org/sonar/batch/mediumtest/xoo/sample/xources/hello/helloscala.xoo.measures
@@ -0,0 +1,3 @@
+lines:5
+ncloc:5
+complexity:2