Skip to content

Commit

Permalink
Fix #46 New rule: Features should be written in the defined language
Browse files Browse the repository at this point in the history
  • Loading branch information
racodond committed Dec 19, 2016
1 parent 7f2bd93 commit 6e78f0a
Show file tree
Hide file tree
Showing 19 changed files with 302 additions and 7 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Number of features.
* Duplicated steps should be removed
* End-line characters should be consistent
* Examples data table should contain data at least two data rows
* Features should be written in the defined language
* Features should have a description
* Features should have a name
* Features should have a unique name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
import org.sonar.gherkin.parser.GherkinDialectProvider;
import org.sonar.gherkin.parser.GherkinParserBuilder;
import org.sonar.gherkin.visitors.CharsetAwareVisitor;
import org.sonar.gherkin.visitors.GherkinVisitorContext;
Expand Down Expand Up @@ -81,7 +82,19 @@ public static CheckMessagesVerifier issues(GherkinCheck check, File file, Charse
if (check instanceof CharsetAwareVisitor) {
((CharsetAwareVisitor) check).setCharset(charset);
}
return CheckMessagesVerifier.verify(TreeCheckTest.getIssues(file.getAbsolutePath(), check, charset));
return CheckMessagesVerifier.verify(TreeCheckTest.getIssues(file.getAbsolutePath(), check, charset, GherkinDialectProvider.DEFAULT_LANGUAGE));
}

/**
* See {@link GherkinCheckVerifier#issues(GherkinCheck, File)}
*
* @param language Language of the file to test.
*/
public static CheckMessagesVerifier issues(GherkinCheck check, File file, String language) {
if (check instanceof CharsetAwareVisitor) {
((CharsetAwareVisitor) check).setCharset(Charsets.UTF_8);
}
return CheckMessagesVerifier.verify(TreeCheckTest.getIssues(file.getAbsolutePath(), check, Charsets.UTF_8, language));
}

/**
Expand Down Expand Up @@ -113,7 +126,7 @@ public static CheckMessagesVerifier issues(GherkinCheck check, File file, Charse
* </pre>
*/
public static void verify(GherkinCheck check, File file) {
verify(check, file, Charsets.UTF_8);
verify(check, file, Charsets.UTF_8, GherkinDialectProvider.DEFAULT_LANGUAGE);
}

/**
Expand All @@ -122,7 +135,17 @@ public static void verify(GherkinCheck check, File file) {
* @param charset Charset of the file to test.
*/
public static void verify(GherkinCheck check, File file, Charset charset) {
GherkinDocumentTree propertiesTree = (GherkinDocumentTree) GherkinParserBuilder.createTestParser(charset).parse(file);
verify(check, file, charset, GherkinDialectProvider.DEFAULT_LANGUAGE);
}

/**
* See {@link GherkinCheckVerifier#verify(GherkinCheck, File)}
*
* @param charset Charset of the file to test.
* @param language Language of the file to test.
*/
public static void verify(GherkinCheck check, File file, Charset charset, String language) {
GherkinDocumentTree propertiesTree = (GherkinDocumentTree) GherkinParserBuilder.createTestParser(charset, language).parse(file);
GherkinVisitorContext context = new GherkinVisitorContext(propertiesTree, file);

GherkinCheckVerifier checkVerifier = new GherkinCheckVerifier();
Expand Down Expand Up @@ -152,6 +175,15 @@ public static void verify(GherkinCheck check, File file, Charset charset) {
}
}

/**
* See {@link GherkinCheckVerifier#verify(GherkinCheck, File)}
*
* @param language Language of the file to test.
*/
public static void verify(GherkinCheck check, File file, String language) {
verify(check, file, Charsets.UTF_8, language);
}

