--- /dev/null
+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
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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"};
+ }
+}
--- /dev/null
+/*
+ * 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);
+
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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];
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
rootProject.name = 'sonarqube'
include 'plugins:sonar-xoo-plugin'
+include 'plugins:sonar-education-plugin'
include 'server:sonar-auth-common'
include 'server:sonar-auth-bitbucket'