]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-17206 added new code snippets (to display diff) to education plugin
authorLukasz Jarocki <lukasz.jarocki@sonarsource.com>
Mon, 22 Aug 2022 12:38:18 +0000 (14:38 +0200)
committersonartech <sonartech@sonarsource.com>
Mon, 22 Aug 2022 20:03:13 +0000 (20:03 +0000)
plugins/sonar-education-plugin/src/main/java/org/sonar/education/EducationRulesDefinition.java
plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWith2LinkedCodeSnippetsSensor.java [new file with mode: 0644]
plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWith4LinkedCodeSnippetsSensor.java [new file with mode: 0644]
plugins/sonar-education-plugin/src/main/resources/org/sonar/education/2codeSnippets.html [new file with mode: 0644]
plugins/sonar-education-plugin/src/main/resources/org/sonar/education/2completelyDifferentSnippets.html [new file with mode: 0644]
plugins/sonar-education-plugin/src/main/resources/org/sonar/education/4codeSnippets.html [new file with mode: 0644]
plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWith2LinkedCodeSnippetsSensorTest.java [new file with mode: 0644]
plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWith4LinkedCodeSnippetsSensorTest.java [new file with mode: 0644]

index 6d3c1613f8506e9bfa241c41927b5a04c07265fc..5c8a6512ceb7c9d09e4d8d9675ec22f5514fa49c 100644 (file)
  */
 package org.sonar.education;
 
+import java.nio.charset.StandardCharsets;
+import org.sonar.api.internal.apachecommons.io.IOUtils;
 import org.sonar.api.server.rule.RuleDescriptionSection;
 import org.sonar.api.server.rule.RulesDefinition;
 import org.sonar.education.sensors.EducationPrinciplesSensor;
 import org.sonar.education.sensors.EducationWithContextsSensor;
 import org.sonar.education.sensors.EducationWithDetectedContextSensor;
 
-import static org.sonar.education.sensors.EducationWithSingleContextSensor.EDUCATION_WITH_SINGLE_CONTEXT_RULE_KEY;
 import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.ASSESS_THE_PROBLEM_SECTION_KEY;
 import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.HOW_TO_FIX_SECTION_KEY;
 import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.INTRODUCTION_SECTION_KEY;
 import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.RESOURCES_SECTION_KEY;
 import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.ROOT_CAUSE_SECTION_KEY;
