aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/description.html.erb2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/batch/ProjectReferentialsAction.java41
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/qualitygate/RegisterQualityGates.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileFactory.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java40
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapping.java43
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/search/ws/BaseMapping.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/batch/BatchWsTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/batch/ProjectReferentialsActionTest.java44
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/rule/RuleUpdaterMediumTest.java52
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexMediumTest.java95
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/rule/ws/RulesWebServiceMediumTest.java112
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_active_rules.json25
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rule.json14
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_and_overridden_debt_values.json18
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_linear_offset_and_overridden_constant_debt.json17
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_linear_offset_and_overridden_linear_debt.json17
-rw-r--r--server/sonar-web/Gruntfile.coffee18
-rw-r--r--server/sonar-web/pom.xml1
-rw-r--r--server/sonar-web/src/main/coffee/component-viewer/app.coffee1
-rw-r--r--server/sonar-web/src/main/coffee/component-viewer/header.coffee14
-rw-r--r--server/sonar-web/src/main/coffee/component-viewer/header/basic-header.coffee2
-rw-r--r--server/sonar-web/src/main/coffee/component-viewer/header/coverage-header.coffee12
-rw-r--r--server/sonar-web/src/main/coffee/component-viewer/header/duplications-header.coffee3
-rw-r--r--server/sonar-web/src/main/coffee/component-viewer/header/issues-header.coffee12
-rw-r--r--server/sonar-web/src/main/coffee/component-viewer/header/scm-header.coffee9
-rw-r--r--server/sonar-web/src/main/coffee/component-viewer/main.coffee7
-rw-r--r--server/sonar-web/src/main/coffee/component-viewer/mixins/main-coverage.coffee3
-rw-r--r--server/sonar-web/src/main/coffee/component-viewer/mixins/main-issues.coffee8
-rw-r--r--server/sonar-web/src/main/coffee/component-viewer/mockjax.coffee27
-rw-r--r--server/sonar-web/src/main/coffee/dashboard/file-app.coffee1
-rw-r--r--server/sonar-web/src/main/coffee/drilldown/app.coffee1
-rw-r--r--server/sonar-web/src/main/coffee/drilldown/conf.coffee28
-rw-r--r--server/sonar-web/src/main/hbs/coding-rules/coding-rules-detail.hbs25
-rw-r--r--server/sonar-web/src/main/hbs/component-viewer/cw-source.hbs2
-rw-r--r--server/sonar-web/src/main/hbs/component-viewer/cw-workspace.hbs14
-rw-r--r--server/sonar-web/src/main/js/navigator/filters/choice-filters.js2
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/lib.js23
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-spec.js29
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-spec/show_x1.json13
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec.js455
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/cross-project-duplications.json33
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/duplications-in-deleted-files.json24
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/resources.json154
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/test-cases.json235
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/tests/app.json4
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/tests/covered-files.json7
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/tests/resources.json25
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/tests/source.json24
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/tests/tests.json14
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/views/layouts/main.jade3
-rw-r--r--server/sonar-web/src/main/less/component-viewer.less1
-rw-r--r--server/sonar-web/src/main/less/style.less2
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb4
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/project/history.html.erb96
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/highlighting/DefaultHighlightingBuilder.java52
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingData.java (renamed from sonar-batch/src/main/java/org/sonar/batch/source/SyntaxHighlightingData.java)10
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilder.java (renamed from sonar-batch/src/main/java/org/sonar/batch/source/SyntaxHighlightingDataBuilder.java)9
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingRule.java (renamed from sonar-batch/src/main/java/org/sonar/batch/source/SyntaxHighlightingRule.java)2
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/highlighting/package-info.java23
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/ComponentDataCache.java4
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/Data.java2
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/StringData.java5
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java69
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java20
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan2/AnalysisPublisher.java11
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerOptimizer.java20
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java19
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java3
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan2/SensorsExecutor.java8
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/source/DefaultHighlightable.java19
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolTable.java51
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolizable.java5
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/source/HighlightableBuilder.java3
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/symbol/DefaultSymbolTableBuilder.java90
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/symbol/SymbolData.java (renamed from sonar-batch/src/main/java/org/sonar/batch/source/SymbolData.java)25
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/highlighting/DefaultHighlightingBuilderTest.java50
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilderTest.java (renamed from sonar-batch/src/test/java/org/sonar/batch/source/SyntaxHighlightingDataBuilderTest.java)3
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/highlighting/SyntaxHighlightingDataTest.java (renamed from sonar-batch/src/test/java/org/sonar/batch/source/SyntaxHighlightingDataTest.java)4
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/index/ComponentDataCacheTest.java4
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java92
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java87
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/XooPlugin.java4
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/MeasureSensor.java2
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/SymbolReferencesSensor.java98
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/SyntaxHighlightingSensor.java95
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/scan/SensorContextAdapterTest.java4
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/source/DefaultHighlightableTest.java13
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolTableTest.java8
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/source/SymbolDataTest.java60
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/symbol/DefaultSymbolTableBuilderTest.java108
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties6
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java18
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/highlighting/HighlightingBuilder.java78
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/highlighting/package-info.java21
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/symbol/Symbol.java33
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/symbol/SymbolTableBuilder.java48
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/symbol/internal/DefaultSymbol.java (renamed from sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbol.java)20
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/symbol/internal/package-info.java21
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/symbol/package-info.java21
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java4
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/source/Highlightable.java3
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/source/Symbol.java10
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/source/Symbolizable.java3
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_and_overridden_debt_values.json18
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_linear_offset_and_overridden_constant_debt.json17
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_linear_offset_and_overridden_linear_debt.json17
107 files changed, 2864 insertions, 422 deletions
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/description.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/description.html.erb
index 3e9a2b3a64a..6d587793407 100644
--- a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/description.html.erb
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/description.html.erb
@@ -45,7 +45,7 @@
end %>
<%
- unless @resource.qualifier == 'DEV' or @resource.qualifier == 'SVW'
+ unless @resource.qualifier == 'DEV' or @resource.qualifier == 'VW' or @resource.qualifier == 'SVW'
using_default=false
quality_gate=Property.value('sonar.qualitygate', @resource && @resource.id, nil)
unless quality_gate
diff --git a/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectReferentialsAction.java b/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectReferentialsAction.java
index 8b4a9f23054..ba9163afecd 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectReferentialsAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectReferentialsAction.java
@@ -28,7 +28,6 @@ import org.sonar.api.server.ws.RequestHandler;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.batch.protocol.input.ProjectReferentials;
-import org.sonar.batch.protocol.input.QProfile;
import org.sonar.core.UtcDateUtils;
import org.sonar.core.component.ComponentDto;
import org.sonar.core.permission.GlobalPermissions;
@@ -39,7 +38,11 @@ import org.sonar.core.properties.PropertyDto;
import org.sonar.core.qualityprofile.db.QualityProfileDto;
import org.sonar.server.db.DbClient;
import org.sonar.server.plugins.MimeTypes;
+import org.sonar.server.qualityprofile.ActiveRule;
import org.sonar.server.qualityprofile.QProfileFactory;
+import org.sonar.server.qualityprofile.QProfileLoader;
+import org.sonar.server.rule.Rule;
+import org.sonar.server.rule.RuleService;
import org.sonar.server.user.UserSession;
import java.util.List;
@@ -54,12 +57,17 @@ public class ProjectReferentialsAction implements RequestHandler {
private final DbClient dbClient;
private final PropertiesDao propertiesDao;
private final QProfileFactory qProfileFactory;
+ private final QProfileLoader qProfileLoader;
+ private final RuleService ruleService;
private final Languages languages;
- public ProjectReferentialsAction(DbClient dbClient, PropertiesDao propertiesDao, QProfileFactory qProfileFactory, Languages languages) {
+ public ProjectReferentialsAction(DbClient dbClient, PropertiesDao propertiesDao, QProfileFactory qProfileFactory, QProfileLoader qProfileLoader,
+ RuleService ruleService, Languages languages) {
this.dbClient = dbClient;
this.propertiesDao = propertiesDao;
this.qProfileFactory = qProfileFactory;
+ this.qProfileLoader = qProfileLoader;
+ this.ruleService = ruleService;
this.languages = languages;
}
@@ -88,6 +96,7 @@ public class ProjectReferentialsAction implements RequestHandler {
String projectKey = request.mandatoryParam(PARAM_KEY);
addSettings(ref, projectKey, hasScanPerm, session);
addProfiles(ref, projectKey, session);
+ addActiveRules(ref);
response.stream().setMediaType(MimeTypes.JSON);
IOUtils.write(ref.toJson(), response.stream().output());
@@ -127,9 +136,31 @@ public class ProjectReferentialsAction implements RequestHandler {
QualityProfileDto qualityProfileDto = qProfileFactory.getByProjectAndLanguage(session, projectKey, languageKey);
qualityProfileDto = qualityProfileDto != null ? qualityProfileDto : qProfileFactory.getDefault(session, languageKey);
if (qualityProfileDto != null) {
- QProfile profile = new QProfile(qualityProfileDto.getKey(), qualityProfileDto.getName(), qualityProfileDto.getLanguage(),
- UtcDateUtils.parseDateTime(qualityProfileDto.getRulesUpdatedAt()));
- ref.addQProfile(profile);
+ ref.addQProfile(new org.sonar.batch.protocol.input.QProfile(
+ qualityProfileDto.getKey(),
+ qualityProfileDto.getName(),
+ qualityProfileDto.getLanguage(),
+ UtcDateUtils.parseDateTime(qualityProfileDto.getRulesUpdatedAt())));
+ }
+ }
+ }
+
+ private void addActiveRules(ProjectReferentials ref) {
+ for (org.sonar.batch.protocol.input.QProfile qProfile : ref.qProfiles()) {
+ for (ActiveRule activeRule : qProfileLoader.findActiveRulesByProfile(qProfile.key())) {
+ Rule rule = ruleService.getByKey(activeRule.key().ruleKey());
+ org.sonar.batch.protocol.input.ActiveRule inputActiveRule = new org.sonar.batch.protocol.input.ActiveRule(
+ activeRule.key().ruleKey().repository(),
+ activeRule.key().ruleKey().rule(),
+ rule.name(),
+ activeRule.severity(),
+ rule.internalKey(),
+ qProfile.language()
+ );
+ for (Map.Entry<String, String> entry : activeRule.params().entrySet()) {
+ inputActiveRule.addParam(entry.getKey(), entry.getValue());
+ }
+ ref.addActiveRule(inputActiveRule);
}
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/RegisterQualityGates.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/RegisterQualityGates.java
index fecade74cd9..474d7f42f47 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/RegisterQualityGates.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/RegisterQualityGates.java
@@ -58,7 +58,7 @@ public class RegisterQualityGates implements Startable {
private void createBuiltinQualityGate() {
QualityGateDto builtin = qualityGates.create(BUILTIN_QUALITY_GATE);
qualityGates.createCondition(builtin.getId(), CoreMetrics.BLOCKER_VIOLATIONS_KEY, QualityGateConditionDto.OPERATOR_GREATER_THAN, null, "0", null);
- qualityGates.createCondition(builtin.getId(), CoreMetrics.CRITICAL_VIOLATIONS_KEY, QualityGateConditionDto.OPERATOR_GREATER_THAN, null, "0", null);
+ qualityGates.createCondition(builtin.getId(), CoreMetrics.CRITICAL_VIOLATIONS_KEY, QualityGateConditionDto.OPERATOR_GREATER_THAN, null, "0", 3);
qualityGates.createCondition(builtin.getId(), CoreMetrics.TEST_ERRORS_KEY, QualityGateConditionDto.OPERATOR_GREATER_THAN, null, "0", null);
qualityGates.createCondition(builtin.getId(), CoreMetrics.TEST_FAILURES_KEY, QualityGateConditionDto.OPERATOR_GREATER_THAN, null, "0", null);
qualityGates.createCondition(builtin.getId(), CoreMetrics.NEW_COVERAGE_KEY, QualityGateConditionDto.OPERATOR_LESS_THAN, null, "80", 3);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileFactory.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileFactory.java
index 0e7b1042e12..03af506f7b5 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileFactory.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileFactory.java
@@ -63,7 +63,7 @@ public class QProfileFactory implements ServerComponent {
return profile;
}
- QualityProfileDto create(DbSession dbSession, QProfileName name) {
+ public QualityProfileDto create(DbSession dbSession, QProfileName name) {
QualityProfileDto dto = db.qualityProfileDao().getByNameAndLanguage(name.getName(), name.getLanguage(), dbSession);
if (dto != null) {
throw new BadRequestException("Quality profile already exists: " + name);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java
index 6ce3e100b32..ae6bf7a2154 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java
@@ -25,9 +25,15 @@ import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.unit.TimeValue;
-import org.elasticsearch.index.query.*;
+import org.elasticsearch.index.query.BoolFilterBuilder;
+import org.elasticsearch.index.query.BoolQueryBuilder;
+import org.elasticsearch.index.query.FilterBuilder;
+import org.elasticsearch.index.query.FilterBuilders;
+import org.elasticsearch.index.query.MatchQueryBuilder;
+import org.elasticsearch.index.query.QueryBuilder;
+import org.elasticsearch.index.query.QueryBuilders;
+import org.elasticsearch.index.query.SimpleQueryStringBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
@@ -43,11 +49,22 @@ import org.sonar.core.profiling.StopWatch;
import org.sonar.core.rule.RuleDto;
import org.sonar.server.qualityprofile.index.ActiveRuleNormalizer;
import org.sonar.server.rule.Rule;
-import org.sonar.server.search.*;
+import org.sonar.server.search.BaseIndex;
+import org.sonar.server.search.ESNode;
+import org.sonar.server.search.IndexDefinition;
+import org.sonar.server.search.IndexField;
+import org.sonar.server.search.QueryOptions;
+import org.sonar.server.search.Result;
import javax.annotation.CheckForNull;
import java.io.IOException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import static com.google.common.collect.Lists.newArrayList;
@@ -166,16 +183,11 @@ public class RuleIndex extends BaseIndex<Rule, RuleDto, RuleKey> {
String queryString = query.getQueryText();
// Human readable type of querying
- qb.should(QueryBuilders.queryString(query.getQueryText())
- .field(RuleNormalizer.RuleField.NAME.field() + "." + IndexField.SEARCH_WORDS_SUFFIX, 20f)
- .field(RuleNormalizer.RuleField.HTML_DESCRIPTION.field() + "." + IndexField.SEARCH_WORDS_SUFFIX, 3f)
- .enablePositionIncrements(true)
- .defaultOperator(QueryStringQueryBuilder.Operator.AND)
- .fuzziness(Fuzziness.ONE)
- .autoGeneratePhraseQueries(true)
- .lenient(false)
- .useDisMax(true)
- .boost(20f));
+ qb.should(QueryBuilders.simpleQueryString(query.getQueryText())
+ .field(RuleNormalizer.RuleField.NAME.field() + "." + IndexField.SEARCH_WORDS_SUFFIX, 20f)
+ .field(RuleNormalizer.RuleField.HTML_DESCRIPTION.field() + "." + IndexField.SEARCH_WORDS_SUFFIX, 3f)
+ .defaultOperator(SimpleQueryStringBuilder.Operator.AND)
+ ).boost(20f);
// Match and partial Match queries
qb.should(this.termQuery(RuleNormalizer.RuleField.KEY, queryString, 15f));
diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapping.java b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapping.java
index b118a340140..dba6cd180a7 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapping.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapping.java
@@ -105,23 +105,44 @@ public class RuleMapping extends BaseMapping<RuleDoc, RuleMappingContext> {
private void mapDebtFields() {
map("defaultDebtChar", new IndexStringMapper("defaultDebtChar", RuleNormalizer.RuleField.DEFAULT_CHARACTERISTIC.field()));
map("defaultDebtSubChar", new IndexStringMapper("defaultDebtSubChar", RuleNormalizer.RuleField.DEFAULT_SUB_CHARACTERISTIC.field()));
+
map("debtChar", new IndexStringMapper("debtChar", RuleNormalizer.RuleField.CHARACTERISTIC.field(),
RuleNormalizer.RuleField.DEFAULT_CHARACTERISTIC.field()));
map("debtSubChar", new IndexStringMapper("debtSubChar", RuleNormalizer.RuleField.SUB_CHARACTERISTIC.field(),
RuleNormalizer.RuleField.DEFAULT_SUB_CHARACTERISTIC.field()));
+
map("debtCharName", new CharacteristicNameMapper());
map("debtSubCharName", new SubCharacteristicNameMapper());
- map("debtRemFn", new IndexStringMapper("debtRemFnType", RuleNormalizer.RuleField.DEBT_FUNCTION_TYPE.field(),
- RuleNormalizer.RuleField.DEFAULT_DEBT_FUNCTION_TYPE.field()));
- map("debtRemFn", new IndexStringMapper("debtRemFnCoeff", RuleNormalizer.RuleField.DEBT_FUNCTION_COEFFICIENT.field(),
- RuleNormalizer.RuleField.DEFAULT_DEBT_FUNCTION_COEFFICIENT.field()));
- map("debtRemFn", new IndexStringMapper("debtRemFnOffset", RuleNormalizer.RuleField.DEBT_FUNCTION_OFFSET.field(),
- RuleNormalizer.RuleField.DEFAULT_DEBT_FUNCTION_OFFSET.field()));
+
map("defaultDebtRemFn", new IndexStringMapper("defaultDebtRemFnType", RuleNormalizer.RuleField.DEFAULT_DEBT_FUNCTION_TYPE.field()));
map("defaultDebtRemFn", new IndexStringMapper("defaultDebtRemFnCoeff", RuleNormalizer.RuleField.DEFAULT_DEBT_FUNCTION_COEFFICIENT.field()));
map("defaultDebtRemFn", new IndexStringMapper("defaultDebtRemFnOffset", RuleNormalizer.RuleField.DEFAULT_DEBT_FUNCTION_OFFSET.field()));
map("effortToFixDescription", RuleNormalizer.RuleField.FIX_DESCRIPTION.field());
map("debtOverloaded", new OverriddenMapper());
+
+ map("debtRemFn", new EffectiveDebtRemFn("debtRemFnType", RuleNormalizer.RuleField.DEBT_FUNCTION_TYPE.field(),
+ RuleNormalizer.RuleField.DEFAULT_DEBT_FUNCTION_TYPE.field()));
+ map("debtRemFn", new EffectiveDebtRemFn("debtRemFnCoeff", RuleNormalizer.RuleField.DEBT_FUNCTION_COEFFICIENT.field(),
+ RuleNormalizer.RuleField.DEFAULT_DEBT_FUNCTION_COEFFICIENT.field()));
+ map("debtRemFn", new EffectiveDebtRemFn("debtRemFnOffset", RuleNormalizer.RuleField.DEBT_FUNCTION_OFFSET.field(),
+ RuleNormalizer.RuleField.DEFAULT_DEBT_FUNCTION_OFFSET.field()));
+ }
+
+ public static class EffectiveDebtRemFn extends IndexStringMapper<RuleDoc,RuleMappingContext> {
+
+ public EffectiveDebtRemFn(String key, String indexKey, String defaultIndexKey) {
+ super(key, indexKey, defaultIndexKey);
+ }
+
+ @Override
+ public void write(JsonWriter json, RuleDoc doc, RuleMappingContext context) {
+ if(doc.debtOverloaded()){
+ Object val = doc.getNullableField(indexFields[0]);
+ json.prop(key, val != null ? val.toString() : null);
+ } else {
+ super.write(json,doc,context);
+ }
+ }
}
private void mapParamFields() {
@@ -144,14 +165,12 @@ public class RuleMapping extends BaseMapping<RuleDoc, RuleMappingContext> {
public void write(Rule rule, JsonWriter json, @Nullable SearchOptions options) {
RuleMappingContext context = new RuleMappingContext();
- String characteristicKey;
- if (needDebtCharacteristicNames(options) && (characteristicKey = rule.debtCharacteristicKey()) != null) {
+ if (needDebtCharacteristicNames(options) && rule.debtCharacteristicKey() != null) {
// load debt characteristics if requested
- context.add(debtModel.characteristicByKey(characteristicKey));
+ context.add(debtModel.characteristicByKey(rule.debtCharacteristicKey()));
}
- String subCharacteristicKey;
- if (needDebtSubCharacteristicNames(options) && (subCharacteristicKey = rule.debtSubCharacteristicKey()) != null) {
- context.add(debtModel.characteristicByKey(subCharacteristicKey));
+ if (needDebtSubCharacteristicNames(options) && rule.debtSubCharacteristicKey() != null) {
+ context.add(debtModel.characteristicByKey(rule.debtSubCharacteristicKey()));
}
doWrite((RuleDoc) rule, context, json, options);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/ws/BaseMapping.java b/server/sonar-server/src/main/java/org/sonar/server/search/ws/BaseMapping.java
index 69a73937c6a..8a4096a47a8 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/search/ws/BaseMapping.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/search/ws/BaseMapping.java
@@ -133,7 +133,7 @@ public abstract class BaseMapping<DOC extends BaseDoc, CTX> implements ServerCom
* String field
*/
public static class IndexStringMapper<DOC extends BaseDoc, CTX> extends IndexMapper<DOC,CTX> {
- private final String key;
+ protected final String key;
public IndexStringMapper(String key, String indexKey, String defaultIndexKey) {
super(indexKey, defaultIndexKey);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/batch/BatchWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/batch/BatchWsTest.java
index 50a7c340705..1a497bb014a 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/batch/BatchWsTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/batch/BatchWsTest.java
@@ -32,6 +32,8 @@ import org.sonar.api.resources.Languages;
import org.sonar.core.properties.PropertiesDao;
import org.sonar.server.db.DbClient;
import org.sonar.server.qualityprofile.QProfileFactory;
+import org.sonar.server.qualityprofile.QProfileLoader;
+import org.sonar.server.rule.RuleService;
import org.sonar.server.ws.WsTester;
import java.io.File;
@@ -59,7 +61,7 @@ public class BatchWsTest {
public void before() throws IOException {
tester = new WsTester(new BatchWs(batchIndex,
new GlobalReferentialsAction(mock(DbClient.class), mock(PropertiesDao.class)),
- new ProjectReferentialsAction(mock(DbClient.class), mock(PropertiesDao.class), mock(QProfileFactory.class), mock(Languages.class))));
+ new ProjectReferentialsAction(mock(DbClient.class), mock(PropertiesDao.class), mock(QProfileFactory.class), mock(QProfileLoader.class), mock(RuleService.class), mock(Languages.class))));
}
@Test
diff --git a/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectReferentialsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectReferentialsActionTest.java
index a04fcc58cf6..1c5a6aa7bb5 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectReferentialsActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectReferentialsActionTest.java
@@ -20,6 +20,7 @@
package org.sonar.server.batch;
+import com.google.common.collect.ImmutableMap;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -27,15 +28,22 @@ import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.sonar.api.resources.Language;
import org.sonar.api.resources.Languages;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.Severity;
import org.sonar.core.component.ComponentDto;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.properties.PropertiesDao;
import org.sonar.core.properties.PropertyDto;
+import org.sonar.core.qualityprofile.db.ActiveRuleKey;
import org.sonar.core.qualityprofile.db.QualityProfileDto;
import org.sonar.server.component.persistence.ComponentDao;
import org.sonar.server.db.DbClient;
+import org.sonar.server.qualityprofile.ActiveRule;
import org.sonar.server.qualityprofile.QProfileFactory;
+import org.sonar.server.qualityprofile.QProfileLoader;
+import org.sonar.server.rule.Rule;
+import org.sonar.server.rule.RuleService;
import org.sonar.server.user.MockUserSession;
import org.sonar.server.ws.WsTester;
@@ -59,6 +67,12 @@ public class ProjectReferentialsActionTest {
QProfileFactory qProfileFactory;
@Mock
+ QProfileLoader qProfileLoader;
+
+ @Mock
+ RuleService ruleService;
+
+ @Mock
Languages languages;
@Mock
@@ -75,7 +89,8 @@ public class ProjectReferentialsActionTest {
when(language.getKey()).thenReturn("java");
when(languages.all()).thenReturn(new Language[]{language});
- tester = new WsTester(new BatchWs(mock(BatchIndex.class), mock(GlobalReferentialsAction.class), new ProjectReferentialsAction(dbClient, propertiesDao, qProfileFactory, languages)));
+ tester = new WsTester(new BatchWs(mock(BatchIndex.class), mock(GlobalReferentialsAction.class),
+ new ProjectReferentialsAction(dbClient, propertiesDao, qProfileFactory, qProfileLoader, ruleService, languages)));
}
@Test
@@ -126,7 +141,6 @@ public class ProjectReferentialsActionTest {
@Test
public void return_quality_profiles() throws Exception {
MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION, GlobalPermissions.DRY_RUN_EXECUTION);
-
String projectKey = "org.codehaus.sonar:sonar";
when(qProfileFactory.getByProjectAndLanguage(session, projectKey, "java")).thenReturn(
@@ -140,7 +154,6 @@ public class ProjectReferentialsActionTest {
@Test
public void return_quality_profile_from_default_profile() throws Exception {
MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION, GlobalPermissions.DRY_RUN_EXECUTION);
-
String projectKey = "org.codehaus.sonar:sonar";
when(qProfileFactory.getDefault(session, "java")).thenReturn(
@@ -151,4 +164,29 @@ public class ProjectReferentialsActionTest {
request.execute().assertJson(getClass(), "return_quality_profile_from_default_profile.json");
}
+ @Test
+ public void return_active_rules() throws Exception {
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION, GlobalPermissions.DRY_RUN_EXECUTION);
+ String projectKey = "org.codehaus.sonar:sonar";
+
+ when(qProfileFactory.getByProjectAndLanguage(session, projectKey, "java")).thenReturn(
+ QualityProfileDto.createFor("abcd").setName("Default").setLanguage("java").setRulesUpdatedAt("2014-01-14T14:00:00+0200")
+ );
+
+ RuleKey ruleKey = RuleKey.of("squid", "AvoidCycle");
+ ActiveRule activeRule = mock(ActiveRule.class);
+ when(activeRule.key()).thenReturn(ActiveRuleKey.of("abcd", ruleKey));
+ when(activeRule.severity()).thenReturn(Severity.MINOR);
+ when(activeRule.params()).thenReturn(ImmutableMap.of("max", "2"));
+ when(qProfileLoader.findActiveRulesByProfile("abcd")).thenReturn(newArrayList(activeRule));
+
+ Rule rule = mock(Rule.class);
+ when(rule.name()).thenReturn("Avoid Cycle");
+ when(rule.internalKey()).thenReturn("squid-1");
+ when(ruleService.getByKey(ruleKey)).thenReturn(rule);
+
+ WsTester.TestRequest request = tester.newGetRequest("batch", "project").setParam("key", projectKey);
+ request.execute().assertJson(getClass(), "return_active_rules.json");
+ }
+
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/RuleUpdaterMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/RuleUpdaterMediumTest.java
index 35eabc87859..18fb0f0f6f7 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/rule/RuleUpdaterMediumTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/rule/RuleUpdaterMediumTest.java
@@ -244,7 +244,13 @@ public class RuleUpdaterMediumTest {
assertThat(indexedRule.debtRemediationFunction().type()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE);
assertThat(indexedRule.debtRemediationFunction().coefficient()).isNull();
assertThat(indexedRule.debtRemediationFunction().offset()).isEqualTo("1min");
+
assertThat(indexedRule.debtOverloaded()).isTrue();
+ assertThat(indexedRule.defaultDebtCharacteristicKey()).isEqualTo("RELIABILITY");
+ assertThat(indexedRule.defaultDebtSubCharacteristicKey()).isEqualTo("HARD_RELIABILITY");
+ assertThat(indexedRule.defaultDebtRemediationFunction().type()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET);
+ assertThat(indexedRule.defaultDebtRemediationFunction().coefficient()).isEqualTo("1d");
+ assertThat(indexedRule.defaultDebtRemediationFunction().offset()).isEqualTo("5min");
}
@Test
@@ -272,7 +278,47 @@ public class RuleUpdaterMediumTest {
assertThat(indexedRule.debtRemediationFunction().type()).isEqualTo(DebtRemediationFunction.Type.LINEAR);
assertThat(indexedRule.debtRemediationFunction().coefficient()).isEqualTo("2d");
assertThat(indexedRule.debtRemediationFunction().offset()).isNull();
+
assertThat(indexedRule.debtOverloaded()).isTrue();
+ assertThat(indexedRule.defaultDebtCharacteristicKey()).isEqualTo("RELIABILITY");
+ assertThat(indexedRule.defaultDebtSubCharacteristicKey()).isEqualTo("HARD_RELIABILITY");
+ assertThat(indexedRule.defaultDebtRemediationFunction().type()).isEqualTo(DebtRemediationFunction.Type.LINEAR);
+ assertThat(indexedRule.defaultDebtRemediationFunction().coefficient()).isEqualTo("1d");
+ assertThat(indexedRule.defaultDebtRemediationFunction().offset()).isNull();
+ }
+
+ @Test
+ public void override_debt_from_linear_with_offset_to_constant() throws Exception {
+ insertDebtCharacteristics(dbSession);
+ ruleDao.insert(dbSession, RuleTesting.newDto(RULE_KEY)
+ .setDefaultSubCharacteristicId(hardReliabilityId)
+ .setDefaultRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name())
+ .setDefaultRemediationCoefficient("1d")
+ .setDefaultRemediationOffset("5min")
+ .setRemediationFunction(null)
+ .setRemediationCoefficient(null)
+ .setRemediationOffset(null));
+ dbSession.commit();
+
+ RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY)
+ .setDebtRemediationFunction(new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE, null, "10min"));
+ updater.update(update, UserSession.get());
+ dbSession.clearCache();
+
+ // verify debt is overridden
+ Rule indexedRule = tester.get(RuleIndex.class).getByKey(RULE_KEY);
+ assertThat(indexedRule.debtCharacteristicKey()).isEqualTo("RELIABILITY");
+ assertThat(indexedRule.debtSubCharacteristicKey()).isEqualTo("HARD_RELIABILITY");
+ assertThat(indexedRule.debtRemediationFunction().type()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE);
+ assertThat(indexedRule.debtRemediationFunction().coefficient()).isNull();
+ assertThat(indexedRule.debtRemediationFunction().offset()).isEqualTo("10min");
+
+ assertThat(indexedRule.debtOverloaded()).isTrue();
+ assertThat(indexedRule.defaultDebtCharacteristicKey()).isEqualTo("RELIABILITY");
+ assertThat(indexedRule.defaultDebtSubCharacteristicKey()).isEqualTo("HARD_RELIABILITY");
+ assertThat(indexedRule.defaultDebtRemediationFunction().type()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET);
+ assertThat(indexedRule.defaultDebtRemediationFunction().coefficient()).isEqualTo("1d");
+ assertThat(indexedRule.defaultDebtRemediationFunction().offset()).isEqualTo("5min");
}
@Test
@@ -301,7 +347,13 @@ public class RuleUpdaterMediumTest {
assertThat(indexedRule.debtRemediationFunction().type()).isEqualTo(DebtRemediationFunction.Type.LINEAR);
assertThat(indexedRule.debtRemediationFunction().coefficient()).isEqualTo("1d");
assertThat(indexedRule.debtRemediationFunction().offset()).isEqualTo("5min");
+
assertThat(indexedRule.debtOverloaded()).isFalse();
+ assertThat(indexedRule.defaultDebtCharacteristicKey()).isEqualTo("RELIABILITY");
+ assertThat(indexedRule.defaultDebtSubCharacteristicKey()).isEqualTo("HARD_RELIABILITY");
+ assertThat(indexedRule.defaultDebtRemediationFunction().type()).isEqualTo(DebtRemediationFunction.Type.LINEAR);
+ assertThat(indexedRule.defaultDebtRemediationFunction().coefficient()).isEqualTo("1d");
+ assertThat(indexedRule.defaultDebtRemediationFunction().offset()).isEqualTo("5min");
}
@Test
diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexMediumTest.java
index 8d763d52e3e..3b7fdb0ed29 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexMediumTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexMediumTest.java
@@ -50,8 +50,12 @@ import org.sonar.server.search.Result;
import org.sonar.server.tester.ServerTester;
import javax.annotation.Nullable;
-
-import java.util.*;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
import static org.fest.assertions.Assertions.assertThat;
@@ -609,7 +613,7 @@ public class RuleIndexMediumTest {
// 4. get all active rules on profile
result = index.search(new RuleQuery().setActivation(true)
- .setQProfileKey(qualityProfileDto2.getKey()),
+ .setQProfileKey(qualityProfileDto2.getKey()),
new QueryOptions());
assertThat(result.getHits()).hasSize(1);
assertThat(result.getHits().get(0).name()).isEqualTo(rule1.getName());
@@ -646,7 +650,7 @@ public class RuleIndexMediumTest {
ActiveRuleDto.createFor(qualityProfileDto2, rule3)
.setSeverity("BLOCKER")
.setInheritance(ActiveRule.Inheritance.INHERITED.name())
- );
+ );
dbSession.commit();
@@ -668,77 +672,77 @@ public class RuleIndexMediumTest {
// 3. get Inherited Rules on profile1
result = index.search(new RuleQuery().setActivation(true)
- .setQProfileKey(qualityProfileDto1.getKey())
- .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.INHERITED.name())),
+ .setQProfileKey(qualityProfileDto1.getKey())
+ .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.INHERITED.name())),
new QueryOptions()
- );
+ );
assertThat(result.getHits()).hasSize(0);
// 4. get Inherited Rules on profile2
result = index.search(new RuleQuery().setActivation(true)
- .setQProfileKey(qualityProfileDto2.getKey())
- .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.INHERITED.name())),
+ .setQProfileKey(qualityProfileDto2.getKey())
+ .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.INHERITED.name())),
new QueryOptions()
- );
+ );
assertThat(result.getHits()).hasSize(2);
// 5. get Overridden Rules on profile1
result = index.search(new RuleQuery().setActivation(true)
- .setQProfileKey(qualityProfileDto1.getKey())
- .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.OVERRIDES.name())),
+ .setQProfileKey(qualityProfileDto1.getKey())
+ .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.OVERRIDES.name())),
new QueryOptions()
- );
+ );
assertThat(result.getHits()).hasSize(0);
// 6. get Overridden Rules on profile2
result = index.search(new RuleQuery().setActivation(true)
- .setQProfileKey(qualityProfileDto2.getKey())
- .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.OVERRIDES.name())),
+ .setQProfileKey(qualityProfileDto2.getKey())
+ .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.OVERRIDES.name())),
new QueryOptions()
- );
+ );
assertThat(result.getHits()).hasSize(1);
// 7. get Inherited AND Overridden Rules on profile1
result = index.search(new RuleQuery().setActivation(true)
- .setQProfileKey(qualityProfileDto1.getKey())
- .setInheritance(ImmutableSet.of(
- ActiveRule.Inheritance.INHERITED.name(), ActiveRule.Inheritance.OVERRIDES.name())),
+ .setQProfileKey(qualityProfileDto1.getKey())
+ .setInheritance(ImmutableSet.of(
+ ActiveRule.Inheritance.INHERITED.name(), ActiveRule.Inheritance.OVERRIDES.name())),
new QueryOptions()
- );
+ );
assertThat(result.getHits()).hasSize(0);
// 8. get Inherited AND Overridden Rules on profile2
result = index.search(new RuleQuery().setActivation(true)
- .setQProfileKey(qualityProfileDto2.getKey())
- .setInheritance(ImmutableSet.of(
- ActiveRule.Inheritance.INHERITED.name(), ActiveRule.Inheritance.OVERRIDES.name())),
+ .setQProfileKey(qualityProfileDto2.getKey())
+ .setInheritance(ImmutableSet.of(
+ ActiveRule.Inheritance.INHERITED.name(), ActiveRule.Inheritance.OVERRIDES.name())),
new QueryOptions()
- );
+ );
assertThat(result.getHits()).hasSize(3);
// 9. get rules active on profile1 with active severity BLOCKER
result = index.search(new RuleQuery().setActivation(true)
- .setQProfileKey(qualityProfileDto1.getKey())
- .setActiveSeverities(ImmutableSet.of(
- Severity.BLOCKER.toString())),
+ .setQProfileKey(qualityProfileDto1.getKey())
+ .setActiveSeverities(ImmutableSet.of(
+ Severity.BLOCKER.toString())),
new QueryOptions()
- );
+ );
assertThat(result.getHits()).hasSize(3);
// 10. get rules active on profile2 with active severity MINOR, then BLOCKER + MINOR
result = index.search(new RuleQuery().setActivation(true)
- .setQProfileKey(qualityProfileDto2.getKey())
- .setActiveSeverities(ImmutableSet.of(
- Severity.MINOR.toString())),
+ .setQProfileKey(qualityProfileDto2.getKey())
+ .setActiveSeverities(ImmutableSet.of(
+ Severity.MINOR.toString())),
new QueryOptions()
- );
+ );
assertThat(result.getHits()).hasSize(1);
result = index.search(new RuleQuery().setActivation(true)
- .setQProfileKey(qualityProfileDto2.getKey())
- .setActiveSeverities(ImmutableSet.of(
- Severity.BLOCKER.toString(), Severity.MINOR.toString())),
+ .setQProfileKey(qualityProfileDto2.getKey())
+ .setActiveSeverities(ImmutableSet.of(
+ Severity.BLOCKER.toString(), Severity.MINOR.toString())),
new QueryOptions()
- );
+ );
assertThat(result.getHits()).hasSize(3);
}
@@ -951,7 +955,24 @@ public class RuleIndexMediumTest {
assertThat(index.search(availableSinceNowQuery, new QueryOptions()).getHits()).hasSize(0);
}
- private static List<String> ruleKeys(List<Rule> rules){
+ @Test
+ public void search_protected_chars() throws InterruptedException {
+ String nameWithProtectedChars = "ja#va&sc\"r:ipt";
+
+ RuleDto ruleDto = RuleTesting.newXooX1().setName(nameWithProtectedChars);
+ dao.insert(dbSession, ruleDto);
+ dbSession.commit();
+
+ Rule rule = index.getByKey(RuleTesting.XOO_X1);
+ assertThat(rule.name()).isEqualTo(nameWithProtectedChars);
+
+ RuleQuery protectedCharsQuery = new RuleQuery().setQueryText(nameWithProtectedChars);
+ List<Rule> results = index.search(protectedCharsQuery, new QueryOptions()).getHits();
+ assertThat(results).hasSize(1);
+ assertThat(results.get(0).key()).isEqualTo(RuleTesting.XOO_X1);
+ }
+
+ private static List<String> ruleKeys(List<Rule> rules) {
return newArrayList(Iterables.transform(rules, new Function<Rule, String>() {
@Override
public String apply(@Nullable Rule input) {
diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/ws/RulesWebServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/ws/RulesWebServiceMediumTest.java
index 1fadc233225..fe2cd94eeaa 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/rule/ws/RulesWebServiceMediumTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/rule/ws/RulesWebServiceMediumTest.java
@@ -25,6 +25,7 @@ import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.sonar.api.rule.RuleKey;
+import org.sonar.api.server.debt.DebtRemediationFunction;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.DateUtils;
import org.sonar.core.persistence.DbSession;
@@ -34,7 +35,9 @@ import org.sonar.core.qualityprofile.db.QualityProfileDao;
import org.sonar.core.qualityprofile.db.QualityProfileDto;
import org.sonar.core.rule.RuleDto;
import org.sonar.core.rule.RuleParamDto;
+import org.sonar.core.technicaldebt.db.CharacteristicDto;
import org.sonar.server.db.DbClient;
+import org.sonar.server.debt.DebtTesting;
import org.sonar.server.qualityprofile.QProfileTesting;
import org.sonar.server.qualityprofile.db.ActiveRuleDao;
import org.sonar.server.rule.RuleTesting;
@@ -61,13 +64,16 @@ public class RulesWebServiceMediumTest {
private static final String API_SHOW_METHOD = "show";
private static final String API_TAGS_METHOD = "tags";
+ DbClient db;
RulesWebService ws;
RuleDao ruleDao;
DbSession session;
+ int reliabilityId, softReliabilityId, hardReliabilityId;
@Before
public void setUp() throws Exception {
tester.clearDbAndIndexes();
+ db = tester.get(DbClient.class);
ruleDao = tester.get(RuleDao.class);
ws = tester.get(RulesWebService.class);
session = tester.get(DbClient.class).openSession(false);
@@ -170,21 +176,101 @@ public class RulesWebServiceMediumTest {
@Test
public void search_debt_rules() throws Exception {
+ insertDebtCharacteristics(session);
+
ruleDao.insert(session, RuleTesting.newXooX1()
- .setDefaultRemediationCoefficient("DefaultCoef")
- .setDefaultRemediationFunction("DefaultFunction")
- .setDefaultRemediationCoefficient("DefaultCoef")
- .setDefaultSubCharacteristicId(1));
+ .setDefaultSubCharacteristicId(hardReliabilityId)
+ .setDefaultRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name())
+ .setDefaultRemediationCoefficient("1h")
+ .setDefaultRemediationOffset("15min")
+
+ .setSubCharacteristicId(null)
+ .setRemediationFunction(null)
+ .setRemediationCoefficient(null)
+ .setRemediationOffset(null)
+ );
session.commit();
MockUserSession.set();
WsTester.TestRequest request = tester.wsTester().newGetRequest(API_ENDPOINT, API_SEARCH_METHOD);
- request.setParam(SearchOptions.PARAM_FIELDS, "debtRemFn,debtChar,debtCharName");
+ request.setParam(SearchOptions.PARAM_FIELDS, "debtChar,debtCharName,debtSubChar,debtSubCharName,debtRemFn,debtOverloaded,defaultDebtChar,defaultDebtSubChar,defaultDebtRemFn");
WsTester.Result result = request.execute();
result.assertJson(this.getClass(), "search_debt_rule.json");
}
@Test
+ public void search_debt_rules_with_default_and_overridden_debt_values() throws Exception {
+ insertDebtCharacteristics(session);
+
+ ruleDao.insert(session, RuleTesting.newXooX1()
+ .setDefaultSubCharacteristicId(hardReliabilityId)
+ .setDefaultRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name())
+ .setDefaultRemediationCoefficient("1h")
+ .setDefaultRemediationOffset("15min")
+
+ .setSubCharacteristicId(softReliabilityId)
+ .setRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name())
+ .setRemediationCoefficient("2h")
+ .setRemediationOffset("25min")
+ );
+ session.commit();
+
+ MockUserSession.set();
+ WsTester.TestRequest request = tester.wsTester().newGetRequest(API_ENDPOINT, API_SEARCH_METHOD);
+ request.setParam(SearchOptions.PARAM_FIELDS, "debtChar,debtCharName,debtSubChar,debtSubCharName,debtRemFn,debtOverloaded,defaultDebtChar,defaultDebtSubChar,defaultDebtRemFn");
+ WsTester.Result result = request.execute();
+ result.assertJson(this.getClass(), "search_debt_rules_with_default_and_overridden_debt_values.json");
+ }
+
+ @Test
+ public void search_debt_rules_with_default_linear_offset_and_overridden_constant_debt() throws Exception {
+ insertDebtCharacteristics(session);
+
+ ruleDao.insert(session, RuleTesting.newXooX1()
+ .setDefaultSubCharacteristicId(hardReliabilityId)
+ .setDefaultRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name())
+ .setDefaultRemediationCoefficient("1h")
+ .setDefaultRemediationOffset("15min")
+
+ .setSubCharacteristicId(softReliabilityId)
+ .setRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE.name())
+ .setRemediationCoefficient(null)
+ .setRemediationOffset("5min")
+ );
+ session.commit();
+
+ MockUserSession.set();
+ WsTester.TestRequest request = tester.wsTester().newGetRequest(API_ENDPOINT, API_SEARCH_METHOD);
+ request.setParam(SearchOptions.PARAM_FIELDS, "debtChar,debtCharName,debtSubChar,debtSubCharName,debtRemFn,debtOverloaded,defaultDebtChar,defaultDebtSubChar,defaultDebtRemFn");
+ WsTester.Result result = request.execute();
+ result.assertJson(this.getClass(), "search_debt_rules_with_default_linear_offset_and_overridden_constant_debt.json");
+ }
+
+ @Test
+ public void search_debt_rules_with_default_linear_offset_and_overridden_linear_debt() throws Exception {
+ insertDebtCharacteristics(session);
+
+ ruleDao.insert(session, RuleTesting.newXooX1()
+ .setDefaultSubCharacteristicId(hardReliabilityId)
+ .setDefaultRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name())
+ .setDefaultRemediationCoefficient("1h")
+ .setDefaultRemediationOffset("15min")
+
+ .setSubCharacteristicId(softReliabilityId)
+ .setRemediationFunction(DebtRemediationFunction.Type.LINEAR.name())
+ .setRemediationCoefficient("1h")
+ .setRemediationOffset(null)
+ );
+ session.commit();
+
+ MockUserSession.set();
+ WsTester.TestRequest request = tester.wsTester().newGetRequest(API_ENDPOINT, API_SEARCH_METHOD);
+ request.setParam(SearchOptions.PARAM_FIELDS, "debtChar,debtCharName,debtSubChar,debtSubCharName,debtRemFn,debtOverloaded,defaultDebtChar,defaultDebtSubChar,defaultDebtRemFn");
+ WsTester.Result result = request.execute();
+ result.assertJson(this.getClass(), "search_debt_rules_with_default_linear_offset_and_overridden_linear_debt.json");
+ }
+
+ @Test
public void search_template_rules() throws Exception {
RuleDto templateRule = RuleTesting.newXooX1().setIsTemplate(true);
ruleDao.insert(session, templateRule);
@@ -430,4 +516,20 @@ public class RulesWebServiceMediumTest {
.setInheritance(null)
.setSeverity("BLOCKER");
}
+
+ private void insertDebtCharacteristics(DbSession dbSession) {
+ CharacteristicDto reliability = DebtTesting.newCharacteristicDto("RELIABILITY").setName("Reliability");
+ db.debtCharacteristicDao().insert(reliability, dbSession);
+ reliabilityId = reliability.getId();
+
+ CharacteristicDto softReliability = DebtTesting.newCharacteristicDto("SOFT_RELIABILITY").setName("Soft Reliability")
+ .setParentId(reliability.getId());
+ db.debtCharacteristicDao().insert(softReliability, dbSession);
+ softReliabilityId = softReliability.getId();
+
+ CharacteristicDto hardReliability = DebtTesting.newCharacteristicDto("HARD_RELIABILITY").setName("Hard Reliability")
+ .setParentId(reliability.getId());
+ db.debtCharacteristicDao().insert(hardReliability, dbSession);
+ hardReliabilityId = hardReliability.getId();
+ }
}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_active_rules.json b/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_active_rules.json
new file mode 100644
index 00000000000..f7f8faf299b
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_active_rules.json
@@ -0,0 +1,25 @@
+{
+ "timestamp": 0,
+ "qprofilesByLanguage": {
+ "java": {
+ "key": "abcd",
+ "name": "Default",
+ "language": "java",
+ "rulesUpdatedAt": "Jan 14, 2014 1:00:00 PM"
+ }
+ },
+ "activeRules": [
+ {
+ "repositoryKey": "squid",
+ "ruleKey": "AvoidCycle",
+ "name": "Avoid Cycle",
+ "severity": "MINOR",
+ "internalKey": "squid-1",
+ "language": "java",
+ "params": {
+ "max" : "2"
+ }
+ }
+ ],
+ "settingsByModule": {}
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rule.json b/server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rule.json
index 1505da0346b..3f83f0921a3 100644
--- a/server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rule.json
+++ b/server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rule.json
@@ -1,8 +1,18 @@
{"total": 1, "p": 1, "ps": 10, "rules": [
{
"key": "xoo:x1",
- "debtRemFnType": "LINEAR",
+ "debtChar": "RELIABILITY",
+ "debtCharName": "Reliability",
+ "debtSubChar": "HARD_RELIABILITY",
+ "debtSubCharName": "Hard Reliability",
+ "debtRemFnType": "LINEAR_OFFSET",
"debtRemFnCoeff": "1h",
- "debtRemFnOffset": "5min"
+ "debtRemFnOffset": "15min",
+ "debtOverloaded": false,
+ "defaultDebtChar": "RELIABILITY",
+ "defaultDebtSubChar": "HARD_RELIABILITY",
+ "defaultDebtRemFnType": "LINEAR_OFFSET",
+ "defaultDebtRemFnCoeff": "1h",
+ "defaultDebtRemFnOffset": "15min"
}
]}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_and_overridden_debt_values.json b/server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_and_overridden_debt_values.json
new file mode 100644
index 00000000000..3aa6211c764
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_and_overridden_debt_values.json
@@ -0,0 +1,18 @@
+{"total": 1, "p": 1, "ps": 10, "rules": [
+ {
+ "key": "xoo:x1",
+ "debtChar": "RELIABILITY",
+ "debtCharName": "Reliability",
+ "debtSubChar": "SOFT_RELIABILITY",
+ "debtSubCharName": "Soft Reliability",
+ "debtRemFnType": "LINEAR_OFFSET",
+ "debtRemFnCoeff": "2h",
+ "debtRemFnOffset": "25min",
+ "debtOverloaded": true,
+ "defaultDebtChar": "RELIABILITY",
+ "defaultDebtSubChar": "HARD_RELIABILITY",
+ "defaultDebtRemFnType": "LINEAR_OFFSET",
+ "defaultDebtRemFnCoeff": "1h",
+ "defaultDebtRemFnOffset": "15min"
+ }
+]}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_linear_offset_and_overridden_constant_debt.json b/server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_linear_offset_and_overridden_constant_debt.json
new file mode 100644
index 00000000000..6ae75abecb5
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_linear_offset_and_overridden_constant_debt.json
@@ -0,0 +1,17 @@
+{"total": 1, "p": 1, "ps": 10, "rules": [
+ {
+ "key": "xoo:x1",
+ "debtChar": "RELIABILITY",
+ "debtCharName": "Reliability",
+ "debtSubChar": "SOFT_RELIABILITY",
+ "debtSubCharName": "Soft Reliability",
+ "debtRemFnType": "CONSTANT_ISSUE",
+ "debtRemFnOffset": "5min",
+ "debtOverloaded": true,
+ "defaultDebtChar": "RELIABILITY",
+ "defaultDebtSubChar": "HARD_RELIABILITY",
+ "defaultDebtRemFnType": "LINEAR_OFFSET",
+ "defaultDebtRemFnCoeff": "1h",
+ "defaultDebtRemFnOffset": "15min"
+ }
+]}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_linear_offset_and_overridden_linear_debt.json b/server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_linear_offset_and_overridden_linear_debt.json
new file mode 100644
index 00000000000..b1c457e13c0
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_linear_offset_and_overridden_linear_debt.json
@@ -0,0 +1,17 @@
+{"total": 1, "p": 1, "ps": 10, "rules": [
+ {
+ "key": "xoo:x1",
+ "debtChar": "RELIABILITY",
+ "debtCharName": "Reliability",
+ "debtSubChar": "SOFT_RELIABILITY",
+ "debtSubCharName": "Soft Reliability",
+ "debtRemFnType": "LINEAR",
+ "debtRemFnCoeff": "1h",
+ "debtOverloaded": true,
+ "defaultDebtChar": "RELIABILITY",
+ "defaultDebtSubChar": "HARD_RELIABILITY",
+ "defaultDebtRemFnType": "LINEAR_OFFSET",
+ "defaultDebtRemFnCoeff": "1h",
+ "defaultDebtRemFnOffset": "15min"
+ }
+]}
diff --git a/server/sonar-web/Gruntfile.coffee b/server/sonar-web/Gruntfile.coffee
index d1975327285..e222dcb4112 100644
--- a/server/sonar-web/Gruntfile.coffee
+++ b/server/sonar-web/Gruntfile.coffee
@@ -1,8 +1,4 @@
module.exports = (grunt) ->
- grunt.loadNpmTasks('grunt-karma')
- grunt.loadNpmTasks('grunt-express-server')
- grunt.loadNpmTasks('grunt-casper')
-
pkg = grunt.file.readJSON('package.json')
grunt.initConfig
@@ -273,11 +269,20 @@ module.exports = (grunt) ->
options:
test: true
'no-colors': true
+ concise: true
src: ['<%= pkg.sources %>js/tests/e2e/tests/**/*.js']
cw:
options:
test: true
+ parallel: true,
+ concurrency: 5
src: ['<%= pkg.sources %>js/tests/e2e/tests/component-viewer-spec.js']
+ single:
+ options:
+ test: true
+ parallel: true,
+ concurrency: 5
+ src: ['<%= pkg.sources %>js/tests/e2e/tests/<%= grunt.option("spec") %>-spec.js']
watch:
@@ -313,6 +318,8 @@ module.exports = (grunt) ->
grunt.loadNpmTasks 'grunt-contrib-clean'
grunt.loadNpmTasks 'grunt-contrib-copy'
grunt.loadNpmTasks 'grunt-contrib-concat'
+ grunt.loadNpmTasks 'grunt-express-server'
+ grunt.loadNpmTasks 'grunt-casper'
# Define tasks
@@ -333,3 +340,6 @@ module.exports = (grunt) ->
grunt.registerTask 'cw', ['clean:js', 'coffee:build', 'handlebars:build', 'copy:js', 'concat:dev',
'express:test', 'casper:cw']
+
+ grunt.registerTask 'single', ['clean:js', 'coffee:build', 'handlebars:build', 'copy:js', 'concat:dev',
+ 'express:test', 'casper:single']
diff --git a/server/sonar-web/pom.xml b/server/sonar-web/pom.xml
index 881f84e83f1..a90aad2498f 100644
--- a/server/sonar-web/pom.xml
+++ b/server/sonar-web/pom.xml
@@ -351,6 +351,7 @@
<sonar.log.console>true</sonar.log.console>
<sonar.log.profilingLevel>BASIC</sonar.log.profilingLevel>
<sonar.web.context>/dev</sonar.web.context>
+ <sonar.updatecenter.activate>false</sonar.updatecenter.activate>
</systemProperties>
</configuration>
</plugin>
diff --git a/server/sonar-web/src/main/coffee/component-viewer/app.coffee b/server/sonar-web/src/main/coffee/component-viewer/app.coffee
index d091a396aa6..30d4c76ffc5 100644
--- a/server/sonar-web/src/main/coffee/component-viewer/app.coffee
+++ b/server/sonar-web/src/main/coffee/component-viewer/app.coffee
@@ -5,7 +5,6 @@ requirejs.config
'backbone': 'third-party/backbone'
'backbone.marionette': 'third-party/backbone.marionette'
'handlebars': 'third-party/handlebars'
- 'jquery.mockjax': 'third-party/jquery.mockjax'
shim:
'backbone.marionette':
diff --git a/server/sonar-web/src/main/coffee/component-viewer/header.coffee b/server/sonar-web/src/main/coffee/component-viewer/header.coffee
index 44fdef9b4e6..49d80413125 100644
--- a/server/sonar-web/src/main/coffee/component-viewer/header.coffee
+++ b/server/sonar-web/src/main/coffee/component-viewer/header.coffee
@@ -155,16 +155,14 @@ define [
main: @options.main, state: @state, component: @component, settings: @settings, source: @model, header: @
@ui.expandedBar.addClass 'active'
@ui.expandLinks.filter("[data-scope=#{scope}]").addClass 'active'
- activeHeaderItem = @state.get 'activeHeaderItem'
- if activeHeaderItem
- @$(activeHeaderItem).addClass 'active'
@options.main.fitIntoElement()
enableBarItem: (item, silent = false) ->
$item = @$(item)
+ console.log item, $item
if $item.length > 0
- if silent then @$(item).addClass('active') else @$(item).click()
+ if silent then @$(item).addClass('active') else @$(item).click()
else
@options.main.hideAllLines()
@@ -206,6 +204,14 @@ define [
@$('.component-viewer-header-time-changes').html '<i class="spinner spinner-margin"></i>'
+ unsetFilter: ->
+ @options.main.resetIssues()
+ @options.main.showAllLines()
+ @state.unset 'activeHeaderItem'
+ @$('.item.active').removeClass 'active'
+ @render()
+
+
filterLines: (e, methodName, extra) ->
@$('.component-viewer-header-expanded-bar-section-list .active').removeClass 'active'
$(e.currentTarget).addClass 'active'
diff --git a/server/sonar-web/src/main/coffee/component-viewer/header/basic-header.coffee b/server/sonar-web/src/main/coffee/component-viewer/header/basic-header.coffee
index e39ebeca502..09190ce58a2 100644
--- a/server/sonar-web/src/main/coffee/component-viewer/header/basic-header.coffee
+++ b/server/sonar-web/src/main/coffee/component-viewer/header/basic-header.coffee
@@ -19,10 +19,12 @@ define [
filterByLines: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByLines'
@state.set 'activeHeaderItem', '.js-filter-lines'
filterByNcloc: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByNcloc'
@state.set 'activeHeaderItem', '.js-filter-ncloc'
diff --git a/server/sonar-web/src/main/coffee/component-viewer/header/coverage-header.coffee b/server/sonar-web/src/main/coffee/component-viewer/header/coverage-header.coffee
index 330f8b0f71a..b0258f1ac85 100644
--- a/server/sonar-web/src/main/coffee/component-viewer/header/coverage-header.coffee
+++ b/server/sonar-web/src/main/coffee/component-viewer/header/coverage-header.coffee
@@ -48,61 +48,73 @@ define [
filterByLinesToCover: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByLinesToCover'
@state.set 'activeHeaderItem', '.js-filter-lines-to-cover'
filterByUncoveredLines: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByUncoveredLines'
@state.set 'activeHeaderItem', '.js-filter-uncovered-lines'
filterByBranchesToCover: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByBranchesToCover'
@state.set 'activeHeaderItem', '.js-filter-branches-to-cover'
filterByUncoveredBranches: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByUncoveredBranches'
@state.set 'activeHeaderItem', '.js-filter-uncovered-branches'
filterByLinesToCoverIT: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByLinesToCoverIT'
@state.set 'activeHeaderItem', '.js-filter-lines-to-cover-it'
filterByUncoveredLinesIT: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByUncoveredLinesIT'
@state.set 'activeHeaderItem', '.js-filter-uncovered-lines-it'
filterByBranchesToCoverIT: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByBranchesToCoverIT'
@state.set 'activeHeaderItem', '.js-filter-branches-to-cover-it'
filterByUncoveredBranchesIT: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByUncoveredBranchesIT'
@state.set 'activeHeaderItem', '.js-filter-uncovered-branches-it'
filterByLinesToCoverOverall: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByLinesToCoverOverall'
@state.set 'activeHeaderItem', '.js-filter-lines-to-cover-overall'
filterByUncoveredLinesOverall: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByUncoveredLinesOverall'
@state.set 'activeHeaderItem', '.js-filter-uncovered-lines-overall'
filterByBranchesToCoverOverall: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByBranchesToCoverOverall'
@state.set 'activeHeaderItem', '.js-filter-branches-to-cover-overall'
filterByUncoveredBranchesOverall: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByUncoveredBranchesOverall'
@state.set 'activeHeaderItem', '.js-filter-uncovered-branches-overall'
diff --git a/server/sonar-web/src/main/coffee/component-viewer/header/duplications-header.coffee b/server/sonar-web/src/main/coffee/component-viewer/header/duplications-header.coffee
index 8cb931878af..f0f4f3bba20 100644
--- a/server/sonar-web/src/main/coffee/component-viewer/header/duplications-header.coffee
+++ b/server/sonar-web/src/main/coffee/component-viewer/header/duplications-header.coffee
@@ -8,6 +8,8 @@ define [
BaseHeaderView
) ->
+ $ = jQuery
+
class extends BaseHeaderView
template: Templates['cw-duplications-header']
@@ -18,5 +20,6 @@ define [
filterByDuplications: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByDuplications'
@state.set 'activeHeaderItem', '.js-filter-duplications'
diff --git a/server/sonar-web/src/main/coffee/component-viewer/header/issues-header.coffee b/server/sonar-web/src/main/coffee/component-viewer/header/issues-header.coffee
index f7d24a90601..b765c2f3cc4 100644
--- a/server/sonar-web/src/main/coffee/component-viewer/header/issues-header.coffee
+++ b/server/sonar-web/src/main/coffee/component-viewer/header/issues-header.coffee
@@ -63,62 +63,74 @@ define [
filterByCurrentIssue: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByCurrentIssue'
@state.set 'activeHeaderItem', '.js-filter-current-issues'
filterByAllIssues: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByAllIssues'
@state.set 'activeHeaderItem', '.js-filter-all-issues'
filterByFixedIssues: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByFixedIssues'
@state.set 'activeHeaderItem', '.js-filter-fixed-issues'
filterByUnresolvedIssues: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByUnresolvedIssues'
@state.set 'activeHeaderItem', '.js-filter-unresolved-issues'
filterByFalsePositiveIssues: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByFalsePositiveIssues'
@state.set 'activeHeaderItem', '.js-filter-false-positive-issues'
filterByOpenIssues: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByOpenIssues'
@state.set 'activeHeaderItem', '.js-filter-open-issues'
filterByRule: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
rule = $(e.currentTarget).data 'rule'
@header.filterLines e, 'filterByRule', rule
@state.set 'activeHeaderItem', ".js-filter-rule[data-rule='#{rule}']"
filterByBlockerIssues: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByBlockerIssues'
@state.set 'activeHeaderItem', '.js-filter-BLOCKER-issues'
filterByCriticalIssues: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByCriticalIssues'
@state.set 'activeHeaderItem', '.js-filter-CRITICAL-issues'
filterByMajorIssues: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByMajorIssues'
@state.set 'activeHeaderItem', '.js-filter-MAJOR-issues'
filterByMinorIssues: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByMinorIssues'
@state.set 'activeHeaderItem', '.js-filter-MINOR-issues'
filterByInfoIssues: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
@header.filterLines e, 'filterByInfoIssues'
@state.set 'activeHeaderItem', '.js-filter-INFO-issues'
diff --git a/server/sonar-web/src/main/coffee/component-viewer/header/scm-header.coffee b/server/sonar-web/src/main/coffee/component-viewer/header/scm-header.coffee
index 66b43636a47..a3f9c2f68a1 100644
--- a/server/sonar-web/src/main/coffee/component-viewer/header/scm-header.coffee
+++ b/server/sonar-web/src/main/coffee/component-viewer/header/scm-header.coffee
@@ -19,6 +19,7 @@ define [
events:
'click .js-scm-time-changes': 'scmTimeChanges'
+ 'click .js-filter-modified-lines': 'filterBySCM'
scmTimeChanges: (e) ->
@@ -29,7 +30,13 @@ define [
main: @options.main
bottom: true
popup.render()
- popup.on 'change', (period) => @main.enablePeriod period
+ popup.on 'change', (period) => @main.enablePeriod period, '.js-filter-modified-lines'
+
+
+ filterBySCM: (e) ->
+ return @header.unsetFilter() if $(e.currentTarget).is('.active')
+ @header.filterLines e, 'filterBySCM'
+ @state.set 'activeHeaderItem', '.js-filter-modified-lines'
serializeData: ->
diff --git a/server/sonar-web/src/main/coffee/component-viewer/main.coffee b/server/sonar-web/src/main/coffee/component-viewer/main.coffee
index a7f4c7415a2..6dd645d853b 100644
--- a/server/sonar-web/src/main/coffee/component-viewer/main.coffee
+++ b/server/sonar-web/src/main/coffee/component-viewer/main.coffee
@@ -16,8 +16,6 @@ define [
'component-viewer/source'
'component-viewer/header'
'component-viewer/utils'
-
- 'component-viewer/mockjax'
], (
Backbone
Marionette
@@ -352,8 +350,7 @@ define [
$.when(@requestMeasures(@key, period?.get('key')), @requestIssuesPeriod(@key, period?.get('key')), @requestSCM(@key)).done =>
if activeHeaderItem?
@state.set 'activeHeaderItem', activeHeaderItem
- @headerView.render()
- else @filterBySCM()
+ @headerView.render()
addTransition: (transition, options) ->
@@ -391,8 +388,10 @@ define [
filterByLines: ->
+ @resetIssues()
@showAllLines()
filterByNcloc: ->
+ @resetIssues()
@_filterByLines (line) -> line?.executable
diff --git a/server/sonar-web/src/main/coffee/component-viewer/mixins/main-coverage.coffee b/server/sonar-web/src/main/coffee/component-viewer/mixins/main-coverage.coffee
index 63a4bc5f78a..971074c918f 100644
--- a/server/sonar-web/src/main/coffee/component-viewer/mixins/main-coverage.coffee
+++ b/server/sonar-web/src/main/coffee/component-viewer/mixins/main-coverage.coffee
@@ -51,6 +51,7 @@ define [], () ->
if @settings.get('issues') && !@state.get('hasIssues')
requests.push @requestIssues @key
$.when.apply($, requests).done =>
+ @resetIssues()
@_filterByCoverage(predicate)
@@ -59,6 +60,7 @@ define [], () ->
if @settings.get('issues') && !@state.get('hasIssues')
requests.push @requestIssues @key
$.when.apply($, requests).done =>
+ @resetIssues()
@_filterByCoverage(predicate)
@@ -67,6 +69,7 @@ define [], () ->
if @settings.get('issues') && !@state.get('hasIssues')
requests.push @requestIssues @key
$.when.apply($, requests).done =>
+ @resetIssues()
@_filterByCoverage(predicate)
diff --git a/server/sonar-web/src/main/coffee/component-viewer/mixins/main-issues.coffee b/server/sonar-web/src/main/coffee/component-viewer/mixins/main-issues.coffee
index 9feefc4770d..477420b5d29 100644
--- a/server/sonar-web/src/main/coffee/component-viewer/mixins/main-issues.coffee
+++ b/server/sonar-web/src/main/coffee/component-viewer/mixins/main-issues.coffee
@@ -20,8 +20,14 @@ define [
$.get API_ISSUES, options, (data) =>
@state.set 'hasIssues', true
issues = _.sortBy data.issues, (issue) -> "#{issue.rule}_____#{issue.message}"
+ @source.set issues: issues
+ @resetIssues()
+
+
+ resetIssues: ->
+ issues = @source.get('issues')
+ if _.isArray issues
@source.set
- issues: issues
activeIssues: issues.filter (issue) -> !issue.resolution
diff --git a/server/sonar-web/src/main/coffee/component-viewer/mockjax.coffee b/server/sonar-web/src/main/coffee/component-viewer/mockjax.coffee
deleted file mode 100644
index 65137174334..00000000000
--- a/server/sonar-web/src/main/coffee/component-viewer/mockjax.coffee
+++ /dev/null
@@ -1,27 +0,0 @@
-define ['third-party/jquery.mockjax'], ->
-
- jQuery.mockjaxSettings.contentType = 'text/json';
- jQuery.mockjaxSettings.responseTime = 250;
-
- jQuery.mockjax
- url: "#{baseUrl}/api/sources/app"
- responseText: JSON.stringify
- key: 'org.codehaus.sonar:sonar-dev-maven-plugin:src/main/java/org/sonar/dev/UploadMojo.java'
- path: 'src/main/java/org/sonar/dev/UploadMojo.java'
- name: 'UploadMojo.java'
- q: 'FIL'
- fav: false
- project: 'org.codehaus.sonar:sonar-dev-maven-plugin'
- projectName: 'SonarQube Development Maven Plugin'
- periods: []
- measures:
- 'ncloc': 69
- 'coverage': '30%'
- 'duplication_density': '7.4%'
- 'debt': '3d 2h'
- 'issues': 4
- 'blocker_issues': 1
- 'critical_issues': 2
- 'major_issues': 0
- 'minor_issues': 1
- 'info_issues': 0 \ No newline at end of file
diff --git a/server/sonar-web/src/main/coffee/dashboard/file-app.coffee b/server/sonar-web/src/main/coffee/dashboard/file-app.coffee
index 661562cef33..0e35c084710 100644
--- a/server/sonar-web/src/main/coffee/dashboard/file-app.coffee
+++ b/server/sonar-web/src/main/coffee/dashboard/file-app.coffee
@@ -5,7 +5,6 @@ requirejs.config
'backbone': 'third-party/backbone'
'backbone.marionette': 'third-party/backbone.marionette'
'handlebars': 'third-party/handlebars'
- 'jquery.mockjax': 'third-party/jquery.mockjax'
shim:
'backbone.marionette':
diff --git a/server/sonar-web/src/main/coffee/drilldown/app.coffee b/server/sonar-web/src/main/coffee/drilldown/app.coffee
index fc62830b711..c1179e3e05d 100644
--- a/server/sonar-web/src/main/coffee/drilldown/app.coffee
+++ b/server/sonar-web/src/main/coffee/drilldown/app.coffee
@@ -5,7 +5,6 @@ requirejs.config
'backbone': 'third-party/backbone'
'backbone.marionette': 'third-party/backbone.marionette'
'handlebars': 'third-party/handlebars'
- 'jquery.mockjax': 'third-party/jquery.mockjax'
shim:
'backbone.marionette':
diff --git a/server/sonar-web/src/main/coffee/drilldown/conf.coffee b/server/sonar-web/src/main/coffee/drilldown/conf.coffee
index b5f31642c18..db5ee65a97f 100644
--- a/server/sonar-web/src/main/coffee/drilldown/conf.coffee
+++ b/server/sonar-web/src/main/coffee/drilldown/conf.coffee
@@ -34,20 +34,20 @@ define
'new_it_uncovered_conditions': { tab: 'coverage', item: '.js-filter-uncovered-branches-it' }
# Overall
- 'overall_coverage': { tab: 'coverage', item: '.js-filter-lines-to-cover' }
- 'overall_lines_to_cover': { tab: 'coverage', item: '.js-filter-lines-to-cover' }
- 'overall_uncovered_lines': { tab: 'coverage', item: '.js-filter-uncovered-lines' }
- 'overall_line_coverage': { tab: 'coverage', item: '.js-filter-lines-to-cover' }
- 'overall_conditions_to_cover': { tab: 'coverage', item: '.js-filter-branches-to-cover' }
- 'overall_uncovered_conditions': { tab: 'coverage', item: '.js-filter-uncovered-branches' }
- 'overall_branch_coverage': { tab: 'coverage', item: '.js-filter-branches-to-cover' }
- 'new_overall_coverage': { tab: 'coverage', item: '.js-filter-lines-to-cover' }
- 'new_overall_uncovered_lines': { tab: 'coverage', item: '.js-filter-uncovered-lines' }
- 'new_overall_line_coverage': { tab: 'coverage', item: '.js-filter-lines-to-cover' }
- 'new_overall_lines_to_cover': { tab: 'coverage', item: '.js-filter-lines-to-cover' }
- 'new_overall_branch_coverage': { tab: 'coverage', item: '.js-filter-branches-to-cover' }
- 'new_overall_conditions_to_cover': { tab: 'coverage', item: '.js-filter-branches-to-cover' }
- 'new_overall_uncovered_conditions': { tab: 'coverage', item: '.js-filter-uncovered-branches' }
+ 'overall_coverage': { tab: 'coverage', item: '.js-filter-lines-to-cover-overall' }
+ 'overall_lines_to_cover': { tab: 'coverage', item: '.js-filter-lines-to-cover-overall' }
+ 'overall_uncovered_lines': { tab: 'coverage', item: '.js-filter-uncovered-lines-overall' }
+ 'overall_line_coverage': { tab: 'coverage', item: '.js-filter-lines-to-cover-overall' }
+ 'overall_conditions_to_cover': { tab: 'coverage', item: '.js-filter-branches-to-cover-overall' }
+ 'overall_uncovered_conditions': { tab: 'coverage', item: '.js-filter-uncovered-branches-overall' }
+ 'overall_branch_coverage': { tab: 'coverage', item: '.js-filter-branches-to-cover-overall' }
+ 'new_overall_coverage': { tab: 'coverage', item: '.js-filter-lines-to-cover-overall' }
+ 'new_overall_uncovered_lines': { tab: 'coverage', item: '.js-filter-uncovered-lines-overall' }
+ 'new_overall_line_coverage': { tab: 'coverage', item: '.js-filter-lines-to-cover-overall' }
+ 'new_overall_lines_to_cover': { tab: 'coverage', item: '.js-filter-lines-to-cover-overall' }
+ 'new_overall_branch_coverage': { tab: 'coverage', item: '.js-filter-branches-to-cover-overall' }
+ 'new_overall_conditions_to_cover': { tab: 'coverage', item: '.js-filter-branches-to-cover-overall' }
+ 'new_overall_uncovered_conditions': { tab: 'coverage', item: '.js-filter-uncovered-branches-overall' }
# Issues
diff --git a/server/sonar-web/src/main/hbs/coding-rules/coding-rules-detail.hbs b/server/sonar-web/src/main/hbs/coding-rules/coding-rules-detail.hbs
index f839bb8963c..b64e0c037ef 100644
--- a/server/sonar-web/src/main/hbs/coding-rules/coding-rules-detail.hbs
+++ b/server/sonar-web/src/main/hbs/coding-rules/coding-rules-detail.hbs
@@ -49,31 +49,6 @@
{{/if}}
</ul>
-{{#if subcharacteristic}}
-<div class="coding-rules-subcharacteristic-more inline-help">
- <ul>
- {{#if debtRemFnType}}
- <li>
- <h3>{{t 'coding_rules.remediation_function'}}</h3>
- {{t 'coding_rules.remediation_function' debtRemFnType}}
- </li>
- {{/if}}
- {{#if debtRemFnCoeff}}
- <li>
- <h3>{{t 'coding_rules.remediation_function.coeff'}}</h3>
- {{debtRemFnCoeff}}
- </li>
- {{/if}}
- {{#if debtRemFnOffset}}
- <li>
- <h3>{{t 'coding_rules.remediation_function.offset'}}</h3>
- {{debtRemFnOffset}}
- </li>
- {{/if}}
- </ul>
-</div>
-{{/if}}
-
<div class="coding-rules-detail-description rule-desc markdown">{{{htmlDesc}}}</div>
{{#if isEditable}}
diff --git a/server/sonar-web/src/main/hbs/component-viewer/cw-source.hbs b/server/sonar-web/src/main/hbs/component-viewer/cw-source.hbs
index 79c44c4460f..e8f68051bf9 100644
--- a/server/sonar-web/src/main/hbs/component-viewer/cw-source.hbs
+++ b/server/sonar-web/src/main/hbs/component-viewer/cw-source.hbs
@@ -5,7 +5,7 @@
{{else}}
{{#if state.duplicationsInDeletedFiles}}
- <p class="marginbottom10">{{t 'duplications.dups_found_on_deleted_resource'}}</p>
+ <p class="marginbottom10 js-duplications-in-deleted-files">{{t 'duplications.dups_found_on_deleted_resource'}}</p>
{{/if}}
{{#if issuesLimitReached}}
diff --git a/server/sonar-web/src/main/hbs/component-viewer/cw-workspace.hbs b/server/sonar-web/src/main/hbs/component-viewer/cw-workspace.hbs
index 1c0137f9f12..781b1ceba55 100644
--- a/server/sonar-web/src/main/hbs/component-viewer/cw-workspace.hbs
+++ b/server/sonar-web/src/main/hbs/component-viewer/cw-workspace.hbs
@@ -14,28 +14,24 @@
<ul class="component-viewer-workspace-list">
{{#eachReverse workspace}}
<li class="component-viewer-workspace-item {{#if active}}active{{/if}}">
- {{qualifierIcon component.q}}
- <a class="link-action" data-key="{{component.key}}">{{component.name}}</a>
<div class="text-ellipsis subtitle" title="{{component.projectName}} / {{component.subProjectName}}">
{{component.projectName}} / {{component.subProjectName}}
</div>
- {{#if component.dir}}
- <div class="text-ellipsis subtitle">{{component.dir}}</div>
- {{/if}}
+ {{#if component.dir}}<div class="text-ellipsis subtitle">{{component.dir}}</div>{{/if}}
+ {{qualifierIcon component.q}} <a class="link-action" data-key="{{component.key}}">{{component.name}}</a>
+
{{#if options}}
<div class="component-viewer-workspace-transition">{{t 'component_viewer.transition' ../transition}}</div>
<ul class="component-viewer-workspace-options">
{{#each options}}
<li class="component-viewer-workspace-option text-ellipsis {{#if active}}active{{/if}}" title="{{name}}">
- <a class="link-action" data-workspace-key="{{../component.key}}" data-key="{{key}}">{{name}}</a>
{{#if component}}
<div class="text-ellipsis subtitle" title="{{component.projectName}} / {{component.subProjectName}}">
{{component.projectName}} / {{component.subProjectName}}
</div>
{{/if}}
- {{#if subname}}
- <div class="text-ellipsis subtitle" title="{{subname}}">{{subname}}</div>
- {{/if}}
+ {{#if subname}}<div class="text-ellipsis subtitle" title="{{subname}}">{{subname}}</div>{{/if}}
+ <a class="link-action" data-workspace-key="{{../component.key}}" data-key="{{key}}">{{name}}</a>
</li>
{{/each}}
</ul>
diff --git a/server/sonar-web/src/main/js/navigator/filters/choice-filters.js b/server/sonar-web/src/main/js/navigator/filters/choice-filters.js
index 3dec5f94708..b4553847159 100644
--- a/server/sonar-web/src/main/js/navigator/filters/choice-filters.js
+++ b/server/sonar-web/src/main/js/navigator/filters/choice-filters.js
@@ -31,7 +31,7 @@ define([
collection.each(function (item) {
container.append(
that.itemTemplate(_.extend(item.toJSON(), {
- multiple: that.model.get('multiple')
+ multiple: that.model.get('multiple') && item.get('id')[0] !== '!'
}))
);
});
diff --git a/server/sonar-web/src/main/js/tests/e2e/lib.js b/server/sonar-web/src/main/js/tests/e2e/lib.js
index 20ba1903c6c..6650aa509de 100644
--- a/server/sonar-web/src/main/js/tests/e2e/lib.js
+++ b/server/sonar-web/src/main/js/tests/e2e/lib.js
@@ -22,8 +22,10 @@ exports.initMessages = function () {
exports.changeWorkingDirectory = function (dir) {
+ var commandLineArgs = require('system').args;
// Since Casper has control, the invoked script is deep in the argument stack
- var currentFile = require('system').args[4];
+ // commandLineArgs = casper/bin/bootstrap.js,--casper-path=.../casperjs,--cli,--test,[file(s) under test],[options]
+ var currentFile = commandLineArgs[4];
var curFilePath = fs.absolute(currentFile).split(fs.separator);
if (curFilePath.length > 1) {
curFilePath.pop(); // PhantomJS does not have an equivalent path.baseName()-like method
@@ -33,6 +35,16 @@ exports.changeWorkingDirectory = function (dir) {
};
+exports.testName = function () {
+ var head = Array.prototype.slice.call(arguments, 0);
+ return function () {
+ var tail = Array.prototype.slice.call(arguments, 0),
+ body = head.concat(tail);
+ return body.join(' :: ');
+ };
+};
+
+
var mockRequest = function (url, response) {
return casper.evaluate(function (url, response) {
return jQuery.mockjax({ url: url, responseText: response});
@@ -74,3 +86,12 @@ exports.setDefaultViewport = function () {
exports.capture = function (fileName) {
casper.capture(fileName, { top: 0, left: 0, width: WINDOW_WIDTH, height: WINDOW_HEIGHT });
};
+
+
+exports.waitForElementCount = function (selector, count, callback) {
+ return casper.waitFor(function () {
+ return casper.evaluate(function (selector, count) {
+ return document.querySelectorAll(selector).length === count;
+ }, selector, count)
+ }, callback);
+};
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-spec.js b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-spec.js
index e2cf097fab7..bc7a0cb2fd6 100644
--- a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-spec.js
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-spec.js
@@ -1,11 +1,12 @@
-var lib = require('../lib');
+var lib = require('../lib'),
+ testName = lib.testName('Coding Rules');
lib.initMessages();
lib.changeWorkingDirectory('coding-rules-spec');
-casper.test.begin('Coding Rules - Readonly Tests', function suite(test) {
+casper.test.begin(testName('Readonly Tests'), function suite(test) {
var appId = null;
var showId = null;
@@ -44,6 +45,7 @@ casper.test.begin('Coding Rules - Readonly Tests', function suite(test) {
test.assertSelectorHasText('ol.navigator-results-list li.active', 'BETA');
});
+
casper.waitForSelector('h3.coding-rules-detail-header', function showFirstRule() {
test.assertSelectorHasText('h3.coding-rules-detail-header', 'No empty line');
test.assertSelectorHasText('.navigator-details .subtitle', 'squid-xoo:x1');
@@ -53,7 +55,28 @@ casper.test.begin('Coding Rules - Readonly Tests', function suite(test) {
test.assertSelectorHasText('.coding-rules-detail-property:nth-child(4)', 'Testability > Integration level testability');
test.assertSelectorHasText('.coding-rules-detail-property:nth-child(6)', 'SonarQube (Xoo)');
+
+ casper.click('.coding-rules-subcharacteristic');
+ casper.waitForSelector('.coding-rules-debt-popup', function checkDebtPopup() {
+ test.assertElementCount('ul.bubble-popup-list li', 3);
+ test.assertSelectorHasText('.bubble-popup-list li:nth-child(1)', 'LINEAR_OFFSET');
+ test.assertSelectorHasText('.bubble-popup-list li:nth-child(2)', '1h');
+ test.assertSelectorHasText('.bubble-popup-list li:nth-child(3)', '30min');
+ });
+
+
test.assertDoesntExist('button#coding-rules-detail-extend-description');
+
+
+ casper.then(function checkParameters() {
+ test.assertElementCount('.coding-rules-detail-parameter', 3);
+ test.assertVisible('.coding-rules-detail-parameter-description[data-key=acceptWhitespace]');
+ test.assertSelectorHasText('.coding-rules-detail-parameter-description[data-key=acceptWhitespace]', 'Accept whitespace');
+ casper.click('.coding-rules-detail-parameter:nth-child(1) .coding-rules-detail-parameter-name');
+ test.assertNotVisible('.coding-rules-detail-parameter-description[data-key=acceptWhitespace]');
+ casper.click('.coding-rules-detail-parameter:nth-child(1) .coding-rules-detail-parameter-name');
+ test.assertVisible('.coding-rules-detail-parameter-description[data-key=acceptWhitespace]');
+ });
});
});
@@ -75,7 +98,7 @@ casper.test.begin('Coding Rules - Readonly Tests', function suite(test) {
});
});
-casper.test.begin('Coding Rules - Admin Tests', function suite(test) {
+casper.test.begin(testName('Admin Tests'), function suite(test) {
var showId = null;
var updateId = null;
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-spec/show_x1.json b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-spec/show_x1.json
index e3c47578ff2..d360d7b4730 100644
--- a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-spec/show_x1.json
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-spec/show_x1.json
@@ -38,7 +38,20 @@
{
"key": "acceptWhitespace",
"htmlDesc": "<h1>Accept whitespace (<code>\\s|\\t</code>) on the line\n</h1>This property is available so that a line containing only whitespace is not considered empty.<br/><h2>Example with property set to ``false``\n</h2><pre lang=\"xoo\"><code> &lt;- One issue here\n&lt;- And one here</code></pre><br/><br/><h2>Example with property set to ``true``\n</h2><pre lang=\"xoo\"><code> &lt;- No issue here\n&lt;- But one here</code></pre><br/>",
+ "type": "BOOLEAN",
"defaultValue": "false"
+ },
+ {
+ "key": "textParameter",
+ "htmlDesc": "This is a parameter of type TEXT",
+ "type": "TEXT",
+ "defaultValue": "false"
+ },
+ {
+ "key": "skipLines",
+ "htmlDesc": "Skip a number of lines at the beginning of the file",
+ "type": "INTEGER",
+ "defaultValue": "0"
}
]
},
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec.js b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec.js
index 1058b8a1838..d01f7992644 100644
--- a/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec.js
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec.js
@@ -1,10 +1,11 @@
-var lib = require('../lib');
+var lib = require('../lib'),
+ testName = lib.testName('Component Viewer');
lib.initMessages();
lib.changeWorkingDirectory('component-viewer-spec');
-casper.test.begin('Component Viewer Base Tests', function (test) {
+casper.test.begin(testName('Base'), function (test) {
casper
.start(lib.buildUrl('component-viewer#component=component'), function () {
lib.setDefaultViewport();
@@ -12,24 +13,18 @@ casper.test.begin('Component Viewer Base Tests', function (test) {
lib.mockRequest('/api/l10n/index', '{}');
lib.mockRequestFromFile('/api/components/app', 'app.json');
lib.mockRequestFromFile('/api/sources/show', 'source.json');
- lib.mockRequestFromFile('/api/issues/search', 'issues.json');
- lib.mockRequestFromFile('/api/coverage/show', 'coverage.json');
- lib.mockRequestFromFile('/api/duplications/show', 'duplications.json');
- lib.mockRequestFromFile('/api/sources/scm', 'scm.json');
- lib.mockRequest('*', '{}'); // Trick to see all ajax requests
})
.then(function () {
casper.waitForSelector('.component-viewer-source .row', function () {
-
// Check header elements
- test.assertElementCount('.component-viewer-header', 1);
+ test.assertExists('.component-viewer-header');
test.assertSelectorContains('.component-viewer-header-component-project', 'SonarQube');
test.assertSelectorContains('.component-viewer-header-component-project', 'SonarQube :: Batch');
test.assertSelectorContains('.component-viewer-header-component-name',
'src/main/java/org/sonar/batch/index/Cache.java');
- test.assertElementCount('.component-viewer-header-favorite', 1);
- test.assertElementCount('.component-viewer-header-actions', 1);
+ test.assertExists('.component-viewer-header-favorite');
+ test.assertExists('.component-viewer-header-actions');
// Check main measures
test.assertSelectorContains('.js-header-tab-basic', '379');
@@ -37,18 +32,42 @@ casper.test.begin('Component Viewer Base Tests', function (test) {
test.assertSelectorContains('.js-header-tab-issues', '3h 30min');
test.assertSelectorContains('.js-header-tab-issues', '6');
test.assertSelectorContains('.js-header-tab-coverage', '74.3%');
- test.assertElementCount('.js-header-tab-scm', 1);
+ test.assertExists('.js-header-tab-scm');
// Check source
test.assertElementCount('.component-viewer-source .row', 520);
test.assertSelectorContains('.component-viewer-source', 'public class Cache');
// Check workspace
- test.assertElementCount('.component-viewer-workspace', 1);
+ test.assertExists('.component-viewer-workspace');
});
})
- .then(function() {
+ .run(function () {
+ test.done();
+ });
+});
+
+
+casper.test.begin(testName('Decoration'), function (test) {
+ casper
+ .start(lib.buildUrl('component-viewer#component=component'), function () {
+ lib.setDefaultViewport();
+
+ lib.mockRequest('/api/l10n/index', '{}');
+ lib.mockRequestFromFile('/api/components/app', 'app.json');
+ lib.mockRequestFromFile('/api/sources/show', 'source.json');
+ lib.mockRequestFromFile('/api/issues/search', 'issues.json');
+ lib.mockRequestFromFile('/api/coverage/show', 'coverage.json');
+ lib.mockRequestFromFile('/api/duplications/show', 'duplications.json');
+ lib.mockRequestFromFile('/api/sources/scm', 'scm.json');
+ })
+
+ .then(function () {
+ casper.waitForSelector('.component-viewer-source .row');
+ })
+
+ .then(function () {
// Check issues decoration
casper.click('.js-toggle-issues');
casper.waitForSelector('.code-issue', function () {
@@ -57,7 +76,7 @@ casper.test.begin('Component Viewer Base Tests', function (test) {
casper.click('.js-toggle-issues');
casper.waitWhileSelector('.code-issue', function () {
- test.assertElementCount('.code-issue', 0);
+ test.assertDoesntExist('.code-issue');
});
});
})
@@ -74,7 +93,7 @@ casper.test.begin('Component Viewer Base Tests', function (test) {
casper.click('.js-toggle-coverage');
casper.waitWhileSelector('.coverage-green', function () {
- test.assertElementCount('.coverage-green', 0);
+ test.assertDoesntExist('.coverage-green');
});
});
})
@@ -87,7 +106,7 @@ casper.test.begin('Component Viewer Base Tests', function (test) {
casper.click('.js-toggle-duplications');
casper.waitWhileSelector('.duplication-exists', function () {
- test.assertElementCount('.duplication-exists', 0);
+ test.assertDoesntExist('.duplication-exists');
});
});
})
@@ -104,8 +123,148 @@ casper.test.begin('Component Viewer Base Tests', function (test) {
casper.click('.js-toggle-scm');
casper.waitWhileSelector('.scm-author', function () {
- test.assertElementCount('.scm-author', 0);
- test.assertElementCount('.scm-date', 0);
+ test.assertDoesntExist('.scm-author');
+ test.assertDoesntExist('.scm-date');
+ });
+ });
+ })
+
+ .run(function () {
+ test.done();
+ });
+});
+
+
+casper.test.begin(testName('Header'), function (test) {
+ casper
+ .start(lib.buildUrl('component-viewer#component=component'), function () {
+ lib.setDefaultViewport();
+
+ lib.mockRequest('/api/l10n/index', '{}');
+ lib.mockRequestFromFile('/api/components/app', 'app.json');
+ lib.mockRequestFromFile('/api/sources/show', 'source.json');
+ lib.mockRequestFromFile('/api/resources', 'resources.json');
+ })
+
+ .then(function () {
+ casper.waitForSelector('.component-viewer-source .row');
+ })
+
+ .then(function () {
+ // Check issues header and filters
+ casper.click('.js-header-tab-issues');
+ casper.waitForSelector('.js-filter-unresolved-issues', function () {
+ test.assertExists('.js-filter-open-issues');
+ test.assertExists('.js-filter-fixed-issues');
+ test.assertExists('.js-filter-false-positive-issues');
+ test.assertSelectorContains('.js-filter-MAJOR-issues', '1');
+ test.assertSelectorContains('.js-filter-MINOR-issues', '1');
+ test.assertSelectorContains('.js-filter-INFO-issues', '4');
+ test.assertSelectorContains('.js-filter-rule[data-rule="common-java:DuplicatedBlocks"]', '1');
+ test.assertSelectorContains('.js-filter-rule[data-rule="squid:S1192"]', '1');
+ test.assertSelectorContains('.js-filter-rule[data-rule="squid:S1135"]', '4');
+ test.assertExists('.js-issues-time-changes');
+
+ casper.click('.js-header-tab-issues');
+ casper.waitWhileSelector('.js-filter-unresolved-issues', function () {
+ test.assertDoesntExist('.js-filter-open-issues');
+ test.assertDoesntExist('.js-filter-MAJOR-issues');
+ test.assertDoesntExist('.js-filter-rule');
+ });
+ });
+ })
+
+ .then(function () {
+ // Check coverage header and filters
+ casper.click('.js-header-tab-coverage');
+ casper.waitForSelector('.js-filter-lines-to-cover', function () {
+ test.assertExists('.js-filter-uncovered-lines');
+ test.assertExists('.js-filter-branches-to-cover');
+ test.assertExists('.js-filter-uncovered-branches');
+ test.assertSelectorContains('[data-metric="coverage"]', '74.3%');
+ test.assertSelectorContains('[data-metric="line_coverage"]', '74.2%');
+ test.assertSelectorContains('[data-metric="lines_to_cover"]', '194');
+ test.assertSelectorContains('[data-metric="uncovered_lines"]', '50');
+ test.assertSelectorContains('[data-metric="branch_coverage"]', '75.0%');
+ test.assertSelectorContains('[data-metric="conditions_to_cover"]', '16');
+ test.assertSelectorContains('[data-metric="uncovered_conditions"]', '4');
+ test.assertExists('.js-coverage-time-changes');
+
+ casper.click('.js-header-tab-coverage');
+ casper.waitWhileSelector('.js-filter-lines-to-cover', function () {
+ test.assertDoesntExist('.js-filter-uncovered-lines');
+ test.assertDoesntExist('[data-metric="coverage"]');
+ test.assertDoesntExist('[data-metric="branch_coverage"]');
+ });
+ });
+ })
+
+ .then(function () {
+ // Check duplications header and filters
+ casper.click('.js-header-tab-duplications');
+ casper.waitForSelector('.js-filter-duplications', function () {
+ test.assertSelectorContains('[data-metric="duplicated_blocks"]', '2');
+ test.assertSelectorContains('[data-metric="duplicated_lines"]', '30');
+
+ casper.click('.js-header-tab-duplications');
+ casper.waitWhileSelector('.js-filter-duplications', function () {
+ test.assertDoesntExist('[data-metric="duplicated_blocks"]');
+ test.assertDoesntExist('[data-metric="duplicated_lines"]');
+ });
+ });
+ })
+
+ .then(function () {
+ // Check scm header and filters
+ casper.click('.js-header-tab-scm');
+ casper.waitForSelector('.js-filter-modified-lines', function () {
+ test.assertExists('.js-scm-time-changes');
+
+ casper.click('.js-header-tab-scm');
+ casper.waitWhileSelector('.js-filter-modified-lines', function () {
+ test.assertDoesntExist('.js-scm-time-changes');
+ });
+ });
+ })
+
+ .run(function () {
+ test.done();
+ });
+});
+
+
+casper.test.begin(testName('Test File'), function (test) {
+ casper
+ .start(lib.buildUrl('component-viewer#component=component'), function () {
+ lib.setDefaultViewport();
+
+ lib.mockRequest('/api/l10n/index', '{}');
+ lib.mockRequestFromFile('/api/components/app', 'tests/app.json');
+ lib.mockRequestFromFile('/api/sources/show', 'tests/source.json');
+ lib.mockRequestFromFile('/api/resources', 'tests/resources.json');
+ lib.mockRequestFromFile('/api/tests/show', 'tests/tests.json');
+ lib.mockRequestFromFile('/api/tests/covered_files', 'tests/covered-files.json');
+ })
+
+ .then(function () {
+ casper.waitForSelector('.component-viewer-source .row');
+ })
+
+ .then(function () {
+ // Check coverage header and filters
+ casper.click('.js-header-tab-tests');
+ casper.waitForSelector('.js-unit-test', function () {
+ test.assertSelectorContains('[data-metric="test_execution_time"]', '12');
+ test.assertElementCount('.js-unit-test', 2);
+ test.assertSelectorContains('.js-unit-test[data-name="should_return_i"]', 'should_return_i');
+ test.assertSelectorContains('.js-unit-test[data-name="should_return_i"]', '5');
+ test.assertSelectorContains('.js-unit-test[data-name="should_return_to_string"]', 'should_return_to_string');
+ test.assertSelectorContains('.js-unit-test[data-name="should_return_to_string"]', '4');
+
+ casper.click('.js-unit-test[data-name="should_return_to_string"]');
+ casper.waitForSelector('.bubble-popup', function () {
+ test.assertSelectorContains('.bubble-popup', 'Sample.java');
+ test.assertSelectorContains('.bubble-popup', 'src/main/java/sample');
});
});
})
@@ -114,3 +273,261 @@ casper.test.begin('Component Viewer Base Tests', function (test) {
test.done();
});
});
+
+
+casper.test.begin(testName('Go From Coverage to Test File'), function (test) {
+ casper
+ .start(lib.buildUrl('component-viewer#component=component'), function () {
+ lib.setDefaultViewport();
+ lib.mockRequest('/api/l10n/index', '{}');
+ lib.mockRequestFromFile('/api/components/app', 'app.json');
+ lib.mockRequestFromFile('/api/sources/show', 'source.json');
+ lib.mockRequestFromFile('/api/coverage/show', 'coverage.json');
+ lib.mockRequestFromFile('/api/tests/test_cases', 'test-cases.json');
+ })
+
+ .then(function () {
+ casper.waitForSelector('.component-viewer-source .row');
+ })
+
+ .then(function () {
+ casper.click('.js-toggle-coverage');
+ casper.waitForSelector('.coverage-green', function () {
+ casper.click('.coverage-green .coverage-tests');
+ casper.waitForSelector('.bubble-popup', function () {
+ test.assertSelectorContains('.bubble-popup', 'src/test/java/org/sonar/batch/issue/IssueCacheTest.java');
+ test.assertSelectorContains('.bubble-popup', 'should_update_existing_issue');
+ test.assertSelectorContains('.bubble-popup li[title="should_update_existing_issue"]', '293');
+
+ lib.clearRequestMocks();
+ lib.mockRequestFromFile('/api/components/app', 'tests/app.json');
+ lib.mockRequestFromFile('/api/sources/show', 'tests/source.json');
+ lib.mockRequestFromFile('/api/resources', 'tests/resources.json');
+ lib.mockRequest('/api/coverage/show', '{}');
+ lib.mockRequestFromFile('/api/tests/show', 'tests/tests.json');
+ casper.click('.component-viewer-popup-test-file[data-key]');
+
+ casper.waitForSelector('.js-unit-test', function () {
+ test.assertElementCount('.js-unit-test', 2);
+ });
+ });
+ });
+ })
+
+ .run(function () {
+ test.done();
+ });
+});
+
+
+casper.test.begin(testName('Coverage Filters'), function (test) {
+ casper
+ .start(lib.buildUrl('component-viewer#component=component'), function () {
+ lib.setDefaultViewport();
+ lib.mockRequest('/api/l10n/index', '{}');
+ lib.mockRequestFromFile('/api/components/app', 'app.json');
+ lib.mockRequestFromFile('/api/sources/show', 'source.json');
+ lib.mockRequestFromFile('/api/resources', 'resources.json');
+ lib.mockRequestFromFile('/api/coverage/show', 'coverage.json');
+ })
+
+ .then(function () {
+ casper.waitForSelector('.component-viewer-source .row');
+ })
+
+ .then(function () {
+ casper.click('.js-header-tab-coverage');
+ casper.waitForSelector('.js-filter-lines-to-cover');
+ })
+
+ .then(function () {
+ casper.click('.js-filter-lines-to-cover');
+ casper.waitForSelector('.coverage-green', function () {
+ test.assertElementCount('.coverage-green', 149);
+ test.assertElementCount('.coverage-red', 51);
+ test.assertElementCount('.coverage-orange', 2);
+ test.assertElementCount('.component-viewer-source .row', 369);
+ });
+ })
+
+ .then(function () {
+ casper.click('.js-filter-uncovered-lines');
+ casper.waitForSelector('.coverage-green', function () {
+ test.assertElementCount('.coverage-green', 18);
+ test.assertElementCount('.coverage-red', 51);
+ test.assertElementCount('.coverage-orange', 0);
+ test.assertElementCount('.component-viewer-source .row', 136);
+ });
+ })
+
+ .then(function () {
+ casper.click('.js-filter-branches-to-cover');
+ casper.waitForSelector('.coverage-green', function () {
+ test.assertElementCount('.coverage-green', 26);
+ test.assertElementCount('.coverage-red', 4);
+ test.assertElementCount('.coverage-orange', 2);
+ test.assertElementCount('.component-viewer-source .row', 33);
+ });
+ })
+
+ .then(function () {
+ casper.click('.js-filter-uncovered-branches');
+ casper.waitForSelector('.coverage-green', function () {
+ test.assertElementCount('.coverage-green', 6);
+ test.assertElementCount('.coverage-red', 4);
+ test.assertElementCount('.coverage-orange', 2);
+ test.assertElementCount('.component-viewer-source .row', 13);
+ });
+ })
+
+ .run(function () {
+ test.done();
+ });
+});
+
+
+casper.test.begin(testName('Ability to Deselect Filters'), function (test) {
+ casper
+ .start(lib.buildUrl('component-viewer#component=component'), function () {
+ lib.setDefaultViewport();
+ lib.mockRequest('/api/l10n/index', '{}');
+ lib.mockRequestFromFile('/api/components/app', 'app.json');
+ lib.mockRequestFromFile('/api/sources/show', 'source.json');
+ lib.mockRequestFromFile('/api/resources', 'resources.json');
+ lib.mockRequestFromFile('/api/issues/search', 'issues.json');
+ lib.mockRequestFromFile('/api/coverage/show', 'coverage.json');
+ lib.mockRequestFromFile('/api/duplications/show', 'duplications.json');
+ })
+
+ .then(function () {
+ casper.waitForSelector('.component-viewer-source .row');
+ })
+
+ .then(function () {
+ casper.click('.js-header-tab-issues');
+ var testFilter = '.js-filter-unresolved-issues';
+ casper.waitForSelector(testFilter, function () {
+ casper.click(testFilter);
+ lib.waitForElementCount('.component-viewer-source .row', 56, function () {
+ test.assertExists(testFilter + '.active');
+ casper.click(testFilter);
+ lib.waitForElementCount('.component-viewer-source .row', 520, function () {
+ test.assertDoesntExist(testFilter + '.active');
+ casper.click(testFilter);
+ lib.waitForElementCount('.component-viewer-source .row', 56, function () {
+ test.assertExists(testFilter + '.active');
+ });
+ });
+ })
+ });
+ })
+
+ .then(function () {
+ casper.click('.js-header-tab-coverage');
+ var testFilter = '.js-filter-lines-to-cover';
+ casper.waitForSelector(testFilter, function () {
+ casper.click(testFilter);
+ lib.waitForElementCount('.component-viewer-source .row', 369, function () {
+ test.assertExists(testFilter + '.active');
+ casper.click(testFilter);
+ lib.waitForElementCount('.component-viewer-source .row', 520, function () {
+ test.assertDoesntExist(testFilter + '.active');
+ casper.click(testFilter);
+ lib.waitForElementCount('.component-viewer-source .row', 369, function () {
+ test.assertExists(testFilter + '.active');
+ });
+ });
+ })
+ });
+ })
+
+ .then(function () {
+ casper.click('.js-header-tab-duplications');
+ var testFilter = '.js-filter-duplications';
+ casper.waitForSelector(testFilter, function () {
+ casper.click(testFilter);
+ lib.waitForElementCount('.component-viewer-source .row', 39, function () {
+ test.assertExists(testFilter + '.active');
+ casper.click(testFilter);
+ lib.waitForElementCount('.component-viewer-source .row', 520, function () {
+ test.assertDoesntExist(testFilter + '.active');
+ casper.click(testFilter);
+ lib.waitForElementCount('.component-viewer-source .row', 39, function () {
+ test.assertExists(testFilter + '.active');
+ });
+ });
+ })
+ });
+ })
+
+ .run(function () {
+ test.done();
+ });
+});
+
+
+casper.test.begin(testName('Cross-Project Duplications'), function (test) {
+ casper
+ .start(lib.buildUrl('component-viewer#component=component'), function () {
+ lib.setDefaultViewport();
+ lib.mockRequest('/api/l10n/index', '{}');
+ lib.mockRequestFromFile('/api/components/app', 'app.json');
+ lib.mockRequestFromFile('/api/sources/show', 'source.json');
+ lib.mockRequestFromFile('/api/resources', 'resources.json');
+ lib.mockRequestFromFile('/api/duplications/show', 'cross-project-duplications.json');
+ })
+
+ .then(function () {
+ casper.waitForSelector('.component-viewer-source .row');
+ })
+
+ .then(function () {
+ casper.click('.js-header-tab-duplications');
+ casper.waitForSelector('.js-filter-duplications', function () {
+ casper.click('.js-filter-duplications');
+ casper.waitForSelector('.duplication-exists', function () {
+ casper.click('.duplication-exists');
+ casper.waitForSelector('.bubble-popup', function () {
+ test.assertSelectorContains('.bubble-popup', 'JavaScript');
+ test.assertSelectorContains('.bubble-popup', 'JavaScript :: Sonar Plugin');
+ test.assertExists('a[data-key="org.codehaus.sonar-plugins.javascript:sonar-javascript-plugin:src/main/java/org/sonar/plugins/javascript/core/JavaScript.java"]');
+ test.assertSelectorContains('.bubble-popup', 'src/main/java/org/sonar/plugins/javascript/core/JavaScript.java');
+ test.assertSelectorContains('.bubble-popup', '455'); // Line from
+ test.assertSelectorContains('.bubble-popup', '470'); // Line to
+ });
+ });
+ });
+ })
+
+ .run(function () {
+ test.done();
+ });
+});
+
+
+casper.test.begin(testName('Duplications in Deleted Files'), function (test) {
+ casper
+ .start(lib.buildUrl('component-viewer#component=component'), function () {
+ lib.setDefaultViewport();
+ lib.mockRequest('/api/l10n/index', '{}');
+ lib.mockRequestFromFile('/api/components/app', 'app.json');
+ lib.mockRequestFromFile('/api/sources/show', 'source.json');
+ lib.mockRequestFromFile('/api/resources', 'resources.json');
+ lib.mockRequestFromFile('/api/duplications/show', 'duplications-in-deleted-files.json');
+ })
+
+ .then(function () {
+ casper.waitForSelector('.component-viewer-source .row');
+ })
+
+ .then(function () {
+ casper.click('.js-toggle-duplications');
+ casper.waitForSelector('.duplication-exists', function () {
+ test.assertExists('.js-duplications-in-deleted-files');
+ });
+ })
+
+ .run(function () {
+ test.done();
+ });
+});
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/cross-project-duplications.json b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/cross-project-duplications.json
new file mode 100644
index 00000000000..cd445d5f5b2
--- /dev/null
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/cross-project-duplications.json
@@ -0,0 +1,33 @@
+{"duplications": [
+ {
+ "blocks": [
+ {
+ "from": 404,
+ "size": 15,
+ "_ref": "1"
+ },
+ {
+ "from": 455,
+ "size": 15,
+ "_ref": "2"
+ }
+ ]
+ }
+], "files": {
+ "1": {
+ "key": "org.codehaus.sonar:sonar-batch:src/main/java/org/sonar/batch/index/Cache.java",
+ "name": "src/main/java/org/sonar/batch/index/Cache.java",
+ "project": "org.codehaus.sonar:sonar",
+ "projectName": "SonarQube",
+ "subProject": "org.codehaus.sonar:sonar-batch",
+ "subProjectName": "SonarQube :: Batch"
+ },
+ "2": {
+ "key": "org.codehaus.sonar-plugins.javascript:sonar-javascript-plugin:src/main/java/org/sonar/plugins/javascript/core/JavaScript.java",
+ "name": "src/main/java/org/sonar/plugins/javascript/core/JavaScript.java",
+ "project": "org.codehaus.sonar-plugins.javascript:javascript",
+ "projectName": "JavaScript",
+ "subProject": "org.codehaus.sonar-plugins.javascript:sonar-javascript-plugin",
+ "subProjectName": "JavaScript :: Sonar Plugin"
+ }
+}}
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/duplications-in-deleted-files.json b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/duplications-in-deleted-files.json
new file mode 100644
index 00000000000..9de23d1ce7d
--- /dev/null
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/duplications-in-deleted-files.json
@@ -0,0 +1,24 @@
+{"duplications": [
+ {
+ "blocks": [
+ {
+ "from": 404,
+ "size": 15,
+ "_ref": "1"
+ },
+ {
+ "from": 455,
+ "size": 15
+ }
+ ]
+ }
+], "files": {
+ "1": {
+ "key": "org.codehaus.sonar:sonar-batch:src/main/java/org/sonar/batch/index/Cache.java",
+ "name": "src/main/java/org/sonar/batch/index/Cache.java",
+ "project": "org.codehaus.sonar:sonar",
+ "projectName": "SonarQube",
+ "subProject": "org.codehaus.sonar:sonar-batch",
+ "subProjectName": "SonarQube :: Batch"
+ }
+}}
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/resources.json b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/resources.json
new file mode 100644
index 00000000000..415718c7cd5
--- /dev/null
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/resources.json
@@ -0,0 +1,154 @@
+[
+ {
+ "id": 19983,
+ "key": "org.codehaus.sonar:sonar-batch:src/main/java/org/sonar/batch/index/Cache.java",
+ "name": "Cache.java",
+ "scope": "FIL",
+ "qualifier": "FIL",
+ "date": "2014-07-21T23:18:51+0200",
+ "creationDate": "2013-04-17T04:06:45+0200",
+ "lname": "src/main/java/org/sonar/batch/index/Cache.java",
+ "lang": "java",
+ "msr": [
+ {
+ "key": "lines",
+ "val": 519.0,
+ "frmt_val": "519"
+ },
+ {
+ "key": "ncloc",
+ "val": 379.0,
+ "frmt_val": "379"
+ },
+ {
+ "key": "classes",
+ "val": 6.0,
+ "frmt_val": "6"
+ },
+ {
+ "key": "functions",
+ "val": 56.0,
+ "frmt_val": "56"
+ },
+ {
+ "key": "accessors",
+ "val": 0.0,
+ "frmt_val": "0"
+ },
+ {
+ "key": "statements",
+ "val": 174.0,
+ "frmt_val": "174"
+ },
+ {
+ "key": "public_api",
+ "val": 33.0,
+ "frmt_val": "33"
+ },
+ {
+ "key": "comment_lines",
+ "val": 23.0,
+ "frmt_val": "23"
+ },
+ {
+ "key": "comment_lines_density",
+ "val": 5.7,
+ "frmt_val": "5.7%"
+ },
+ {
+ "key": "public_documented_api_density",
+ "val": 36.4,
+ "frmt_val": "36.4%"
+ },
+ {
+ "key": "public_undocumented_api",
+ "val": 21.0,
+ "frmt_val": "21"
+ },
+ {
+ "key": "complexity",
+ "val": 116.0,
+ "frmt_val": "116"
+ },
+ {
+ "key": "function_complexity",
+ "val": 2.1,
+ "frmt_val": "2.1"
+ },
+ {
+ "key": "coverage",
+ "val": 74.3,
+ "frmt_val": "74.3%"
+ },
+ {
+ "key": "lines_to_cover",
+ "val": 194.0,
+ "frmt_val": "194"
+ },
+ {
+ "key": "uncovered_lines",
+ "val": 50.0,
+ "frmt_val": "50"
+ },
+ {
+ "key": "line_coverage",
+ "val": 74.2,
+ "frmt_val": "74.2%"
+ },
+ {
+ "key": "conditions_to_cover",
+ "val": 16.0,
+ "frmt_val": "16"
+ },
+ {
+ "key": "uncovered_conditions",
+ "val": 4.0,
+ "frmt_val": "4"
+ },
+ {
+ "key": "branch_coverage",
+ "val": 75.0,
+ "frmt_val": "75.0%"
+ },
+ {
+ "key": "duplicated_lines",
+ "val": 30.0,
+ "frmt_val": "30"
+ },
+ {
+ "key": "duplicated_blocks",
+ "val": 2.0,
+ "frmt_val": "2"
+ },
+ {
+ "key": "duplicated_files",
+ "val": 1.0,
+ "frmt_val": "1"
+ },
+ {
+ "key": "duplicated_lines_density",
+ "val": 5.8,
+ "frmt_val": "5.8%"
+ },
+ {
+ "key": "major_violations",
+ "val": 1.0,
+ "frmt_val": "1"
+ },
+ {
+ "key": "minor_violations",
+ "val": 1.0,
+ "frmt_val": "1"
+ },
+ {
+ "key": "info_violations",
+ "val": 4.0,
+ "frmt_val": "4"
+ },
+ {
+ "key": "ncloc_data",
+ "data": "1=0;2=0;3=0;4=0;5=0;6=0;7=0;8=0;9=0;10=0;11=0;12=0;13=0;14=0;15=0;17=0;16=0;19=0;18=0;21=0;20=1;23=1;22=1;25=1;24=1;27=1;26=1;29=1;28=0;31=1;30=0;34=1;35=0;32=1;33=1;38=0;39=0;36=0;37=0;42=0;43=1;40=0;41=1;46=1;47=1;44=1;45=0;51=1;50=0;49=1;48=1;55=0;54=1;53=1;52=1;59=1;58=1;57=1;56=1;63=1;62=1;61=1;60=0;68=1;69=1;70=0;71=1;64=1;65=0;66=1;67=1;76=1;77=1;78=1;79=1;72=1;73=1;74=1;75=1;85=1;84=1;87=1;86=1;81=0;80=0;83=0;82=0;93=1;92=1;95=1;94=1;89=0;88=0;91=0;90=0;102=1;103=1;100=0;101=1;98=0;99=0;96=1;97=0;110=1;111=1;108=0;109=0;106=0;107=0;104=1;105=1;119=1;118=1;117=1;116=1;115=0;114=1;113=1;112=1;127=1;126=0;125=1;124=1;123=1;122=1;121=1;120=1;137=1;136=1;139=1;138=1;141=1;140=0;143=1;142=1;129=1;128=1;131=1;130=0;133=1;132=1;135=0;134=1;152=1;153=1;154=1;155=1;156=0;157=1;158=1;159=1;144=1;145=0;146=1;147=1;148=1;149=1;150=0;151=1;171=1;170=0;169=1;168=1;175=0;174=1;173=1;172=1;163=1;162=1;161=1;160=0;167=1;166=1;165=0;164=1;186=1;187=1;184=1;185=0;190=0;191=0;188=1;189=0;178=1;179=1;176=1;177=1;182=1;183=1;180=0;181=1;205=1;204=0;207=1;206=1;201=1;200=1;203=1;202=1;197=1;196=1;199=0;198=1;193=0;192=0;195=1;194=0;220=1;221=1;222=1;223=1;216=1;217=1;218=1;219=1;212=1;213=1;214=0;215=1;208=1;209=0;210=1;211=1;239=0;238=0;237=0;236=1;235=1;234=1;233=1;232=1;231=1;230=1;229=1;228=0;227=0;226=0;225=0;224=1;254=1;255=1;252=1;253=1;250=1;251=1;248=1;249=1;246=1;247=1;244=0;245=1;242=0;243=0;240=0;241=0;275=1;274=1;273=1;272=1;279=0;278=1;277=1;276=1;283=0;282=0;281=0;280=0;287=1;286=1;285=1;284=0;258=1;259=1;256=1;257=1;262=1;263=1;260=1;261=0;266=1;267=1;264=1;265=1;270=1;271=1;268=1;269=1;305=1;304=1;307=1;306=1;309=1;308=1;311=1;310=1;313=1;312=1;315=1;314=0;317=1;316=1;319=0;318=0;288=1;289=1;290=1;291=1;292=1;293=1;294=1;295=1;296=1;297=1;298=1;299=0;300=0;301=0;302=0;303=1;343=1;342=1;341=1;340=1;339=1;338=1;337=1;336=0;351=1;350=1;349=1;348=1;347=0;346=1;345=1;344=1;326=1;327=1;324=1;325=1;322=1;323=1;320=0;321=0;334=0;335=0;332=1;333=0;330=1;331=1;328=1;329=1;373=1;372=1;375=1;374=0;369=0;368=1;371=1;370=1;381=0;380=1;383=0;382=0;377=1;376=1;379=1;378=1;356=1;357=1;358=1;359=0;352=1;353=0;354=1;355=1;364=0;365=1;366=1;367=1;360=1;361=1;362=1;363=1;410=1;411=1;408=1;409=1;414=1;415=1;412=1;413=1;402=0;403=1;400=1;401=1;406=1;407=0;404=1;405=1;395=1;394=1;393=1;392=0;399=1;398=0;397=1;396=1;387=1;386=1;385=0;384=0;391=1;390=1;389=1;388=0;440=1;441=1;442=1;443=0;444=1;445=1;446=1;447=1;432=1;433=1;434=1;435=1;436=0;437=1;438=1;439=0;425=1;424=1;427=1;426=1;429=1;428=1;431=1;430=0;417=1;416=0;419=1;418=1;421=1;420=1;423=1;422=1;478=1;479=1;476=1;477=1;474=1;475=1;472=1;473=1;470=1;471=1;468=1;469=1;466=1;467=0;464=1;465=1;463=1;462=1;461=1;460=1;459=1;458=0;457=1;456=1;455=1;454=1;453=0;452=1;451=1;450=1;449=0;448=1;508=1;509=1;510=1;511=0;504=1;505=1;506=0;507=1;500=1;501=1;502=0;503=1;496=1;497=0;498=1;499=1;493=0;492=1;495=1;494=1;489=1;488=1;491=1;490=1;485=1;484=1;487=0;486=1;481=1;480=1;483=1;482=1;516=1;517=0;518=1;512=1;513=1;514=1;515=1"
+ }
+ ]
+ }
+]
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/test-cases.json b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/test-cases.json
new file mode 100644
index 00000000000..233229a803b
--- /dev/null
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/test-cases.json
@@ -0,0 +1,235 @@
+{"tests": [
+ {
+ "name": "should_update_existing_issue",
+ "status": "OK",
+ "durationInMs": 293,
+ "_ref": "1"
+ },
+ {
+ "name": "testDistributionMeasure",
+ "status": "OK",
+ "durationInMs": 148,
+ "_ref": "2"
+ },
+ {
+ "name": "one_part_key",
+ "status": "OK",
+ "durationInMs": 96,
+ "_ref": "3"
+ },
+ {
+ "name": "testIssueExclusion",
+ "status": "OK",
+ "durationInMs": 305,
+ "_ref": "4"
+ },
+ {
+ "name": "should_add_measure_with_same_metric",
+ "status": "OK",
+ "durationInMs": 141,
+ "_ref": "5"
+ },
+ {
+ "name": "should_create_cache",
+ "status": "OK",
+ "durationInMs": 81,
+ "_ref": "6"
+ },
+ {
+ "name": "scanProjectWithMixedSourcesAndTests",
+ "status": "OK",
+ "durationInMs": 239,
+ "_ref": "7"
+ },
+ {
+ "name": "remove_versus_clear",
+ "status": "OK",
+ "durationInMs": 111,
+ "_ref": "3"
+ },
+ {
+ "name": "should_persist_component_data",
+ "status": "OK",
+ "durationInMs": 132,
+ "_ref": "8"
+ },
+ {
+ "name": "scanProjectWithSourceDir",
+ "status": "OK",
+ "durationInMs": 212,
+ "_ref": "7"
+ },
+ {
+ "name": "should_add_measure_with_too_big_data_for_persistit",
+ "status": "OK",
+ "durationInMs": 336,
+ "_ref": "5"
+ },
+ {
+ "name": "failForDuplicateInputFile",
+ "status": "OK",
+ "durationInMs": 127,
+ "_ref": "7"
+ },
+ {
+ "name": "should_not_create_cache_twice",
+ "status": "OK",
+ "durationInMs": 101,
+ "_ref": "6"
+ },
+ {
+ "name": "should_get_and_set_data",
+ "status": "OK",
+ "durationInMs": 190,
+ "_ref": "9"
+ },
+ {
+ "name": "should_add_input_file",
+ "status": "OK",
+ "durationInMs": 122,
+ "_ref": "10"
+ },
+ {
+ "name": "scanTempProject",
+ "status": "OK",
+ "durationInMs": 345,
+ "_ref": "4"
+ },
+ {
+ "name": "should_get_measures",
+ "status": "OK",
+ "durationInMs": 114,
+ "_ref": "5"
+ },
+ {
+ "name": "test_key_being_prefix_of_another_key",
+ "status": "OK",
+ "durationInMs": 94,
+ "_ref": "3"
+ },
+ {
+ "name": "should_add_measure_with_big_data",
+ "status": "OK",
+ "durationInMs": 242,
+ "_ref": "5"
+ },
+ {
+ "name": "should_get_all_issues",
+ "status": "OK",
+ "durationInMs": 104,
+ "_ref": "1"
+ },
+ {
+ "name": "should_add_measure",
+ "status": "OK",
+ "durationInMs": 117,
+ "_ref": "5"
+ },
+ {
+ "name": "computeMeasuresOnSampleProject",
+ "status": "OK",
+ "durationInMs": 315,
+ "_ref": "2"
+ },
+ {
+ "name": "test_measure_coder",
+ "status": "OK",
+ "durationInMs": 110,
+ "_ref": "5"
+ },
+ {
+ "name": "should_add_measure_with_too_big_data_for_persistit_pre_patch",
+ "status": "OK",
+ "durationInMs": 821,
+ "_ref": "5"
+ },
+ {
+ "name": "three_parts_key",
+ "status": "OK",
+ "durationInMs": 122,
+ "_ref": "3"
+ },
+ {
+ "name": "two_parts_key",
+ "status": "OK",
+ "durationInMs": 118,
+ "_ref": "3"
+ },
+ {
+ "name": "should_add_new_issue",
+ "status": "OK",
+ "durationInMs": 95,
+ "_ref": "1"
+ },
+ {
+ "name": "empty_cache",
+ "status": "OK",
+ "durationInMs": 104,
+ "_ref": "3"
+ },
+ {
+ "name": "scanProjectWithTestDir",
+ "status": "OK",
+ "durationInMs": 322,
+ "_ref": "7"
+ },
+ {
+ "name": "scanSampleProject",
+ "status": "OK",
+ "durationInMs": 184,
+ "_ref": "4"
+ },
+ {
+ "name": "computeMeasuresOnTempProject",
+ "status": "OK",
+ "durationInMs": 274,
+ "_ref": "2"
+ },
+ {
+ "name": "should_get_and_set_string_data",
+ "status": "OK",
+ "durationInMs": 95,
+ "_ref": "9"
+ }
+], "files": {
+ "3": {
+ "key": "org.codehaus.sonar:sonar-batch:src/test/java/org/sonar/batch/index/CacheTest.java",
+ "longName": "src/test/java/org/sonar/batch/index/CacheTest.java"
+ },
+ "1": {
+ "key": "org.codehaus.sonar:sonar-batch:src/test/java/org/sonar/batch/issue/IssueCacheTest.java",
+ "longName": "src/test/java/org/sonar/batch/issue/IssueCacheTest.java"
+ },
+ "4": {
+ "key": "org.codehaus.sonar:sonar-batch:src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java",
+ "longName": "src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java"
+ },
+ "2": {
+ "key": "org.codehaus.sonar:sonar-batch:src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java",
+ "longName": "src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java"
+ },
+ "8": {
+ "key": "org.codehaus.sonar:sonar-batch:src/test/java/org/sonar/batch/index/ComponentDataPersisterTest.java",
+ "longName": "src/test/java/org/sonar/batch/index/ComponentDataPersisterTest.java"
+ },
+ "9": {
+ "key": "org.codehaus.sonar:sonar-batch:src/test/java/org/sonar/batch/index/ComponentDataCacheTest.java",
+ "longName": "src/test/java/org/sonar/batch/index/ComponentDataCacheTest.java"
+ },
+ "10": {
+ "key": "org.codehaus.sonar:sonar-batch:src/test/java/org/sonar/batch/scan/filesystem/InputFileCacheTest.java",
+ "longName": "src/test/java/org/sonar/batch/scan/filesystem/InputFileCacheTest.java"
+ },
+ "7": {
+ "key": "org.codehaus.sonar:sonar-batch:src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java",
+ "longName": "src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java"
+ },
+ "5": {
+ "key": "org.codehaus.sonar:sonar-batch:src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java",
+ "longName": "src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java"
+ },
+ "6": {
+ "key": "org.codehaus.sonar:sonar-batch:src/test/java/org/sonar/batch/index/CachesTest.java",
+ "longName": "src/test/java/org/sonar/batch/index/CachesTest.java"
+ }
+}}
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/tests/app.json b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/tests/app.json
new file mode 100644
index 00000000000..d2793968cb3
--- /dev/null
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/tests/app.json
@@ -0,0 +1,4 @@
+{"key": "com.sonarsource.it.samples:sample-with-tests:src/test/java/sample/SampleTest.java", "path": "src/test/java/sample/SampleTest.java", "name": "SampleTest.java", "longName": "src/test/java/sample/SampleTest.java", "q": "UTS", "project": "com.sonarsource.it.samples:sample-with-tests", "projectName": "Sonar :: Integration Tests :: Sample with tests", "fav": false, "canMarkAsFavourite": false, "canBulkChange": false, "canCreateManualIssue": false, "periods": [], "severities": [], "rules": [], "measures": {
+ "fTests": "2",
+ "fIssues": "0"
+}}
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/tests/covered-files.json b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/tests/covered-files.json
new file mode 100644
index 00000000000..73f2e044078
--- /dev/null
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/tests/covered-files.json
@@ -0,0 +1,7 @@
+{"files": [
+ {
+ "key": "com.sonarsource.it.samples:sample-with-tests:src/main/java/sample/Sample.java",
+ "longName": "src/main/java/sample/Sample.java",
+ "coveredLines": 5
+ }
+]}
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/tests/resources.json b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/tests/resources.json
new file mode 100644
index 00000000000..3490bce6f94
--- /dev/null
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/tests/resources.json
@@ -0,0 +1,25 @@
+[
+ {
+ "id": 5,
+ "key": "com.sonarsource.it.samples:sample-with-tests:src/test/java/sample/SampleTest.java",
+ "name": "SampleTest.java",
+ "scope": "FIL",
+ "qualifier": "UTS",
+ "date": "2014-07-24T10:57:16+0200",
+ "creationDate": "2014-07-24T10:57:17+0200",
+ "lname": "src/test/java/sample/SampleTest.java",
+ "lang": "java",
+ "msr": [
+ {
+ "key": "tests",
+ "val": 2.0,
+ "frmt_val": "2"
+ },
+ {
+ "key": "test_execution_time",
+ "val": 12.0,
+ "frmt_val": "12 ms"
+ }
+ ]
+ }
+]
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/tests/source.json b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/tests/source.json
new file mode 100644
index 00000000000..02e9da776b2
--- /dev/null
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/tests/source.json
@@ -0,0 +1,24 @@
+{"sources": [
+ [1, "<span class=\"k\">package</span> sample;"],
+ [2, ""],
+ [3, "<span class=\"k\">import</span> org.hamcrest.CoreMatchers;"],
+ [4, "<span class=\"k\">import</span> org.junit.Test;"],
+ [5, ""],
+ [6, "<span class=\"k\">import</span> <span class=\"k\">static</span> org.junit.Assert.assertThat;"],
+ [7, ""],
+ [8, "<span class=\"k\">public</span> <span class=\"k\">class</span> SampleTest {"],
+ [9, ""],
+ [10, " <span class=\"a\">@Test</span>"],
+ [11, " <span class=\"k\">public</span> <span class=\"k\">void</span> should_return_i() {"],
+ [12, " Sample sample = <span class=\"k\">new</span> Sample(1);"],
+ [13, " assertThat(sample.getI(), CoreMatchers.is(1));"],
+ [14, " }"],
+ [15, ""],
+ [16, " <span class=\"a\">@Test</span>"],
+ [17, " <span class=\"k\">public</span> <span class=\"k\">void</span> should_return_to_string() {"],
+ [18, " assertThat(<span class=\"k\">new</span> Sample(1).toString(), CoreMatchers.is(<span class=\"s\">\"1\"</span>));"],
+ [19, " }"],
+ [20, ""],
+ [21, "}"],
+ [22, ""]
+]}
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/tests/tests.json b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/tests/tests.json
new file mode 100644
index 00000000000..f1ef789035c
--- /dev/null
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/tests/tests.json
@@ -0,0 +1,14 @@
+{"tests": [
+ {
+ "name": "should_return_to_string",
+ "status": "OK",
+ "durationInMs": 1,
+ "coveredLines": 4
+ },
+ {
+ "name": "should_return_i",
+ "status": "OK",
+ "durationInMs": 11,
+ "coveredLines": 5
+ }
+]}
diff --git a/server/sonar-web/src/main/js/tests/e2e/views/layouts/main.jade b/server/sonar-web/src/main/js/tests/e2e/views/layouts/main.jade
index f00db74124a..6da4815d09a 100644
--- a/server/sonar-web/src/main/js/tests/e2e/views/layouts/main.jade
+++ b/server/sonar-web/src/main/js/tests/e2e/views/layouts/main.jade
@@ -9,7 +9,8 @@ html
var $j = jQuery.noConflict();
window.suppressTranslationWarnings = true;
jQuery.mockjaxSettings.contentType = 'text/json';
- jQuery.mockjaxSettings.responseTime = 250;
+ jQuery.mockjaxSettings.responseTime = 50;
+ jQuery.mockjaxSettings.throwUnmocked = true;
$j(document).ready(function () { $j('.open-modal').modal(); });
block header
body
diff --git a/server/sonar-web/src/main/less/component-viewer.less b/server/sonar-web/src/main/less/component-viewer.less
index 8ba6499c025..84ce9a28f63 100644
--- a/server/sonar-web/src/main/less/component-viewer.less
+++ b/server/sonar-web/src/main/less/component-viewer.less
@@ -70,6 +70,7 @@
white-space: nowrap;
&.active > a { font-weight: bold; }
+ .subtitle { line-height: 16px; }
}
.component-viewer-workspace-item + .component-viewer-workspace-item {
diff --git a/server/sonar-web/src/main/less/style.less b/server/sonar-web/src/main/less/style.less
index e7b6275252d..8a607cfa136 100644
--- a/server/sonar-web/src/main/less/style.less
+++ b/server/sonar-web/src/main/less/style.less
@@ -565,7 +565,7 @@ table.form td img {
height: 1px;
}
-table#project-history > tbody > tr > td {
+table#project-history tr > td {
vertical-align: top;
}
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb
index 5cf89173487..934cb04c518 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb
@@ -155,9 +155,9 @@
if update_center_activated %>
<li class="<%= 'active' if controller.controller_path=='updatecenter' -%>">
<a href="<%= ApplicationController.root_context -%>/updatecenter"><%= message('update_center.page') -%></a></li>
- <li class="<%= 'active' if controller.controller_path=='system' -%>">
- <a href="<%= ApplicationController.root_context -%>/system"><%= message('system_info.page') -%></a></li>
<% end %>
+ <li class="<%= 'active' if controller.controller_path=='system' -%>">
+ <a href="<%= ApplicationController.root_context -%>/system"><%= message('system_info.page') -%></a></li>
<% end #of admin part %>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/project/history.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/project/history.html.erb
index 1815d10ee92..e582b6e2e12 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/project/history.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/project/history.html.erb
@@ -50,46 +50,56 @@
%>
<tr class="<%= cycle 'even','odd' -%> hoverable snapshot">
+ <%# Year %>
<td class="thin nowrap"><b><%= time.year unless time.year == current_year -%></b></td>
+ <%# Month %>
<td class="thin nowrap"><b><%= l(time, :format => '%B').capitalize unless time.month == current_month -%></b></td>
+ <%# Day %>
<td class="thin nowrap"><%= l(time, :format => '%d') -%></td>
+ <%# Time %>
<td class="thin nowrap"><%= l(time, :format => '%H:%M') -%></td>
+ <%# Version %>
<td class="thin nowrap" style="padding-left: 20px;">
- <table class="width100">
- <tr id="version_<%= index -%>">
- <% if version_event %>
- <td class="width100"><%= version_event.name if version_event -%></td>
- <td class="small edit_actions" style="padding-left:20px">
- <a id="version_<%= index -%>_change" href="#" onclick="$j('#version_<%= index -%>').hide();$j('#version_<%= index -%>_form').show();$j('#version_name_<%= index -%>').focus();return false;"><%= message('project_history.rename_version') -%></a>
- <% if version_event && !snapshot.islast? %>
- <%= link_to( message('project_history.remove_version'),
- { :action => 'delete_version', :sid => snapshot.id},
- :confirm => message('project_history.do_you_want_to_remove_version', :params => version_event.name) ) -%>
- <% end %>
- </td>
- <% else %>
- <td class="small edit_actions" colspan="2">
- <a id="version_<%= index -%>_change" href="#" onclick="$j('#version_<%= index -%>').hide();$j('#version_<%= index -%>_form').show();$j('#version_name_<%= index -%>').focus();return false;"><%= message('project_history.create_version') -%></a>
- </td>
+ <table class="width100">
+ <tr id="version_<%= index -%>">
+
+ <% if version_event %>
+ <td class="width100"><%= version_event.name if version_event -%></td>
+ <td class="small edit_actions" style="padding-left:20px">
+ <a id="version_<%= index -%>_change" href="#" onclick="$j('#version_<%= index -%>').hide();$j('#version_<%= index -%>_form').show();$j('#version_name_<%= index -%>').focus();return false;"><%= message('project_history.rename_version') -%></a>
+
+ <% if version_event && !snapshot.islast? %>
+ <%= link_to( message('project_history.remove_version'),
+ { :action => 'delete_version', :sid => snapshot.id},
+ :confirm => message('project_history.do_you_want_to_remove_version', :params => version_event.name) ) -%>
<% end %>
- </tr>
- <tr id="version_<%= index -%>_form" style="display:none;">
- <td coslpan="2" class="admin">
- <% form_tag( {:action => 'update_version', :sid => snapshot.id }) do -%>
- <input id="version_name_<%= index -%>" name="version_name" type="text" value="<%= version_event ? version_event.name : '' -%>"
- onKeyUp="if (this.value=='') $j('#save_version_<%= index -%>').disabled='true'; else $j('#save_version_<%= index -%>').disabled='';"/>
- <%= submit_tag message('save'), :id => 'save_version_' + index.to_s %>
- <a href="#" onclick="$j('#version_<%= index -%>').show();$j('#version_<%= index -%>_form').hide();"><%= message('cancel') -%></a>
- <% end %>
- </td>
- </tr>
- </table>
+ </td>
+
+ <% else %>
+ <td class="small edit_actions" colspan="2">
+ <a id="version_<%= index -%>_change" href="#" onclick="$j('#version_<%= index -%>').hide();$j('#version_<%= index -%>_form').show();$j('#version_name_<%= index -%>').focus();return false;"><%= message('project_history.create_version') -%></a>
+ </td>
+
+ <% end %>
+ </tr>
+ <tr id="version_<%= index -%>_form" style="display:none;">
+ <td coslpan="2" class="admin">
+ <% form_tag( {:action => 'update_version', :sid => snapshot.id }) do -%>
+ <input id="version_name_<%= index -%>" name="version_name" type="text" value="<%= version_event ? version_event.name : '' -%>"
+ onKeyUp="if (this.value=='') $j('#save_version_<%= index -%>').disabled='true'; else $j('#save_version_<%= index -%>').disabled='';"/>
+ <%= submit_tag message('save'), :id => 'save_version_' + index.to_s %>
+ <a href="#" onclick="$j('#version_<%= index -%>').show();$j('#version_<%= index -%>_form').hide();"><%= message('cancel') -%></a>
+ <% end %>
+ </td>
+ </tr>
+ </table>
</td>
+ <%# Events %>
<td class="thin nowrap" style="padding-left: 20px;">
<table class="width100">
<%
@@ -99,28 +109,26 @@
<tr id="event_<%= event_index -%>">
<td class="width100"><%= event.name -%></td>
<td class="small edit_actions" style="padding-left:20px">
- <a id="event_<%= event_index -%>_change" href="#" onclick="$j('#event_<%= event_index -%>').hide();$j('#event_<%= event_index -%>_form').show();$j('#event_name_<%= event_index -%>').focus();return false;"><%= message('project_history.rename_event') -%></a>
- <%= link_to( message('project_history.remove_version'),
- { :action => 'delete_event', :id => event.id},
- :confirm => message('project_history.do_you_want_to_remove_version', :params => event.name) ) -%>
+ <a id="event_<%= event_index -%>_change" href="#" onclick="$j('#event_<%= event_index -%>').hide();$j('#event_<%= event_index -%>_form').show();$j('#event_name_<%= event_index -%>').focus();return false;"><%= message('project_history.rename_event') -%></a>
+ <%= link_to( message('project_history.remove_version'),
+ { :action => 'delete_event', :id => event.id},
+ :confirm => message('project_history.do_you_want_to_remove_version', :params => event.name) ) -%>
</td>
</tr>
<tr id="event_<%= event_index -%>_form" style="display:none;">
- <td colspan="2" class="admin">
- <% form_tag( {:action => 'update_event', :id => event.id }) do -%>
- <input id="event_name_<%= event_index -%>" name="event_name" type="text" value="<%= event.name -%>"
- onKeyUp="if (this.value=='') $j('#save_event_<%= event_index -%>').disabled='true'; else $j('#save_event_<%= event_index -%>').disabled='';"/>
- <%= submit_tag message('save'), :id => 'save_event_' + event_index %>
- <a href="#" onclick="$j('#event_<%= event_index -%>').show();$j('#event_<%= event_index -%>_form').hide();"><%= message('cancel') -%></a>
- <% end %>
- </td>
+ <td colspan="2" class="admin">
+ <% form_tag( {:action => 'update_event', :id => event.id }) do -%>
+ <input id="event_name_<%= event_index -%>" name="event_name" type="text" value="<%= event.name -%>"
+ onKeyUp="if (this.value=='') $j('#save_event_<%= event_index -%>').disabled='true'; else $j('#save_event_<%= event_index -%>').disabled='';"/>
+ <%= submit_tag message('save'), :id => 'save_event_' + event_index %>
+ <a href="#" onclick="$j('#event_<%= event_index -%>').show();$j('#event_<%= event_index -%>_form').hide();"><%= message('cancel') -%></a>
+ <% end %>
+ </td>
</tr>
<% end %>
<tr id="create_event_<%= index -%>">
- <td colspan="2" class="create_actions">
- <span class="small">
- <a id="create_event_<%= index -%>_change" href="#" onclick="$j('#create_event_<%= index -%>').hide();$j('#create_event_<%= index -%>_form').show();$j('#create_event_name_<%= index -%>').focus();return false;"><%= message('project_history.create_event') -%></a>
- </span>
+ <td colspan="2" class="small create_actions">
+ <a id="create_event_<%= index -%>_change" href="#" onclick="$j('#create_event_<%= index -%>').hide();$j('#create_event_<%= index -%>_form').show();$j('#create_event_name_<%= index -%>').focus();return false;"><%= message('project_history.create_event') -%></a>
</td>
</tr>
<tr id="create_event_<%= index -%>_form" style="display:none;">
diff --git a/sonar-batch/src/main/java/org/sonar/batch/highlighting/DefaultHighlightingBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/highlighting/DefaultHighlightingBuilder.java
new file mode 100644
index 00000000000..786ad0cca68
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/highlighting/DefaultHighlightingBuilder.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.highlighting;
+
+import com.google.common.base.Preconditions;
+import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder;
+import org.sonar.batch.index.ComponentDataCache;
+import org.sonar.core.source.SnapshotDataTypes;
+
+public class DefaultHighlightingBuilder implements HighlightingBuilder {
+
+ private final SyntaxHighlightingDataBuilder builder;
+ private String componentKey;
+ private ComponentDataCache cache;
+ private boolean done = false;
+
+ public DefaultHighlightingBuilder(String componentKey, ComponentDataCache cache) {
+ this.componentKey = componentKey;
+ this.cache = cache;
+ this.builder = new SyntaxHighlightingDataBuilder();
+ }
+
+ @Override
+ public HighlightingBuilder highlight(int startOffset, int endOffset, TypeOfText typeOfText) {
+ Preconditions.checkState(!done, "done() already called");
+ builder.registerHighlightingRule(startOffset, endOffset, typeOfText.cssClass());
+ return this;
+ }
+
+ @Override
+ public void done() {
+ Preconditions.checkState(!done, "done() already called");
+ cache.setData(componentKey, SnapshotDataTypes.SYNTAX_HIGHLIGHTING, builder.build());
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/SyntaxHighlightingData.java b/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingData.java
index cf0dde3580f..56c9b795ac9 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/source/SyntaxHighlightingData.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingData.java
@@ -17,7 +17,7 @@
* 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.source;
+package org.sonar.batch.highlighting;
import org.sonar.batch.index.Data;
@@ -34,6 +34,10 @@ public class SyntaxHighlightingData implements Data {
this.syntaxHighlightingRuleSet = syntaxHighlightingRuleSet;
}
+ public List<SyntaxHighlightingRule> syntaxHighlightingRuleSet() {
+ return syntaxHighlightingRuleSet;
+ }
+
@Override
public String writeString() {
StringBuilder sb = new StringBuilder();
@@ -51,8 +55,4 @@ public class SyntaxHighlightingData implements Data {
return sb.toString();
}
- @Override
- public void readString(String s) {
- throw new UnsupportedOperationException();
- }
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/SyntaxHighlightingDataBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilder.java
index 58dbd717a58..38ae875af98 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/source/SyntaxHighlightingDataBuilder.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilder.java
@@ -17,7 +17,7 @@
* 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.source;
+package org.sonar.batch.highlighting;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Predicate;
@@ -26,6 +26,7 @@ import com.google.common.collect.Ordering;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
+
import java.util.Collection;
import java.util.List;
@@ -83,11 +84,11 @@ public class SyntaxHighlightingDataBuilder {
}
@VisibleForTesting
- protected List<SyntaxHighlightingRule> getSortedRules() {
+ public List<SyntaxHighlightingRule> getSortedRules() {
Ordering<SyntaxHighlightingRule> ruleOrdering = new Ordering<SyntaxHighlightingRule>() {
@Override
public int compare(@Nullable SyntaxHighlightingRule left,
- @Nullable SyntaxHighlightingRule right) {
+ @Nullable SyntaxHighlightingRule right) {
int result;
if (left != null && right != null) {
result = left.getStartPosition() - right.getStartPosition();
@@ -100,6 +101,6 @@ public class SyntaxHighlightingDataBuilder {
}
};
- return ruleOrdering.immutableSortedCopy(syntaxHighlightingRuleSet);
+ return ruleOrdering.sortedCopy(syntaxHighlightingRuleSet);
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/SyntaxHighlightingRule.java b/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingRule.java
index d8232c1ba9d..08985752d60 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/source/SyntaxHighlightingRule.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingRule.java
@@ -17,7 +17,7 @@
* 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.source;
+package org.sonar.batch.highlighting;
import java.io.Serializable;
diff --git a/sonar-batch/src/main/java/org/sonar/batch/highlighting/package-info.java b/sonar-batch/src/main/java/org/sonar/batch/highlighting/package-info.java
new file mode 100644
index 00000000000..93b92f3e9ae
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/highlighting/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.batch.highlighting;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/ComponentDataCache.java b/sonar-batch/src/main/java/org/sonar/batch/index/ComponentDataCache.java
index 16be9501117..0523212bf75 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/index/ComponentDataCache.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/index/ComponentDataCache.java
@@ -21,6 +21,8 @@ package org.sonar.batch.index;
import org.sonar.api.BatchComponent;
+import javax.annotation.CheckForNull;
+
public class ComponentDataCache implements BatchComponent {
private final Cache cache;
@@ -37,10 +39,12 @@ public class ComponentDataCache implements BatchComponent {
return setData(componentKey, dataType, new StringData(data));
}
+ @CheckForNull
public <D extends Data> D getData(String componentKey, String dataType) {
return (D) cache.get(componentKey, dataType);
}
+ @CheckForNull
public String getStringData(String componentKey, String dataType) {
Data data = (Data) cache.get(componentKey, dataType);
return data == null ? null : ((StringData) data).data();
diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/Data.java b/sonar-batch/src/main/java/org/sonar/batch/index/Data.java
index 66e5b596fc4..aa47c04e799 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/index/Data.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/index/Data.java
@@ -25,6 +25,4 @@ public interface Data extends Serializable {
String writeString();
- void readString(String s);
-
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/StringData.java b/sonar-batch/src/main/java/org/sonar/batch/index/StringData.java
index 17605657112..6a88b5979b2 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/index/StringData.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/index/StringData.java
@@ -37,9 +37,4 @@ public class StringData implements Data {
public String writeString() {
return data;
}
-
- @Override
- public void readString(String s) {
- this.data = s;
- }
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java
index c3aa2646c6c..c305474b7f2 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java
@@ -20,14 +20,19 @@
package org.sonar.batch.mediumtest;
import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.sonar.api.SonarPlugin;
import org.sonar.api.batch.bootstrap.ProjectReactor;
import org.sonar.api.batch.debt.internal.DefaultDebtModel;
import org.sonar.api.batch.fs.InputDir;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.InputPath;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder;
import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.api.batch.sensor.measure.Measure;
+import org.sonar.api.batch.sensor.symbol.Symbol;
import org.sonar.api.config.Settings;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Metric;
@@ -36,6 +41,9 @@ import org.sonar.api.resources.Languages;
import org.sonar.batch.bootstrap.PluginsReferential;
import org.sonar.batch.bootstrapper.Batch;
import org.sonar.batch.bootstrapper.EnvironmentInformation;
+import org.sonar.batch.highlighting.SyntaxHighlightingData;
+import org.sonar.batch.highlighting.SyntaxHighlightingRule;
+import org.sonar.batch.index.ComponentDataCache;
import org.sonar.batch.protocol.input.ActiveRule;
import org.sonar.batch.protocol.input.GlobalReferentials;
import org.sonar.batch.protocol.input.ProjectReferentials;
@@ -47,8 +55,12 @@ 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.batch.symbol.SymbolData;
import org.sonar.core.plugins.DefaultPluginMetadata;
import org.sonar.core.plugins.RemotePlugin;
+import org.sonar.core.source.SnapshotDataTypes;
+
+import javax.annotation.CheckForNull;
import java.io.File;
import java.io.FileReader;
@@ -59,6 +71,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
+import java.util.Set;
public class BatchMediumTester {
@@ -200,13 +213,19 @@ public class BatchMediumTester {
}
public static class TaskResult implements ScanTaskObserver {
+
+ private static final Logger LOG = LoggerFactory.getLogger(BatchMediumTester.TaskResult.class);
+
private List<Issue> issues = new ArrayList<Issue>();
private List<Measure> measures = new ArrayList<Measure>();
private List<InputFile> inputFiles = new ArrayList<InputFile>();
private List<InputDir> inputDirs = new ArrayList<InputDir>();
+ private Map<InputFile, SyntaxHighlightingData> highlightingPerFile = new HashMap<InputFile, SyntaxHighlightingData>();
+ private Map<InputFile, SymbolData> symbolTablePerFile = new HashMap<InputFile, SymbolData>();
@Override
public void scanTaskCompleted(ProjectScanContainer container) {
+ LOG.info("Store analysis results in memory for later assertions in medium test");
for (Issue issue : container.getComponentByType(AnalyzerIssueCache.class).all()) {
issues.add(issue);
}
@@ -223,6 +242,19 @@ public class BatchMediumTester {
inputDirs.add((InputDir) inputPath);
}
}
+
+ ComponentDataCache componentDataCache = container.getComponentByType(ComponentDataCache.class);
+ for (InputFile file : inputFiles) {
+ SyntaxHighlightingData highlighting = componentDataCache.getData(((DefaultInputFile) file).key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING);
+ if (highlighting != null) {
+ highlightingPerFile.put(file, highlighting);
+ }
+ SymbolData symbolTable = componentDataCache.getData(((DefaultInputFile) file).key(), SnapshotDataTypes.SYMBOL_HIGHLIGHTING);
+ if (symbolTable != null) {
+ symbolTablePerFile.put(file, symbolTable);
+ }
+ }
+
}
public List<Issue> issues() {
@@ -240,6 +272,43 @@ public class BatchMediumTester {
public List<InputDir> inputDirs() {
return inputDirs;
}
+
+ /**
+ * Get highlighting type at a given position in an inputfile
+ * @param charIndex 0-based offset in file
+ */
+ @CheckForNull
+ public HighlightingBuilder.TypeOfText highlightingTypeFor(InputFile file, int charIndex) {
+ SyntaxHighlightingData syntaxHighlightingData = highlightingPerFile.get(file);
+ if (syntaxHighlightingData == null) {
+ return null;
+ }
+ for (SyntaxHighlightingRule sortedRule : syntaxHighlightingData.syntaxHighlightingRuleSet()) {
+ if (sortedRule.getStartPosition() <= charIndex && sortedRule.getEndPosition() > charIndex) {
+ return HighlightingBuilder.TypeOfText.forCssClass(sortedRule.getTextType());
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get list of all positions of a symbol in an inputfile
+ * @param symbolStartOffset 0-based start offset for the symbol in file
+ * @param symbolEndOffset 0-based end offset for the symbol in file
+ */
+ @CheckForNull
+ public Set<Integer> symbolReferencesFor(InputFile file, int symbolStartOffset, int symbolEndOffset) {
+ SymbolData data = symbolTablePerFile.get(file);
+ if (data == null) {
+ return null;
+ }
+ for (Symbol symbol : data.referencesBySymbol().keySet()) {
+ if (symbol.getDeclarationStartOffset() == symbolStartOffset && symbol.getDeclarationEndOffset() == symbolEndOffset) {
+ return data.referencesBySymbol().get(symbol);
+ }
+ }
+ return null;
+ }
}
private static class FakeGlobalReferentialsLoader implements GlobalReferentialsLoader {
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java b/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java
index a0c99598d31..1de8fd7201c 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java
@@ -23,15 +23,18 @@ import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputDir;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.InputPath;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.measure.Metric;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder;
import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.api.batch.sensor.issue.IssueBuilder;
import org.sonar.api.batch.sensor.issue.internal.DefaultIssueBuilder;
import org.sonar.api.batch.sensor.measure.Measure;
import org.sonar.api.batch.sensor.measure.MeasureBuilder;
import org.sonar.api.batch.sensor.measure.internal.DefaultMeasureBuilder;
+import org.sonar.api.batch.sensor.symbol.SymbolTableBuilder;
import org.sonar.api.component.ResourcePerspectives;
import org.sonar.api.config.Settings;
import org.sonar.api.issue.Issuable;
@@ -46,6 +49,9 @@ import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.resources.Scopes;
import org.sonar.api.rule.RuleKey;
+import org.sonar.batch.highlighting.DefaultHighlightingBuilder;
+import org.sonar.batch.index.ComponentDataCache;
+import org.sonar.batch.symbol.DefaultSymbolTableBuilder;
import java.io.Serializable;
@@ -62,9 +68,10 @@ public class SensorContextAdaptor implements SensorContext {
private Settings settings;
private FileSystem fs;
private ActiveRules activeRules;
+ private ComponentDataCache componentDataCache;
public SensorContextAdaptor(org.sonar.api.batch.SensorContext sensorContext, MetricFinder metricFinder, Project project, ResourcePerspectives perspectives,
- Settings settings, FileSystem fs, ActiveRules activeRules) {
+ Settings settings, FileSystem fs, ActiveRules activeRules, ComponentDataCache componentDataCache) {
this.sensorContext = sensorContext;
this.metricFinder = metricFinder;
this.project = project;
@@ -72,6 +79,7 @@ public class SensorContextAdaptor implements SensorContext {
this.settings = settings;
this.fs = fs;
this.activeRules = activeRules;
+ this.componentDataCache = componentDataCache;
}
@Override
@@ -236,4 +244,14 @@ public class SensorContextAdaptor implements SensorContext {
.build();
}
+ @Override
+ public HighlightingBuilder highlightingBuilder(InputFile inputFile) {
+ return new DefaultHighlightingBuilder(((DefaultInputFile) inputFile).key(), componentDataCache);
+ }
+
+ @Override
+ public SymbolTableBuilder symbolTableBuilder(InputFile inputFile) {
+ return new DefaultSymbolTableBuilder(((DefaultInputFile) inputFile).key(), componentDataCache);
+ }
+
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalysisPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalysisPublisher.java
index 757f320f559..7fb7dacf375 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalysisPublisher.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalysisPublisher.java
@@ -44,9 +44,11 @@ public final class AnalysisPublisher {
private final FileSystem fs;
private final AnalyzerMeasureCache measureCache;
private final ProjectDefinition def;
- private AnalyzerIssueCache issueCache;
+ private final AnalyzerIssueCache issueCache;
- public AnalysisPublisher(ProjectDefinition def, Settings settings, FileSystem fs, AnalyzerMeasureCache measureCache, AnalyzerIssueCache analyzerIssueCache) {
+ public AnalysisPublisher(ProjectDefinition def, Settings settings, FileSystem fs,
+ AnalyzerMeasureCache measureCache,
+ AnalyzerIssueCache analyzerIssueCache) {
this.def = def;
this.settings = settings;
this.fs = fs;
@@ -132,8 +134,9 @@ public final class AnalysisPublisher {
for (Measure<?> measure : measureCache.byModule(def.getKey())) {
jsonWriter.beginObject()
.prop("metricKey", measure.metric().key());
- if (measure.inputFile() != null) {
- jsonWriter.prop("filePath", measure.inputFile().relativePath());
+ InputFile inputFile = measure.inputFile();
+ if (inputFile != null) {
+ jsonWriter.prop("filePath", inputFile.relativePath());
}
jsonWriter.prop("value", String.valueOf(measure.value()))
.endObject();
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerOptimizer.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerOptimizer.java
index fb461854c69..4ae3ddfce09 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerOptimizer.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerOptimizer.java
@@ -19,16 +19,19 @@
*/
package org.sonar.batch.scan2;
-import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
-
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.sonar.api.BatchComponent;
import org.sonar.api.batch.fs.FilePredicate;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.rule.ActiveRules;
+import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
public class AnalyzerOptimizer implements BatchComponent {
+ private static final Logger LOG = LoggerFactory.getLogger(AnalyzerOptimizer.class);
+
private final FileSystem fs;
private final ActiveRules activeRules;
@@ -41,10 +44,15 @@ public class AnalyzerOptimizer implements BatchComponent {
* Decide if the given Analyzer should be executed.
*/
public boolean shouldExecute(DefaultSensorDescriptor descriptor) {
- // FS Conditions
- boolean fsCondition = fsCondition(descriptor);
- boolean activeRulesCondition = activeRulesCondition(descriptor);
- return fsCondition && activeRulesCondition;
+ if (!fsCondition(descriptor)) {
+ LOG.debug("'{}' skipped because there is no related file in current project", descriptor.name());
+ return false;
+ }
+ if (!activeRulesCondition(descriptor)) {
+ LOG.debug("'{}' skipped because there is no related rule activated in the quality profile", descriptor.name());
+ return false;
+ }
+ return true;
}
private boolean activeRulesCondition(DefaultSensorDescriptor descriptor) {
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java
index 6f4b2bdec70..dcd6efd1ca4 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java
@@ -23,10 +23,12 @@ import com.google.common.base.Strings;
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.fs.internal.DefaultInputFile;
import org.sonar.api.batch.measure.Metric;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.rule.internal.DefaultActiveRule;
import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder;
import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.api.batch.sensor.issue.IssueBuilder;
import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
@@ -35,11 +37,15 @@ import org.sonar.api.batch.sensor.measure.Measure;
import org.sonar.api.batch.sensor.measure.MeasureBuilder;
import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
import org.sonar.api.batch.sensor.measure.internal.DefaultMeasureBuilder;
+import org.sonar.api.batch.sensor.symbol.SymbolTableBuilder;
import org.sonar.api.config.Settings;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.MessageException;
+import org.sonar.batch.highlighting.DefaultHighlightingBuilder;
+import org.sonar.batch.index.ComponentDataCache;
import org.sonar.batch.issue.IssueFilters;
import org.sonar.batch.scan.SensorContextAdaptor;
+import org.sonar.batch.symbol.DefaultSymbolTableBuilder;
import org.sonar.core.component.ComponentKeys;
import java.io.Serializable;
@@ -53,9 +59,10 @@ public class DefaultSensorContext implements SensorContext {
private final FileSystem fs;
private final ActiveRules activeRules;
private final IssueFilters issueFilters;
+ private final ComponentDataCache componentDataCache;
public DefaultSensorContext(ProjectDefinition def, AnalyzerMeasureCache measureCache, AnalyzerIssueCache issueCache,
- Settings settings, FileSystem fs, ActiveRules activeRules, IssueFilters issueFilters) {
+ Settings settings, FileSystem fs, ActiveRules activeRules, IssueFilters issueFilters, ComponentDataCache componentDataCache) {
this.def = def;
this.measureCache = measureCache;
this.issueCache = issueCache;
@@ -63,6 +70,7 @@ public class DefaultSensorContext implements SensorContext {
this.fs = fs;
this.activeRules = activeRules;
this.issueFilters = issueFilters;
+ this.componentDataCache = componentDataCache;
}
@Override
@@ -155,7 +163,16 @@ public class DefaultSensorContext implements SensorContext {
if (issue.severity() == null) {
issue.setSeverity(activeRule.severity());
}
+ }
+ @Override
+ public HighlightingBuilder highlightingBuilder(InputFile inputFile) {
+ return new DefaultHighlightingBuilder(((DefaultInputFile) inputFile).key(), componentDataCache);
+ }
+
+ @Override
+ public SymbolTableBuilder symbolTableBuilder(InputFile inputFile) {
+ return new DefaultSymbolTableBuilder(((DefaultInputFile) inputFile).key(), componentDataCache);
}
}
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 b0738e420e4..e13fca37d28 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
@@ -34,6 +34,7 @@ import org.sonar.batch.bootstrap.ExtensionInstaller;
import org.sonar.batch.bootstrap.ExtensionMatcher;
import org.sonar.batch.bootstrap.ExtensionUtils;
import org.sonar.batch.index.Caches;
+import org.sonar.batch.index.ComponentDataCache;
import org.sonar.batch.languages.DefaultLanguagesReferential;
import org.sonar.batch.profiling.PhasesSumUpTimeProfiler;
import org.sonar.batch.referential.DefaultProjectReferentialsLoader;
@@ -104,6 +105,8 @@ public class ProjectScanContainer extends ComponentContainer {
// issues
AnalyzerIssueCache.class,
+ ComponentDataCache.class,
+
ScanTaskObservers.class);
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/SensorsExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/SensorsExecutor.java
index 14b6326b150..d437eadb6fd 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan2/SensorsExecutor.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/SensorsExecutor.java
@@ -19,13 +19,12 @@
*/
package org.sonar.batch.scan2;
-import org.sonar.api.batch.sensor.Sensor;
-import org.sonar.api.batch.sensor.SensorContext;
-import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.BatchComponent;
+import org.sonar.api.batch.sensor.Sensor;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
import org.sonar.batch.bootstrap.BatchExtensionDictionnary;
import java.util.Collection;
@@ -51,7 +50,6 @@ public class SensorsExecutor implements BatchComponent {
analyzer.describe(descriptor);
if (!optimizer.shouldExecute(descriptor)) {
- LOG.debug("Analyzer skipped: " + descriptor.name());
continue;
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/DefaultHighlightable.java b/sonar-batch/src/main/java/org/sonar/batch/source/DefaultHighlightable.java
index 249450fad15..5b15a3abcd8 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/source/DefaultHighlightable.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/source/DefaultHighlightable.java
@@ -21,12 +21,15 @@ package org.sonar.batch.source;
import org.sonar.api.component.Component;
import org.sonar.api.source.Highlightable;
+import org.sonar.batch.highlighting.SyntaxHighlightingDataBuilder;
import org.sonar.batch.index.ComponentDataCache;
import org.sonar.core.source.SnapshotDataTypes;
/**
* @since 3.6
+ * @deprecated since 4.5 no more used in batch 2.0
*/
+@Deprecated
public class DefaultHighlightable implements Highlightable {
private final Component component;
@@ -41,7 +44,7 @@ public class DefaultHighlightable implements Highlightable {
@Override
public HighlightingBuilder newHighlighting() {
- return new DefaultHighlightingBuilder();
+ return new DefaultHighlightingBuilder(component.key(), cache, builder);
}
@Override
@@ -53,7 +56,17 @@ public class DefaultHighlightable implements Highlightable {
return builder;
}
- private class DefaultHighlightingBuilder implements HighlightingBuilder {
+ private static class DefaultHighlightingBuilder implements HighlightingBuilder {
+
+ private final SyntaxHighlightingDataBuilder builder;
+ private String componentKey;
+ private ComponentDataCache cache;
+
+ public DefaultHighlightingBuilder(String componentKey, ComponentDataCache cache, SyntaxHighlightingDataBuilder builder) {
+ this.componentKey = componentKey;
+ this.cache = cache;
+ this.builder = builder;
+ }
@Override
public HighlightingBuilder highlight(int startOffset, int endOffset, String typeOfText) {
@@ -63,7 +76,7 @@ public class DefaultHighlightable implements Highlightable {
@Override
public void done() {
- cache.setStringData(component().key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING, builder.build().writeString());
+ cache.setData(componentKey, SnapshotDataTypes.SYNTAX_HIGHLIGHTING, builder.build());
}
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolTable.java b/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolTable.java
index 0d66c31b8f1..ee8997c259c 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolTable.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolTable.java
@@ -20,35 +20,35 @@
package org.sonar.batch.source;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.SortedSetMultimap;
import com.google.common.collect.TreeMultimap;
+import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbol;
import org.sonar.api.source.Symbol;
import org.sonar.api.source.Symbolizable;
+import org.sonar.batch.symbol.DefaultSymbolTableBuilder;
-import java.io.Serializable;
import java.util.ArrayList;
-import java.util.Comparator;
import java.util.List;
public class DefaultSymbolTable implements Symbolizable.SymbolTable {
- private Multimap<Symbol, Integer> referencesBySymbol;
+ private SortedSetMultimap<org.sonar.api.batch.sensor.symbol.Symbol, Integer> referencesBySymbol;
- private DefaultSymbolTable(Multimap<Symbol, Integer> referencesBySymbol) {
+ private DefaultSymbolTable(SortedSetMultimap<org.sonar.api.batch.sensor.symbol.Symbol, Integer> referencesBySymbol) {
this.referencesBySymbol = referencesBySymbol;
}
- public static Builder builder() {
- return new Builder();
- }
-
- public Multimap<Symbol, Integer> getReferencesBySymbol() {
+ public SortedSetMultimap<org.sonar.api.batch.sensor.symbol.Symbol, Integer> getReferencesBySymbol() {
return referencesBySymbol;
}
@Override
public List<Symbol> symbols() {
- return new ArrayList<Symbol>(referencesBySymbol.keySet());
+ List<Symbol> result = new ArrayList<Symbol>();
+ for (org.sonar.api.batch.sensor.symbol.Symbol symbol : referencesBySymbol.keySet()) {
+ result.add((Symbol) symbol);
+ }
+ return result;
}
@Override
@@ -58,15 +58,17 @@ public class DefaultSymbolTable implements Symbolizable.SymbolTable {
public static class Builder implements Symbolizable.SymbolTableBuilder {
- private final Multimap<Symbol, Integer> referencesBySymbol;
+ private final SortedSetMultimap<org.sonar.api.batch.sensor.symbol.Symbol, Integer> referencesBySymbol;
+ private final String componentKey;
- public Builder() {
- referencesBySymbol = TreeMultimap.create(new SymbolComparator(), new ReferenceComparator());
+ public Builder(String componentKey) {
+ this.componentKey = componentKey;
+ referencesBySymbol = TreeMultimap.create(new DefaultSymbolTableBuilder.SymbolComparator(), new DefaultSymbolTableBuilder.ReferenceComparator());
}
@Override
public Symbol newSymbol(int fromOffset, int toOffset) {
- Symbol symbol = new DefaultSymbol(fromOffset, toOffset);
+ Symbol symbol = new DefaultSymbol(componentKey, fromOffset, toOffset);
referencesBySymbol.put(symbol, symbol.getDeclarationStartOffset());
return symbol;
}
@@ -84,24 +86,5 @@ public class DefaultSymbolTable implements Symbolizable.SymbolTable {
return new DefaultSymbolTable(referencesBySymbol);
}
- private static class SymbolComparator implements Comparator<Symbol>, Serializable {
- @Override
- public int compare(Symbol left, Symbol right) {
- return left.getDeclarationStartOffset() - right.getDeclarationStartOffset();
- }
- }
-
- private static class ReferenceComparator implements Comparator<Integer>, Serializable {
- @Override
- public int compare(Integer left, Integer right) {
- int result;
- if (left != null & right != null) {
- result = left - right;
- } else {
- result = left == null ? -1 : 1;
- }
- return result;
- }
- }
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolizable.java b/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolizable.java
index 1e3b33d939e..de073e50629 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolizable.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolizable.java
@@ -23,6 +23,7 @@ package org.sonar.batch.source;
import org.sonar.api.component.Component;
import org.sonar.api.source.Symbolizable;
import org.sonar.batch.index.ComponentDataCache;
+import org.sonar.batch.symbol.SymbolData;
import org.sonar.core.source.SnapshotDataTypes;
public class DefaultSymbolizable implements Symbolizable {
@@ -42,12 +43,12 @@ public class DefaultSymbolizable implements Symbolizable {
@Override
public SymbolTableBuilder newSymbolTableBuilder() {
- return new DefaultSymbolTable.Builder();
+ return new DefaultSymbolTable.Builder(component.key());
}
@Override
public void setSymbolTable(SymbolTable symbolTable) {
- SymbolData symbolData = new SymbolData(symbolTable);
+ SymbolData symbolData = new SymbolData(((DefaultSymbolTable) symbolTable).getReferencesBySymbol());
cache.setStringData(component().key(), SnapshotDataTypes.SYMBOL_HIGHLIGHTING, symbolData.writeString());
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/HighlightableBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/source/HighlightableBuilder.java
index 82f5c8dd213..d20fbebb147 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/source/HighlightableBuilder.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/source/HighlightableBuilder.java
@@ -28,11 +28,14 @@ import org.sonar.core.component.PerspectiveBuilder;
import org.sonar.core.component.ResourceComponent;
import javax.annotation.CheckForNull;
+
import java.util.Set;
/**
* @since 3.6
+ * @deprecated since 4.5 no more used in batch 2.0
*/
+@Deprecated
public class HighlightableBuilder extends PerspectiveBuilder<Highlightable> {
private static final Set<String> SUPPORTED_QUALIFIERS = ImmutableSet.of(Qualifiers.FILE, Qualifiers.UNIT_TEST_FILE);
diff --git a/sonar-batch/src/main/java/org/sonar/batch/symbol/DefaultSymbolTableBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/symbol/DefaultSymbolTableBuilder.java
new file mode 100644
index 00000000000..ed347e60bd0
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/symbol/DefaultSymbolTableBuilder.java
@@ -0,0 +1,90 @@
+/*
+ * 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.symbol;
+
+import com.google.common.collect.SortedSetMultimap;
+import com.google.common.collect.TreeMultimap;
+import org.sonar.api.batch.sensor.symbol.Symbol;
+import org.sonar.api.batch.sensor.symbol.SymbolTableBuilder;
+import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbol;
+import org.sonar.batch.index.ComponentDataCache;
+import org.sonar.core.source.SnapshotDataTypes;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+public class DefaultSymbolTableBuilder implements SymbolTableBuilder {
+
+ private final String componentKey;
+ private final ComponentDataCache cache;
+ private final SortedSetMultimap<Symbol, Integer> referencesBySymbol;
+
+ public DefaultSymbolTableBuilder(String componentKey, ComponentDataCache cache) {
+ this.componentKey = componentKey;
+ this.cache = cache;
+ this.referencesBySymbol = TreeMultimap.create(new SymbolComparator(), new ReferenceComparator());
+ }
+
+ @Override
+ public Symbol newSymbol(int fromOffset, int toOffset) {
+ Symbol symbol = new DefaultSymbol(componentKey, fromOffset, toOffset);
+ referencesBySymbol.put(symbol, symbol.getDeclarationStartOffset());
+ return symbol;
+ }
+
+ @Override
+ public void newReference(Symbol symbol, int fromOffset) {
+ String otherComponentKey = ((DefaultSymbol) symbol).componentKey();
+ if (!otherComponentKey.equals(componentKey)) {
+ throw new UnsupportedOperationException("Cannot add reference from (" + componentKey + ") to another file (" + otherComponentKey + ")");
+ }
+ if (fromOffset >= symbol.getDeclarationStartOffset() && fromOffset < symbol.getDeclarationEndOffset()) {
+ throw new UnsupportedOperationException("Cannot add reference (" + fromOffset + ") overlapping " + symbol);
+ }
+ referencesBySymbol.put(symbol, fromOffset);
+ }
+
+ @Override
+ public void done() {
+ SymbolData symbolData = new SymbolData(referencesBySymbol);
+ cache.setData(componentKey, SnapshotDataTypes.SYMBOL_HIGHLIGHTING, symbolData);
+ }
+
+ public static class SymbolComparator implements Comparator<Symbol>, Serializable {
+ @Override
+ public int compare(Symbol left, Symbol right) {
+ return left.getDeclarationStartOffset() - right.getDeclarationStartOffset();
+ }
+ }
+
+ public static class ReferenceComparator implements Comparator<Integer>, Serializable {
+ @Override
+ public int compare(Integer left, Integer right) {
+ int result;
+ if (left != null & right != null) {
+ result = left - right;
+ } else {
+ result = left == null ? -1 : 1;
+ }
+ return result;
+ }
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/SymbolData.java b/sonar-batch/src/main/java/org/sonar/batch/symbol/SymbolData.java
index 38ed3ed42f9..11ab5cca7e5 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/source/SymbolData.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/symbol/SymbolData.java
@@ -18,11 +18,10 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.batch.source;
+package org.sonar.batch.symbol;
-import com.google.common.collect.Multimap;
-import org.sonar.api.source.Symbol;
-import org.sonar.api.source.Symbolizable;
+import com.google.common.collect.SortedSetMultimap;
+import org.sonar.api.batch.sensor.symbol.Symbol;
import org.sonar.batch.index.Data;
import java.util.Collection;
@@ -32,19 +31,21 @@ public class SymbolData implements Data {
private static final String FIELD_SEPARATOR = ",";
private static final String SYMBOL_SEPARATOR = ";";
- private final Symbolizable.SymbolTable symbolTable;
+ private final SortedSetMultimap<Symbol, Integer> referencesBySymbol;
- public SymbolData(Symbolizable.SymbolTable symbolTable) {
- this.symbolTable = symbolTable;
+ public SymbolData(SortedSetMultimap<Symbol, Integer> referencesBySymbol) {
+ this.referencesBySymbol = referencesBySymbol;
+ }
+
+ public SortedSetMultimap<Symbol, Integer> referencesBySymbol() {
+ return referencesBySymbol;
}
@Override
public String writeString() {
StringBuilder sb = new StringBuilder();
- Multimap<Symbol, Integer> referencesBySymbol = ((DefaultSymbolTable)symbolTable).getReferencesBySymbol();
-
- for (Symbol symbol : ((DefaultSymbolTable)symbolTable).getReferencesBySymbol().keySet()) {
+ for (Symbol symbol : referencesBySymbol.keySet()) {
sb.append(symbol.getDeclarationStartOffset())
.append(FIELD_SEPARATOR)
@@ -59,8 +60,4 @@ public class SymbolData implements Data {
return sb.toString();
}
- @Override
- public void readString(String s) {
- throw new UnsupportedOperationException();
- }
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/highlighting/DefaultHighlightingBuilderTest.java b/sonar-batch/src/test/java/org/sonar/batch/highlighting/DefaultHighlightingBuilderTest.java
new file mode 100644
index 00000000000..aefc5afd732
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/highlighting/DefaultHighlightingBuilderTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.highlighting;
+
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder.TypeOfText;
+import org.sonar.batch.index.ComponentDataCache;
+import org.sonar.core.source.SnapshotDataTypes;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class DefaultHighlightingBuilderTest {
+
+ @Test
+ public void should_apply_registered_highlighting() throws Exception {
+
+ ComponentDataCache cache = mock(ComponentDataCache.class);
+
+ DefaultHighlightingBuilder highlightable = new DefaultHighlightingBuilder("myComponent", cache);
+ highlightable
+ .highlight(0, 10, TypeOfText.KEYWORD)
+ .highlight(20, 30, TypeOfText.CPP_DOC)
+ .done();
+
+ ArgumentCaptor<SyntaxHighlightingData> argCaptor = ArgumentCaptor.forClass(SyntaxHighlightingData.class);
+ verify(cache).setData(eq("myComponent"), eq(SnapshotDataTypes.SYNTAX_HIGHLIGHTING), argCaptor.capture());
+ assertThat(argCaptor.getValue().writeString()).isEqualTo("0,10,k;20,30,cppd;");
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/source/SyntaxHighlightingDataBuilderTest.java b/sonar-batch/src/test/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilderTest.java
index c190edc3ac7..6fad264c831 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/source/SyntaxHighlightingDataBuilderTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilderTest.java
@@ -17,9 +17,10 @@
* 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.source;
+package org.sonar.batch.highlighting;
+import org.sonar.batch.highlighting.SyntaxHighlightingDataBuilder;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
diff --git a/sonar-batch/src/test/java/org/sonar/batch/source/SyntaxHighlightingDataTest.java b/sonar-batch/src/test/java/org/sonar/batch/highlighting/SyntaxHighlightingDataTest.java
index 8a9ece89ef3..59de88ca848 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/source/SyntaxHighlightingDataTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/highlighting/SyntaxHighlightingDataTest.java
@@ -17,7 +17,7 @@
* 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.source;
+package org.sonar.batch.highlighting;
import com.google.common.collect.Lists;
import org.junit.Test;
@@ -38,7 +38,7 @@ public class SyntaxHighlightingDataTest {
SyntaxHighlightingRule.create(24, 38, "k"),
SyntaxHighlightingRule.create(24, 65, "cppd"),
SyntaxHighlightingRule.create(42, 50, "k")
- );
+ );
String serializedRules = new SyntaxHighlightingData(orderedHighlightingRules).writeString();
assertThat(serializedRules).isEqualTo("0,10,cd;10,12,k;12,20,cd;24,38,k;24,65,cppd;42,50,k;");
diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/ComponentDataCacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/ComponentDataCacheTest.java
index ff0e7eed20d..3a8835e7a3c 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/index/ComponentDataCacheTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/index/ComponentDataCacheTest.java
@@ -83,9 +83,5 @@ public class ComponentDataCacheTest {
return String.valueOf(data);
}
- @Override
- public void readString(String s) {
- data = Long.parseLong(s);
- }
}
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java
new file mode 100644
index 00000000000..259b2ffcec0
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.highlighting;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult;
+import org.sonar.batch.mediumtest.xoo.plugin.XooPlugin;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class HighlightingMediumTest {
+
+ @org.junit.Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .registerPlugin("xoo", new XooPlugin())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .bootstrapProperties(ImmutableMap.of("sonar.analysis.mode", "sensor"))
+ .build();
+
+ @Before
+ public void prepare() {
+ tester.start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ public void computeSyntaxHighlightingOnTempProject() throws IOException {
+
+ File baseDir = temp.newFolder();
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ File xoohighlightingFile = new File(srcDir, "sample.xoo.highlighting");
+ FileUtils.write(xooFile, "Sample xoo\ncontent");
+ FileUtils.write(xoohighlightingFile, "0:10:s\n11:18:k");
+
+ 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();
+
+ InputFile file = result.inputFiles().get(0);
+ assertThat(result.highlightingTypeFor(file, 0)).isEqualTo(HighlightingBuilder.TypeOfText.STRING);
+ assertThat(result.highlightingTypeFor(file, 9)).isEqualTo(HighlightingBuilder.TypeOfText.STRING);
+ assertThat(result.highlightingTypeFor(file, 10)).isNull();
+ assertThat(result.highlightingTypeFor(file, 11)).isEqualTo(HighlightingBuilder.TypeOfText.KEYWORD);
+
+ }
+
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java
new file mode 100644
index 00000000000..c3875c625f3
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.symbol;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult;
+import org.sonar.batch.mediumtest.xoo.plugin.XooPlugin;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class SymbolMediumTest {
+
+ @org.junit.Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .registerPlugin("xoo", new XooPlugin())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .bootstrapProperties(ImmutableMap.of("sonar.analysis.mode", "sensor"))
+ .build();
+
+ @Before
+ public void prepare() {
+ tester.start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ public void computeSyntaxHighlightingOnTempProject() throws IOException {
+
+ File baseDir = temp.newFolder();
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ File xooSymbolFile = new File(srcDir, "sample.xoo.symbol");
+ FileUtils.write(xooFile, "Sample xoo\ncontent\nanother xoo");
+ FileUtils.write(xooSymbolFile, "7,10,27");
+
+ 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();
+
+ InputFile file = result.inputFiles().get(0);
+ assertThat(result.symbolReferencesFor(file, 7, 10)).containsOnly(7, 27);
+ }
+
+}
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
index f281ea09899..78718da3682 100644
--- 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
@@ -23,6 +23,8 @@ import org.sonar.api.SonarPlugin;
import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo;
import org.sonar.batch.mediumtest.xoo.plugin.lang.MeasureSensor;
import org.sonar.batch.mediumtest.xoo.plugin.lang.ScmActivitySensor;
+import org.sonar.batch.mediumtest.xoo.plugin.lang.SymbolReferencesSensor;
+import org.sonar.batch.mediumtest.xoo.plugin.lang.SyntaxHighlightingSensor;
import org.sonar.batch.mediumtest.xoo.plugin.rule.CreateIssueByInternalKeySensor;
import org.sonar.batch.mediumtest.xoo.plugin.rule.OneIssueOnDirPerFileSensor;
import org.sonar.batch.mediumtest.xoo.plugin.rule.OneIssuePerLineSensor;
@@ -38,6 +40,8 @@ public final class XooPlugin extends SonarPlugin {
// language
MeasureSensor.class,
ScmActivitySensor.class,
+ SyntaxHighlightingSensor.class,
+ SymbolReferencesSensor.class,
Xoo.class,
// sensors
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/MeasureSensor.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/MeasureSensor.java
index 9a4b46afd1a..c28ebd5a05c 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/MeasureSensor.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/MeasureSensor.java
@@ -104,7 +104,7 @@ public class MeasureSensor implements Sensor {
@Override
public void describe(SensorDescriptor descriptor) {
descriptor
- .name("Xoo Measure Analyzer")
+ .name("Xoo Measure Sensor")
.provides(CoreMetrics.LINES)
.workOnLanguages(Xoo.KEY)
.workOnFileTypes(InputFile.Type.MAIN, InputFile.Type.TEST);
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/SymbolReferencesSensor.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/SymbolReferencesSensor.java
new file mode 100644
index 00000000000..91fa61e5c78
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/SymbolReferencesSensor.java
@@ -0,0 +1,98 @@
+/*
+ * 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 com.google.common.base.Splitter;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.sensor.Sensor;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.SensorDescriptor;
+import org.sonar.api.batch.sensor.symbol.Symbol;
+import org.sonar.api.batch.sensor.symbol.SymbolTableBuilder;
+import org.sonar.api.measures.CoreMetrics;
+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.util.Iterator;
+import java.util.List;
+
+/**
+ * Parse files *.xoo.symbol
+ */
+public class SymbolReferencesSensor implements Sensor {
+
+ private static final String SYMBOL_EXTENSION = ".symbol";
+
+ private void processFileHighlighting(InputFile inputFile, SensorContext context) {
+ File ioFile = inputFile.file();
+ File symbolFile = new File(ioFile.getParentFile(), ioFile.getName() + SYMBOL_EXTENSION);
+ if (symbolFile.exists()) {
+ XooConstants.LOG.debug("Processing " + symbolFile.getAbsolutePath());
+ try {
+ List<String> lines = FileUtils.readLines(symbolFile, context.fileSystem().encoding().name());
+ int lineNumber = 0;
+ SymbolTableBuilder symbolTableBuilder = context.symbolTableBuilder(inputFile);
+ for (String line : lines) {
+ lineNumber++;
+ if (StringUtils.isBlank(line)) {
+ continue;
+ }
+ if (line.startsWith("#")) {
+ continue;
+ }
+ try {
+ Iterator<String> split = Splitter.on(",").split(line).iterator();
+ int startOffset = Integer.parseInt(split.next());
+ int endOffset = Integer.parseInt(split.next());
+ Symbol s = symbolTableBuilder.newSymbol(startOffset, endOffset);
+ while (split.hasNext()) {
+ symbolTableBuilder.newReference(s, Integer.parseInt(split.next()));
+ }
+ } catch (Exception e) {
+ throw new IllegalStateException("Error processing line " + lineNumber + " of file " + symbolFile.getAbsolutePath(), e);
+ }
+ }
+ symbolTableBuilder.done();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ @Override
+ public void describe(SensorDescriptor descriptor) {
+ descriptor
+ .name("Xoo Highlighting Sensor")
+ .provides(CoreMetrics.LINES)
+ .workOnLanguages(Xoo.KEY)
+ .workOnFileTypes(InputFile.Type.MAIN, InputFile.Type.TEST);
+ }
+
+ @Override
+ public void execute(SensorContext context) {
+ for (InputFile file : context.fileSystem().inputFiles(context.fileSystem().predicates().hasLanguages(Xoo.KEY))) {
+ processFileHighlighting(file, context);
+ }
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/SyntaxHighlightingSensor.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/SyntaxHighlightingSensor.java
new file mode 100644
index 00000000000..5b78759dbb9
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/SyntaxHighlightingSensor.java
@@ -0,0 +1,95 @@
+/*
+ * 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 com.google.common.base.Splitter;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.sensor.Sensor;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.SensorDescriptor;
+import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder;
+import org.sonar.api.measures.CoreMetrics;
+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.util.Iterator;
+import java.util.List;
+
+/**
+ * Parse files *.xoo.highlighting
+ */
+public class SyntaxHighlightingSensor implements Sensor {
+
+ private static final String HIGHLIGHTING_EXTENSION = ".highlighting";
+
+ private void processFileHighlighting(InputFile inputFile, SensorContext context) {
+ File ioFile = inputFile.file();
+ File highlightingFile = new File(ioFile.getParentFile(), ioFile.getName() + HIGHLIGHTING_EXTENSION);
+ if (highlightingFile.exists()) {
+ XooConstants.LOG.debug("Processing " + highlightingFile.getAbsolutePath());
+ try {
+ List<String> lines = FileUtils.readLines(highlightingFile, context.fileSystem().encoding().name());
+ int lineNumber = 0;
+ HighlightingBuilder highlightingBuilder = context.highlightingBuilder(inputFile);
+ for (String line : lines) {
+ lineNumber++;
+ if (StringUtils.isBlank(line)) {
+ continue;
+ }
+ if (line.startsWith("#")) {
+ continue;
+ }
+ try {
+ Iterator<String> split = Splitter.on(":").split(line).iterator();
+ int startOffset = Integer.parseInt(split.next());
+ int endOffset = Integer.parseInt(split.next());
+ HighlightingBuilder.TypeOfText type = HighlightingBuilder.TypeOfText.forCssClass(split.next());
+ highlightingBuilder.highlight(startOffset, endOffset, type);
+ } catch (Exception e) {
+ throw new IllegalStateException("Error processing line " + lineNumber + " of file " + highlightingFile.getAbsolutePath(), e);
+ }
+ }
+ highlightingBuilder.done();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ @Override
+ public void describe(SensorDescriptor descriptor) {
+ descriptor
+ .name("Xoo Highlighting Sensor")
+ .provides(CoreMetrics.LINES)
+ .workOnLanguages(Xoo.KEY)
+ .workOnFileTypes(InputFile.Type.MAIN, InputFile.Type.TEST);
+ }
+
+ @Override
+ public void execute(SensorContext context) {
+ for (InputFile file : context.fileSystem().inputFiles(context.fileSystem().predicates().hasLanguages(Xoo.KEY))) {
+ processFileHighlighting(file, context);
+ }
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/SensorContextAdapterTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/SensorContextAdapterTest.java
index b4a8fdab7f0..7b7eb21ee58 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/scan/SensorContextAdapterTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/scan/SensorContextAdapterTest.java
@@ -42,6 +42,7 @@ import org.sonar.api.measures.MetricFinder;
import org.sonar.api.resources.File;
import org.sonar.api.resources.Project;
import org.sonar.api.rule.RuleKey;
+import org.sonar.batch.index.ComponentDataCache;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Matchers.eq;
@@ -69,8 +70,9 @@ public class SensorContextAdapterTest {
sensorContext = mock(SensorContext.class);
settings = new Settings();
resourcePerspectives = mock(ResourcePerspectives.class);
+ ComponentDataCache componentDataCache = mock(ComponentDataCache.class);
adaptor = new SensorContextAdaptor(sensorContext, metricFinder, new Project("myProject"),
- resourcePerspectives, settings, fs, activeRules);
+ resourcePerspectives, settings, fs, activeRules, componentDataCache);
}
@Test
diff --git a/sonar-batch/src/test/java/org/sonar/batch/source/DefaultHighlightableTest.java b/sonar-batch/src/test/java/org/sonar/batch/source/DefaultHighlightableTest.java
index 421f5d87679..2f98e07cc79 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/source/DefaultHighlightableTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/source/DefaultHighlightableTest.java
@@ -22,12 +22,17 @@ package org.sonar.batch.source;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.mockito.ArgumentCaptor;
import org.sonar.api.component.Component;
+import org.sonar.batch.highlighting.SyntaxHighlightingData;
import org.sonar.batch.index.ComponentDataCache;
import org.sonar.core.source.SnapshotDataTypes;
import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Mockito.*;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
public class DefaultHighlightableTest {
@@ -36,7 +41,7 @@ public class DefaultHighlightableTest {
@Test
public void should_store_highlighting_rules() throws Exception {
- DefaultHighlightable highlightablePerspective = new DefaultHighlightable(null, null);
+ DefaultHighlightable highlightablePerspective = new DefaultHighlightable(mock(Component.class), null);
highlightablePerspective.newHighlighting().highlight(0, 10, "k").highlight(20, 30, "cppd");
assertThat(highlightablePerspective.getHighlightingRules().getSortedRules()).hasSize(2);
@@ -55,6 +60,8 @@ public class DefaultHighlightableTest {
.highlight(20, 30, "cppd")
.done();
- verify(cache).setStringData("myComponent", SnapshotDataTypes.SYNTAX_HIGHLIGHTING, "0,10,k;20,30,cppd;");
+ ArgumentCaptor<SyntaxHighlightingData> argCaptor = ArgumentCaptor.forClass(SyntaxHighlightingData.class);
+ verify(cache).setData(eq("myComponent"), eq(SnapshotDataTypes.SYNTAX_HIGHLIGHTING), argCaptor.capture());
+ assertThat(argCaptor.getValue().writeString()).isEqualTo("0,10,k;20,30,cppd;");
}
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolTableTest.java b/sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolTableTest.java
index 2e65c12473b..a1058b27d64 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolTableTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolTableTest.java
@@ -35,7 +35,7 @@ public class DefaultSymbolTableTest {
@Test
public void should_order_symbol_and_references() throws Exception {
- Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder();
+ Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder("foo");
Symbol firstSymbol = symbolTableBuilder.newSymbol(10, 20);
symbolTableBuilder.newReference(firstSymbol, 32);
Symbol secondSymbol = symbolTableBuilder.newSymbol(84, 92);
@@ -54,16 +54,16 @@ public class DefaultSymbolTableTest {
public void should_reject_reference_conflicting_with_declaration() throws Exception {
throwable.expect(UnsupportedOperationException.class);
- Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder();
+ Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder("foo");
Symbol symbol = symbolTableBuilder.newSymbol(10, 20);
symbolTableBuilder.newReference(symbol, 15);
}
@Test
public void test_toString() throws Exception {
- Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder();
+ Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder("foo");
Symbol symbol = symbolTableBuilder.newSymbol(10, 20);
- assertThat(symbol.toString()).isEqualTo("Symbol{offset=10-20}");
+ assertThat(symbol.toString()).isEqualTo("Symbol{component=foo, offset=10-20}");
}
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/source/SymbolDataTest.java b/sonar-batch/src/test/java/org/sonar/batch/source/SymbolDataTest.java
deleted file mode 100644
index 8dfdd1cccd5..00000000000
--- a/sonar-batch/src/test/java/org/sonar/batch/source/SymbolDataTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.source;
-
-import org.junit.Test;
-import org.sonar.api.source.Symbol;
-import org.sonar.api.source.Symbolizable;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class SymbolDataTest {
-
- @Test
- public void should_serialize_symbols_in_natural_order() throws Exception {
-
- Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder();
- Symbol firstSymbol = symbolTableBuilder.newSymbol(10, 20);
- symbolTableBuilder.newReference(firstSymbol, 32);
- Symbol secondSymbol = symbolTableBuilder.newSymbol(84, 92);
- symbolTableBuilder.newReference(secondSymbol, 124);
- Symbol thirdSymbol = symbolTableBuilder.newSymbol(55, 62);
- symbolTableBuilder.newReference(thirdSymbol, 70);
- Symbolizable.SymbolTable symbolTable = symbolTableBuilder.build();
-
- SymbolData dataRepository = new SymbolData(symbolTable);
- String serializedSymbolData = dataRepository.writeString();
-
- assertThat(serializedSymbolData).isEqualTo("10,20,10,32;55,62,55,70;84,92,84,124;");
- }
-
- @Test
- public void should_serialize_unused_symbol() throws Exception {
-
- Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder();
- symbolTableBuilder.newSymbol(10, 20);
-
- SymbolData dataRepository = new SymbolData(symbolTableBuilder.build());
- String serializedSymbolData = dataRepository.writeString();
-
- assertThat(serializedSymbolData).isEqualTo("10,20,10;");
- }
-}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/symbol/DefaultSymbolTableBuilderTest.java b/sonar-batch/src/test/java/org/sonar/batch/symbol/DefaultSymbolTableBuilderTest.java
new file mode 100644
index 00000000000..c661da30af1
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/symbol/DefaultSymbolTableBuilderTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.symbol;
+
+import com.google.common.collect.SortedSetMultimap;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.mockito.ArgumentCaptor;
+import org.sonar.api.batch.sensor.symbol.Symbol;
+import org.sonar.api.batch.sensor.symbol.SymbolTableBuilder;
+import org.sonar.batch.index.ComponentDataCache;
+import org.sonar.core.source.SnapshotDataTypes;
+
+import java.util.ArrayList;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class DefaultSymbolTableBuilderTest {
+
+ @Rule
+ public ExpectedException throwable = ExpectedException.none();
+
+ @Test
+ public void should_order_symbol_and_references() throws Exception {
+ ComponentDataCache componentDataCache = mock(ComponentDataCache.class);
+ SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTableBuilder("foo", componentDataCache);
+ Symbol firstSymbol = symbolTableBuilder.newSymbol(10, 20);
+ symbolTableBuilder.newReference(firstSymbol, 32);
+ Symbol secondSymbol = symbolTableBuilder.newSymbol(84, 92);
+ symbolTableBuilder.newReference(secondSymbol, 124);
+ Symbol thirdSymbol = symbolTableBuilder.newSymbol(55, 62);
+ symbolTableBuilder.newReference(thirdSymbol, 70);
+ symbolTableBuilder.done();
+
+ ArgumentCaptor<SymbolData> argCaptor = ArgumentCaptor.forClass(SymbolData.class);
+ verify(componentDataCache).setData(eq("foo"), eq(SnapshotDataTypes.SYMBOL_HIGHLIGHTING), argCaptor.capture());
+
+ SortedSetMultimap<Symbol, Integer> referencesBySymbol = argCaptor.getValue().referencesBySymbol();
+
+ assertThat(new ArrayList<Symbol>(referencesBySymbol.keySet())).containsExactly(firstSymbol, thirdSymbol, secondSymbol);
+ assertThat(new ArrayList<Integer>(referencesBySymbol.get(firstSymbol))).containsExactly(10, 32);
+ assertThat(new ArrayList<Integer>(referencesBySymbol.get(secondSymbol))).containsExactly(84, 124);
+ assertThat(new ArrayList<Integer>(referencesBySymbol.get(thirdSymbol))).containsExactly(55, 70);
+
+ assertThat(argCaptor.getValue().writeString()).isEqualTo("10,20,10,32;55,62,55,70;84,92,84,124;");
+ }
+
+ @Test
+ public void should_serialize_unused_symbol() throws Exception {
+
+ ComponentDataCache componentDataCache = mock(ComponentDataCache.class);
+ SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTableBuilder("foo", componentDataCache);
+ symbolTableBuilder.newSymbol(10, 20);
+ symbolTableBuilder.done();
+
+ ArgumentCaptor<SymbolData> argCaptor = ArgumentCaptor.forClass(SymbolData.class);
+ verify(componentDataCache).setData(eq("foo"), eq(SnapshotDataTypes.SYMBOL_HIGHLIGHTING), argCaptor.capture());
+
+ assertThat(argCaptor.getValue().writeString()).isEqualTo("10,20,10;");
+ }
+
+ @Test
+ public void should_reject_reference_conflicting_with_declaration() throws Exception {
+ throwable.expect(UnsupportedOperationException.class);
+
+ ComponentDataCache componentDataCache = mock(ComponentDataCache.class);
+ SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTableBuilder("foo", componentDataCache);
+ Symbol symbol = symbolTableBuilder.newSymbol(10, 20);
+ symbolTableBuilder.newReference(symbol, 15);
+ }
+
+ @Test
+ public void should_reject_reference_from_another_file() throws Exception {
+ throwable.expect(UnsupportedOperationException.class);
+
+ ComponentDataCache componentDataCache = mock(ComponentDataCache.class);
+ SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTableBuilder("foo", componentDataCache);
+ Symbol symbol = symbolTableBuilder.newSymbol(10, 20);
+
+ SymbolTableBuilder symbolTableBuilder2 = new DefaultSymbolTableBuilder("foo2", componentDataCache);
+ Symbol symbol2 = symbolTableBuilder2.newSymbol(30, 40);
+
+ symbolTableBuilder.newReference(symbol2, 15);
+ }
+
+}
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index 2d8b6615c1b..8188fb9766a 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -407,7 +407,7 @@ layout.print=Print
layout.permalink=Permalink
layout.sonar.slogan=Embrace Quality
layout.dashboards=Dashboards
-layout.configuration=Configuration
+layout.configuration=Project Configuration
layout.projects=Projects
layout.recent_projects=Recent Projects
layout.user_panel.my_profile=My profile
@@ -598,7 +598,7 @@ measure_filter.display.treemap=Treemap
measure_filter.list.change=Change Columns
measure_filter.treemap.change=Change Treemap
measure_filter.add_column_button=Add Column
-measure_filter.widget.unknown_filter_warning=This widget is configured to display a measure filter that doesn't exist anymore.
+measure_filter.widget.unknown_filter_warning=This widget is configured to display a measure filter that does not exist anymore.
measure_filter.error.UNKNOWN=Unexpected error. Please contact the administrator.
measure_filter.error.TOO_MANY_RESULTS=Too many results. Please refine your search.
measure_filter.error.VALUE_SHOULD_BE_A_NUMBER=Value used for metric should be a number.
@@ -1958,7 +1958,7 @@ bulk_deletion.delete_all_ghosts=Delete all ghosts
provisioning.no_results=There is currently no provisioned project.
provisioning.missing.key=Key is missing
provisioning.missing.name=Name is missing
-provisioning.no_analysis=No analysis has been performed since creation. The only available section is Configuration.
+provisioning.no_analysis=No analysis has been performed since creation. The only available section is Project Configuration.
#------------------------------------------------------------------------------
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
index 234b3865ca3..787d2fddb3d 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
@@ -24,10 +24,12 @@ 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.batch.sensor.highlighting.HighlightingBuilder;
import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.api.batch.sensor.issue.IssueBuilder;
import org.sonar.api.batch.sensor.measure.Measure;
import org.sonar.api.batch.sensor.measure.MeasureBuilder;
+import org.sonar.api.batch.sensor.symbol.SymbolTableBuilder;
import org.sonar.api.config.Settings;
import javax.annotation.CheckForNull;
@@ -108,4 +110,20 @@ public interface SensorContext {
*/
boolean addIssue(Issue issue);
+ // ------------ HIGHLIGHTING ------------
+
+ /**
+ * Builder to define highlighting of a file.
+ * @since 4.5
+ */
+ HighlightingBuilder highlightingBuilder(InputFile inputFile);
+
+ // ------------ SYMBOL REFERENCES ------------
+
+ /**
+ * Builder to define symbol references in a file.
+ * @since 4.5
+ */
+ SymbolTableBuilder symbolTableBuilder(InputFile inputFile);
+
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/highlighting/HighlightingBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/highlighting/HighlightingBuilder.java
new file mode 100644
index 00000000000..db8d5c46e93
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/highlighting/HighlightingBuilder.java
@@ -0,0 +1,78 @@
+/*
+ * 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.api.batch.sensor.highlighting;
+
+
+/**
+ * This builder is used to define syntax highlighting (aka code coloration) on files.
+ * @since 4.5
+ */
+public interface HighlightingBuilder {
+
+ /**
+ * See sonar-colorizer.css
+ */
+ enum TypeOfText {
+ ANNOTATION("a"),
+ CONSTANT("c"),
+ JAVADOC("j"),
+ CLASSIC_COMMENT("cd"),
+ CPP_DOC("cppd"),
+ KEYWORD("k"),
+ STRING("s"),
+ KEYWORD_LIGHT("h"),
+ PREPROCESS_DIRECTIVE("p");
+
+ private final String cssClass;
+
+ private TypeOfText(String cssClass) {
+ this.cssClass = cssClass;
+ }
+
+ public static TypeOfText forCssClass(String cssClass) {
+ for (TypeOfText typeOfText : TypeOfText.values()) {
+ if (typeOfText.cssClass().equals(cssClass)) {
+ return typeOfText;
+ }
+ }
+ throw new IllegalArgumentException("No TypeOfText for CSS class " + cssClass);
+ }
+
+ /**
+ * For internal use
+ */
+ public String cssClass() {
+ return cssClass;
+ }
+ }
+
+ /**
+ * Call this method to indicate the type of text in a range.
+ * @param startOffset Starting position in file for this type of text. Beginning of a file starts with offset '0'.
+ * @param endOffset End position in file for this type of text.
+ * @param typeOfText see {@link TypeOfText} values.
+ */
+ HighlightingBuilder highlight(int startOffset, int endOffset, TypeOfText typeOfText);
+
+ /**
+ * Call this method only once when your are done with defining highlighting of the file.
+ */
+ void done();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/highlighting/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/highlighting/package-info.java
new file mode 100644
index 00000000000..b4d89fb5783
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/highlighting/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+@javax.annotation.ParametersAreNonnullByDefault
+package org.sonar.api.batch.sensor.highlighting;
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/symbol/Symbol.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/symbol/Symbol.java
new file mode 100644
index 00000000000..9e1e3e8259e
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/symbol/Symbol.java
@@ -0,0 +1,33 @@
+/*
+ * 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.api.batch.sensor.symbol;
+
+/**
+ * Represent a symbol in a source file.
+ * @since 4.5
+ */
+public interface Symbol {
+
+ int getDeclarationStartOffset();
+
+ int getDeclarationEndOffset();
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/symbol/SymbolTableBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/symbol/SymbolTableBuilder.java
new file mode 100644
index 00000000000..3be82175d42
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/symbol/SymbolTableBuilder.java
@@ -0,0 +1,48 @@
+/*
+ * 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.api.batch.sensor.symbol;
+
+/**
+ * Use this builder to create symbol references. For now only references
+ * in the same file are supported.
+ * @since 4.5
+ */
+public interface SymbolTableBuilder {
+
+ /**
+ * Create a new symbol.
+ * @param fromOffset Starting offset in a file for the symbol declaration. File starts at offset '0'.
+ * @param toOffset Ending offset of symbol declaration.
+ * @return a new Symbol that can be used later in {@link #newReference(Symbol, int)}
+ */
+ Symbol newSymbol(int fromOffset, int toOffset);
+
+ /**
+ * Records that a {@link Symbol} is referenced at another location in the same file.
+ * @param symbol Symbol previously created with {@link #newSymbol(int, int)}
+ * @param fromOffset Starting offset of the place symbol is referenced. No need for end offset here since we assume it is same length.
+ */
+ void newReference(Symbol symbol, int fromOffset);
+
+ /**
+ * Call this method only once when your are done with defining symbols of the file.
+ */
+ void done();
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbol.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/symbol/internal/DefaultSymbol.java
index 629633a05de..8f071278505 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbol.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/symbol/internal/DefaultSymbol.java
@@ -18,29 +18,40 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.batch.source;
+package org.sonar.api.batch.sensor.symbol.internal;
import com.google.common.base.Objects;
-import org.sonar.api.source.Symbol;
+import org.sonar.api.batch.sensor.symbol.Symbol;
-public class DefaultSymbol implements Symbol {
+import java.io.Serializable;
+public class DefaultSymbol implements Symbol, org.sonar.api.source.Symbol, Serializable {
+
+ private final String componentKey;
private final int declarationStartOffset;
private final int declarationEndOffset;
- public DefaultSymbol(int startOffset, int endOffset) {
+ public DefaultSymbol(String componentKey, int startOffset, int endOffset) {
+ this.componentKey = componentKey;
this.declarationStartOffset = startOffset;
this.declarationEndOffset = endOffset;
}
+ public String componentKey() {
+ return componentKey;
+ }
+
+ @Override
public int getDeclarationStartOffset() {
return declarationStartOffset;
}
+ @Override
public int getDeclarationEndOffset() {
return declarationEndOffset;
}
+ @Override
public String getFullyQualifiedName() {
return null;
}
@@ -48,6 +59,7 @@ public class DefaultSymbol implements Symbol {
@Override
public String toString() {
return Objects.toStringHelper("Symbol")
+ .add("component", componentKey)
.add("offset", String.format("%d-%d", declarationStartOffset, declarationEndOffset))
.toString();
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/symbol/internal/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/symbol/internal/package-info.java
new file mode 100644
index 00000000000..e0ccdf1c9b3
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/symbol/internal/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+@javax.annotation.ParametersAreNonnullByDefault
+package org.sonar.api.batch.sensor.symbol.internal;
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/symbol/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/symbol/package-info.java
new file mode 100644
index 00000000000..7acf7f3b056
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/symbol/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+@javax.annotation.ParametersAreNonnullByDefault
+package org.sonar.api.batch.sensor.symbol;
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java
index 45328b52e49..006eb0866cd 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java
@@ -48,6 +48,7 @@ import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
+
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@@ -195,7 +196,6 @@ public class Rule {
this.id = id;
}
- @CheckForNull
public String getName() {
return name;
}
@@ -203,7 +203,7 @@ public class Rule {
/**
* Sets the rule name
*/
- public Rule setName(@Nullable String name) {
+ public Rule setName(String name) {
this.name = removeNewLineCharacters(name);
return this;
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/source/Highlightable.java b/sonar-plugin-api/src/main/java/org/sonar/api/source/Highlightable.java
index 0ab229be03c..6d5bd6bd14d 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/source/Highlightable.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/source/Highlightable.java
@@ -19,11 +19,14 @@
*/
package org.sonar.api.source;
+import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.component.Perspective;
/**
* @since 3.6
+ * @deprecated since 4.5 use {@link SensorContext#highlightingBuilder(org.sonar.api.batch.fs.InputFile)}
*/
+@Deprecated
public interface Highlightable extends Perspective {
interface HighlightingBuilder {
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/source/Symbol.java b/sonar-plugin-api/src/main/java/org/sonar/api/source/Symbol.java
index 260d6c8ecd7..31702ecb065 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/source/Symbol.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/source/Symbol.java
@@ -20,15 +20,21 @@
package org.sonar.api.source;
-public interface Symbol {
+/**
+ * @deprecated since 4.5 replaced by {@link org.sonar.api.batch.sensor.symbol.Symbol}
+ */
+@Deprecated
+public interface Symbol extends org.sonar.api.batch.sensor.symbol.Symbol {
+ @Override
int getDeclarationStartOffset();
+ @Override
int getDeclarationEndOffset();
/**
* @since unused
- * @deprecated in 4.3
+ * @deprecated in 4.3 not used.
*/
@Deprecated
String getFullyQualifiedName();
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/source/Symbolizable.java b/sonar-plugin-api/src/main/java/org/sonar/api/source/Symbolizable.java
index 3a49ada34df..0b7f765cfe7 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/source/Symbolizable.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/source/Symbolizable.java
@@ -19,13 +19,16 @@
*/
package org.sonar.api.source;
+import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.component.Perspective;
import java.util.List;
/**
* @since 3.6
+ * @deprecated since 4.5 use {@link SensorContext#symbolTableBuilder(org.sonar.api.batch.fs.InputFile)}
*/
+@Deprecated
public interface Symbolizable extends Perspective {
interface SymbolTableBuilder {
diff --git a/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_and_overridden_debt_values.json b/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_and_overridden_debt_values.json
new file mode 100644
index 00000000000..3aa6211c764
--- /dev/null
+++ b/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_and_overridden_debt_values.json
@@ -0,0 +1,18 @@
+{"total": 1, "p": 1, "ps": 10, "rules": [
+ {
+ "key": "xoo:x1",
+ "debtChar": "RELIABILITY",
+ "debtCharName": "Reliability",
+ "debtSubChar": "SOFT_RELIABILITY",
+ "debtSubCharName": "Soft Reliability",
+ "debtRemFnType": "LINEAR_OFFSET",
+ "debtRemFnCoeff": "2h",
+ "debtRemFnOffset": "25min",
+ "debtOverloaded": true,
+ "defaultDebtChar": "RELIABILITY",
+ "defaultDebtSubChar": "HARD_RELIABILITY",
+ "defaultDebtRemFnType": "LINEAR_OFFSET",
+ "defaultDebtRemFnCoeff": "1h",
+ "defaultDebtRemFnOffset": "15min"
+ }
+]}
diff --git a/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_linear_offset_and_overridden_constant_debt.json b/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_linear_offset_and_overridden_constant_debt.json
new file mode 100644
index 00000000000..6ae75abecb5
--- /dev/null
+++ b/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_linear_offset_and_overridden_constant_debt.json
@@ -0,0 +1,17 @@
+{"total": 1, "p": 1, "ps": 10, "rules": [
+ {
+ "key": "xoo:x1",
+ "debtChar": "RELIABILITY",
+ "debtCharName": "Reliability",
+ "debtSubChar": "SOFT_RELIABILITY",
+ "debtSubCharName": "Soft Reliability",
+ "debtRemFnType": "CONSTANT_ISSUE",
+ "debtRemFnOffset": "5min",
+ "debtOverloaded": true,
+ "defaultDebtChar": "RELIABILITY",
+ "defaultDebtSubChar": "HARD_RELIABILITY",
+ "defaultDebtRemFnType": "LINEAR_OFFSET",
+ "defaultDebtRemFnCoeff": "1h",
+ "defaultDebtRemFnOffset": "15min"
+ }
+]}
diff --git a/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_linear_offset_and_overridden_linear_debt.json b/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_linear_offset_and_overridden_linear_debt.json
new file mode 100644
index 00000000000..b1c457e13c0
--- /dev/null
+++ b/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWebServiceMediumTest/search_debt_rules_with_default_linear_offset_and_overridden_linear_debt.json
@@ -0,0 +1,17 @@
+{"total": 1, "p": 1, "ps": 10, "rules": [
+ {
+ "key": "xoo:x1",
+ "debtChar": "RELIABILITY",
+ "debtCharName": "Reliability",
+ "debtSubChar": "SOFT_RELIABILITY",
+ "debtSubCharName": "Soft Reliability",
+ "debtRemFnType": "LINEAR",
+ "debtRemFnCoeff": "1h",
+ "debtOverloaded": true,
+ "defaultDebtChar": "RELIABILITY",
+ "defaultDebtSubChar": "HARD_RELIABILITY",
+ "defaultDebtRemFnType": "LINEAR_OFFSET",
+ "defaultDebtRemFnCoeff": "1h",
+ "defaultDebtRemFnOffset": "15min"
+ }
+]}