diff --git a/ecocode-rules-specifications/pom.xml b/ecocode-rules-specifications/pom.xml
index df7b95fe3..59423ee7a 100644
--- a/ecocode-rules-specifications/pom.xml
+++ b/ecocode-rules-specifications/pom.xml
@@ -126,6 +126,13 @@
+
+
+
+
+
+
+
@@ -171,6 +178,18 @@
+
+ assembly-python
+ prepare-package
+
+ single
+
+
+
+ ${project.basedir}/src/main/assembly/python.xml
+
+
+
true
diff --git a/ecocode-rules-specifications/src/main/assembly/python.xml b/ecocode-rules-specifications/src/main/assembly/python.xml
new file mode 100644
index 000000000..7c294fd24
--- /dev/null
+++ b/ecocode-rules-specifications/src/main/assembly/python.xml
@@ -0,0 +1,18 @@
+
+ python
+
+ jar
+
+ false
+
+
+ ${project.build.outputDirectory}
+
+ io/ecocode/rules/python/*.*
+
+
+
+
+
diff --git a/ecocode-rules-specifications/src/main/rules/EC10/EC10.json b/ecocode-rules-specifications/src/main/rules/EC10/EC10.json
new file mode 100644
index 000000000..ec7fbffd9
--- /dev/null
+++ b/ecocode-rules-specifications/src/main/rules/EC10/EC10.json
@@ -0,0 +1,14 @@
+{
+ "title": "Avoid using unoptimized vector images",
+ "type": "CODE_SMELL",
+ "status": "ready",
+ "remediation": {
+ "func": "Constant\/Issue",
+ "constantCost": "60min"
+ },
+ "tags": [
+ "eco-design",
+ "ecocode"
+ ],
+ "defaultSeverity": "Minor"
+}
diff --git a/python-plugin/src/main/resources/fr/greencodeinitiative/l10n/python/rules/python/EC10.html b/ecocode-rules-specifications/src/main/rules/EC10/python/EC10.asciidoc
similarity index 100%
rename from python-plugin/src/main/resources/fr/greencodeinitiative/l10n/python/rules/python/EC10.html
rename to ecocode-rules-specifications/src/main/rules/EC10/python/EC10.asciidoc
diff --git a/ecocode-rules-specifications/src/main/rules/EC203/EC203.json b/ecocode-rules-specifications/src/main/rules/EC203/EC203.json
new file mode 100644
index 000000000..3f46aefd8
--- /dev/null
+++ b/ecocode-rules-specifications/src/main/rules/EC203/EC203.json
@@ -0,0 +1,16 @@
+{
+ "title": "Detect unoptimized image format",
+ "type": "CODE_SMELL",
+ "status": "ready",
+ "remediation": {
+ "func": "Constant\/Issue",
+ "constantCost": "60min"
+ },
+ "tags": [
+ "performance",
+ "user-experience",
+ "eco-design",
+ "ecocode"
+ ],
+ "defaultSeverity": "Minor"
+}
diff --git a/python-plugin/src/main/resources/fr/greencodeinitiative/l10n/python/rules/python/EC203.html b/ecocode-rules-specifications/src/main/rules/EC203/python/EC203.asciidoc
similarity index 100%
rename from python-plugin/src/main/resources/fr/greencodeinitiative/l10n/python/rules/python/EC203.html
rename to ecocode-rules-specifications/src/main/rules/EC203/python/EC203.asciidoc
diff --git a/python-plugin/src/main/resources/fr/greencodeinitiative/l10n/python/rules/python/EC34.html b/ecocode-rules-specifications/src/main/rules/EC34/python/EC34.asciidoc
similarity index 100%
rename from python-plugin/src/main/resources/fr/greencodeinitiative/l10n/python/rules/python/EC34.html
rename to ecocode-rules-specifications/src/main/rules/EC34/python/EC34.asciidoc
diff --git a/python-plugin/src/main/resources/fr/greencodeinitiative/l10n/python/rules/python/EC4.html b/ecocode-rules-specifications/src/main/rules/EC4/python/EC4.asciidoc
similarity index 100%
rename from python-plugin/src/main/resources/fr/greencodeinitiative/l10n/python/rules/python/EC4.html
rename to ecocode-rules-specifications/src/main/rules/EC4/python/EC4.asciidoc
diff --git a/ecocode-rules-specifications/src/main/rules/EC404/EC404.json b/ecocode-rules-specifications/src/main/rules/EC404/EC404.json
new file mode 100644
index 000000000..c04d919d3
--- /dev/null
+++ b/ecocode-rules-specifications/src/main/rules/EC404/EC404.json
@@ -0,0 +1,15 @@
+{
+ "title": "Use generator comprehension instead of list comprehension in for loop declaration",
+ "type": "CODE_SMELL",
+ "status": "ready",
+ "remediation": {
+ "func": "Constant\/Issue",
+ "constantCost": "15min"
+ },
+ "tags": [
+ "performance",
+ "eco-design",
+ "ecocode"
+ ],
+ "defaultSeverity": "Minor"
+}
diff --git a/python-plugin/src/main/resources/fr/greencodeinitiative/l10n/python/rules/python/EC404.html b/ecocode-rules-specifications/src/main/rules/EC404/python/EC404.asciidoc
similarity index 100%
rename from python-plugin/src/main/resources/fr/greencodeinitiative/l10n/python/rules/python/EC404.html
rename to ecocode-rules-specifications/src/main/rules/EC404/python/EC404.asciidoc
diff --git a/python-plugin/src/main/resources/fr/greencodeinitiative/l10n/python/rules/python/EC66.html b/ecocode-rules-specifications/src/main/rules/EC66/python/EC66.asciidoc
similarity index 100%
rename from python-plugin/src/main/resources/fr/greencodeinitiative/l10n/python/rules/python/EC66.html
rename to ecocode-rules-specifications/src/main/rules/EC66/python/EC66.asciidoc
diff --git a/python-plugin/src/main/resources/fr/greencodeinitiative/l10n/python/rules/python/EC69.html b/ecocode-rules-specifications/src/main/rules/EC69/python/EC69.asciidoc
similarity index 100%
rename from python-plugin/src/main/resources/fr/greencodeinitiative/l10n/python/rules/python/EC69.html
rename to ecocode-rules-specifications/src/main/rules/EC69/python/EC69.asciidoc
diff --git a/ecocode-rules-specifications/src/main/rules/EC7/EC7.json b/ecocode-rules-specifications/src/main/rules/EC7/EC7.json
new file mode 100644
index 000000000..46c194d56
--- /dev/null
+++ b/ecocode-rules-specifications/src/main/rules/EC7/EC7.json
@@ -0,0 +1,15 @@
+{
+ "title": "Avoid creating getter and setter methods in classes",
+ "type": "CODE_SMELL",
+ "status": "ready",
+ "remediation": {
+ "func": "Constant\/Issue",
+ "constantCost": "5min"
+ },
+ "tags": [
+ "eco-design",
+ "performance",
+ "ecocode"
+ ],
+ "defaultSeverity": "Minor"
+}
diff --git a/python-plugin/src/main/resources/fr/greencodeinitiative/l10n/python/rules/python/EC7.html b/ecocode-rules-specifications/src/main/rules/EC7/python/EC7.asciidoc
similarity index 100%
rename from python-plugin/src/main/resources/fr/greencodeinitiative/l10n/python/rules/python/EC7.html
rename to ecocode-rules-specifications/src/main/rules/EC7/python/EC7.asciidoc
diff --git a/python-plugin/src/main/resources/fr/greencodeinitiative/l10n/python/rules/python/EC72.html b/ecocode-rules-specifications/src/main/rules/EC72/python/EC72.asciidoc
similarity index 100%
rename from python-plugin/src/main/resources/fr/greencodeinitiative/l10n/python/rules/python/EC72.html
rename to ecocode-rules-specifications/src/main/rules/EC72/python/EC72.asciidoc
diff --git a/python-plugin/src/main/resources/fr/greencodeinitiative/l10n/python/rules/python/EC74.html b/ecocode-rules-specifications/src/main/rules/EC74/python/EC74.asciidoc
similarity index 100%
rename from python-plugin/src/main/resources/fr/greencodeinitiative/l10n/python/rules/python/EC74.html
rename to ecocode-rules-specifications/src/main/rules/EC74/python/EC74.asciidoc
diff --git a/python-plugin/pom.xml b/python-plugin/pom.xml
index 7fd808178..508826430 100644
--- a/python-plugin/pom.xml
+++ b/python-plugin/pom.xml
@@ -16,16 +16,24 @@
https://github.com/green-code-initiative/ecoCode/tree/main/python-plugin
+
+ ${project.groupId}
+ ecocode-rules-specifications
+ ${project.version}
+ python
+
org.sonarsource.python
sonar-python-plugin
sonar-plugin
+ provided
org.sonarsource.sonarqube
sonar-plugin-api
+ provided
@@ -63,6 +71,11 @@
${java.version}
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+
org.apache.maven.plugins
maven-dependency-plugin
diff --git a/python-plugin/src/main/java/fr/greencodeinitiative/python/PythonRuleRepository.java b/python-plugin/src/main/java/fr/greencodeinitiative/python/PythonRuleRepository.java
index 2718b28f0..23ff4f51e 100644
--- a/python-plugin/src/main/java/fr/greencodeinitiative/python/PythonRuleRepository.java
+++ b/python-plugin/src/main/java/fr/greencodeinitiative/python/PythonRuleRepository.java
@@ -17,58 +17,32 @@
package fr.greencodeinitiative.python;
import fr.greencodeinitiative.python.checks.*;
-import org.sonar.api.rules.RuleType;
+import org.sonar.api.SonarRuntime;
import org.sonar.api.server.rule.RulesDefinition;
-import org.sonar.api.server.rule.RulesDefinitionAnnotationLoader;
import org.sonar.plugins.python.api.PythonCustomRuleRepository;
+import org.sonarsource.analyzer.commons.RuleMetadataLoader;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
public class PythonRuleRepository implements RulesDefinition, PythonCustomRuleRepository {
public static final String LANGUAGE = "py";
public static final String NAME = "ecoCode";
- public static final String RESOURCE_BASE_PATH = "/fr/greencodeinitiative/l10n/python/rules/python/";
+ public static final String RESOURCE_BASE_PATH = "io/ecocode/rules/python";
public static final String REPOSITORY_KEY = "ecocode-python";
- @Override
- public void define(Context context) {
- NewRepository repository = context.createRepository(repositoryKey(), LANGUAGE).setName(NAME);
-
- new RulesDefinitionAnnotationLoader().load(repository, checkClasses().toArray(new Class[] {}));
-
- // technical debt
- Map remediationCosts = new HashMap<>();
- remediationCosts.put(AvoidSQLRequestInLoop.RULE_KEY, "10min");
- remediationCosts.put(AvoidFullSQLRequest.RULE_KEY, "20min");
- repository.rules().forEach(rule -> {
- rule.setType(RuleType.CODE_SMELL);
- String debt = remediationCosts.get(rule.key());
-
- // TODO DDC : create support to use org.apache.commons.lang.StringUtils
-// if (StringUtils.isBlank(debt)) {
- if (debt == null || debt.trim().equals("")) {
- // default debt to 5min for issue correction
- rule.setDebtRemediationFunction(
- rule.debtRemediationFunctions().constantPerIssue("5min"));
- } else {
- rule.setDebtRemediationFunction(
- rule.debtRemediationFunctions().constantPerIssue(debt));
- }
- });
+ private final SonarRuntime sonarRuntime;
- // HTML description
- repository.rules().forEach(rule ->
- rule.setHtmlDescription(loadResource(RESOURCE_BASE_PATH + rule.key() + ".html")));
+ public PythonRuleRepository(SonarRuntime sonarRuntime) {
+ this.sonarRuntime = sonarRuntime;
+ }
+ @Override
+ public void define(Context context) {
+ NewRepository repository = context.createRepository(REPOSITORY_KEY, LANGUAGE).setName(NAME);
+ RuleMetadataLoader ruleMetadataLoader = new RuleMetadataLoader(RESOURCE_BASE_PATH, sonarRuntime);
+ ruleMetadataLoader.addRulesByAnnotatedClass(repository, (List) checkClasses());
repository.done();
}
@@ -92,21 +66,4 @@ public List checkClasses() {
DetectUnoptimizedImageFormat.class
);
}
-
- private String loadResource(String path) {
- URL resource = getClass().getResource(path);
- if (resource == null) {
- throw new IllegalStateException("Resource not found: " + path);
- }
- ByteArrayOutputStream result = new ByteArrayOutputStream();
- try (InputStream in = resource.openStream()) {
- byte[] buffer = new byte[1024];
- for (int len = in.read(buffer); len != -1; len = in.read(buffer)) {
- result.write(buffer, 0, len);
- }
- return new String(result.toByteArray(), StandardCharsets.UTF_8);
- } catch (IOException e) {
- throw new IllegalStateException("Failed to read resource: " + path, e);
- }
- }
}
diff --git a/python-plugin/src/test/java/fr/greencodeinitiative/python/PythonPluginTest.java b/python-plugin/src/test/java/fr/greencodeinitiative/python/PythonPluginTest.java
index 477f5d4bb..e270ad449 100644
--- a/python-plugin/src/test/java/fr/greencodeinitiative/python/PythonPluginTest.java
+++ b/python-plugin/src/test/java/fr/greencodeinitiative/python/PythonPluginTest.java
@@ -16,20 +16,26 @@
*/
package fr.greencodeinitiative.python;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
import org.sonar.api.Plugin;
import org.sonar.api.SonarRuntime;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
-public class PythonPluginTest {
+class PythonPluginTest {
+ private Plugin.Context context;
- @Test
- public void test() {
+ @BeforeEach
+ void init() {
SonarRuntime sonarRuntime = mock(SonarRuntime.class);
- Plugin.Context context = new Plugin.Context(sonarRuntime);
+ context = new Plugin.Context(sonarRuntime);
new PythonPlugin().define(context);
+ }
+
+ @Test
+ void test() {
assertThat(context.getExtensions()).hasSize(1);
}
diff --git a/python-plugin/src/test/java/fr/greencodeinitiative/python/PythonRuleRepositoryTest.java b/python-plugin/src/test/java/fr/greencodeinitiative/python/PythonRuleRepositoryTest.java
index 828447a4d..714ef278e 100644
--- a/python-plugin/src/test/java/fr/greencodeinitiative/python/PythonRuleRepositoryTest.java
+++ b/python-plugin/src/test/java/fr/greencodeinitiative/python/PythonRuleRepositoryTest.java
@@ -16,45 +16,88 @@
*/
package fr.greencodeinitiative.python;
-import static org.assertj.core.api.Assertions.assertThat;
-
import org.assertj.core.api.SoftAssertions;
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.sonar.api.SonarRuntime;
import org.sonar.api.server.rule.RulesDefinition;
+import org.sonar.api.utils.Version;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.fail;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
-public class PythonRuleRepositoryTest {
+class PythonRuleRepositoryTest {
- private PythonRuleRepository pythonRuleRepository;
- private RulesDefinition.Context context;
private RulesDefinition.Repository repository;
- @Before
- public void init() {
- pythonRuleRepository = new PythonRuleRepository();
- context = new RulesDefinition.Context();
- pythonRuleRepository.define(context);
- repository = context.repository(PythonRuleRepository.REPOSITORY_KEY);
+ @BeforeEach
+ void init() {
+ // TODO: Remove this check after Git repo split
+ /*
+ On an IDE (like IntelliJ), if the developer runs the unit tests without building/generating the Maven goals on the
+ "ecocode-rules-specifications" module before, the unit tests will not see the generated HTML descriptions (from ASCIIDOC files).
+ The developer must therefore configure his IDE to build the `ecocode-rules-specifications` module before launching the Tests.
+
+ When the `python-plugin` submodule is in a specific Git repository, `ecocode-rules-specifications` will be fetched from a classic
+ external Maven dependency. There will therefore no longer be any need to perform this specific configuration.
+ */
+ if (PythonRuleRepository.class.getResource("/io/ecocode/rules/python/EC4.json") == null) {
+ String message = "'ecocode-rules-specification' resources corrupted. Please check build of 'ecocode-rules-specification' module";
+ if (System.getProperties().keySet().stream().anyMatch(k -> k.toString().startsWith("idea."))) {
+ message += "\n\nOn 'IntelliJ IDEA':" +
+ "\n1. go to settings :" +
+ "\n > Build, Execution, Deployment > Build Tools > Maven > Runner" +
+ "\n2. check option:" +
+ "\n > Delegate IDE build/run actions to Maven" +
+ "\n3. Click on menu: " +
+ "\n > Build > Build Project"
+ ;
+ }
+ fail(message);
+ }
+
+ final SonarRuntime sonarRuntime = mock(SonarRuntime.class);
+ doReturn(Version.create(0, 0)).when(sonarRuntime).getApiVersion();
+ PythonRuleRepository rulesDefinition = new PythonRuleRepository(sonarRuntime);
+ RulesDefinition.Context context = new RulesDefinition.Context();
+ rulesDefinition.define(context);
+ repository = context.repository(rulesDefinition.repositoryKey());
}
@Test
- public void test() {
- assertThat(pythonRuleRepository.repositoryKey()).isEqualTo(PythonRuleRepository.REPOSITORY_KEY);
- assertThat(context.repositories()).hasSize(1).extracting("key").containsExactly(pythonRuleRepository.repositoryKey());
- assertThat(context.repositories().get(0).rules()).hasSize(10);
- assertThat(pythonRuleRepository.checkClasses()).hasSize(10);
+ @DisplayName("Test repository metadata")
+ void testMetadata() {
+ assertThat(repository.name()).isEqualTo("ecoCode");
+ assertThat(repository.language()).isEqualTo("py");
+ assertThat(repository.key()).isEqualTo("ecocode-python");
}
+ @Test
+ void testRegistredRules() {
+ assertThat(repository.rules()).hasSize(10);
+ }
- /**
- * Check all rule keys must be prefixed by 'EC'
- */
@Test
- public void testRuleKeyPrefix() {
+ @DisplayName("All rule keys must be prefixed by 'EC'")
+ void testRuleKeyPrefix() {
SoftAssertions assertions = new SoftAssertions();
repository.rules().forEach(
rule -> assertions.assertThat(rule.key()).startsWith("EC")
);
assertions.assertAll();
}
+
+ @Test
+ void testAllRuleParametersHaveDescription() {
+ SoftAssertions assertions = new SoftAssertions();
+ repository.rules().stream()
+ .flatMap(rule -> rule.params().stream())
+ .forEach(param -> assertions.assertThat(param.description())
+ .as("description for " + param.key())
+ .isNotEmpty());
+ assertions.assertAll();
+ }
}