+import static org.sonar.education.sensors.EducationWith2LinkedCodeSnippetsSensor.EDUCATION_WITH_2_LINKED_CODE_SNIPPETS_RULE_KEY;
+import static org.sonar.education.sensors.EducationWith4LinkedCodeSnippetsSensor.EDUCATION_WITH_4_LINKED_CODE_SNIPPETS_RULE_KEY;
+import static org.sonar.education.sensors.EducationWithSingleContextSensor.EDUCATION_WITH_SINGLE_CONTEXT_RULE_KEY;
 
 public class EducationRulesDefinition implements RulesDefinition {
 
   public static final String EDUCATION_RULE_REPOSITORY_KEY = "edu";
   public static final String EDUCATION_KEY = "education";
 
+  private static final String[] ALL_SECTIONS = {INTRODUCTION_SECTION_KEY, ROOT_CAUSE_SECTION_KEY, ASSESS_THE_PROBLEM_SECTION_KEY,
+    RESOURCES_SECTION_KEY, HOW_TO_FIX_SECTION_KEY};
   private static final String IGNORED_FAKE_SECTION = "fake_section_to_be_ignored";
 
   public static final String[] CONTEXTS = {"spring", "hibernate", "apache-commons", "vaadin", "mybatis"};
@@ -55,6 +61,10 @@ public class EducationRulesDefinition implements RulesDefinition {
     createRuleWithDescriptionSectionsAndMultipleContexts(repo);
     createRuleWithDescriptionSectionsAndMultipleContextsIncludingOneDetected(repo);
 
+    createRuleWith2LinkedCodeSnippetsInTwoSections(repo);
+
+    createRuleWith4LinkedCodeSnippetsInOneSection(repo);
+
     repo.done();
   }
 
@@ -66,7 +76,7 @@ public class EducationRulesDefinition implements RulesDefinition {
 
     ruleWithSingleContext
       .setHtmlDescription(String.format("This rule contains description sections. To trigger an issue using this rule you need to " +
-          "scan any text file with a line containing %s key word. This rule also contains 2 education principles - %s and %s",
+        "scan any text file with a line containing %s key word. This rule also contains 2 education principles - %s and %s",
         ruleKey, educationPrinciples[0], educationPrinciples[1]))
       .addEducationPrincipleKeys(educationPrinciples);
 
@@ -77,7 +87,7 @@ public class EducationRulesDefinition implements RulesDefinition {
   private void createRuleWithDescriptionSectionsAndSingleContext(NewRepository repo) {
     String ruleKey = EDUCATION_WITH_SINGLE_CONTEXT_RULE_KEY;
     NewRule ruleWithSingleContext = repo.createRule(ruleKey).setName("Rule with description sections and single " +
-        "contexts for 'how to fix'")
+      "contexts for 'how to fix'")
       .addTags(EDUCATION_KEY);
 
     ruleWithSingleContext
@@ -96,7 +106,7 @@ public class EducationRulesDefinition implements RulesDefinition {
 
     ruleWithMultipleContexts
       .setHtmlDescription(String.format("This rule contains description sections and multiple contexts for 'how to fix' section. To trigger " +
-          "an issue using this rule you need to scan any text file with a line containing %s keyword.",
+        "an issue using this rule you need to scan any text file with a line containing %s keyword.",
         EducationWithContextsSensor.EDUCATION_WITH_CONTEXTS_RULE_KEY));
 
     addNonContextualizedDescriptionSections(ruleWithMultipleContexts);
@@ -113,7 +123,7 @@ public class EducationRulesDefinition implements RulesDefinition {
 
     ruleWithMultipleContexts
       .setHtmlDescription(String.format("This rule contains description sections and multiple contexts (including one detected) for " +
-          "'how to fix' section. To trigger an issue using this rule you need to scan any text file with a line containing %s keyword.",
+        "'how to fix' section. To trigger an issue using this rule you need to scan any text file with a line containing %s keyword.",
         EducationWithDetectedContextSensor.EDUCATION_WITH_DETECTED_CONTEXT_RULE_KEY));
 
     addNonContextualizedDescriptionSections(ruleWithMultipleContexts);
@@ -123,6 +133,46 @@ public class EducationRulesDefinition implements RulesDefinition {
     }
   }
 
+
+  private void createRuleWith2LinkedCodeSnippetsInTwoSections(NewRepository repo) {
+    String ruleKey = EDUCATION_WITH_2_LINKED_CODE_SNIPPETS_RULE_KEY;
+    NewRule rule2CodeSnippetsIn2Sections = repo.createRule(ruleKey).setName("Rule with description sections and code snippets " +
+        "linked to each other in 2 different sections in order to display diff between them.")
+      .addTags(EDUCATION_KEY);
+
+    rule2CodeSnippetsIn2Sections
+      .setHtmlDescription(String.format("This rule contains description sections and 2 linked code snippets in 2 sections. To " +
+          "trigger an issue using this rule you need to scan any text file with a line containing %s key word.", ruleKey));
+
+    String completelyDifferentSnippetsHtml = readResource("2completelyDifferentSnippets.html");
+    String codeSnippetsHtml = readResource("2codeSnippets.html");
+
+    rule2CodeSnippetsIn2Sections.addDescriptionSection(descriptionSection(INTRODUCTION_SECTION_KEY))
+      .addDescriptionSection(descriptionSection(ROOT_CAUSE_SECTION_KEY, completelyDifferentSnippetsHtml))
+      .addDescriptionSection(descriptionSection(ASSESS_THE_PROBLEM_SECTION_KEY))
+      .addDescriptionSection(descriptionSection(HOW_TO_FIX_SECTION_KEY, codeSnippetsHtml))
+      .addDescriptionSection(descriptionSection(RESOURCES_SECTION_KEY));
+  }
+
+  private void createRuleWith4LinkedCodeSnippetsInOneSection(NewRepository repo) {
+    String ruleKey = EDUCATION_WITH_4_LINKED_CODE_SNIPPETS_RULE_KEY;
+    NewRule rule2CodeSnippetsInTheSameSection = repo.createRule(ruleKey).setName("Rule with description sections and code snippets " +
+        "linked to each other in the same section in order to display diff between them.")
+      .addTags(EDUCATION_KEY);
+
+    rule2CodeSnippetsInTheSameSection
+      .setHtmlDescription(String.format("This rule contains description sections and 4 linked code snippets in the same section. To " +
+        "trigger an issue using this rule you need to scan any text file with a line containing %s key word.", ruleKey));
+
+    String codeSnippetsHtml = readResource("4codeSnippets.html");
+
+    rule2CodeSnippetsInTheSameSection.addDescriptionSection(descriptionSection(INTRODUCTION_SECTION_KEY))
+      .addDescriptionSection(descriptionSection(ROOT_CAUSE_SECTION_KEY, codeSnippetsHtml))
+      .addDescriptionSection(descriptionSection(ASSESS_THE_PROBLEM_SECTION_KEY))
+      .addDescriptionSection(descriptionSection(HOW_TO_FIX_SECTION_KEY))
+      .addDescriptionSection(descriptionSection(RESOURCES_SECTION_KEY));
+  }
+
   private static void addNonContextualizedDescriptionSections(NewRule newRule) {
     newRule.addDescriptionSection(descriptionSection(INTRODUCTION_SECTION_KEY))
       .addDescriptionSection(descriptionSection(ROOT_CAUSE_SECTION_KEY))
@@ -141,9 +191,21 @@ public class EducationRulesDefinition implements RulesDefinition {
   }
 
   private static RuleDescriptionSection descriptionSection(String sectionKey) {
+    return descriptionSection(sectionKey, HTML_LOREM_IPSUM);
+  }
+
+  private static RuleDescriptionSection descriptionSection(String sectionKey, String htmlContent) {
     return RuleDescriptionSection.builder()
       .sectionKey(sectionKey)
-      .htmlContent(String.format("%s: %s", sectionKey, HTML_LOREM_IPSUM))
+      .htmlContent(htmlContent)
       .build();
   }
+
+  private String readResource(String file) {
+    try {
+      return IOUtils.toString(getClass().getResource(file), StandardCharsets.UTF_8);
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+  }
 }
diff --git a/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWith2LinkedCodeSnippetsSensor.java b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWith2LinkedCodeSnippetsSensor.java
new file mode 100644 (file)
index 0000000..93e89c7
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.education.sensors;
+
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.rule.ActiveRules;
+
+public class EducationWith2LinkedCodeSnippetsSensor extends EducationRuleSensor {
+
+  public static final String EDUCATION_WITH_2_LINKED_CODE_SNIPPETS_RULE_KEY = "2_LINKED_CODE_SNIPPETS";
+
+  public EducationWith2LinkedCodeSnippetsSensor(FileSystem fs, ActiveRules activeRules) {
+    super(fs, activeRules, EDUCATION_WITH_2_LINKED_CODE_SNIPPETS_RULE_KEY);
+  }
+}
diff --git a/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWith4LinkedCodeSnippetsSensor.java b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWith4LinkedCodeSnippetsSensor.java
new file mode 100644 (file)
index 0000000..62e222b
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.education.sensors;
+
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.rule.ActiveRules;
+
+public class EducationWith4LinkedCodeSnippetsSensor extends EducationRuleSensor {
+
+  public static final String EDUCATION_WITH_4_LINKED_CODE_SNIPPETS_RULE_KEY = "4_LINKED_CODE_SNIPPETS";
+
+  public EducationWith4LinkedCodeSnippetsSensor(FileSystem fs, ActiveRules activeRules) {
+    super(fs, activeRules, EDUCATION_WITH_4_LINKED_CODE_SNIPPETS_RULE_KEY);
+  }
+}
diff --git a/plugins/sonar-education-plugin/src/main/resources/org/sonar/education/2codeSnippets.html b/plugins/sonar-education-plugin/src/main/resources/org/sonar/education/2codeSnippets.html
new file mode 100644 (file)
index 0000000..f54da21
--- /dev/null
@@ -0,0 +1,45 @@
+<p>An infinite loop is one that will never end while the program is running, i.e., you have to kill the program to get out of the loop. Whether it is
+    by meeting the loop’s end condition or via a <code>break</code>, every loop should have an end condition.</p>
+<h3>Known Limitations</h3>
+<ul>
+    <li> False positives: when <code>yield</code> is used - <a href="https://github.com/SonarSource/SonarJS/issues/674">Issue #674</a>. </li>
+    <li> False positives: when an exception is raised by a function invoked within the loop. </li>
+    <li> False negatives: when a loop condition is based on an element of an array or object. </li>
+</ul>
+<h2>Noncompliant Code Example</h2>
+<pre class="diff-id-1 diff-noncompliant">for (;;) {  // Noncompliant; end condition omitted
+  // ...
+}
+
+var j = 0;
+while (true) { // Noncompliant; constant end condition
+  j++;
+}
+
+var k;
+var b = true;
+while (b) { // Noncompliant; constant end condition
+  k++;
+}
+</pre>
+<h2>Compliant Solution</h2>
+<pre class="diff-id-1 diff-noncompliant">while (true) { // break will potentially allow leaving the loop
+  if (someCondition) {
+    break;
+  }
+}
+
+var k;
+var b = true;
+while (b) {
+  k++;
+  b = k &lt; 10;
+}
+
+outer:
+while(true) {
+  while(true) {
+    break outer;
+  }
+}
+</pre>
\ No newline at end of file
diff --git a/plugins/sonar-education-plugin/src/main/resources/org/sonar/education/2completelyDifferentSnippets.html b/plugins/sonar-education-plugin/src/main/resources/org/sonar/education/2completelyDifferentSnippets.html
new file mode 100644 (file)
index 0000000..ef08cfe
--- /dev/null
@@ -0,0 +1,23 @@
+Example with 2 completely different code snippets
+
+<h2>Noncompliant Code Example</h2>
+<pre class="diff-id-1 diff-noncompliant">
+while (true) { // Noncompliant; constant end condition
+  j++;
+}
+
+var k;
+var b = true;
+while (b) { // Noncompliant; constant end condition
+  k++;
+}
+</pre>
+<h2>Compliant Solution</h2>
+<pre class="diff-id-1 diff-compliant">while (true) { // break will potentially allow leaving the loop
+var x = 3;
+for(let i=2; i<4; i++) {
+    console.log("Hello there");
+}
+alert('Click here');
+thisIsNiceFunction();
+</pre>
\ No newline at end of file
diff --git a/plugins/sonar-education-plugin/src/main/resources/org/sonar/education/4codeSnippets.html b/plugins/sonar-education-plugin/src/main/resources/org/sonar/education/4codeSnippets.html
new file mode 100644 (file)
index 0000000..b14c53f
--- /dev/null
@@ -0,0 +1,34 @@
+<p>A <code>catch</code> clause that only rethrows the caught exception has the same effect as omitting the <code>catch</code> altogether and letting
+    it bubble up automatically, but with more code and the additional detriment of leaving maintainers scratching their heads.</p>
+<p>Such clauses should either be eliminated or populated with the appropriate logic.</p>
+<h2>Noncompliant Code Example</h2>
+<pre class="diff-id-1 diff-noncompliant">try {
+  doSomething();
+} catch (ex) {  // Noncompliant
+  throw ex;
+}
+</pre>
+<h2>Compliant Solution</h2>
+<pre class="diff-id-1 diff-compliant">try {
+  doSomething();
+} catch (ex) {
+  console.err(ex);
+  throw ex;
+}
+</pre>
+
+<h2>Noncompliant Code Example</h2>
+<pre class="diff-id-2 diff-noncompliant">try {
+  doSomethingElse();
+} catch (ex) {  // Noncompliant
+  throw ex;
+}
+</pre>
+<h2>Compliant Solution</h2>
+<pre class="diff-id-2 diff-compliant">try {
+  doSomethingElse();
+} catch (ex) {
+  console.err(ex);
+  throw ex;
+}
+</pre>
\ No newline at end of file
diff --git a/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWith2LinkedCodeSnippetsSensorTest.java b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWith2LinkedCodeSnippetsSensorTest.java
new file mode 100644 (file)
index 0000000..466ad33
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.education.sensors;
+
+import java.io.IOException;
+import org.junit.Test;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.sensor.internal.SensorContextTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.education.sensors.EducationWith2LinkedCodeSnippetsSensor.EDUCATION_WITH_2_LINKED_CODE_SNIPPETS_RULE_KEY;
+
+public class EducationWith2LinkedCodeSnippetsSensorTest extends EducationSensorTest {
+  @Test
+  public void processFile_givenCorrectTagPassed_oneSecurityHotspotWithContextsIsRaised() throws IOException {
+    DefaultInputFile inputFile = newTestFile(EDUCATION_WITH_2_LINKED_CODE_SNIPPETS_RULE_KEY);
+
+    DefaultFileSystem fs = new DefaultFileSystem(temp.newFolder());
+    fs.add(inputFile);
+
+    SensorContextTester sensorContextTester = SensorContextTester.create(temp.newFolder().toPath());
+    var sensor = new EducationWith2LinkedCodeSnippetsSensor(fs, activeRules);
+
+    sensor.execute(sensorContextTester);
+
+    assertThat(sensorContextTester.allIssues()).hasSize(1);
+  }
+}
diff --git a/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWith4LinkedCodeSnippetsSensorTest.java b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWith4LinkedCodeSnippetsSensorTest.java
new file mode 100644 (file)
index 0000000..85e46d9
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.education.sensors;
+
+import java.io.IOException;
+import org.junit.Test;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.sensor.internal.SensorContextTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.education.sensors.EducationWith4LinkedCodeSnippetsSensor.EDUCATION_WITH_4_LINKED_CODE_SNIPPETS_RULE_KEY;
+
+public class EducationWith4LinkedCodeSnippetsSensorTest extends EducationSensorTest {
+
+  @Test
+  public void processFile_givenCorrectTagPassed_oneSecurityHotspotWithContextsIsRaised() throws IOException {
+    DefaultInputFile inputFile = newTestFile(EDUCATION_WITH_4_LINKED_CODE_SNIPPETS_RULE_KEY);
+
+    DefaultFileSystem fs = new DefaultFileSystem(temp.newFolder());
+    fs.add(inputFile);
+
+    SensorContextTester sensorContextTester = SensorContextTester.create(temp.newFolder().toPath());
+    var sensor = new EducationWith4LinkedCodeSnippetsSensor(fs, activeRules);
+
+    sensor.execute(sensorContextTester);
+
+    assertThat(sensorContextTester.allIssues()).hasSize(1);
+  }
+}