private static Iterator<Issue> getActualIssues(GherkinCheck check, GherkinVisitorContext context) {
List<Issue> issues = check.scanFile(context);
List<Issue> sortedIssues = Ordering.natural().onResultOf(new IssueToLine()).sortedCopy(issues);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ class TreeCheckTest {
private TreeCheckTest() {
}

public static Collection<CheckMessage> getIssues(String relativePath, GherkinCheck check, Charset charset) {
public static Collection<CheckMessage> getIssues(String relativePath, GherkinCheck check, Charset charset, String language) {
File file = new File(relativePath);

GherkinDocumentTree propertiesTree = (GherkinDocumentTree) GherkinParserBuilder.createTestParser(charset).parse(file);
GherkinDocumentTree propertiesTree = (GherkinDocumentTree) GherkinParserBuilder.createTestParser(charset, language).parse(file);
GherkinVisitorContext context = new GherkinVisitorContext(propertiesTree, file);
List<Issue> issues = check.scanFile(context);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* SonarQube Gherkin Analyzer
* Copyright (C) 2016-2016 David RACODON
* david.racodon@gmail.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.gherkin.checks;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import org.sonar.check.Priority;
import org.sonar.check.Rule;
import org.sonar.check.RuleProperty;
import org.sonar.gherkin.parser.GherkinDialectProvider;
import org.sonar.plugins.gherkin.api.tree.GherkinDocumentTree;
import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitorCheck;
import org.sonar.squidbridge.annotations.SqaleConstantRemediation;

import java.util.Set;

@Rule(
key = "defined-feature-language",
name = "Features should be written in the defined language",
priority = Priority.MAJOR,
tags = {Tags.DESIGN})
@SqaleConstantRemediation("30min")
public class DefinedFeatureLanguageCheck extends DoubleDispatchVisitorCheck {

private static final String DEFAULT_LANGUAGE = GherkinDialectProvider.DEFAULT_LANGUAGE;

private static final Set<String> SUPPORTED_LANGUAGES = ImmutableSet.of("af", "am", "ar", "ast", "az", "bg", "bm",
"bs", "ca", "cs", "cy-GB", "da", "de", "el", "em", "en", "en-Scouse", "en-au", "en-lol", "en-old", "en-pirate",
"eo", "es", "et", "fa", "fi", "fr", "ga", "gj", "gl", "he", "hi", "hr", "ht", "hu", "id", "is", "it", "ja", "jv",
"ka", "kn", "ko", "lt", "lu", "lv", "mn", "nl", "no", "pa", "pl", "pt", "ro", "ru", "sk", "sl", "sr-Cyrl",
"sr-Latn", "sv", "ta", "th", "tl", "tlh", "tr", "tt", "uk", "ur", "uz", "vi", "zh-CN", "zh-TW");

private static final String SUPPORTED_LANGUAGES_AS_STRING = "af, am, ar, ast, az, bg, bm, bs, ca, cs, "
+ "cy-GB, da, de, el, em, en, en-Scouse, en-au, en-lol, en-old, en-pirate, eo, es, et, fa, fi, fr, ga, gj, gl, "
+ "he, hi, hr, ht, hu, id, is, it, ja, jv, ka, kn, ko, lt, lu, lv, mn, nl, no, pa, pl, pt, ro, ru, sk, sl, "
+ "sr-Cyrl, sr-Latn, sv, ta, th, tl, tlh, tr, tt, uk, ur, uz, vi, zh-CN, zh-TW";

@RuleProperty(
key = "language",
description = "Language of the Gherkin documents. Allowed values are: " + SUPPORTED_LANGUAGES_AS_STRING,
defaultValue = DEFAULT_LANGUAGE)
private String language = DEFAULT_LANGUAGE;

@Override
public void visitGherkinDocument(GherkinDocumentTree tree) {
if (!tree.language().equals(language)) {
if (tree.languageDeclaration() != null) {
addPreciseIssue(tree.languageDeclaration(), "Update the language definition to '" + language + "' and translate the content of the file.");
} else {
addFileIssue("Add a '" + language + "' language definition and translate the content of the file.");
}
}
}

@Override
public void validateParameters() {
if (!SUPPORTED_LANGUAGES.contains(language)) {
throw new IllegalStateException(languageParamErrorMessage());
}
}

private String languageParamErrorMessage() {
return CheckUtils.paramErrorMessage(
this.getClass(),
"language parameter \"" + language + "\" is not valid. Allowed values are: " + SUPPORTED_LANGUAGES_AS_STRING);
}

@VisibleForTesting
void setLanguage(String language) {
this.language = language;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<p>
This rule checks that all the Gherkin documents are written in the defined language.
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* SonarQube Gherkin Analyzer
* Copyright (C) 2016-2016 David RACODON
* david.racodon@gmail.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.gherkin.checks;

import org.junit.Test;
import org.sonar.gherkin.checks.verifier.GherkinCheckVerifier;

import static org.fest.assertions.Assertions.assertThat;

public class DefinedFeatureLanguageCheckTest {

@Test
public void should_be_written_in_the_default_language_and_not_raise_any_issue() {
GherkinCheckVerifier.issues(
new DefinedFeatureLanguageCheck(),
CheckTestUtils.getTestFile("defined-feature-language/default.feature"))
.noMore();
}

@Test
public void should_be_written_in_the_custom_language_and_not_raise_any_issue() {
DefinedFeatureLanguageCheck check = new DefinedFeatureLanguageCheck();
check.setLanguage("fr");

GherkinCheckVerifier.issues(
check,
CheckTestUtils.getTestFile("defined-feature-language/fr.feature"),
"fr")
.noMore();
}

@Test
public void should_not_be_written_in_the_default_language_and_raise_an_issue() {
GherkinCheckVerifier.issues(
new DefinedFeatureLanguageCheck(),
CheckTestUtils.getTestFile("defined-feature-language/not-default.feature"),
"fr")
.next().atLine(1).withMessage("Update the language definition to 'en' and translate the content of the file.")
.noMore();
}

@Test
public void should_not_be_written_in_the_custom_language_and_raise_an_issue() {
DefinedFeatureLanguageCheck check = new DefinedFeatureLanguageCheck();
check.setLanguage("fr");

GherkinCheckVerifier.issues(
check,
CheckTestUtils.getTestFile("defined-feature-language/not-fr.feature"))
.next().withMessage("Add a 'fr' language definition and translate the content of the file.")
.noMore();
}

@Test
public void should_throw_an_illegal_state_exception_as_the_language_parameter_is_not_valid() {
try {
DefinedFeatureLanguageCheck check = new DefinedFeatureLanguageCheck();
check.setLanguage("abc");

GherkinCheckVerifier.issues(check, CheckTestUtils.getTestFile("defined-feature-language/default.feature")).noMore();

} catch (IllegalStateException e) {
assertThat(e.getMessage()).isEqualTo("Check gherkin:defined-feature-language (Features should be written in the defined language): "
+ "language parameter \"abc\" is not valid. Allowed values are: af, am, ar, ast, az, bg, bm, bs, ca, cs, "
+ "cy-GB, da, de, el, em, en, en-Scouse, en-au, en-lol, en-old, en-pirate, eo, es, et, fa, fi, fr, ga, gj, gl, "
+ "he, hi, hr, ht, hu, id, is, it, ja, jv, ka, kn, ko, lt, lu, lv, mn, nl, no, pa, pl, pt, ro, ru, sk, sl, "
+ "sr-Cyrl, sr-Latn, sv, ta, th, tl, tlh, tr, tt, uk, ur, uz, vi, zh-CN, zh-TW");
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Feature: My feature - defined feature language default
Blabla...

Scenario: My scenario - defined feature language default
Given Blabla given...
When Blabla when...
Then Blabla then...
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# language: fr
Fonctionnalité: My feature - defined feature language french
Blabla...

Scénario: My scenario - defined feature language french
Soit Blabla given...
Lorsque Blabla when...
Alors Blabla then...
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# language: fr
Fonctionnalité: My feature - defined feature language not default
Blabla...

Scénario: My scenario - defined feature language not default
Soit Blabla given...
Lorsque Blabla when...
Alors Blabla then...
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Feature: My feature - defined feature language not french
Blabla...

Scenario: My scenario - defined feature language not french
Given Blabla given...
When Blabla when...
Then Blabla then...
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ public static GherkinParser createTestParser(Charset charset, GrammarRuleKey roo
return createParser(charset, rootRule, GherkinDialectProvider.DEFAULT_LANGUAGE);
}

public static GherkinParser createTestParser(Charset charset, String language) {
return createParser(charset, GherkinLexicalGrammar.GHERKIN_DOCUMENT, language);
}

public static GherkinParser createTestParser(Charset charset) {
return createParser(charset, GherkinLexicalGrammar.GHERKIN_DOCUMENT, GherkinDialectProvider.DEFAULT_LANGUAGE);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Feature: My feature - defined feature language default
Blabla...

Scenario: My scenario - defined feature language default
Given Blabla given...
When Blabla when...
Then Blabla then...
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# language: fr
Fonctionnalité: My feature - defined feature language french
Blabla...

Scénario: My scenario - defined feature language french
Soit Blabla given...
Lorsque Blabla when...
Alors Blabla then...
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# language: fr
Fonctionnalité: My feature - defined feature language not default
Blabla...

Scénario: My scenario - defined feature language not default
Soit Blabla given...
Lorsque Blabla when...
Alors Blabla then...
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Feature: My feature - defined feature language not french
Blabla...

Scenario: My scenario - defined feature language not french
Given Blabla given...
When Blabla when...
Then Blabla then...
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
'project:custom/defined-feature-language/fr.feature':[
1,
],
'project:custom/defined-feature-language/not-default.feature':[
1,
],
'project:custom/french.feature':[
1,
],
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,12 @@ public void test() throws Exception {
.setProperty("dump.new", FileLocation.of("target/actual").getFile().getAbsolutePath())
.setProperty("lits.differences", litsDifferencesFile.getAbsolutePath())
.setProperty("sonar.cpd.skip", "true")
.setProperty("sonar.exclusions", "custom/spelling/spelling-fr.feature,custom/indentation/indentation-custom-ok.feature,custom/indentation/indentation-custom-ko.feature");

.setProperty("sonar.exclusions",
"custom/spelling/spelling-fr.feature,"
+ "custom/indentation/indentation-custom-ok.feature,"
+ "custom/indentation/indentation-custom-ko.feature"
);
orchestrator.executeBuild(build);

assertThat(Files.toString(litsDifferencesFile, StandardCharsets.UTF_8)).isEmpty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public static Collection<Class> getChecks() {
BOMCheck.class,
CommentConventionCheck.class,
CommentRegularExpressionCheck.class,
DefinedFeatureLanguageCheck.class,
DuplicatedFeatureNamesCheck.class,
DuplicatedScenarioNamesCheck.class,
DuplicatedStepsCheck.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public void test() {

assertThat(repository.name()).isEqualTo("SonarQube");
assertThat(repository.language()).isEqualTo("gherkin");
assertThat(repository.rules()).hasSize(45);
assertThat(repository.rules()).hasSize(46);

RulesDefinition.Rule lineLengthRule = repository.rule(TabCharacterCheck.class.getAnnotation(Rule.class).key());
assertThat(lineLengthRule).isNotNull();
Expand Down

0 comments on commit 6e78f0a

Please sign in to comment.