@@ -53,6 +53,7 @@ import org.sonar.xoo.rule.OneIssuePerDirectorySensor; | |||
import org.sonar.xoo.rule.OneIssuePerFileSensor; | |||
import org.sonar.xoo.rule.OneIssuePerLineSensor; | |||
import org.sonar.xoo.rule.OneIssuePerModuleSensor; | |||
import org.sonar.xoo.rule.OneIssuePerTestFileSensor; | |||
import org.sonar.xoo.rule.OneIssuePerUnknownFileSensor; | |||
import org.sonar.xoo.rule.OneVulnerabilityIssuePerModuleSensor; | |||
import org.sonar.xoo.rule.RandomAccessSensor; | |||
@@ -127,6 +128,7 @@ public class XooPlugin implements Plugin { | |||
OneIssuePerLineSensor.class, | |||
OneDayDebtPerFileSensor.class, | |||
OneIssuePerFileSensor.class, | |||
OneIssuePerTestFileSensor.class, | |||
OneIssuePerDirectorySensor.class, | |||
OneIssuePerModuleSensor.class, | |||
OneIssueOnDirPerFileSensor.class, |
@@ -0,0 +1,43 @@ | |||
package org.sonar.xoo.rule; | |||
import org.sonar.api.batch.fs.FileSystem; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.fs.InputFile.Type; | |||
import org.sonar.api.batch.rule.ActiveRules; | |||
import org.sonar.api.batch.sensor.SensorContext; | |||
import org.sonar.api.batch.sensor.SensorDescriptor; | |||
import org.sonar.api.batch.sensor.issue.NewIssue; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.xoo.Xoo; | |||
public class OneIssuePerTestFileSensor extends AbstractXooRuleSensor { | |||
public static final String RULE_KEY = "OneIssuePerTestFile"; | |||
public OneIssuePerTestFileSensor(FileSystem fs, ActiveRules activeRules) { | |||
super(fs, activeRules); | |||
} | |||
@Override | |||
protected String getRuleKey() { | |||
return RULE_KEY; | |||
} | |||
@Override | |||
protected void processFile(InputFile inputFile, SensorContext context, RuleKey ruleKey, String languageKey) { | |||
NewIssue newIssue = context.newIssue(); | |||
newIssue | |||
.forRule(ruleKey) | |||
.at(newIssue.newLocation().message("This issue is generated on each test file") | |||
.on(inputFile)) | |||
.save(); | |||
} | |||
@Override | |||
public void describe(SensorDescriptor descriptor) { | |||
descriptor | |||
.onlyOnLanguage(Xoo.KEY) | |||
.onlyOnFileType(Type.TEST) | |||
.createIssuesForRuleRepository(XooRulesDefinition.XOO_REPOSITORY); | |||
} | |||
} |
@@ -19,6 +19,7 @@ | |||
*/ | |||
package org.sonar.xoo.rule; | |||
import org.sonar.api.rule.RuleScope; | |||
import org.sonar.api.rules.RuleType; | |||
import org.sonar.api.server.rule.RuleParamType; | |||
import org.sonar.api.server.rule.RulesDefinition; | |||
@@ -35,6 +36,8 @@ public class XooRulesDefinition implements RulesDefinition { | |||
public static final String XOO_REPOSITORY = "xoo"; | |||
public static final String XOO2_REPOSITORY = "xoo2"; | |||
private static final String TEN_MIN = "10min"; | |||
@Override | |||
public void define(Context context) { | |||
defineRulesXoo(context); | |||
@@ -89,11 +92,16 @@ public class XooRulesDefinition implements RulesDefinition { | |||
NewRule oneIssuePerFile = repo.createRule(OneIssuePerFileSensor.RULE_KEY).setName("One Issue Per File") | |||
.setHtmlDescription("Generate an issue on each file"); | |||
oneIssuePerFile.setDebtRemediationFunction(oneIssuePerFile.debtRemediationFunctions().linear("10min")); | |||
oneIssuePerFile.setDebtRemediationFunction(oneIssuePerFile.debtRemediationFunctions().linear(TEN_MIN)); | |||
NewRule oneIssuePerTestFile = repo.createRule(OneIssuePerTestFileSensor.RULE_KEY).setName("One Issue Per Test File") | |||
.setScope(RuleScope.TEST) | |||
.setHtmlDescription("Generate an issue on each test file"); | |||
oneIssuePerTestFile.setDebtRemediationFunction(oneIssuePerTestFile.debtRemediationFunctions().linear(TEN_MIN)); | |||
NewRule oneIssuePerDirectory = repo.createRule(OneIssuePerDirectorySensor.RULE_KEY).setName("One Issue Per Directory") | |||
.setHtmlDescription("Generate an issue on each non-empty directory"); | |||
oneIssuePerFile.setDebtRemediationFunction(oneIssuePerDirectory.debtRemediationFunctions().linear("10min")); | |||
oneIssuePerDirectory.setDebtRemediationFunction(oneIssuePerDirectory.debtRemediationFunctions().linear(TEN_MIN)); | |||
NewRule oneDayDebtPerFile = repo.createRule(OneDayDebtPerFileSensor.RULE_KEY).setName("One Day Debt Per File") | |||
.setHtmlDescription("Generate an issue on each file with a debt of one day"); |
@@ -30,6 +30,7 @@ import org.sonar.api.rules.RuleType; | |||
import org.sonar.api.server.rule.RuleParamType; | |||
import org.sonar.db.organization.OrganizationDto; | |||
import org.sonar.db.rule.RuleDto.Format; | |||
import org.sonar.db.rule.RuleDto.Scope; | |||
import static com.google.common.base.Preconditions.checkNotNull; | |||
import static com.google.common.collect.ImmutableSet.copyOf; | |||
@@ -76,7 +77,8 @@ public class RuleTesting { | |||
.setDefRemediationGapMultiplier(nextInt(10) + "h") | |||
.setDefRemediationFunction("LINEAR_OFFSET") | |||
.setCreatedAt(System.currentTimeMillis()) | |||
.setUpdatedAt(System.currentTimeMillis()); | |||
.setUpdatedAt(System.currentTimeMillis()) | |||
.setScope(Scope.MAIN); | |||
} | |||
public static RuleMetadataDto newRuleMetadata() { | |||
@@ -189,7 +191,8 @@ public class RuleTesting { | |||
.setGapDescription(ruleKey.repository() + "." + ruleKey.rule() + ".effortToFix") | |||
.setType(RuleType.CODE_SMELL) | |||
.setCreatedAt(new Date().getTime()) | |||
.setUpdatedAt(new Date().getTime()); | |||
.setUpdatedAt(new Date().getTime()) | |||
.setScope(Scope.MAIN); | |||
if (organization != null) { | |||
res | |||
.setOrganizationUuid(organization.getUuid()) |
@@ -31,12 +31,14 @@ import org.sonar.api.resources.Languages; | |||
import org.sonar.api.server.debt.DebtRemediationFunction; | |||
import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction; | |||
import org.sonar.db.rule.RuleDefinitionDto; | |||
import org.sonar.db.rule.RuleDto.Scope; | |||
import org.sonar.db.rule.RuleMetadataDto; | |||
import org.sonar.db.rule.RuleParamDto; | |||
import org.sonar.markdown.Markdown; | |||
import org.sonar.server.rule.ws.SearchAction.SearchResult; | |||
import org.sonar.server.text.MacroInterpreter; | |||
import org.sonarqube.ws.Common; | |||
import org.sonarqube.ws.Common.RuleScope; | |||
import org.sonarqube.ws.Rules; | |||
import static java.lang.String.format; | |||
@@ -63,6 +65,7 @@ import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_REPO; | |||
import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_SEVERITY; | |||
import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_STATUS; | |||
import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_SYSTEM_TAGS; | |||
import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_SCOPE; | |||
import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_TAGS; | |||
import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_TEMPLATE_KEY; | |||
@@ -115,6 +118,7 @@ public class RuleMapper { | |||
setTemplateKey(ruleResponse, ruleDefinitionDto, result, fieldsToReturn); | |||
setDefaultDebtRemediationFunctionFields(ruleResponse, ruleDefinitionDto, fieldsToReturn); | |||
setEffortToFixDescription(ruleResponse, ruleDefinitionDto, fieldsToReturn); | |||
setScope(ruleResponse, ruleDefinitionDto, fieldsToReturn); | |||
return ruleResponse; | |||
} | |||
@@ -130,6 +134,25 @@ public class RuleMapper { | |||
} | |||
} | |||
private static void setScope(Rules.Rule.Builder ruleResponse, RuleDefinitionDto ruleDto, Set<String> fieldsToReturn) { | |||
if (shouldReturnField(fieldsToReturn, FIELD_SCOPE)) { | |||
ruleResponse.setScope(toWsRuleScope(ruleDto.getScope())); | |||
} | |||
} | |||
private static RuleScope toWsRuleScope(Scope scope) { | |||
switch (scope) { | |||
case ALL: | |||
return RuleScope.ALL; | |||
case MAIN: | |||
return RuleScope.MAIN; | |||
case TEST: | |||
return RuleScope.TEST; | |||
default: | |||
throw new IllegalArgumentException("Unknown rule scope: " + scope); | |||
} | |||
} | |||
private static void setEffortToFixDescription(Rules.Rule.Builder ruleResponse, RuleDefinitionDto ruleDto, Set<String> fieldsToReturn) { | |||
if ((shouldReturnField(fieldsToReturn, FIELD_EFFORT_TO_FIX_DESCRIPTION) || shouldReturnField(fieldsToReturn, FIELD_GAP_DESCRIPTION)) | |||
&& ruleDto.getGapDescription() != null) { |
@@ -86,6 +86,11 @@ public class RulesWsParameters { | |||
public static final String FIELD_DEBT_OVERLOADED = "debtOverloaded"; | |||
public static final String FIELD_REM_FUNCTION_OVERLOADED = "remFnOverloaded"; | |||
/** | |||
* @since 7.1 | |||
*/ | |||
public static final String FIELD_SCOPE = "scope"; | |||
public static final String FIELD_PARAMS = "params"; | |||
public static final String FIELD_ACTIVES = "actives"; | |||
@@ -94,7 +99,7 @@ public class RulesWsParameters { | |||
FIELD_MARKDOWN_NOTE, FIELD_HTML_NOTE, | |||
FIELD_DEFAULT_DEBT_REM_FUNCTION, FIELD_EFFORT_TO_FIX_DESCRIPTION, FIELD_DEBT_OVERLOADED, FIELD_DEBT_REM_FUNCTION, | |||
FIELD_DEFAULT_REM_FUNCTION, FIELD_GAP_DESCRIPTION, FIELD_REM_FUNCTION_OVERLOADED, FIELD_REM_FUNCTION, | |||
FIELD_PARAMS, FIELD_ACTIVES); | |||
FIELD_PARAMS, FIELD_ACTIVES, FIELD_SCOPE); | |||
private RulesWsParameters() { | |||
// prevent instantiation |
@@ -145,7 +145,8 @@ public class SearchAction implements RulesWsAction { | |||
"<li>\"debtRemFn\" becomes \"remFn\"</li>" + | |||
"<li>\"effortToFixDescription\" becomes \"gapDescription\"</li>" + | |||
"<li>\"debtOverloaded\" becomes \"remFnOverloaded\"</li>" + | |||
"</ul>") | |||
"</ul>" + | |||
"In 7.1, the field 'scope' has been added.") | |||
.setPossibleValues(Ordering.natural().sortedCopy(OPTIONAL_FIELDS)); | |||
Iterator<String> it = OPTIONAL_FIELDS.iterator(); | |||
paramFields.setExampleValue(format("%s,%s", it.next(), it.next())); | |||
@@ -190,7 +191,8 @@ public class SearchAction implements RulesWsAction { | |||
"<li>\"debtRemFnOffset\" becomes \"remFnBaseEffort\"</li>" + | |||
"<li>\"defaultDebtRemFnOffset\" becomes \"defaultRemFnBaseEffort\"</li>" + | |||
"<li>\"debtOverloaded\" becomes \"remFnOverloaded\"</li>" + | |||
"</ul>") | |||
"</ul>" + | |||
"In 7.1, a new field 'scope' has been added to the response.") | |||
.setResponseExample(getClass().getResource("search-example.json")) | |||
.setSince("4.4") | |||
.setHandler(this); |
@@ -75,7 +75,8 @@ public class ShowAction implements RulesWsAction { | |||
"<li>\"debtRemFnOffset\" becomes \"remFnBaseEffort\"</li>" + | |||
"<li>\"defaultDebtRemFnOffset\" becomes \"defaultRemFnBaseEffort\"</li>" + | |||
"<li>\"debtOverloaded\" becomes \"remFnOverloaded\"</li>" + | |||
"</ul>") | |||
"</ul>" + | |||
"In 7.1, the field 'scope' has been added.") | |||
.setSince("4.2") | |||
.setResponseExample(Resources.getResource(getClass(), "example-show.json")) | |||
.setHandler(this); |
@@ -19,6 +19,7 @@ | |||
"gapDescription": "java.S001.effortToFix", | |||
"lang": "java", | |||
"langName": "Java", | |||
"scope": "MAIN", | |||
"type": "CODE_SMELL", | |||
"params": [ | |||
{ |
@@ -17,6 +17,7 @@ | |||
"sysTags": ["brain-overload"], | |||
"lang": "java", | |||
"langName": "Java", | |||
"scope": "MAIN", | |||
"type": "CODE_SMELL", | |||
"params": [ | |||
{ | |||
@@ -40,6 +41,7 @@ | |||
"sysTags": ["brain-overload"], | |||
"lang": "java", | |||
"langName": "Java", | |||
"scope": "MAIN", | |||
"type": "BUG", | |||
"params": [ | |||
{ | |||
@@ -63,6 +65,7 @@ | |||
"sysTags": ["brain-overload"], | |||
"lang": "java", | |||
"langName": "Java", | |||
"scope": "MAIN", | |||
"type": "VULNERABILITY", | |||
"params": [ | |||
{ | |||
@@ -89,6 +92,7 @@ | |||
"noteLogin": "eric.hartmann", | |||
"lang": "java", | |||
"langName": "Java", | |||
"scope": "MAIN", | |||
"type": "CODE_SMELL", | |||
"params": [ | |||
{ | |||
@@ -118,6 +122,7 @@ | |||
"sysTags": [ ], | |||
"lang": "java", | |||
"langName": "Java", | |||
"scope": "MAIN", | |||
"type": "CODE_SMELL", | |||
"params": [ | |||
{ |
@@ -45,6 +45,7 @@ import org.sonar.db.rule.RuleDao; | |||
import org.sonar.db.rule.RuleDefinitionDto; | |||
import org.sonar.db.rule.RuleDto; | |||
import org.sonar.db.rule.RuleDto.Format; | |||
import org.sonar.db.rule.RuleDto.Scope; | |||
import org.sonar.db.rule.RuleMetadataDto; | |||
import org.sonar.db.rule.RuleParamDto; | |||
import org.sonar.db.rule.RuleTesting; | |||
@@ -246,7 +247,8 @@ public class ShowActionTest { | |||
.setLanguage("xoo") | |||
.setTags(newHashSet("tag1", "tag2")) | |||
.setSystemTags(newHashSet("systag1", "systag2")) | |||
.setType(RuleType.BUG); | |||
.setType(RuleType.BUG) | |||
.setScope(Scope.ALL); | |||
RuleDefinitionDto definition = ruleDto.getDefinition(); | |||
RuleDao ruleDao = dbClient.ruleDao(); | |||
DbSession session = dbTester.getSession(); | |||
@@ -276,7 +278,8 @@ public class ShowActionTest { | |||
.setDefRemediationBaseEffort("10h") | |||
.setRemediationFunction(null) | |||
.setRemediationGapMultiplier(null) | |||
.setRemediationBaseEffort(null); | |||
.setRemediationBaseEffort(null) | |||
.setScope(Scope.ALL); | |||
RuleDao ruleDao = dbClient.ruleDao(); | |||
DbSession session = dbTester.getSession(); | |||
ruleDao.insert(session, ruleDto.getDefinition()); | |||
@@ -304,7 +307,8 @@ public class ShowActionTest { | |||
.setDefRemediationBaseEffort(null) | |||
.setRemediationFunction("LINEAR_OFFSET") | |||
.setRemediationGapMultiplier("5d") | |||
.setRemediationBaseEffort("10h"); | |||
.setRemediationBaseEffort("10h") | |||
.setScope(Scope.ALL); | |||
RuleDao ruleDao = dbClient.ruleDao(); | |||
DbSession session = dbTester.getSession(); | |||
ruleDao.insert(session, ruleDto.getDefinition()); | |||
@@ -331,7 +335,8 @@ public class ShowActionTest { | |||
.setDefRemediationBaseEffort(null) | |||
.setRemediationFunction("LINEAR_OFFSET") | |||
.setRemediationGapMultiplier("5d") | |||
.setRemediationBaseEffort("10h"); | |||
.setRemediationBaseEffort("10h") | |||
.setScope(Scope.ALL); | |||
RuleDao ruleDao = dbClient.ruleDao(); | |||
DbSession session = dbTester.getSession(); | |||
ruleDao.insert(session, ruleDto.getDefinition()); | |||
@@ -356,7 +361,8 @@ public class ShowActionTest { | |||
.setLanguage("xoo") | |||
.setDefRemediationFunction(null) | |||
.setDefRemediationGapMultiplier(null) | |||
.setDefRemediationBaseEffort(null); | |||
.setDefRemediationBaseEffort(null) | |||
.setScope(Scope.ALL); | |||
RuleDao ruleDao = dbClient.ruleDao(); | |||
DbSession session = dbTester.getSession(); | |||
ruleDao.insert(session, ruleDto); | |||
@@ -413,7 +419,8 @@ public class ShowActionTest { | |||
.setDefRemediationBaseEffort("11h") | |||
.setRemediationFunction("LINEAR_OFFSET") | |||
.setRemediationGapMultiplier("5d") | |||
.setRemediationBaseEffort("10h"); | |||
.setRemediationBaseEffort("10h") | |||
.setScope(Scope.ALL); | |||
RuleDao ruleDao = dbClient.ruleDao(); | |||
DbSession session = dbTester.getSession(); | |||
ruleDao.insert(session, ruleDto.getDefinition()); | |||
@@ -436,7 +443,8 @@ public class ShowActionTest { | |||
.setLanguage("xoo") | |||
.setType(RuleType.BUG) | |||
.setCreatedAt(new Date().getTime()) | |||
.setUpdatedAt(new Date().getTime()); | |||
.setUpdatedAt(new Date().getTime()) | |||
.setScope(Scope.ALL); | |||
RuleDao ruleDao = dbClient.ruleDao(); | |||
DbSession session = dbTester.getSession(); | |||
ruleDao.insert(session, ruleDto); |
@@ -72,6 +72,12 @@ enum RuleStatus { | |||
REMOVED = 3; | |||
} | |||
enum RuleScope { | |||
MAIN = 0; | |||
TEST = 1; | |||
ALL = 2; | |||
} | |||
// Lines start at 1 and line offsets start at 0 | |||
message TextRange { | |||
// Start line. Should never be absent |
@@ -119,6 +119,7 @@ message Rule { | |||
optional string remFnBaseEffort = 43; | |||
optional bool remFnOverloaded = 45; | |||
optional string gapDescription = 44; | |||
optional sonarqube.ws.commons.RuleScope scope = 46; | |||
optional sonarqube.ws.commons.RuleType type = 37; | |||
@@ -25,13 +25,16 @@ import java.util.function.Predicate; | |||
import org.junit.ClassRule; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.sonarqube.tests.Category6Suite; | |||
import org.sonarqube.qa.util.Tester; | |||
import org.sonarqube.tests.Category6Suite; | |||
import org.sonarqube.ws.Common.RuleScope; | |||
import org.sonarqube.ws.Organizations.Organization; | |||
import org.sonarqube.ws.Qualityprofiles.CreateWsResponse; | |||
import org.sonarqube.ws.Qualityprofiles.SearchWsResponse; | |||
import org.sonarqube.ws.Rules; | |||
import org.sonarqube.ws.Rules.ShowResponse; | |||
import org.sonarqube.ws.client.rules.SearchRequest; | |||
import org.sonarqube.ws.client.rules.ShowRequest; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
@@ -40,6 +43,7 @@ public class RulesWsTest { | |||
private static final String RULE_HAS_TAG = "xoo:HasTag"; | |||
private static final String RULE_ONE_ISSUE_PER_LINE = "xoo:OneIssuePerLine"; | |||
private static final String RULE_ONE_ISSUE_PER_FILE = "xoo:OneIssuePerFile"; | |||
private static final String RULE_ONE_ISSUE_PER_TEST_FILE = "xoo:OneIssuePerTestFile"; | |||
private static final String RULE_ONE_BUG_PER_LINE = "xoo:OneBugIssuePerLine"; | |||
private static final String PROFILE_SONAR_WAY = "Sonar way"; | |||
private static final String LANGUAGE_XOO = "xoo"; | |||
@@ -82,6 +86,13 @@ public class RulesWsTest { | |||
.containsExactlyInAnyOrder(RULE_HAS_TAG, RULE_ONE_ISSUE_PER_FILE); | |||
} | |||
@Test | |||
public void show_rule_with_test_scope() { | |||
ShowResponse show = tester.wsClient().rules().show(new ShowRequest().setKey(RULE_ONE_ISSUE_PER_TEST_FILE)); | |||
assertThat(show.getRule().getScope()).isEqualTo(RuleScope.TEST); | |||
} | |||
private SearchWsResponse.QualityProfile getProfile(Organization organization, Predicate<SearchWsResponse.QualityProfile> filter) { | |||
return tester.qProfiles().service().search(new org.sonarqube.ws.client.qualityprofiles.SearchRequest() | |||
.setOrganization(organization.getKey())).getProfilesList() |