aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLukasz Jarocki <lukasz.jarocki@sonarsource.com>2022-07-20 14:08:09 +0200
committersonartech <sonartech@sonarsource.com>2022-07-21 20:03:05 +0000
commitb65c752c9a177e2401037dbc822a4362470be650 (patch)
treea2af58a9399a0e9e8a9311b1219e6dc4168fc519
parent0a7d6f7245e753feb25bceae733fd659591b8f81 (diff)
downloadsonarqube-b65c752c9a177e2401037dbc822a4362470be650.tar.gz
sonarqube-b65c752c9a177e2401037dbc822a4362470be650.zip
SONAR-16610 added example plugin to test education topics in SonarQube
-rw-r--r--plugins/sonar-education-plugin/build.gradle43
-rw-r--r--plugins/sonar-education-plugin/src/main/java/org/sonar/education/EducationBuiltInQualityProfileDefinition.java42
-rw-r--r--plugins/sonar-education-plugin/src/main/java/org/sonar/education/EducationLanguage.java40
-rw-r--r--plugins/sonar-education-plugin/src/main/java/org/sonar/education/EducationPlugin.java41
-rw-r--r--plugins/sonar-education-plugin/src/main/java/org/sonar/education/EducationRulesDefinition.java149
-rw-r--r--plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationPrinciplesSensor.java32
-rw-r--r--plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationRuleSensor.java97
-rw-r--r--plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWithContextsSensor.java32
-rw-r--r--plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWithDetectedContextSensor.java39
-rw-r--r--plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWithSingleContextSensor.java32
-rw-r--r--plugins/sonar-education-plugin/src/test/java/org/sonar/education/EducationBuiltInQualityProfileDefinitionTest.java41
-rw-r--r--plugins/sonar-education-plugin/src/test/java/org/sonar/education/EducationLanguageTest.java50
-rw-r--r--plugins/sonar-education-plugin/src/test/java/org/sonar/education/EducationPluginTest.java45
-rw-r--r--plugins/sonar-education-plugin/src/test/java/org/sonar/education/EducationRulesDefinitionTest.java45
-rw-r--r--plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationPrinciplesSensorTest.java47
-rw-r--r--plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationSensorTest.java55
-rw-r--r--plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWithContextsSensorTest.java47
-rw-r--r--plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWithDetectedContextSensorTest.java47
-rw-r--r--plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWithSingleContextSensorTest.java47
-rw-r--r--settings.gradle1
20 files changed, 972 insertions, 0 deletions
diff --git a/plugins/sonar-education-plugin/build.gradle b/plugins/sonar-education-plugin/build.gradle
new file mode 100644
index 00000000000..71bad6499f1
--- /dev/null
+++ b/plugins/sonar-education-plugin/build.gradle
@@ -0,0 +1,43 @@
+configurations {
+ testImplementation.extendsFrom(compileOnly)
+}
+
+dependencies {
+ compileOnly 'org.sonarsource.api.plugin:sonar-plugin-api'
+
+ testCompile 'junit:junit'
+ testCompile 'org.assertj:assertj-core'
+ testCompile 'org.mockito:mockito-core'
+ testCompile project(':sonar-plugin-api-impl')
+}
+
+jar {
+ manifest {
+ attributes(
+ 'Plugin-Key': 'education',
+ 'Plugin-Version': project.version,
+ 'Plugin-Class': 'org.sonar.education.EducationPlugin',
+ 'Plugin-ChildFirstClassLoader': 'false',
+ 'Sonar-Version': project.version,
+ 'SonarLint-Supported': 'true',
+ 'Plugin-Name': 'Education'
+ )
+ }
+ into('META-INF/lib') {
+ from configurations.compileClasspath
+ }
+}
+
+artifactoryPublish.skip = false
+
+publishing {
+ publications {
+ mavenJava(MavenPublication) {
+ from components.java
+ if (release) {
+ artifact sourcesJar
+ artifact javadocJar
+ }
+ }
+ }
+}
diff --git a/plugins/sonar-education-plugin/src/main/java/org/sonar/education/EducationBuiltInQualityProfileDefinition.java b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/EducationBuiltInQualityProfileDefinition.java
new file mode 100644
index 00000000000..14a36f8c524
--- /dev/null
+++ b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/EducationBuiltInQualityProfileDefinition.java
@@ -0,0 +1,42 @@
+/*
+ * 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;
+
+import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition;
+
+import static org.sonar.education.EducationRulesDefinition.EDUCATION_KEY;
+import static org.sonar.education.EducationRulesDefinition.EDUCATION_RULE_REPOSITORY_KEY;
+import static org.sonar.education.sensors.EducationPrinciplesSensor.EDUCATION_WITH_GENERIC_CONCEPTS_RULE_KEY;
+import static org.sonar.education.sensors.EducationWithContextsSensor.EDUCATION_WITH_CONTEXTS_RULE_KEY;
+import static org.sonar.education.sensors.EducationWithDetectedContextSensor.EDUCATION_WITH_DETECTED_CONTEXT_RULE_KEY;
+import static org.sonar.education.sensors.EducationWithSingleContextSensor.EDUCATION_WITH_SINGLE_CONTEXT_RULE_KEY;
+
+public class EducationBuiltInQualityProfileDefinition implements BuiltInQualityProfilesDefinition {
+ @Override
+ public void define(Context context) {
+ NewBuiltInQualityProfile profile = context.createBuiltInQualityProfile("Built in QP for Education", EDUCATION_KEY);
+ profile.setDefault(true);
+ profile.activateRule(EDUCATION_RULE_REPOSITORY_KEY, EDUCATION_WITH_GENERIC_CONCEPTS_RULE_KEY);
+ profile.activateRule(EDUCATION_RULE_REPOSITORY_KEY, EDUCATION_WITH_SINGLE_CONTEXT_RULE_KEY);
+ profile.activateRule(EDUCATION_RULE_REPOSITORY_KEY, EDUCATION_WITH_CONTEXTS_RULE_KEY);
+ profile.activateRule(EDUCATION_RULE_REPOSITORY_KEY, EDUCATION_WITH_DETECTED_CONTEXT_RULE_KEY);
+ profile.done();
+ }
+}
diff --git a/plugins/sonar-education-plugin/src/main/java/org/sonar/education/EducationLanguage.java b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/EducationLanguage.java
new file mode 100644
index 00000000000..5487da02d3d
--- /dev/null
+++ b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/EducationLanguage.java
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+import org.sonar.api.resources.Language;
+
+public class EducationLanguage implements Language {
+
+ @Override
+ public String getKey() {
+ return EducationRulesDefinition.EDUCATION_KEY;
+ }
+
+ @Override
+ public String getName() {
+ return "Education";
+ }
+
+ @Override
+ public String[] getFileSuffixes() {
+ return new String[]{".edu"};
+ }
+}
diff --git a/plugins/sonar-education-plugin/src/main/java/org/sonar/education/EducationPlugin.java b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/EducationPlugin.java
new file mode 100644
index 00000000000..6cae2713a50
--- /dev/null
+++ b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/EducationPlugin.java
@@ -0,0 +1,41 @@
+/*
+ * 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;
+
+import org.sonar.education.sensors.EducationWithContextsSensor;
+import org.sonar.education.sensors.EducationWithDetectedContextSensor;
+import org.sonar.education.sensors.EducationPrinciplesSensor;
+import org.sonar.education.sensors.EducationWithSingleContextSensor;
+import org.sonar.api.Plugin;
+
+public class EducationPlugin implements Plugin {
+ @Override
+ public void define(Context context) {
+ context.addExtensions(
+ EducationRulesDefinition.class,
+ EducationLanguage.class,
+ EducationWithContextsSensor.class,
+ EducationWithDetectedContextSensor.class,
+ EducationPrinciplesSensor.class,
+ EducationWithSingleContextSensor.class,
+ EducationBuiltInQualityProfileDefinition.class);
+
+ }
+}
diff --git a/plugins/sonar-education-plugin/src/main/java/org/sonar/education/EducationRulesDefinition.java b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/EducationRulesDefinition.java
new file mode 100644
index 00000000000..79887611754
--- /dev/null
+++ b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/EducationRulesDefinition.java
@@ -0,0 +1,149 @@
+/*
+ * 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;
+
+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;
+
+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 IGNORED_FAKE_SECTION = "fake_section_to_be_ignored";
+
+ public static final String[] CONTEXTS = {"spring", "hibernate", "apache-commons", "vaadin", "mybatis"};
+
+ private static final String HTML_LOREM_IPSUM = "<a href=\"https://google.com\">Lorem</a> ipsum dolor sit amet, consectetur adipiscing " +
+ "elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco " +
+ "laboris nisi ut aliquip ex ea commodo consequat.<br />Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
+ "fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
+
+ @Override
+ public void define(Context context) {
+ NewRepository repo = context.createRepository(EDUCATION_RULE_REPOSITORY_KEY, EDUCATION_KEY);
+
+ createRuleWithDescriptionSectionsAndEducationPrinciples(repo);
+ createRuleWithDescriptionSectionsAndSingleContext(repo);
+ createRuleWithDescriptionSectionsAndMultipleContexts(repo);
+ createRuleWithDescriptionSectionsAndMultipleContextsIncludingOneDetected(repo);
+
+ repo.done();
+ }
+
+ private void createRuleWithDescriptionSectionsAndEducationPrinciples(NewRepository repo) {
+ String[] educationPrinciples = {"defense_in_depth", "least_trust_principle"};
+ String ruleKey = EducationPrinciplesSensor.EDUCATION_WITH_GENERIC_CONCEPTS_RULE_KEY;
+ NewRule ruleWithSingleContext = repo.createRule(ruleKey).setName("Rule with description sections and education principles")
+ .addTags(EDUCATION_KEY);
+
+ 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",
+ ruleKey, educationPrinciples[0], educationPrinciples[1]))
+ .addEducationPrincipleKeys(educationPrinciples);
+
+ addNonContextualizedDescriptionSections(ruleWithSingleContext);
+ descriptionSection(HOW_TO_FIX_SECTION_KEY);
+ }
+
+ 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'")
+ .addTags(EDUCATION_KEY);
+
+ ruleWithSingleContext
+ .setHtmlDescription(String.format("This rule contains description sections and single context for 'how to fix' section. To " +
+ "trigger an issue using this rule you need to scan any text file with a line containing %s key word.", ruleKey));
+
+ addNonContextualizedDescriptionSections(ruleWithSingleContext);
+
+ ruleWithSingleContext.addDescriptionSection(descriptionHowToFixSectionWithContext(CONTEXTS[0]));
+ }
+
+ private void createRuleWithDescriptionSectionsAndMultipleContexts(NewRepository repo) {
+ NewRule ruleWithMultipleContexts = repo.createRule(EducationWithContextsSensor.EDUCATION_WITH_CONTEXTS_RULE_KEY)
+ .setName("Rule with description sections and multiple contexts for 'how to fix'")
+ .addTags(EDUCATION_KEY);
+
+ 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.",
+ EducationWithContextsSensor.EDUCATION_WITH_CONTEXTS_RULE_KEY));
+
+ addNonContextualizedDescriptionSections(ruleWithMultipleContexts);
+
+ for (String context : CONTEXTS) {
+ ruleWithMultipleContexts.addDescriptionSection(descriptionHowToFixSectionWithContext(context));
+ }
+ }
+
+ private void createRuleWithDescriptionSectionsAndMultipleContextsIncludingOneDetected(NewRepository repo) {
+ NewRule ruleWithMultipleContexts = repo.createRule(EducationWithDetectedContextSensor.EDUCATION_WITH_DETECTED_CONTEXT_RULE_KEY)
+ .setName("Rule with description sections and multiple contexts (including one detected) for 'how to fix'")
+ .addTags(EDUCATION_KEY);
+
+ 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.",
+ EducationWithDetectedContextSensor.EDUCATION_WITH_DETECTED_CONTEXT_RULE_KEY));
+
+ addNonContextualizedDescriptionSections(ruleWithMultipleContexts);
+
+ for (String context : CONTEXTS) {
+ ruleWithMultipleContexts.addDescriptionSection(descriptionHowToFixSectionWithContext(context));
+ }
+ }
+
+ private static void addNonContextualizedDescriptionSections(NewRule newRule) {
+ newRule.addDescriptionSection(descriptionSection(INTRODUCTION_SECTION_KEY))
+ .addDescriptionSection(descriptionSection(ROOT_CAUSE_SECTION_KEY))
+ .addDescriptionSection(descriptionSection(ASSESS_THE_PROBLEM_SECTION_KEY))
+ .addDescriptionSection(descriptionSection(RESOURCES_SECTION_KEY))
+ .addDescriptionSection(descriptionSection(IGNORED_FAKE_SECTION));
+ }
+
+ private static RuleDescriptionSection descriptionHowToFixSectionWithContext(String context) {
+ var ruleContext = new org.sonar.api.server.rule.Context(context, context);
+ return RuleDescriptionSection.builder()
+ .sectionKey(HOW_TO_FIX_SECTION_KEY)
+ .context(ruleContext)
+ .htmlContent(String.format("%s: %s", HOW_TO_FIX_SECTION_KEY, HTML_LOREM_IPSUM))
+ .build();
+ }
+
+ private static RuleDescriptionSection descriptionSection(String sectionKey) {
+ return RuleDescriptionSection.builder()
+ .sectionKey(sectionKey)
+ .htmlContent(String.format("%s: %s", sectionKey, HTML_LOREM_IPSUM))
+ .build();
+ }
+}
diff --git a/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationPrinciplesSensor.java b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationPrinciplesSensor.java
new file mode 100644
index 00000000000..66a759fd8ad
--- /dev/null
+++ b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationPrinciplesSensor.java
@@ -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 EducationPrinciplesSensor extends EducationRuleSensor {
+
+ public static final String EDUCATION_WITH_GENERIC_CONCEPTS_RULE_KEY = "EDUCATION";
+
+ public EducationPrinciplesSensor(FileSystem fs, ActiveRules activeRules) {
+ super(fs, activeRules, EDUCATION_WITH_GENERIC_CONCEPTS_RULE_KEY);
+ }
+}
diff --git a/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationRuleSensor.java b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationRuleSensor.java
new file mode 100644
index 00000000000..72be37bb1f8
--- /dev/null
+++ b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationRuleSensor.java
@@ -0,0 +1,97 @@
+/*
+ * 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.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+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.Sensor;
+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 static org.sonar.education.EducationRulesDefinition.EDUCATION_KEY;
+import static org.sonar.education.EducationRulesDefinition.EDUCATION_RULE_REPOSITORY_KEY;
+
+public abstract class EducationRuleSensor implements Sensor {
+
+ private final FileSystem fs;
+ private final ActiveRules activeRules;
+ private final String ruleKeyName;
+ private final String tag;
+
+ EducationRuleSensor(FileSystem fs, ActiveRules activeRules, String ruleKey) {
+ this.fs = fs;
+ this.activeRules = activeRules;
+ this.ruleKeyName = ruleKey;
+ this.tag = ruleKey;
+ }
+
+ @Override
+ public void describe(SensorDescriptor descriptor) {
+ descriptor
+ .name("Education sensor for " + tag)
+ .createIssuesForRuleRepository(EDUCATION_RULE_REPOSITORY_KEY);
+ }
+
+ @Override
+ public void execute(SensorContext context) {
+ RuleKey ruleKey = RuleKey.of(EDUCATION_RULE_REPOSITORY_KEY, ruleKeyName);
+ if (activeRules.find(ruleKey) == null) {
+ return;
+ }
+ for (InputFile inputFile : fs.inputFiles(fs.predicates().hasLanguage(EDUCATION_KEY))) {
+ processFile(inputFile, context, ruleKey);
+ }
+ }
+
+ protected String getDetectedContext() {
+ return null;
+ }
+
+ protected void processFile(InputFile inputFile, SensorContext context, RuleKey ruleKey) {
+ try {
+ int[] lineCounter = {1};
+ try (InputStreamReader isr = new InputStreamReader(inputFile.inputStream(), inputFile.charset());
+ BufferedReader reader = new BufferedReader(isr)) {
+ reader.lines().forEachOrdered(lineStr -> {
+ int startIndex = -1;
+ while ((startIndex = lineStr.indexOf(tag, startIndex + 1)) != -1) {
+ NewIssue newIssue = context.newIssue();
+ newIssue
+ .forRule(ruleKey)
+ .at(newIssue.newLocation()
+ .on(inputFile)
+ .at(inputFile.newRange(lineCounter[0], startIndex, lineCounter[0], startIndex + tag.length())))
+ .setRuleDescriptionContextKey(getDetectedContext())
+ .save();
+ }
+ lineCounter[0]++;
+ });
+ }
+ } catch (IOException e) {
+ throw new IllegalStateException("Fail to process " + inputFile, e);
+ }
+ }
+}
diff --git a/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWithContextsSensor.java b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWithContextsSensor.java
new file mode 100644
index 00000000000..00e9365d5c1
--- /dev/null
+++ b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWithContextsSensor.java
@@ -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 EducationWithContextsSensor extends EducationRuleSensor {
+
+ public static final String EDUCATION_WITH_CONTEXTS_RULE_KEY = "CONTEXTS";
+
+ public EducationWithContextsSensor(FileSystem fs, ActiveRules activeRules) {
+ super(fs, activeRules, EDUCATION_WITH_CONTEXTS_RULE_KEY);
+ }
+}
diff --git a/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWithDetectedContextSensor.java b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWithDetectedContextSensor.java
new file mode 100644
index 00000000000..1163dec0153
--- /dev/null
+++ b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWithDetectedContextSensor.java
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+import static org.sonar.education.EducationRulesDefinition.CONTEXTS;
+
+public class EducationWithDetectedContextSensor extends EducationRuleSensor {
+
+ public static final String EDUCATION_WITH_DETECTED_CONTEXT_RULE_KEY = "DETECTED_CONTEXT";
+
+ public EducationWithDetectedContextSensor(FileSystem fs, ActiveRules activeRules) {
+ super(fs, activeRules, EDUCATION_WITH_DETECTED_CONTEXT_RULE_KEY);
+ }
+
+ @Override
+ protected String getDetectedContext() {
+ return CONTEXTS[0];
+ }
+}
diff --git a/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWithSingleContextSensor.java b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWithSingleContextSensor.java
new file mode 100644
index 00000000000..45e2f76ab78
--- /dev/null
+++ b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWithSingleContextSensor.java
@@ -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 EducationWithSingleContextSensor extends EducationRuleSensor {
+
+ public static final String EDUCATION_WITH_SINGLE_CONTEXT_RULE_KEY = "SINGLE_CONTEXT";
+
+ public EducationWithSingleContextSensor(FileSystem fs, ActiveRules activeRules) {
+ super(fs, activeRules, EDUCATION_WITH_SINGLE_CONTEXT_RULE_KEY);
+ }
+}
diff --git a/plugins/sonar-education-plugin/src/test/java/org/sonar/education/EducationBuiltInQualityProfileDefinitionTest.java b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/EducationBuiltInQualityProfileDefinitionTest.java
new file mode 100644
index 00000000000..abb44b76dfb
--- /dev/null
+++ b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/EducationBuiltInQualityProfileDefinitionTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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;
+
+import org.junit.Test;
+import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class EducationBuiltInQualityProfileDefinitionTest {
+
+ private final EducationBuiltInQualityProfileDefinition underTest = new EducationBuiltInQualityProfileDefinition();
+
+ @Test
+ public void define_definesAtLeast4rules() {
+ BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
+
+ underTest.define(context);
+
+ BuiltInQualityProfilesDefinition.BuiltInQualityProfile profile = context.profile("education", "Built in QP for Education");
+ assertThat(profile.isDefault()).isTrue();
+ assertThat(profile.rules()).hasSizeGreaterThanOrEqualTo(4);
+ }
+}
diff --git a/plugins/sonar-education-plugin/src/test/java/org/sonar/education/EducationLanguageTest.java b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/EducationLanguageTest.java
new file mode 100644
index 00000000000..623c3358dda
--- /dev/null
+++ b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/EducationLanguageTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class EducationLanguageTest {
+
+ private final EducationLanguage underTest = new EducationLanguage();
+
+ @Test
+ public void getFileSuffixes_notEmpty() {
+ String[] fileSuffixes = underTest.getFileSuffixes();
+
+ assertThat(fileSuffixes).isNotEmpty();
+ }
+
+ @Test
+ public void getName_notEmpty() {
+ String name = underTest.getName();
+
+ assertThat(name).isNotEmpty();
+ }
+
+ @Test
+ public void getKey_notEmpty() {
+ String key = underTest.getKey();
+
+ assertThat(key).isNotEmpty();
+ }
+}
diff --git a/plugins/sonar-education-plugin/src/test/java/org/sonar/education/EducationPluginTest.java b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/EducationPluginTest.java
new file mode 100644
index 00000000000..f60199703e9
--- /dev/null
+++ b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/EducationPluginTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+import org.junit.Test;
+import org.sonar.api.Plugin;
+import org.sonar.api.SonarEdition;
+import org.sonar.api.SonarQubeSide;
+import org.sonar.api.SonarRuntime;
+import org.sonar.api.internal.PluginContextImpl;
+import org.sonar.api.internal.SonarRuntimeImpl;
+import org.sonar.api.utils.Version;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class EducationPluginTest {
+
+ @Test
+ public void define_addSomeExtensionsForSonarQube() {
+ SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.parse("9.5"), SonarQubeSide.SCANNER, SonarEdition.COMMUNITY);
+ Plugin.Context context = new PluginContextImpl.Builder().setSonarRuntime(runtime).build();
+ EducationPlugin plugin = new EducationPlugin();
+
+ plugin.define(context);
+
+ assertThat(context.getExtensions()).isNotEmpty();
+ }
+}
diff --git a/plugins/sonar-education-plugin/src/test/java/org/sonar/education/EducationRulesDefinitionTest.java b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/EducationRulesDefinitionTest.java
new file mode 100644
index 00000000000..233bba84fc1
--- /dev/null
+++ b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/EducationRulesDefinitionTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+import java.util.List;
+import org.junit.Test;
+import org.sonar.api.server.rule.RulesDefinition;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class EducationRulesDefinitionTest {
+
+ @Test
+ public void define_addsRuleDefinitions() {
+ EducationRulesDefinition educationRulesDefinition = new EducationRulesDefinition();
+
+ RulesDefinition.Context context = new RulesDefinition.Context();
+
+ educationRulesDefinition.define(context);
+
+ List<RulesDefinition.Repository> repositories = context.repositories();
+ assertThat(repositories).hasSize(1);
+
+ RulesDefinition.Repository repository = context.repositories().get(0);
+ List<RulesDefinition.Rule> rules = repository.rules();
+ assertThat(rules).hasSizeGreaterThanOrEqualTo(4);
+ }
+}
diff --git a/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationPrinciplesSensorTest.java b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationPrinciplesSensorTest.java
new file mode 100644
index 00000000000..8e9c9ef722c
--- /dev/null
+++ b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationPrinciplesSensorTest.java
@@ -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.EducationPrinciplesSensor.EDUCATION_WITH_GENERIC_CONCEPTS_RULE_KEY;
+
+public class EducationPrinciplesSensorTest extends EducationSensorTest {
+
+ @Test
+ public void processFile_givenCorrectTagPassed_oneSecurityHotspotWithContextsIsRaised() throws IOException {
+ DefaultInputFile inputFile = newTestFile(EDUCATION_WITH_GENERIC_CONCEPTS_RULE_KEY);
+
+ DefaultFileSystem fs = new DefaultFileSystem(temp.newFolder());
+ fs.add(inputFile);
+
+ SensorContextTester sensorContextTester = SensorContextTester.create(temp.newFolder().toPath());
+ var sensor = new EducationPrinciplesSensor(fs, activeRules);
+
+ sensor.execute(sensorContextTester);
+
+ assertThat(sensorContextTester.allIssues()).hasSize(1);
+ }
+}
diff --git a/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationSensorTest.java b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationSensorTest.java
new file mode 100644
index 00000000000..f8ac4520d99
--- /dev/null
+++ b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationSensorTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.nio.charset.Charset;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
+import org.sonar.api.batch.rule.ActiveRule;
+import org.sonar.api.batch.rule.ActiveRules;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.education.EducationRulesDefinition.EDUCATION_KEY;
+
+public class EducationSensorTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ protected final ActiveRules activeRules = mock(ActiveRules.class);
+
+ @Before
+ public void before() {
+ when(activeRules.find(any())).thenReturn(mock(ActiveRule.class));
+ }
+
+ protected DefaultInputFile newTestFile(String content) {
+ return new TestInputFileBuilder("foo", "hello.edu")
+ .setLanguage(EDUCATION_KEY)
+ .setContents(content)
+ .setCharset(Charset.defaultCharset())
+ .build();
+ }
+}
diff --git a/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWithContextsSensorTest.java b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWithContextsSensorTest.java
new file mode 100644
index 00000000000..55d6856d063
--- /dev/null
+++ b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWithContextsSensorTest.java
@@ -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.EducationWithContextsSensor.EDUCATION_WITH_CONTEXTS_RULE_KEY;
+
+public class EducationWithContextsSensorTest extends EducationSensorTest {
+
+ @Test
+ public void processFile_givenCorrectTagPassed_oneSecurityHotspotWithContextsIsRaised() throws IOException {
+ DefaultInputFile inputFile = newTestFile(EDUCATION_WITH_CONTEXTS_RULE_KEY);
+
+ DefaultFileSystem fs = new DefaultFileSystem(temp.newFolder());
+ fs.add(inputFile);
+
+ SensorContextTester sensorContextTester = SensorContextTester.create(temp.newFolder().toPath());
+ var sensor = new EducationWithContextsSensor(fs, activeRules);
+
+ sensor.execute(sensorContextTester);
+
+ assertThat(sensorContextTester.allIssues()).hasSize(1);
+ }
+}
diff --git a/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWithDetectedContextSensorTest.java b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWithDetectedContextSensorTest.java
new file mode 100644
index 00000000000..e3b6c81e296
--- /dev/null
+++ b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWithDetectedContextSensorTest.java
@@ -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.EducationWithDetectedContextSensor.EDUCATION_WITH_DETECTED_CONTEXT_RULE_KEY;
+
+public class EducationWithDetectedContextSensorTest extends EducationSensorTest {
+
+ @Test
+ public void processFile_givenCorrectTagPassed_oneSecurityHotspotWithContextsIsRaised() throws IOException {
+ DefaultInputFile inputFile = newTestFile(EDUCATION_WITH_DETECTED_CONTEXT_RULE_KEY);
+
+ DefaultFileSystem fs = new DefaultFileSystem(temp.newFolder());
+ fs.add(inputFile);
+
+ SensorContextTester sensorContextTester = SensorContextTester.create(temp.newFolder().toPath());
+ var sensor = new EducationWithDetectedContextSensor(fs, activeRules);
+
+ sensor.execute(sensorContextTester);
+
+ assertThat(sensorContextTester.allIssues()).hasSize(1);
+ }
+}
diff --git a/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWithSingleContextSensorTest.java b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWithSingleContextSensorTest.java
new file mode 100644
index 00000000000..7b56e152911
--- /dev/null
+++ b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWithSingleContextSensorTest.java
@@ -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.EducationWithSingleContextSensor.EDUCATION_WITH_SINGLE_CONTEXT_RULE_KEY;
+
+public class EducationWithSingleContextSensorTest extends EducationSensorTest {
+
+ @Test
+ public void processFile_givenCorrectTagPassed_oneSecurityHotspotWithContextsIsRaised() throws IOException {
+ DefaultInputFile inputFile = newTestFile(EDUCATION_WITH_SINGLE_CONTEXT_RULE_KEY);
+
+ DefaultFileSystem fs = new DefaultFileSystem(temp.newFolder());
+ fs.add(inputFile);
+
+ SensorContextTester sensorContextTester = SensorContextTester.create(temp.newFolder().toPath());
+ var sensor = new EducationWithSingleContextSensor(fs, activeRules);
+
+ sensor.execute(sensorContextTester);
+
+ assertThat(sensorContextTester.allIssues()).hasSize(1);
+ }
+}
diff --git a/settings.gradle b/settings.gradle
index b1998de61aa..6980a920f6c 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -13,6 +13,7 @@ pluginManagement {
rootProject.name = 'sonarqube'
include 'plugins:sonar-xoo-plugin'
+include 'plugins:sonar-education-plugin'
include 'server:sonar-auth-common'
include 'server:sonar-auth-bitbucket'