diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b77fdd8
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,36 @@
+# Maven
+target/
+
+# IntelliJ IDEA
+*.iws
+*.iml
+*.ipr
+.idea/
+
+# Eclipse
+.classpath
+.project
+.settings
+
+# ---- Mac OS X
+.DS_Store
+Icon?
+# Thumbnails
+._*
+# Files that might appear on external disk
+.Spotlight-V100
+.Trashes
+
+# ---- Windows
+# Windows image file caches
+Thumbs.db
+# Folder config file
+Desktop.ini
+
+log.txt
+*~
+*versionsBackup
+
+.sonar/
+.sonarlint/
+sonarlint.json
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..35a2a05
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,23 @@
+language: java
+
+jdk:
+ - oraclejdk8
+
+addons:
+ sonarqube: true
+
+env:
+ global:
+ - secure: "CSzKqFmcrE0QC/wDQPirPPAWQBD0jsM8urbUjis/EEb00vjroI/uSNvDdzPyR0ZvMB6AaiZmYhyfXvVjw5doIyzEaI6pKiHFGvzoQAFfPI4Or1ST7AI3c2sD3mf1FQE3T+lvpoDmGj7gA+rLWsxzrqcfWiWbkggs7E3N2PxLhXGEu4o6gbkt8kiseIiRspIwZpg7ftqL7UGefWGaYFIlf9jX0Xd9ySpCBGbquWHK9+TdgKhhl+nBR7R1SbDgUABfJbhRyTQmR1oyuz+BxwZi5w6twoZsDYiCisIBdioBCmYZNShb+7EpMZLuKPMRkVhLvlJ6aI2taw+UPqlK7r14f9TDU57Hr2cEp/k4YDJZxkaOH8Cxc5OWY9CP1cuf9t0HoK9mwBTHjbxZ5Xm0+u4b0ydUsFq5h9lH7hh9/OHeXTPjBCW9hPyWoWa1JkYikk1WwunLV8TyA6jMBKQlHNjCSADoV7AeedldwCpfRIVOZYO+cewyMxfhBwqyJEOGjuEzpuoc7VyRJnpSoTf4TQkLbG5JeTp8cZOgCRwbEPcaVU0kwYBdXSm03WkTBEyCIC01h94yk3KczVUmbzopXs0uAGoFjXIs48RG38S3D3jKJ9vZN57iTf+8bw8ywUYfnbud5Hz/O9r/cTdsjNakdNeNTmzIpaZX87VGC3DItrbOfZg="
+ - secure: "mx3sD7v64/4HdNe9RCPkJun3JQ57cDVZv7tMgcb7x0RUZAjSkyGb6lenzC7MirBmjEEe2kYHtsJ/0P6qa8VeqrEhVAOHtssk9UZAc37E9Nj5x60DY7n2xG91kg9ih2li8IoKFM1ywuC2jOd0SgKXllfEN97Z3tsgL2AQdSypsasaNzKXZMZmVdHyBngBzWSlPVhnpHlD3UR60ikQyVn8UEX/28KUVap/Alg4upwC8R1RRHUfZ9W9gyg77lADJAKkd/7QIVdECVKHy9RdR091aUfx7OOhhJzRTw+O2G6Fb6lJEtQ3NqGEPUM3tQ2wp7y1P10V+DPCqZtQMVZTqPp5NJsASSYqbLEzURZzMPNMECgjd+bQbdmOQUeCsCXEWlRQA7AmX7vX9tqLsE2fbwVKuxSRwieitZ5MLp3hFZeu2m0TaREezOc6iYqf7ob5aYoCia7/LMoFa+R9w8679e3+ilveJKErhWFuEFb81oC4XDt8+Ztet249JcypwcInFyz29pITLjeAJiMWUzm7RqfSTlMXLAXd4Z+a9pH6+yocnPhGJRSQ+hXo4NZK4s8U04bU1ejF5FrNfEFfbLKbyt/Cwl7LT8L28BQK6nbPIUBAtD20RWOu+bbH8PUvYgeESmdh0YxugdFyhXqt8pSOji5yg/oP7w0ECPrqDw8x1q+Lhz8="
+ matrix:
+ - SQ_VERSION=LTS
+ - SQ_VERSION=LATEST_RELEASE
+ - SQ_VERSION=DEV
+
+cache:
+ directories:
+ - '$HOME/.m2/repository'
+ - '$HOME/.sonar/cache'
+
+script: ./travis.sh
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..6600f1c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,165 @@
+GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3737159
--- /dev/null
+++ b/README.md
@@ -0,0 +1,69 @@
+SonarQube Cucumber Gherkin Plugin
+=================================
+
+[![Build Status](https://api.travis-ci.org/racodond/sonar-gherkin-plugin.svg?branch=master)](https://travis-ci.org/racodond/sonar-gherkin-plugin)
+[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/hhh9gsp77hatvai1/branch/master?svg=true)](https://ci.appveyor.com/project/racodond/sonar-gherkin-plugin/branch/master)
+[![Quality Gate](https://sonarqube.com/api/badges/gate?key=com.racodond.sonarqube.plugin.gherkin:gherkin)](https://sonarqube.com/overview?id=com.racodond.sonarqube.plugin.gherkin%3Agherkin)
+
+## Description
+This plugin enables code QA analysis of [Cucumber Gherkin feature files](https://github.com/cucumber/cucumber/wiki/Gherkin) within [SonarQube](http://www.sonarqube.org):
+
+ * Computes metrics: lines of code, comments lines, etc.
+ * Performs more than [15 checks](#available-checks)
+ * Provide the ability to write your own checks
+
+## Usage
+
+1. [Download and install](http://docs.sonarqube.org/display/SONAR/Setup+and+Upgrade) SonarQube
+1. Install the Cucumber Gherkin plugin either by a [direct download](https://github.com/racodond/sonar-gherkin-plugin/releases) or through the [Update Center](http://docs.sonarqube.org/display/SONAR/Update+Center).
+1. [Install your favorite analyzer](http://docs.sonarqube.org/display/SONAR/Analyzing+Source+Code#AnalyzingSourceCode-RunningAnalysis) (SonarQube Scanner, Maven, Ant, etc.) and analyze your code. Note that Java 8 is required to run an analysis.
+
+Plugin versions and compatibility with SonarQube versions: [http://docs.sonarqube.org/display/PLUG/Plugin+Version+Matrix](http://docs.sonarqube.org/display/PLUG/Plugin+Version+Matrix)
+
+## Metrics
+
+### Statements
+Number of steps.
+
+### Functions
+Number of scenarios and scenario outlines.
+
+### Classes
+Number of features.
+
+
+## Available Checks
+
+### Standard
+
+ * "Examples" table should contain data
+ * "FIXME" tags should be handled
+ * "TODO" tags should be handled
+ * All comments should be formatted consistently
+ * Byte Order Mark (BOM) should not be used for UTF-8 files
+ * End-line characters should be consistent
+ * Features should not contain too many scenarios
+ * Features that do not define any scenario should be removed
+ * File names should comply with a naming convention
+ * Files should contain an empty new line at the end
+ * Files that do not define any feature should be removed
+ * Lines should not end with trailing whitespaces
+ * Only tags from the whitelist should be used
+ * Scenarios should not contain too many steps
+ * Source code should be properly indented
+ * Star (*) step prefix should not be used
+ * Tabulation characters should not be used
+ * Tags should comply with a naming convention
+
+### Templates
+
+ * Regular expression on comment
+
+
+## Custom Checks
+
+You're thinking of new valuable rules? Version 1.0 or greater provides an API to write your own custom checks.
+A sample plugin with detailed explanations is available [here](https://github.com/racodond/sonar-gherkin-custom-rules-plugin).
+If your custom rules may benefit the community, feel free to create a pull request in order to make the rule available in the Cucumber Gherkin plugin.
+
+You're thinking of new rules that may benefit the community but don't have the time or the skills to write them? Feel free to create an [issue](https://github.com/racodond/sonar-gherkin-plugin/issues) for your rules to be taken under consideration.
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..86b576d
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,30 @@
+version: "{build}"
+
+cache:
+ - C:\Users\appveyor\.m2
+
+install:
+ - SET JAVA_HOME=C:\Program Files\Java\jdk1.8.0
+ - SET PATH=C:\maven\apache-maven-3.3.9\bin;%JAVA_HOME%\bin;%PATH%
+
+ - appveyor DownloadFile http://www.us.apache.org/dist/maven/maven-3/3.3.9/binaries/apache-maven-3.3.9-bin.zip
+ - 7z x apache-maven-3.3.9-bin.zip -oC:\maven
+
+environment:
+ matrix:
+ - SQ_VERSION: "LTS"
+ - SQ_VERSION: "LATEST_RELEASE"
+ - SQ_VERSION: "DEV"
+
+platform:
+ - x86
+ - x64
+
+build_script:
+ - mvn clean install -DskipTests=true -Dmaven.javadoc.skip=true -B
+
+test_script:
+ - mvn clean install -B
+ - cd its
+ - mvn -B clean install -Dsonar.runtimeVersion=%SQ_VERSION%
+
diff --git a/gherkin-checks-testkit/pom.xml b/gherkin-checks-testkit/pom.xml
new file mode 100644
index 0000000..cba2b2b
--- /dev/null
+++ b/gherkin-checks-testkit/pom.xml
@@ -0,0 +1,51 @@
+
+
+ 4.0.0
+
+
+ com.racodond.sonarqube.plugin.gherkin
+ gherkin
+ 1.0-SNAPSHOT
+
+
+ gherkin-checks-testkit
+ SonarQube Gherkin Analyzer :: Check Test Toolkit
+
+
+
+ ${project.groupId}
+ gherkin-frontend
+
+
+ org.sonarsource.sonarqube
+ sonar-plugin-api
+
+
+ org.sonarsource.sslr-squid-bridge
+ sslr-squid-bridge
+
+
+ junit
+ junit
+
+
+ org.easytesting
+ fest-assert
+
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+ ${sonar.jacoco.reportPath}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gherkin-checks-testkit/src/main/java/org/sonar/gherkin/checks/verifier/GherkinCheckVerifier.java b/gherkin-checks-testkit/src/main/java/org/sonar/gherkin/checks/verifier/GherkinCheckVerifier.java
new file mode 100644
index 0000000..543df9e
--- /dev/null
+++ b/gherkin-checks-testkit/src/main/java/org/sonar/gherkin/checks/verifier/GherkinCheckVerifier.java
@@ -0,0 +1,325 @@
+/*
+ * 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.verifier;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Function;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Ordering;
+import org.sonar.gherkin.parser.GherkinParserBuilder;
+import org.sonar.gherkin.visitors.CharsetAwareVisitor;
+import org.sonar.gherkin.visitors.GherkinVisitorContext;
+import org.sonar.plugins.gherkin.api.GherkinCheck;
+import org.sonar.plugins.gherkin.api.tree.GherkinDocumentTree;
+import org.sonar.plugins.gherkin.api.tree.SyntaxToken;
+import org.sonar.plugins.gherkin.api.tree.SyntaxTrivia;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.SubscriptionVisitorCheck;
+import org.sonar.plugins.gherkin.api.visitors.issue.*;
+import org.sonar.squidbridge.checks.CheckMessagesVerifier;
+
+import javax.annotation.Nullable;
+import java.io.File;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.junit.Assert.fail;
+
+/**
+ * To unit test checks.
+ */
+public class GherkinCheckVerifier extends SubscriptionVisitorCheck {
+
+ private final List expectedIssues = new ArrayList<>();
+
+ /**
+ * Check issues
+ *
+ * @param check Check to test
+ * @param file File to test
+ *
+ * Example:
+ *
+ * GherkinCheckVerifier.issues(new MyCheck(), myFile)
+ * .next().atLine(2).withMessage("This is message for line 2.")
+ * .next().atLine(3).withMessage("This is message for line 3.").withCost(2.)
+ * .next().atLine(8)
+ * .noMore();
+ *
+ */
+ public static CheckMessagesVerifier issues(GherkinCheck check, File file) {
+ return issues(check, file, Charsets.UTF_8);
+ }
+
+ /**
+ * See {@link GherkinCheckVerifier#issues(GherkinCheck, File)}
+ *
+ * @param charset Charset of the file to test.
+ */
+ public static CheckMessagesVerifier issues(GherkinCheck check, File file, Charset charset) {
+ if (check instanceof CharsetAwareVisitor) {
+ ((CharsetAwareVisitor) check).setCharset(charset);
+ }
+ return CheckMessagesVerifier.verify(TreeCheckTest.getIssues(file.getAbsolutePath(), check, charset));
+ }
+
+ /**
+ * To unit tests checks. Expected issues should be provided as comments in the source file.
+ * Expected issue details should be provided the line prior to the actual issue.
+ * For example:
+ *
+ * # Noncompliant {{Error message for the issue on the next line}}
+ * MyProperty=abc
+ *
+ * # Noncompliant [[sc=2;ec=6;secondary=+2,+4]] {{Error message}}
+ * ...
+ *
+ *
+ * How to write these comments:
+ *
+ *
Put a comment starting with "Noncompliant" if you expect an issue on the next line.
+ *
Optional - In double brackets provide the precise issue location sl, sc, ec, el keywords respectively for start line, start column, end column and end line. sl=+1 by default.
+ *
Optional - In double brackets provide secondary locations with the secondary keyword.
+ *
Optional - In double brackets provide expected effort to fix (cost) with the effortToFix keyword.
+ *
Optional - In double curly braces {{MESSAGE}} provide the expected message.
+ *
To specify the line you can use relative location by putting + or -.
+ *
Note that the order matters: Noncompliant => Parameters => Error message
+ */
+ public static void verify(GherkinCheck check, File file) {
+ verify(check, file, Charsets.UTF_8);
+ }
+
+ /**
+ * See {@link GherkinCheckVerifier#verify(GherkinCheck, File)}
+ *
+ * @param charset Charset of the file to test.
+ */
+ public static void verify(GherkinCheck check, File file, Charset charset) {
+ GherkinDocumentTree propertiesTree = (GherkinDocumentTree) GherkinParserBuilder.createParser(charset).parse(file);
+ GherkinVisitorContext context = new GherkinVisitorContext(propertiesTree, file);
+
+ GherkinCheckVerifier checkVerifier = new GherkinCheckVerifier();
+ checkVerifier.scanFile(context);
+
+ List expectedIssues = checkVerifier.expectedIssues
+ .stream()
+ .sorted((i1, i2) -> Integer.compare(i1.line(), i2.line()))
+ .collect(Collectors.toList());
+
+ if (check instanceof CharsetAwareVisitor) {
+ ((CharsetAwareVisitor) check).setCharset(charset);
+ }
+ Iterator actualIssues = getActualIssues(check, context);
+
+ for (TestIssue expected : expectedIssues) {
+ if (actualIssues.hasNext()) {
+ verifyIssue(expected, actualIssues.next());
+ } else {
+ throw new AssertionError("Missing issue at line " + expected.line());
+ }
+ }
+
+ if (actualIssues.hasNext()) {
+ Issue issue = actualIssues.next();
+ throw new AssertionError("Unexpected issue at line " + line(issue) + ": \"" + message(issue) + "\"");
+ }
+ }
+
+ private static Iterator getActualIssues(GherkinCheck check, GherkinVisitorContext context) {
+ List issues = check.scanFile(context);
+ List sortedIssues = Ordering.natural().onResultOf(new IssueToLine()).sortedCopy(issues);
+ return sortedIssues.iterator();
+ }
+
+ private static void verifyIssue(TestIssue expected, Issue actual) {
+ if (line(actual) > expected.line()) {
+ fail("Missing issue at line " + expected.line());
+ }
+ if (line(actual) < expected.line()) {
+ fail("Unexpected issue at line " + line(actual) + ": \"" + message(actual) + "\"");
+ }
+ if (expected.message() != null) {
+ assertThat(message(actual)).as("Bad message at line " + expected.line()).isEqualTo(expected.message());
+ }
+ if (expected.effortToFix() != null) {
+ assertThat(actual.cost()).as("Bad effortToFix at line " + expected.line()).isEqualTo(expected.effortToFix());
+ }
+ if (expected.startColumn() != null) {
+ assertThat(((PreciseIssue) actual).primaryLocation().startLineOffset() + 1).as("Bad start column at line " + expected.line()).isEqualTo(expected.startColumn());
+ }
+ if (expected.endColumn() != null) {
+ assertThat(((PreciseIssue) actual).primaryLocation().endLineOffset() + 1).as("Bad end column at line " + expected.line()).isEqualTo(expected.endColumn());
+ }
+ if (expected.endLine() != null) {
+ assertThat(((PreciseIssue) actual).primaryLocation().endLine()).as("Bad end line at line " + expected.line()).isEqualTo(expected.endLine());
+ }
+ if (expected.secondaryLines() != null) {
+ assertThat(secondary(actual)).as("Bad secondary locations at line " + expected.line()).isEqualTo(expected.secondaryLines());
+ }
+ }
+
+ @Override
+ public List nodesToVisit() {
+ return ImmutableList.of(Tree.Kind.TOKEN);
+ }
+
+ @Override
+ public void visitNode(Tree tree) {
+ SyntaxToken token = (SyntaxToken) tree;
+ for (SyntaxTrivia trivia : token.trivias()) {
+
+ String text = trivia.text().substring(2).trim();
+ String marker = "Noncompliant";
+
+ if (text.startsWith(marker)) {
+ TestIssue issue = issue(null, trivia.line() + 1);
+ String paramsAndMessage = text.substring(marker.length()).trim();
+ if (paramsAndMessage.startsWith("[[")) {
+ int endIndex = paramsAndMessage.indexOf("]]");
+ addParams(issue, paramsAndMessage.substring(2, endIndex));
+ paramsAndMessage = paramsAndMessage.substring(endIndex + 2).trim();
+ }
+ if (paramsAndMessage.startsWith("{{")) {
+ int endIndex = paramsAndMessage.indexOf("}}");
+ String message = paramsAndMessage.substring(2, endIndex);
+ issue.message(message);
+ }
+ expectedIssues.add(issue);
+ }
+ }
+ }
+
+ private static void addParams(TestIssue issue, String params) {
+ for (String param : Splitter.on(';').split(params)) {
+ int equalIndex = param.indexOf('=');
+ if (equalIndex == -1) {
+ throw new IllegalStateException("Invalid param at line 1: " + param);
+ }
+ String name = param.substring(0, equalIndex);
+ String value = param.substring(equalIndex + 1);
+
+ if ("effortToFix".equalsIgnoreCase(name)) {
+ issue.effortToFix(Integer.valueOf(value));
+
+ } else if ("sc".equalsIgnoreCase(name)) {
+ issue.startColumn(Integer.valueOf(value));
+
+ } else if ("sl".equalsIgnoreCase(name)) {
+ issue.startLine(lineValue(issue.line() - 1, value));
+
+ } else if ("ec".equalsIgnoreCase(name)) {
+ issue.endColumn(Integer.valueOf(value));
+
+ } else if ("el".equalsIgnoreCase(name)) {
+ issue.endLine(lineValue(issue.line(), value));
+
+ } else if ("secondary".equalsIgnoreCase(name)) {
+ addSecondaryLines(issue, value);
+
+ } else {
+ throw new IllegalStateException("Invalid param at line 1: " + name);
+ }
+ }
+ }
+
+ private static void addSecondaryLines(TestIssue issue, String value) {
+ List secondaryLines = new ArrayList<>();
+ if (!"".equals(value)) {
+ for (String secondary : Splitter.on(',').split(value)) {
+ secondaryLines.add(lineValue(issue.line(), secondary));
+ }
+ }
+ issue.secondary(secondaryLines);
+ }
+
+ private static int lineValue(int baseLine, String shift) {
+ if (shift.startsWith("+")) {
+ return baseLine + Integer.valueOf(shift.substring(1));
+ }
+ if (shift.startsWith("-")) {
+ return baseLine - Integer.valueOf(shift.substring(1));
+ }
+ return Integer.valueOf(shift);
+ }
+
+ private static TestIssue issue(@Nullable String message, int lineNumber) {
+ return TestIssue.create(message, lineNumber);
+ }
+
+ private static class IssueToLine implements Function {
+ @Override
+ public Integer apply(Issue issue) {
+ return line(issue);
+ }
+ }
+
+ private static int line(Issue issue) {
+ if (issue instanceof PreciseIssue) {
+ return ((PreciseIssue) issue).primaryLocation().startLine();
+ } else if (issue instanceof FileIssue) {
+ return 0;
+ } else if (issue instanceof LineIssue) {
+ return ((LineIssue) issue).line();
+ } else {
+ throw new IllegalStateException("Unknown type of issue.");
+ }
+ }
+
+ private static String message(Issue issue) {
+ if (issue instanceof PreciseIssue) {
+ return ((PreciseIssue) issue).primaryLocation().message();
+ } else if (issue instanceof FileIssue) {
+ return ((FileIssue) issue).message();
+ } else if (issue instanceof LineIssue) {
+ return ((LineIssue) issue).message();
+ } else {
+ throw new IllegalStateException("Unknown type of issue.");
+ }
+ }
+
+ private static List secondary(Issue issue) {
+ List result = new ArrayList<>();
+
+ if (issue instanceof PreciseIssue) {
+ result.addAll(((PreciseIssue) issue).secondaryLocations()
+ .stream()
+ .map(IssueLocation::startLine)
+ .collect(Collectors.toList()));
+ } else if (issue instanceof FileIssue) {
+ result.addAll(((FileIssue) issue).secondaryLocations()
+ .stream()
+ .map(IssueLocation::startLine)
+ .collect(Collectors.toList()));
+ }
+ return Ordering.natural().sortedCopy(result);
+ }
+
+}
diff --git a/gherkin-checks-testkit/src/main/java/org/sonar/gherkin/checks/verifier/TestIssue.java b/gherkin-checks-testkit/src/main/java/org/sonar/gherkin/checks/verifier/TestIssue.java
new file mode 100644
index 0000000..b52b78d
--- /dev/null
+++ b/gherkin-checks-testkit/src/main/java/org/sonar/gherkin/checks/verifier/TestIssue.java
@@ -0,0 +1,112 @@
+/*
+ * 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.verifier;
+
+import com.google.common.primitives.Ints;
+
+import javax.annotation.Nullable;
+import java.util.List;
+
+class TestIssue {
+
+ private String message;
+ private int line;
+ private Integer effortToFix = null;
+ private Integer startColumn = null;
+ private Integer endColumn = null;
+ private Integer endLine = null;
+ private List secondaryLines = null;
+
+ private TestIssue(@Nullable String message, int line) {
+ this.message = message;
+ this.line = line;
+ }
+
+ public static TestIssue create(@Nullable String message, int lineNumber) {
+ return new TestIssue(message, lineNumber);
+ }
+
+ public TestIssue message(String message) {
+ this.message = message;
+ return this;
+ }
+
+ public TestIssue startColumn(int startColumn) {
+ this.startColumn = startColumn;
+ return this;
+ }
+
+ public TestIssue endColumn(int endColumn) {
+ this.endColumn = endColumn;
+ return this;
+ }
+
+ public TestIssue effortToFix(int effortToFix) {
+ this.effortToFix = effortToFix;
+ return this;
+ }
+
+ public TestIssue startLine(int startLine) {
+ this.line = startLine;
+ return this;
+ }
+
+ public TestIssue endLine(int endLine) {
+ this.endLine = endLine;
+ return this;
+ }
+
+ public TestIssue secondary(int... lines) {
+ return secondary(Ints.asList(lines));
+ }
+
+ public TestIssue secondary(List secondaryLines) {
+ this.secondaryLines = secondaryLines;
+ return this;
+ }
+
+ public int line() {
+ return line;
+ }
+
+ public Integer startColumn() {
+ return startColumn;
+ }
+
+ public Integer endLine() {
+ return endLine;
+ }
+
+ public Integer endColumn() {
+ return endColumn;
+ }
+
+ public String message() {
+ return message;
+ }
+
+ public Integer effortToFix() {
+ return effortToFix;
+ }
+
+ public List secondaryLines() {
+ return secondaryLines;
+ }
+}
diff --git a/gherkin-checks-testkit/src/main/java/org/sonar/gherkin/checks/verifier/TreeCheckTest.java b/gherkin-checks-testkit/src/main/java/org/sonar/gherkin/checks/verifier/TreeCheckTest.java
new file mode 100644
index 0000000..9f98f42
--- /dev/null
+++ b/gherkin-checks-testkit/src/main/java/org/sonar/gherkin/checks/verifier/TreeCheckTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.verifier;
+
+import org.sonar.gherkin.parser.GherkinParserBuilder;
+import org.sonar.gherkin.visitors.GherkinVisitorContext;
+import org.sonar.plugins.gherkin.api.GherkinCheck;
+import org.sonar.plugins.gherkin.api.tree.GherkinDocumentTree;
+import org.sonar.plugins.gherkin.api.visitors.issue.FileIssue;
+import org.sonar.plugins.gherkin.api.visitors.issue.Issue;
+import org.sonar.plugins.gherkin.api.visitors.issue.LineIssue;
+import org.sonar.plugins.gherkin.api.visitors.issue.PreciseIssue;
+import org.sonar.squidbridge.api.CheckMessage;
+
+import java.io.File;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+class TreeCheckTest {
+
+ private TreeCheckTest() {
+ }
+
+ public static Collection getIssues(String relativePath, GherkinCheck check, Charset charset) {
+ File file = new File(relativePath);
+
+ GherkinDocumentTree propertiesTree = (GherkinDocumentTree) GherkinParserBuilder.createParser(charset).parse(file);
+ GherkinVisitorContext context = new GherkinVisitorContext(propertiesTree, file);
+ List issues = check.scanFile(context);
+
+ return getCheckMessages(issues);
+ }
+
+ private static Collection getCheckMessages(List issues) {
+ List checkMessages = new ArrayList<>();
+ for (Issue issue : issues) {
+ CheckMessage checkMessage;
+ if (issue instanceof FileIssue) {
+ FileIssue fileIssue = (FileIssue) issue;
+ checkMessage = new CheckMessage(fileIssue.check(), fileIssue.message());
+
+ } else if (issue instanceof LineIssue) {
+ LineIssue lineIssue = (LineIssue) issue;
+ checkMessage = new CheckMessage(lineIssue.check(), lineIssue.message());
+ checkMessage.setLine(lineIssue.line());
+
+ } else {
+ PreciseIssue preciseIssue = (PreciseIssue) issue;
+ checkMessage = new CheckMessage(preciseIssue.check(), preciseIssue.primaryLocation().message());
+ checkMessage.setLine(preciseIssue.primaryLocation().startLine());
+ }
+
+ if (issue.cost() != null) {
+ checkMessage.setCost(issue.cost());
+ }
+
+ checkMessages.add(checkMessage);
+ }
+
+ return checkMessages;
+ }
+
+}
diff --git a/gherkin-checks-testkit/src/main/java/org/sonar/gherkin/checks/verifier/package-info.java b/gherkin-checks-testkit/src/main/java/org/sonar/gherkin/checks/verifier/package-info.java
new file mode 100644
index 0000000..a9b4e9d
--- /dev/null
+++ b/gherkin-checks-testkit/src/main/java/org/sonar/gherkin/checks/verifier/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.gherkin.checks.verifier;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
diff --git a/gherkin-checks/pom.xml b/gherkin-checks/pom.xml
new file mode 100644
index 0000000..f47dcbd
--- /dev/null
+++ b/gherkin-checks/pom.xml
@@ -0,0 +1,66 @@
+
+
+ 4.0.0
+
+
+ com.racodond.sonarqube.plugin.gherkin
+ gherkin
+ 1.0-SNAPSHOT
+
+
+ gherkin-checks
+ SonarQube Gherkin Analyzer :: Checks
+
+
+
+ ${project.groupId}
+ gherkin-frontend
+
+
+ ${project.groupId}
+ gherkin-checks-testkit
+
+
+ org.sonarsource.sonarqube
+ sonar-plugin-api
+
+
+ com.google.guava
+ guava
+
+
+ commons-lang
+ commons-lang
+
+
+ junit
+ junit
+ test
+
+
+ org.hamcrest
+ hamcrest-all
+ test
+
+
+ org.mockito
+ mockito-all
+ test
+
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+ ${sonar.jacoco.reportPath}
+
+
+
+
+
+
+
diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/AllowedTagsCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/AllowedTagsCheck.java
new file mode 100644
index 0000000..997641b
--- /dev/null
+++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/AllowedTagsCheck.java
@@ -0,0 +1,71 @@
+/*
+ * 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.base.Splitter;
+import com.google.common.collect.Lists;
+import org.sonar.check.Priority;
+import org.sonar.check.Rule;
+import org.sonar.check.RuleProperty;
+import org.sonar.plugins.gherkin.api.tree.GherkinDocumentTree;
+import org.sonar.plugins.gherkin.api.tree.TagTree;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitorCheck;
+import org.sonar.squidbridge.annotations.SqaleConstantRemediation;
+
+import java.util.List;
+
+@Rule(
+ key = "allowed-tags",
+ name = "Only tags from the whitelist should be used",
+ priority = Priority.MINOR,
+ tags = {Tags.CONVENTION})
+@SqaleConstantRemediation("5min")
+public class AllowedTagsCheck extends DoubleDispatchVisitorCheck {
+
+ private static final String DEFAULT = "smoke,nrt";
+ private List listOfAllowedTags;
+
+ @RuleProperty(
+ key = "allowedTags",
+ defaultValue = DEFAULT,
+ description = "Comma-separated list of allowed tags.")
+ private String allowedTags = DEFAULT;
+
+ @Override
+ public void visitGherkinDocument(GherkinDocumentTree tree) {
+ listOfAllowedTags = Lists.newArrayList(Splitter.on(",").split(allowedTags));
+ super.visitGherkinDocument(tree);
+ }
+
+ @Override
+ public void visitTag(TagTree tree) {
+ if (!listOfAllowedTags.contains(tree.text())) {
+ addPreciseIssue(tree, "Remove this tag that is not in the whitelist.");
+ }
+ super.visitTag(tree);
+ }
+
+ @VisibleForTesting
+ void setAllowedTags(String allowedTags) {
+ this.allowedTags = allowedTags;
+ }
+
+}
diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/BOMCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/BOMCheck.java
new file mode 100644
index 0000000..608382d
--- /dev/null
+++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/BOMCheck.java
@@ -0,0 +1,57 @@
+/*
+ * 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.base.Charsets;
+import org.sonar.check.Priority;
+import org.sonar.check.Rule;
+import org.sonar.gherkin.visitors.CharsetAwareVisitor;
+import org.sonar.plugins.gherkin.api.tree.GherkinDocumentTree;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitorCheck;
+import org.sonar.squidbridge.annotations.ActivatedByDefault;
+import org.sonar.squidbridge.annotations.SqaleConstantRemediation;
+
+import java.nio.charset.Charset;
+
+@Rule(
+ key = "bom-utf8-files",
+ name = "Byte Order Mark (BOM) should not be used for UTF-8 files",
+ priority = Priority.MAJOR,
+ tags = {Tags.PITFALL})
+@SqaleConstantRemediation("5min")
+@ActivatedByDefault
+public class BOMCheck extends DoubleDispatchVisitorCheck implements CharsetAwareVisitor {
+
+ private Charset charset;
+
+ @Override
+ public void visitGherkinDocument(GherkinDocumentTree tree) {
+ if (Charsets.UTF_8.equals(charset) && tree.hasByteOrderMark()) {
+ addFileIssue("Remove the Byte Order Mark (BOM).");
+ }
+ super.visitGherkinDocument(tree);
+ }
+
+ @Override
+ public void setCharset(Charset charset) {
+ this.charset = charset;
+ }
+
+}
diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/CheckUtils.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/CheckUtils.java
new file mode 100644
index 0000000..1960c04
--- /dev/null
+++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/CheckUtils.java
@@ -0,0 +1,37 @@
+/*
+ * 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.sonar.check.Rule;
+
+public class CheckUtils {
+
+ public static final String LINK_TO_JAVA_REGEX_PATTERN_DOC = "http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html";
+
+ private CheckUtils() {
+ }
+
+ public static String paramsErrorMessage(Class clazz, String message) {
+ return "Check gherkin:" + ((Rule) clazz.getAnnotation(Rule.class)).key()
+ + " (" + ((Rule) clazz.getAnnotation(Rule.class)).name() + "): "
+ + message;
+ }
+
+}
diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/CommentContainsPatternChecker.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/CommentContainsPatternChecker.java
new file mode 100644
index 0000000..5090efd
--- /dev/null
+++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/CommentContainsPatternChecker.java
@@ -0,0 +1,56 @@
+/*
+ * 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.apache.commons.lang.StringUtils;
+import org.sonar.plugins.gherkin.api.tree.SyntaxTrivia;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitorCheck;
+
+public class
+CommentContainsPatternChecker extends DoubleDispatchVisitorCheck {
+
+ private final String pattern;
+ private final String message;
+
+ public CommentContainsPatternChecker(String pattern, String message) {
+ this.pattern = pattern;
+ this.message = message;
+ }
+
+ @Override
+ public void visitComment(SyntaxTrivia trivia) {
+ String comment = trivia.text();
+ if (StringUtils.containsIgnoreCase(comment, pattern) && !isLetterAround(comment, pattern)) {
+ addPreciseIssue(trivia, message);
+ }
+ super.visitComment(trivia);
+ }
+
+ private static boolean isLetterAround(String line, String pattern) {
+ int start = StringUtils.indexOfIgnoreCase(line, pattern);
+ int end = start + pattern.length();
+
+ boolean pre = start > 0 && Character.isLetter(line.charAt(start - 1));
+ boolean post = end < line.length() - 1 && Character.isLetter(line.charAt(end));
+
+ return pre || post;
+ }
+
+}
diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/CommentConventionCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/CommentConventionCheck.java
new file mode 100644
index 0000000..8ed6ec3
--- /dev/null
+++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/CommentConventionCheck.java
@@ -0,0 +1,46 @@
+/*
+ * 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.sonar.check.Priority;
+import org.sonar.check.Rule;
+import org.sonar.plugins.gherkin.api.tree.SyntaxTrivia;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitorCheck;
+import org.sonar.squidbridge.annotations.ActivatedByDefault;
+import org.sonar.squidbridge.annotations.SqaleConstantRemediation;
+
+@Rule(
+ key = "comment-convention",
+ name = "All comments should be formatted consistently",
+ priority = Priority.MINOR,
+ tags = {Tags.CONVENTION})
+@SqaleConstantRemediation("1min")
+@ActivatedByDefault
+public class CommentConventionCheck extends DoubleDispatchVisitorCheck {
+
+ @Override
+ public void visitComment(SyntaxTrivia trivia) {
+ if (trivia.text().length() > 1 && !" ".equals(trivia.text().substring(1, 2))) {
+ addPreciseIssue(trivia, "Add a whitespace after the starting comment token.");
+ }
+ super.visitComment(trivia);
+ }
+
+}
diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/CommentRegularExpressionCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/CommentRegularExpressionCheck.java
new file mode 100644
index 0000000..20f7d9d
--- /dev/null
+++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/CommentRegularExpressionCheck.java
@@ -0,0 +1,77 @@
+/*
+ * 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.sonar.check.Priority;
+import org.sonar.check.Rule;
+import org.sonar.check.RuleProperty;
+import org.sonar.plugins.gherkin.api.tree.SyntaxTrivia;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitorCheck;
+import org.sonar.squidbridge.annotations.RuleTemplate;
+
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+@Rule(
+ key = "comment-regular-expression",
+ name = "Regular expression on comment",
+ priority = Priority.MAJOR)
+@RuleTemplate
+public class CommentRegularExpressionCheck extends DoubleDispatchVisitorCheck {
+
+ private static final String DEFAULT_REGULAR_EXPRESSION = ".*";
+ private static final String DEFAULT_MESSAGE = "The regular expression matches this comment.";
+
+ @RuleProperty(
+ key = "regularExpression",
+ description = "The regular expression. See " + CheckUtils.LINK_TO_JAVA_REGEX_PATTERN_DOC + " for detailed regular expression syntax.",
+ defaultValue = DEFAULT_REGULAR_EXPRESSION)
+ public String regularExpression = DEFAULT_REGULAR_EXPRESSION;
+
+ @RuleProperty(
+ key = "message",
+ description = "The issue message",
+ defaultValue = DEFAULT_MESSAGE)
+ public String message = DEFAULT_MESSAGE;
+
+ @Override
+ public void visitComment(SyntaxTrivia trivia) {
+ if (trivia.text().matches(regularExpression)) {
+ addPreciseIssue(trivia, message);
+ }
+ super.visitComment(trivia);
+ }
+
+ @Override
+ public void validateParameters() {
+ try {
+ Pattern.compile(regularExpression);
+ } catch (PatternSyntaxException exception) {
+ throw new IllegalStateException(paramsErrorMessage(), exception);
+ }
+ }
+
+ private String paramsErrorMessage() {
+ return CheckUtils.paramsErrorMessage(
+ this.getClass(),
+ "regularExpression parameter \"" + regularExpression + "\" is not a valid regular expression.");
+ }
+
+}
diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/EndLineCharactersCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/EndLineCharactersCheck.java
new file mode 100644
index 0000000..497511c
--- /dev/null
+++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/EndLineCharactersCheck.java
@@ -0,0 +1,96 @@
+/*
+ * 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.io.Files;
+import org.sonar.check.Priority;
+import org.sonar.check.Rule;
+import org.sonar.check.RuleProperty;
+import org.sonar.gherkin.visitors.CharsetAwareVisitor;
+import org.sonar.plugins.gherkin.api.tree.GherkinDocumentTree;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitorCheck;
+import org.sonar.squidbridge.annotations.SqaleConstantRemediation;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.regex.Pattern;
+
+@Rule(
+ key = "end-line-characters",
+ name = "End-line characters should be consistent",
+ priority = Priority.MINOR,
+ tags = {Tags.CONVENTION})
+@SqaleConstantRemediation("5min")
+public class EndLineCharactersCheck extends DoubleDispatchVisitorCheck implements CharsetAwareVisitor {
+
+ private static final String DEFAULT_FORMAT = "LF";
+ private Charset charset;
+
+ @RuleProperty(
+ key = "End-line character",
+ description = "Allowed values: 'CR', 'CRLF', 'LF'",
+ defaultValue = DEFAULT_FORMAT)
+ private String endLineCharacters = DEFAULT_FORMAT;
+
+ @Override
+ public void visitGherkinDocument(GherkinDocumentTree tree) {
+ if (fileContainsIllegalEndLineCharacters()) {
+ addFileIssue("Set all end-line characters to '" + endLineCharacters + "' in this file.");
+ }
+ super.visitGherkinDocument(tree);
+ }
+
+ @Override
+ public void validateParameters() {
+ if (!Arrays.asList("CRLF", "CR", "LF").contains(endLineCharacters)) {
+ throw new IllegalStateException(paramsErrorMessage());
+ }
+ }
+
+ @Override
+ public void setCharset(Charset charset) {
+ this.charset = charset;
+ }
+
+ @VisibleForTesting
+ void setEndLineCharacters(String endLineCharacters) {
+ this.endLineCharacters = endLineCharacters;
+ }
+
+ private boolean fileContainsIllegalEndLineCharacters() {
+ try {
+ String fileContent = Files.toString(getContext().getFile(), charset);
+ return "CR".equals(endLineCharacters) && Pattern.compile("(?s)\n").matcher(fileContent).find()
+ || "LF".equals(endLineCharacters) && Pattern.compile("(?s)\r").matcher(fileContent).find()
+ || "CRLF".equals(endLineCharacters) && Pattern.compile("(?s)(\r(?!\n)|(? nodesToVisit() {
+ return ImmutableList.of(
+ Tree.Kind.FEATURE_DECLARATION,
+ Tree.Kind.BACKGROUND,
+ Tree.Kind.SCENARIO,
+ Tree.Kind.SCENARIO_OUTLINE,
+ Tree.Kind.EXAMPLES,
+ Tree.Kind.STEP,
+ Tree.Kind.FEATURE_PREFIX,
+ Tree.Kind.BACKGROUND_PREFIX,
+ Tree.Kind.SCENARIO_PREFIX,
+ Tree.Kind.SCENARIO_OUTLINE_PREFIX,
+ Tree.Kind.EXAMPLES_PREFIX,
+ Tree.Kind.STEP_PREFIX,
+ Tree.Kind.TABLE,
+ Tree.Kind.DOC_STRING
+ );
+ }
+
+ @Override
+ public void visitNode(Tree tree) {
+ checkAllPrefixesIndentation(tree);
+ checkAllDescriptionsIndentation(tree);
+ checkAllTagsIndentation(tree);
+ checkAllTablesIndentation(tree);
+ checkDocStringsIndentation(tree);
+ }
+
+ @VisibleForTesting
+ void setIndentation(int indentation) {
+ this.indentation = indentation;
+ }
+
+ private void checkAllDescriptionsIndentation(Tree tree) {
+ if (tree instanceof Descriptionable) {
+ int expectedIndentation;
+ int leftSiblingLine;
+
+ switch (tree.getKind()) {
+ case FEATURE_DECLARATION:
+ expectedIndentation = indentation;
+ leftSiblingLine = ((FeatureDeclarationTree) tree).name().value().line();
+ break;
+
+ case BACKGROUND:
+ expectedIndentation = indentation * 2;
+ leftSiblingLine = ((BackgroundTree) tree).colon().line();
+ break;
+
+ case SCENARIO:
+ case SCENARIO_OUTLINE:
+ expectedIndentation = indentation * 2;
+ leftSiblingLine = ((BasicScenarioTree) tree).name().value().line();
+ break;
+
+ case EXAMPLES:
+ expectedIndentation = indentation * 3;
+ leftSiblingLine = ((ExamplesTree) tree).colon().line();
+ break;
+
+ default:
+ throw new IllegalStateException("Unsupported Descriptionable: " + tree.toString());
+ }
+
+ checkDescriptionLinesIndentation(((Descriptionable) tree).description(), expectedIndentation, leftSiblingLine);
+ }
+ }
+
+ private void checkDescriptionLinesIndentation(@Nullable DescriptionTree description, int expectedIndentation, int leftSiblingLine) {
+ if (description != null) {
+ description.descriptionLines()
+ .stream()
+ .filter(d -> d.line() != leftSiblingLine)
+ .forEach(d -> checkIndentation(d, expectedIndentation));
+ }
+ }
+
+ private void checkAllPrefixesIndentation(Tree tree) {
+ if (tree instanceof PrefixTree) {
+ int expectedIndentation;
+
+ switch (tree.getKind()) {
+ case FEATURE_PREFIX:
+ expectedIndentation = 0;
+ break;
+
+ case BACKGROUND_PREFIX:
+ case SCENARIO_PREFIX:
+ case SCENARIO_OUTLINE_PREFIX:
+ expectedIndentation = indentation;
+ break;
+
+ case EXAMPLES_PREFIX:
+ case STEP_PREFIX:
+ expectedIndentation = indentation * 2;
+ break;
+
+ default:
+ throw new IllegalStateException("Unsupported PrefixTree: " + tree.toString());
+ }
+
+ checkIndentation(((PrefixTree) tree).keyword(), expectedIndentation);
+ }
+ }
+
+ private void checkAllTagsIndentation(Tree tree) {
+ if (tree instanceof Taggable) {
+ int expectedIndentation;
+
+ switch (tree.getKind()) {
+ case FEATURE_DECLARATION:
+ expectedIndentation = 0;
+ break;
+
+ case BACKGROUND:
+ case SCENARIO:
+ case SCENARIO_OUTLINE:
+ expectedIndentation = indentation;
+ break;
+
+ case EXAMPLES:
+ expectedIndentation = indentation * 2;
+ break;
+
+ default:
+ throw new IllegalStateException("Unsupported Taggable: " + tree.toString());
+ }
+
+ checkTagsIndentation(((Taggable) tree).tags(), expectedIndentation);
+ }
+
+ }
+
+ private void checkTagsIndentation(List tags, int expectedIndentation) {
+ int previousTagLine = 0;
+ for (TagTree tag : tags) {
+ if (tag.prefix().line() != previousTagLine) {
+ checkIndentation(tag.prefix(), expectedIndentation);
+ previousTagLine = tag.prefix().line();
+ }
+ }
+ }
+
+ private void checkAllTablesIndentation(Tree tree) {
+ if (tree.is(Tree.Kind.TABLE)) {
+ ((TableTree) tree).rows().forEach(d -> checkIndentation(d, indentation * 3));
+ }
+ }
+
+ private void checkDocStringsIndentation(Tree tree) {
+ if (tree.is(Tree.Kind.DOC_STRING)) {
+ checkIndentation(((DocStringTree) tree).prefix(), indentation * 3);
+ checkIndentation(((DocStringTree) tree).suffix(), indentation * 3);
+ }
+ }
+
+ private void checkIndentation(SyntaxToken token, int expectedIndentation) {
+ if (token.column() != expectedIndentation) {
+ addLineIssue(
+ token.line(),
+ "Indent this line at column " + expectedIndentation + " (currently indented at column " + token.column() + ").");
+ }
+ }
+
+}
diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/MaxNumberScenariosCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/MaxNumberScenariosCheck.java
new file mode 100644
index 0000000..bbf19ff
--- /dev/null
+++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/MaxNumberScenariosCheck.java
@@ -0,0 +1,66 @@
+/*
+ * 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 org.sonar.check.Priority;
+import org.sonar.check.Rule;
+import org.sonar.check.RuleProperty;
+import org.sonar.plugins.gherkin.api.tree.FeatureTree;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitorCheck;
+import org.sonar.squidbridge.annotations.ActivatedByDefault;
+import org.sonar.squidbridge.annotations.SqaleConstantRemediation;
+
+@Rule(
+ key = "max-number-scenarios",
+ name = "Features should not contain too many scenarios",
+ priority = Priority.MAJOR,
+ tags = {Tags.UNDERSTANDABILITY})
+@SqaleConstantRemediation("30min")
+@ActivatedByDefault
+public class MaxNumberScenariosCheck extends DoubleDispatchVisitorCheck {
+
+ private static final int DEFAULT = 12;
+
+ @RuleProperty(
+ key = "threshold",
+ defaultValue = DEFAULT + "",
+ description = "Maximum number of scenarios per feature.")
+ private int threshold = DEFAULT;
+
+ @Override
+ public void visitFeature(FeatureTree tree) {
+ if (tree.allScenarios().size() > threshold) {
+ addPreciseIssue(
+ tree.declaration().prefix(),
+ "The number of scenarios (" + tree.allScenarios().size() + ") is greater that the maximum allowed ("
+ + threshold + "). Split the scenarios into different features.");
+ }
+
+ super.visitFeature(tree);
+ }
+
+ @VisibleForTesting
+ void setThreshold(int threshold) {
+ this.threshold = threshold;
+ }
+
+
+}
diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/MaxNumberStepsCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/MaxNumberStepsCheck.java
new file mode 100644
index 0000000..499bf5a
--- /dev/null
+++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/MaxNumberStepsCheck.java
@@ -0,0 +1,66 @@
+/*
+ * 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 org.sonar.check.Priority;
+import org.sonar.check.Rule;
+import org.sonar.check.RuleProperty;
+import org.sonar.plugins.gherkin.api.tree.FeatureTree;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitorCheck;
+import org.sonar.squidbridge.annotations.ActivatedByDefault;
+import org.sonar.squidbridge.annotations.SqaleConstantRemediation;
+
+@Rule(
+ key = "max-number-steps",
+ name = "Scenarios should not contain too many steps",
+ priority = Priority.MAJOR,
+ tags = {Tags.UNDERSTANDABILITY})
+@SqaleConstantRemediation("30min")
+@ActivatedByDefault
+public class MaxNumberStepsCheck extends DoubleDispatchVisitorCheck {
+
+ private static final int DEFAULT = 12;
+
+ @RuleProperty(
+ key = "threshold",
+ defaultValue = DEFAULT + "",
+ description = "Maximum number of steps per scenario.")
+ private int threshold = DEFAULT;
+
+ @Override
+ public void visitFeature(FeatureTree tree) {
+ int numberOfBackgroundSteps = tree.background() != null ? tree.background().steps().size() : 0;
+
+ tree.allScenarios()
+ .stream()
+ .filter(s -> s.steps().size() + numberOfBackgroundSteps > threshold)
+ .forEach(s -> addPreciseIssue(s.prefix(), "Reduce the number of steps (" + (s.steps().size() + numberOfBackgroundSteps) + ", greater than " + threshold + " allowed)"));
+
+ super.visitFeature(tree);
+ }
+
+ @VisibleForTesting
+ void setThreshold(int threshold) {
+ this.threshold = threshold;
+ }
+
+
+}
diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/MissingNewlineAtEndOfFileCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/MissingNewlineAtEndOfFileCheck.java
new file mode 100644
index 0000000..78d9597
--- /dev/null
+++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/MissingNewlineAtEndOfFileCheck.java
@@ -0,0 +1,67 @@
+/*
+ * 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.sonar.check.Priority;
+import org.sonar.check.Rule;
+import org.sonar.plugins.gherkin.api.tree.GherkinDocumentTree;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitorCheck;
+import org.sonar.squidbridge.annotations.ActivatedByDefault;
+import org.sonar.squidbridge.annotations.SqaleConstantRemediation;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+@Rule(
+ key = "empty-line-end-of-file",
+ name = "Files should contain an empty new line at the end",
+ priority = Priority.MINOR,
+ tags = {Tags.CONVENTION})
+@SqaleConstantRemediation("1min")
+@ActivatedByDefault
+public class MissingNewlineAtEndOfFileCheck extends DoubleDispatchVisitorCheck {
+
+ @Override
+ public void visitGherkinDocument(GherkinDocumentTree tree) {
+ try (RandomAccessFile randomAccessFile = new RandomAccessFile(getContext().getFile(), "r")) {
+ if (!endsWithNewline(randomAccessFile)) {
+ addFileIssue("Add an empty new line at the end of this file.");
+ }
+ } catch (IOException e) {
+ throw new IllegalStateException("Check gherkin:" + this.getClass().getAnnotation(Rule.class).key()
+ + ": Error while reading " + getContext().getFile().getName(), e);
+ }
+ super.visitGherkinDocument(tree);
+ }
+
+ private static boolean endsWithNewline(RandomAccessFile randomAccessFile) throws IOException {
+ if (randomAccessFile.length() < 1) {
+ return false;
+ }
+ randomAccessFile.seek(randomAccessFile.length() - 1);
+ byte[] chars = new byte[1];
+ if (randomAccessFile.read(chars) < 1) {
+ return false;
+ }
+ String ch = new String(chars);
+ return "\n".equals(ch) || "\r".equals(ch);
+ }
+
+}
diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/NoFeatureCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/NoFeatureCheck.java
new file mode 100644
index 0000000..fd32acd
--- /dev/null
+++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/NoFeatureCheck.java
@@ -0,0 +1,46 @@
+/*
+ * 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.sonar.check.Priority;
+import org.sonar.check.Rule;
+import org.sonar.plugins.gherkin.api.tree.GherkinDocumentTree;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitorCheck;
+import org.sonar.squidbridge.annotations.ActivatedByDefault;
+import org.sonar.squidbridge.annotations.SqaleConstantRemediation;
+
+@Rule(
+ key = "no-feature",
+ name = "Files that do not define any feature should be removed",
+ priority = Priority.MAJOR,
+ tags = {Tags.PITFALL})
+@SqaleConstantRemediation("5min")
+@ActivatedByDefault
+public class NoFeatureCheck extends DoubleDispatchVisitorCheck {
+
+ @Override
+ public void visitGherkinDocument(GherkinDocumentTree tree) {
+ if (tree.feature() == null) {
+ addFileIssue("Remove this file that does not define any feature.");
+ }
+ super.visitGherkinDocument(tree);
+ }
+
+}
diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/NoScenarioCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/NoScenarioCheck.java
new file mode 100644
index 0000000..8bd7d3b
--- /dev/null
+++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/NoScenarioCheck.java
@@ -0,0 +1,46 @@
+/*
+ * 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.sonar.check.Priority;
+import org.sonar.check.Rule;
+import org.sonar.plugins.gherkin.api.tree.FeatureTree;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitorCheck;
+import org.sonar.squidbridge.annotations.ActivatedByDefault;
+import org.sonar.squidbridge.annotations.SqaleConstantRemediation;
+
+@Rule(
+ key = "no-scenario",
+ name = "Features that do not define any scenario should be removed",
+ priority = Priority.MAJOR,
+ tags = {Tags.PITFALL})
+@SqaleConstantRemediation("5min")
+@ActivatedByDefault
+public class NoScenarioCheck extends DoubleDispatchVisitorCheck {
+
+ @Override
+ public void visitFeature(FeatureTree tree) {
+ if (tree.allScenarios().isEmpty()) {
+ addPreciseIssue(tree.declaration().prefix(), "Remove this feature that does not define any scenario.");
+ }
+ super.visitFeature(tree);
+ }
+
+}
diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/ParsingErrorCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/ParsingErrorCheck.java
new file mode 100644
index 0000000..175ef97
--- /dev/null
+++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/ParsingErrorCheck.java
@@ -0,0 +1,36 @@
+/*
+ * 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.sonar.check.Priority;
+import org.sonar.check.Rule;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitorCheck;
+import org.sonar.squidbridge.annotations.ActivatedByDefault;
+import org.sonar.squidbridge.annotations.SqaleConstantRemediation;
+
+@Rule(
+ key = "S2260",
+ name = "Gherkin parser failure",
+ priority = Priority.CRITICAL,
+ tags = {Tags.BUG})
+@SqaleConstantRemediation("5min")
+@ActivatedByDefault
+public class ParsingErrorCheck extends DoubleDispatchVisitorCheck {
+}
diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/StarStepPrefixCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/StarStepPrefixCheck.java
new file mode 100644
index 0000000..2711fbd
--- /dev/null
+++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/StarStepPrefixCheck.java
@@ -0,0 +1,46 @@
+/*
+ * 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.sonar.check.Priority;
+import org.sonar.check.Rule;
+import org.sonar.plugins.gherkin.api.tree.StepPrefixTree;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitorCheck;
+import org.sonar.squidbridge.annotations.ActivatedByDefault;
+import org.sonar.squidbridge.annotations.SqaleConstantRemediation;
+
+@Rule(
+ key = "star-step-prefix",
+ name = "Star (*) step prefix should not be used",
+ priority = Priority.MAJOR,
+ tags = {Tags.UNDERSTANDABILITY})
+@SqaleConstantRemediation("5min")
+@ActivatedByDefault
+public class StarStepPrefixCheck extends DoubleDispatchVisitorCheck {
+
+ @Override
+ public void visitStepPrefix(StepPrefixTree tree) {
+ if ("*".equals(tree.text())) {
+ addPreciseIssue(tree, "Replace this star prefix with Given/When/Then.");
+ }
+ super.visitStepPrefix(tree);
+ }
+
+}
diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/TabCharacterCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/TabCharacterCheck.java
new file mode 100644
index 0000000..b848a77
--- /dev/null
+++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/TabCharacterCheck.java
@@ -0,0 +1,74 @@
+/*
+ * 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.collect.ImmutableList;
+import com.google.common.io.Files;
+import org.sonar.check.Priority;
+import org.sonar.check.Rule;
+import org.sonar.gherkin.visitors.CharsetAwareVisitor;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.SubscriptionVisitorCheck;
+import org.sonar.squidbridge.annotations.ActivatedByDefault;
+import org.sonar.squidbridge.annotations.SqaleConstantRemediation;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.List;
+
+@Rule(
+ key = "tab-character",
+ name = "Tabulation characters should not be used",
+ priority = Priority.MINOR,
+ tags = {Tags.CONVENTION})
+@SqaleConstantRemediation("2min")
+@ActivatedByDefault
+public class TabCharacterCheck extends SubscriptionVisitorCheck implements CharsetAwareVisitor {
+
+ private Charset charset;
+
+ @Override
+ public List nodesToVisit() {
+ return ImmutableList.of(Tree.Kind.GHERKIN_DOCUMENT);
+ }
+
+ @Override
+ public void visitFile(Tree tree) {
+ List lines;
+ try {
+ lines = Files.readLines(getContext().getFile(), charset);
+ } catch (IOException e) {
+ throw new IllegalStateException("Check gherkin:" + this.getClass().getAnnotation(Rule.class).key()
+ + ": Error while reading " + getContext().getFile().getName(), e);
+ }
+ for (String line : lines) {
+ if (line.contains("\t")) {
+ addFileIssue("Replace all tab characters in this file by sequences of whitespaces.");
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void setCharset(Charset charset) {
+ this.charset = charset;
+ }
+
+}
diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/TagNameCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/TagNameCheck.java
new file mode 100644
index 0000000..4423c42
--- /dev/null
+++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/TagNameCheck.java
@@ -0,0 +1,75 @@
+/*
+ * 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 org.sonar.check.Priority;
+import org.sonar.check.Rule;
+import org.sonar.check.RuleProperty;
+import org.sonar.plugins.gherkin.api.tree.TagTree;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitorCheck;
+import org.sonar.squidbridge.annotations.ActivatedByDefault;
+import org.sonar.squidbridge.annotations.SqaleConstantRemediation;
+
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+@Rule(
+ key = "tag-naming-convention",
+ name = "Tags should comply with a naming convention",
+ priority = Priority.MINOR,
+ tags = {Tags.CONVENTION})
+@SqaleConstantRemediation("5min")
+@ActivatedByDefault
+public class TagNameCheck extends DoubleDispatchVisitorCheck {
+
+ private static final String DEFAULT = "^[a-z][-a-z0-9]*$";
+
+ @RuleProperty(
+ key = "format",
+ defaultValue = DEFAULT,
+ description = "Regular expression that tags should match. See " + CheckUtils.LINK_TO_JAVA_REGEX_PATTERN_DOC + " for detailed regular expression syntax.")
+ private String format = DEFAULT;
+
+ @Override
+ public void visitTag(TagTree tree) {
+ if (!tree.text().matches(format)) {
+ addPreciseIssue(tree, "Rename this tag to match the regular expression: " + format);
+ }
+ super.visitTag(tree);
+ }
+
+ @Override
+ public void validateParameters() {
+ try {
+ Pattern.compile(format);
+ } catch (PatternSyntaxException exception) {
+ throw new IllegalStateException(
+ CheckUtils.paramsErrorMessage(this.getClass(), "format parameter \"" + format + "\" is not a valid regular expression."),
+ exception);
+ }
+ }
+
+ @VisibleForTesting
+ void setFormat(String format) {
+ this.format = format;
+ }
+
+}
diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/Tags.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/Tags.java
new file mode 100644
index 0000000..269c311
--- /dev/null
+++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/Tags.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+public final class Tags {
+
+ public static final String BUG = "bug";
+ public static final String CONVENTION = "convention";
+ public static final String PITFALL = "pitfall";
+ public static final String UNDERSTANDABILITY = "understandability";
+
+ private Tags() {
+ }
+
+}
diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/TodoTagPresenceCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/TodoTagPresenceCheck.java
new file mode 100644
index 0000000..2110a99
--- /dev/null
+++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/TodoTagPresenceCheck.java
@@ -0,0 +1,39 @@
+/*
+ * 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.sonar.check.Priority;
+import org.sonar.check.Rule;
+import org.sonar.squidbridge.annotations.ActivatedByDefault;
+import org.sonar.squidbridge.annotations.SqaleConstantRemediation;
+
+@Rule(
+ key = "S1135",
+ name = "\"TODO\" tags should be handled",
+ priority = Priority.INFO)
+@SqaleConstantRemediation("15min")
+@ActivatedByDefault
+public class TodoTagPresenceCheck extends CommentContainsPatternChecker {
+
+ public TodoTagPresenceCheck() {
+ super("TODO", "Complete the task associated to this TODO comment.");
+ }
+
+}
diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/TrailingWhitespaceCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/TrailingWhitespaceCheck.java
new file mode 100644
index 0000000..e34bff0
--- /dev/null
+++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/TrailingWhitespaceCheck.java
@@ -0,0 +1,71 @@
+/*
+ * 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.io.Files;
+import org.sonar.check.Priority;
+import org.sonar.check.Rule;
+import org.sonar.gherkin.visitors.CharsetAwareVisitor;
+import org.sonar.plugins.gherkin.api.tree.GherkinDocumentTree;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitorCheck;
+import org.sonar.squidbridge.annotations.ActivatedByDefault;
+import org.sonar.squidbridge.annotations.SqaleConstantRemediation;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.List;
+import java.util.regex.Pattern;
+
+@Rule(
+ key = "S1131",
+ name = "Lines should not end with trailing whitespaces",
+ priority = Priority.MINOR,
+ tags = {Tags.CONVENTION})
+@SqaleConstantRemediation("1min")
+@ActivatedByDefault
+public class TrailingWhitespaceCheck extends DoubleDispatchVisitorCheck implements CharsetAwareVisitor {
+
+ private static final String WHITESPACE = "\\t\\u000B\\f\\u0020\\u00A0\\uFEFF\\p{Zs}";
+ private Charset charset;
+
+ @Override
+ public void visitGherkinDocument(GherkinDocumentTree tree) {
+ List lines;
+ try {
+ lines = Files.readLines(getContext().getFile(), charset);
+ } catch (IOException e) {
+ throw new IllegalStateException("Check gherking:" + this.getClass().getAnnotation(Rule.class).key()
+ + ": Error while reading " + getContext().getFile().getName(), e);
+ }
+ for (int i = 0; i < lines.size(); i++) {
+ String line = lines.get(i);
+ if (line.length() > 0 && Pattern.matches("[" + WHITESPACE + "]", line.subSequence(line.length() - 1, line.length()))) {
+ addLineIssue(i + 1, "Remove the useless trailing whitespaces at the end of this line.");
+ }
+ }
+ super.visitGherkinDocument(tree);
+ }
+
+ @Override
+ public void setCharset(Charset charset) {
+ this.charset = charset;
+ }
+
+}
diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/package-info.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/package-info.java
new file mode 100644
index 0000000..9c4e378
--- /dev/null
+++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.gherkin.checks;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
diff --git a/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/S1131.html b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/S1131.html
new file mode 100644
index 0000000..2a7298d
--- /dev/null
+++ b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/S1131.html
@@ -0,0 +1,5 @@
+
+ Trailing whitespaces are simply useless and should not stay in code. They may generate noise when comparing
+ different versions of the same file. If you encounter issues from this rule, this probably means that you are not
+ using an automated code formatter - which you should if you have the opportunity to do so.
+
diff --git a/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/S1134.html b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/S1134.html
new file mode 100644
index 0000000..43f39dd
--- /dev/null
+++ b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/S1134.html
@@ -0,0 +1,12 @@
+
+ FIXME tags are commonly used to mark places where a bug is suspected, but which the developer wants to
+ deal with later. Sometimes the developer will not have the time or will simply forget to get back to that tag. This
+ rule is meant to track those tags, and ensure that they do not go unnoticed.
+
+
+
Noncompliant Code Example
+
+# FIXME: blabla
+Feature My feature...
+ Scenario My scenario...
+
diff --git a/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/S1135.html b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/S1135.html
new file mode 100644
index 0000000..0747b7b
--- /dev/null
+++ b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/S1135.html
@@ -0,0 +1,12 @@
+
+ TODO tags are commonly used to mark places where some more code is required, but which the developer
+ wants to implement later. Sometimes the developer will not have the time or will simply forget to get back to that
+ tag. This rule is meant to track those tags, and ensure that they do not go unnoticed.
+
+
+
Noncompliant Code Example
+
+# TODO: blabla
+Feature My feature...
+ Scenario My scenario...
+
diff --git a/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/S1578.html b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/S1578.html
new file mode 100644
index 0000000..f49dc9a
--- /dev/null
+++ b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/S1578.html
@@ -0,0 +1,16 @@
+
+ Shared coding conventions allow teams to collaborate effectively. This rule checks that all file names match a
+ provided regular expression.
+
diff --git a/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/S2260.html b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/S2260.html
new file mode 100644
index 0000000..910cb57
--- /dev/null
+++ b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/S2260.html
@@ -0,0 +1,4 @@
+
+ When the parser fails, it is possible to record the failure as an issue on the file. This way, not only is it
+ possible to track the number of files that do not parse but also to easily find out why they do not parse.
+
diff --git a/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/allowed-tags.html b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/allowed-tags.html
new file mode 100644
index 0000000..9f3a4e2
--- /dev/null
+++ b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/allowed-tags.html
@@ -0,0 +1,16 @@
+
+ For tags to be usable, the list of allowed tags should be properly defined. This rule raises an issue each time a
+ tag that is not in the whitelist is found.
+
+
+
Noncompliant Code Example
+With default value "smoke,nrt":
+
+@non-regression-test
+
+
+
Compliant Solution
+With default value "smoke,nrt":
+
+@nrt
+
diff --git a/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/bom-utf8-files.html b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/bom-utf8-files.html
new file mode 100644
index 0000000..31baf3e
--- /dev/null
+++ b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/bom-utf8-files.html
@@ -0,0 +1,11 @@
+
+ As stated in the Unicode specifications, use of a Byte Order Mark (BOM) is neither required nor recommended for
+ UTF-8 files.
+
diff --git a/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/comment-convention.html b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/comment-convention.html
new file mode 100644
index 0000000..bf26bec
--- /dev/null
+++ b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/comment-convention.html
@@ -0,0 +1,11 @@
+
For readability reasons, the starting token # should be followed by a whitespace.
+
+
Noncompliant Code Example
+
+#My comments...
+
+
+
Compliant Solution
+
+# My comments...
+
diff --git a/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/comment-regular-expression.html b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/comment-regular-expression.html
new file mode 100644
index 0000000..d74ad8f
--- /dev/null
+++ b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/comment-regular-expression.html
@@ -0,0 +1,13 @@
+
+ This rule template can be used to create rules which will be triggered when a comment matches a given regular
+ expression.
+
+
+ For example, one can create a rule with the regular expression ".*WTF.*" to match all comment
+ containing "WTF". Note that, in order to match WTF regardless of the case, the "(?i)" modifier can be
+ prepended to the expression, as in "(?i).*WTF.*".
+
diff --git a/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/empty-line-end-of-file.html b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/empty-line-end-of-file.html
new file mode 100644
index 0000000..6fb97ef
--- /dev/null
+++ b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/empty-line-end-of-file.html
@@ -0,0 +1,8 @@
+
Some tools such as Git work better when files end with an empty line.
+
This rule simply generates an issue if it is missing.
+
For example, a Git diff looks like:
+
++Then I should be a customer
+\ No newline at end of file
+
+
if the empty line is missing at the end of the file.
diff --git a/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/end-line-characters.html b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/end-line-characters.html
new file mode 100644
index 0000000..8b38684
--- /dev/null
+++ b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/end-line-characters.html
@@ -0,0 +1 @@
+
End-line characters should be consistent in order to prevent polluting SCM history changelog for instance.
diff --git a/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/incomplete-examples-table.html b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/incomplete-examples-table.html
new file mode 100644
index 0000000..12021c7
--- /dev/null
+++ b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/incomplete-examples-table.html
@@ -0,0 +1,96 @@
+
+ Having Scenario Outlines with Examples section containing no table will end up with the
+ execution of zero scenario; that is unlikely the expected behavior. Thus, each Examples section should
+ contain a table.
+
+
+
Noncompliant Code Example
+
+Scenario Outline: Add product to cart
+ Given I am a customer
+ When I add <number> products to my cart
+ Then I should see <number> products in my cart
+
+ Examples:
+
+
+
Compliant Solution
+
+Scenario Outline: Add product to cart
+ Given I am a customer
+ When I add <number> products to my cart
+ Then I should see <number> products in my cart
+
+ Examples:
+ | number |
+ | 1 |
+ | 2 |
+
+
+
+ Having Scenario Outlines with Examples section containing a table with one single row will
+ end up with the execution of zero scenario; that is unlikely the expected behavior. Thus, each Examples
+ table should contain at least two rows.
+
+
+
Noncompliant Code Example
+
+Scenario Outline: Add product to cart
+ Given I am a customer
+ When I add <number> products to my cart
+ Then I should see <number> products in my cart
+
+ Examples:
+ | number |
+
+
+
Compliant Solution
+
+Scenario Outline: Add product to cart
+ Given I am a customer
+ When I add <number> products to my cart
+ Then I should see <number> products in my cart
+
+ Examples:
+ | number |
+ | 1 |
+ | 2 |
+
+
+
+ Having Scenario Outlines with Examples section containing a table with two rows will
+ end up with the execution of a single scenario. Thus, either some data are missing or the Scenario
+ Outline can be safely converted to a Scenario.
+
+
+
Noncompliant Code Example
+
+Scenario Outline: Add product to cart
+ Given I am a customer
+ When I add <number> products to my cart
+ Then I should see <number> products in my cart
+
+ Examples:
+ | number |
+ | 1 |
+
+
+
Compliant Solution
+
+Scenario Outline: Add product to cart
+ Given I am a customer
+ When I add <number> products to my cart
+ Then I should see <number> products in my cart
+
+ Examples:
+ | number |
+ | 1 |
+ | 2 |
+
+OR
+
+Scenario: Add product to cart
+ Given I am a customer
+ When I add 1 product to my cart
+ Then I should see 1 product in my cart
+
diff --git a/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/indentation.html b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/indentation.html
new file mode 100644
index 0000000..def79ad
--- /dev/null
+++ b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/indentation.html
@@ -0,0 +1,20 @@
+
For readability reasons, code should be properly indented
+
+
Noncompliant Code Example
+With default value: 2
+
+Feature: my feature...
+ Scenario: my scenario...
+ Given blabla...
+ When blabla... # Noncompliant
+ Then blabla...
+
+
+
Compliant Solution
+
+Feature: my feature...
+ Scenario: my scenario...
+ Given blabla...
+ When blabla...
+ Then blabla...
+
diff --git a/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/max-number-scenarios.html b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/max-number-scenarios.html
new file mode 100644
index 0000000..71fdae7
--- /dev/null
+++ b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/max-number-scenarios.html
@@ -0,0 +1,4 @@
+
+ A feature should contain scenarios targeting a specific use case or functional area. A feature defining too many
+ scenarios is likely to target several use cases and should be split into several features.
+
diff --git a/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/max-number-steps.html b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/max-number-steps.html
new file mode 100644
index 0000000..de1328c
--- /dev/null
+++ b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/max-number-steps.html
@@ -0,0 +1,21 @@
+
+ For a scenario to remain readable and understandable, the number of its steps should remain limited. The default
+ threshold is 12 and it includes the number of steps in Background.
+
+
+
Noncompliant Code Example
+With a custom threshold of 8:
+
+Background:
+ Given ...
+ And ...
+ And ...
+
+Scenario: ...
+ Given ...
+ When ...
+ Then ...
+ And ...
+ And ...
+ And ...
+
diff --git a/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/no-feature.html b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/no-feature.html
new file mode 100644
index 0000000..0ee4f79
--- /dev/null
+++ b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/no-feature.html
@@ -0,0 +1,16 @@
+
+ Having a file that does not define any feature is useless. It can either be safely removed or a feature should be
+ added.
+
+
+
Noncompliant Code Example
+
+# Empty file
+
+
+
Compliant Solution
+
+Feature: My feature...
+ Scenario: My scenario #1
+ ...
+
diff --git a/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/no-scenario.html b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/no-scenario.html
new file mode 100644
index 0000000..22760fb
--- /dev/null
+++ b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/no-scenario.html
@@ -0,0 +1,16 @@
+
+ Having a feature that does not define any scenario is useless. It can either be safely removed or scenarios should
+ be added.
+
+
+
Noncompliant Code Example
+
+Feature: My feature...
+
+
+
Compliant Solution
+
+Feature: My feature...
+ Scenario: My scenario #1
+ ...
+
diff --git a/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/star-step-prefix.html b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/star-step-prefix.html
new file mode 100644
index 0000000..911e490
--- /dev/null
+++ b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/star-step-prefix.html
@@ -0,0 +1,20 @@
+
+ In order to improve understandability, Given/When/Then step prefix should be used instead of
+ *.
+
+
+
Noncompliant Code Example
+
+Scenario: Add product to cart
+ * I am a customer
+ * I add a product to my cart
+ * I should see the product in my cart
+
+
+
Compliant Solution
+
+Scenario: Add product to cart
+ Given I am a customer
+ When I add a product to my cart
+ Then I should see the product in my cart
+
diff --git a/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/tab-character.html b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/tab-character.html
new file mode 100644
index 0000000..0ca0d97
--- /dev/null
+++ b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/tab-character.html
@@ -0,0 +1,4 @@
+
+ Developers should not need to configure the tab width of their text editors in order to be able to read source code.
+ So the use of tabulation character must be banned.
+
\ No newline at end of file
diff --git a/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/tag-naming-convention.html b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/tag-naming-convention.html
new file mode 100644
index 0000000..781c454
--- /dev/null
+++ b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/tag-naming-convention.html
@@ -0,0 +1,17 @@
+
+ Shared coding conventions allow teams to collaborate effectively. This rule checks that all tags match a provided
+ regular expression.
+
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/AllowedTagsCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/AllowedTagsCheckTest.java
new file mode 100644
index 0000000..e82e88b
--- /dev/null
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/AllowedTagsCheckTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+public class AllowedTagsCheckTest {
+
+ @Test
+ public void test_default_white_list() {
+ GherkinCheckVerifier.verify(new AllowedTagsCheck(), CheckTestUtils.getTestFile("allowed-tags/allowed-tags-default.feature"));
+ }
+
+ @Test
+ public void test_default_custom_white_list() {
+ AllowedTagsCheck check = new AllowedTagsCheck();
+ check.setAllowedTags("mytag,yourtag");
+ GherkinCheckVerifier.verify(check, CheckTestUtils.getTestFile("allowed-tags/allowed-tags-custom.feature"));
+ }
+
+}
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/BOMCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/BOMCheckTest.java
new file mode 100644
index 0000000..daf478f
--- /dev/null
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/BOMCheckTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.base.Charsets;
+import org.junit.Test;
+import org.sonar.gherkin.checks.verifier.GherkinCheckVerifier;
+
+public class BOMCheckTest {
+
+ @Test
+ public void should_find_that_the_UTF8_file_starts_with_a_BOM_and_raise_an_issue() {
+ GherkinCheckVerifier.verify(
+ new BOMCheck(),
+ CheckTestUtils.getTestFile("bom/utf8-with-bom.feature"),
+ Charsets.UTF_8);
+ }
+
+ @Test
+ public void should_find_that_the_UTF8_file_does_not_start_with_a_BOM_and_not_raise_any_issue() {
+ GherkinCheckVerifier.verify(
+ new BOMCheck(),
+ CheckTestUtils.getTestFile("bom/utf8.feature"),
+ Charsets.UTF_8);
+ }
+
+ @Test
+ public void should_find_that_the_UTF16_files_start_with_a_BOM_but_not_raise_any_issue() {
+ GherkinCheckVerifier.verify(
+ new BOMCheck(),
+ CheckTestUtils.getTestFile("bom/utf16-be.feature"),
+ Charsets.UTF_16BE);
+
+ GherkinCheckVerifier.verify(
+ new BOMCheck(),
+ CheckTestUtils.getTestFile("bom/utf16-le.feature"),
+ Charsets.UTF_16LE);
+ }
+
+}
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/CheckTestUtils.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/CheckTestUtils.java
new file mode 100644
index 0000000..6217597
--- /dev/null
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/CheckTestUtils.java
@@ -0,0 +1,33 @@
+/*
+ * 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 java.io.File;
+
+public class CheckTestUtils {
+
+ private CheckTestUtils() {
+ }
+
+ public static File getTestFile(String relativePath) {
+ return new File("src/test/resources/checks/" + relativePath);
+ }
+
+}
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/CommentConventionCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/CommentConventionCheckTest.java
new file mode 100644
index 0000000..752faf4
--- /dev/null
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/CommentConventionCheckTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+public class CommentConventionCheckTest {
+
+ @Test
+ public void test() {
+ GherkinCheckVerifier.issues(new CommentConventionCheck(), CheckTestUtils.getTestFile("comment-convention.feature"))
+ .next().atLine(2).withMessage("Add a whitespace after the starting comment token.")
+ .noMore();
+ }
+
+}
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/CommentRegularExpressionCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/CommentRegularExpressionCheckTest.java
new file mode 100644
index 0000000..b6cc642
--- /dev/null
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/CommentRegularExpressionCheckTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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 java.io.File;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class CommentRegularExpressionCheckTest {
+
+ private final static File FILE = CheckTestUtils.getTestFile("comment-regular-expression.feature");
+ private CommentRegularExpressionCheck check = new CommentRegularExpressionCheck();
+
+ @Test
+ public void should_match_some_comments_and_raise_some_issues() {
+ String message = "Stop annotating lines with WTF! Detail what is wrong instead.";
+ check.regularExpression = "(?i).*WTF.*";
+ check.message = "Stop annotating lines with WTF! Detail what is wrong instead.";
+
+ GherkinCheckVerifier.issues(check, FILE)
+ .next().atLine(2).withMessage(message)
+ .next().atLine(3).withMessage(message)
+ .noMore();
+ }
+
+ @Test
+ public void should_not_match_any_comments_and_not_raise_any_issues() {
+ check.regularExpression = "blabla";
+ check.message = "blabla";
+
+ GherkinCheckVerifier.issues(check, FILE)
+ .noMore();
+ }
+
+ @Test
+ public void should_throw_an_illegal_state_exception_as_the_regular_expression_parameter_is_not_valid() {
+ try {
+ check.regularExpression = "(";
+ check.message = "blabla";
+
+ GherkinCheckVerifier.issues(check, FILE).noMore();
+
+ } catch (IllegalStateException e) {
+ assertThat(e.getMessage()).isEqualTo("Check gherkin:comment-regular-expression (Regular expression on comment): "
+ + "regularExpression parameter \"(\" is not a valid regular expression.");
+ }
+ }
+
+}
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/EndLineCharactersCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/EndLineCharactersCheckTest.java
new file mode 100644
index 0000000..54f0357
--- /dev/null
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/EndLineCharactersCheckTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.base.Charsets;
+import com.google.common.io.Files;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.gherkin.checks.verifier.GherkinCheckVerifier;
+
+import java.io.File;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class EndLineCharactersCheckTest {
+
+ private EndLineCharactersCheck check = new EndLineCharactersCheck();
+
+ @Test
+ public void should_find_only_crlf_and_not_raise_any_issues() throws Exception {
+ check.setEndLineCharacters("CRLF");
+ GherkinCheckVerifier.issues(check, getTestFileWithProperEndLineCharacters("\r\n"))
+ .noMore();
+ }
+
+ @Test
+ public void should_find_only_cr_and_not_raise_any_issues() throws Exception {
+ check.setEndLineCharacters("CR");
+ GherkinCheckVerifier.issues(check, getTestFileWithProperEndLineCharacters("\r"))
+ .noMore();
+ }
+
+ @Test
+ public void should_find_only_lf_and_not_raise_any_issues() throws Exception {
+ check.setEndLineCharacters("LF");
+ GherkinCheckVerifier.issues(check, getTestFileWithProperEndLineCharacters("\n"))
+ .noMore();
+ }
+
+ @Test
+ public void crlf_should_find_lf_and_raise_issues() throws Exception {
+ check.setEndLineCharacters("CRLF");
+ GherkinCheckVerifier.issues(check, getTestFileWithProperEndLineCharacters("\n"))
+ .next().withMessage("Set all end-line characters to 'CRLF' in this file.")
+ .noMore();
+ }
+
+ @Test
+ public void crlf_should_find_cr_and_raise_issues() throws Exception {
+ check.setEndLineCharacters("CRLF");
+ GherkinCheckVerifier.issues(check, getTestFileWithProperEndLineCharacters("\r"))
+ .next().withMessage("Set all end-line characters to 'CRLF' in this file.")
+ .noMore();
+ }
+
+ @Test
+ public void cr_should_find_crlf_and_raise_issues() throws Exception {
+ check.setEndLineCharacters("CR");
+ GherkinCheckVerifier.issues(check, getTestFileWithProperEndLineCharacters("\r\n"))
+ .next().withMessage("Set all end-line characters to 'CR' in this file.")
+ .noMore();
+ }
+
+ @Test
+ public void cr_should_find_lf_and_raise_issues() throws Exception {
+ check.setEndLineCharacters("CR");
+ GherkinCheckVerifier.issues(check, getTestFileWithProperEndLineCharacters("\n"))
+ .next().withMessage("Set all end-line characters to 'CR' in this file.")
+ .noMore();
+ }
+
+ @Test
+ public void lf_should_find_crlf_and_raise_issues() throws Exception {
+ check.setEndLineCharacters("LF");
+ GherkinCheckVerifier.issues(check, getTestFileWithProperEndLineCharacters("\r\n"))
+ .next().withMessage("Set all end-line characters to 'LF' in this file.")
+ .noMore();
+ }
+
+ @Test
+ public void lf_should_find_cr_and_raise_issues() throws Exception {
+ check.setEndLineCharacters("LF");
+ GherkinCheckVerifier.issues(check, getTestFileWithProperEndLineCharacters("\r"))
+ .next().withMessage("Set all end-line characters to 'LF' in this file.")
+ .noMore();
+ }
+
+ @Test
+ public void should_throw_an_illegal_state_exception_as_the_endLineCharacters_parameter_is_not_valid() throws Exception {
+ try {
+ check.setEndLineCharacters("abc");
+ GherkinCheckVerifier.issues(check, getTestFileWithProperEndLineCharacters("\r")).noMore();
+ } catch (IllegalStateException e) {
+ assertThat(e.getMessage()).isEqualTo("Check gherkin:end-line-characters (End-line characters should be consistent): "
+ + "endLineCharacters parameter is not valid.\nActual: 'abc'\nExpected: 'CR' or 'CRLF' or 'LF'");
+ }
+ }
+
+ private static File getTestFileWithProperEndLineCharacters(String endLineCharacter) throws Exception {
+ TemporaryFolder temporaryFolder = new TemporaryFolder();
+ File testFile = temporaryFolder.newFile();
+ Files.write(
+ Files.toString(CheckTestUtils.getTestFile("end-line-characters.feature"), Charsets.UTF_8)
+ .replaceAll("\\r\\n", "\n")
+ .replaceAll("\\r", "\n")
+ .replaceAll("\\n", endLineCharacter),
+ testFile, Charsets.UTF_8);
+ return testFile;
+ }
+
+}
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/FileNameCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/FileNameCheckTest.java
new file mode 100644
index 0000000..912689a
--- /dev/null
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/FileNameCheckTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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 FileNameCheckTest {
+
+ private FileNameCheck check = new FileNameCheck();
+
+ @Test
+ public void should_follow_the_default_naming_convention_and_not_raise_an_issue() {
+ GherkinCheckVerifier.issues(check, CheckTestUtils.getTestFile("file-name/file-name-ok.feature"))
+ .noMore();
+ }
+
+ @Test
+ public void should_not_follow_the_default_naming_convention_and_raise_an_issue() {
+ GherkinCheckVerifier.issues(check, CheckTestUtils.getTestFile("file-name/FileNameKO.feature"))
+ .next().withMessage("Rename this file to match the regular expression: ^[a-z][-A-Za-z0-9]*\\.feature$")
+ .noMore();
+ }
+
+ @Test
+ public void should_follow_a_custom_naming_convention_and_not_raise_an_issue() {
+ check.setFormat("^[a-z][-a-z]+\\.feature");
+ GherkinCheckVerifier.issues(check, CheckTestUtils.getTestFile("file-name/file-name-custom-ok.feature"))
+ .noMore();
+ }
+
+ @Test
+ public void should_not_follow_a_custom_naming_convention_and_raise_an_issue() {
+ check.setFormat("^[a-z]+\\.feature$");
+ GherkinCheckVerifier.issues(check, CheckTestUtils.getTestFile("file-name/file_name.kocustom.feature"))
+ .next().withMessage("Rename this file to match the regular expression: ^[a-z]+\\.feature$")
+ .noMore();
+ }
+
+ @Test
+ public void should_throw_an_illegal_state_exception_as_the_format_parameter_regular_expression_is_not_valid() {
+ try {
+ check.setFormat("(");
+ GherkinCheckVerifier.issues(check, CheckTestUtils.getTestFile("file-name/file-name-ok.feature")).noMore();
+ } catch (IllegalStateException e) {
+ assertThat(e.getMessage()).isEqualTo("Check gherkin:S1578 (File names should comply with a naming convention): " +
+ "format parameter \"(\" is not a valid regular expression.");
+ }
+ }
+
+}
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/FixmeTagPresenceCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/FixmeTagPresenceCheckTest.java
new file mode 100644
index 0000000..740d116
--- /dev/null
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/FixmeTagPresenceCheckTest.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+public class FixmeTagPresenceCheckTest {
+
+ @Test
+ public void should_contain_fixme_tags_and_raise_issues() {
+ GherkinCheckVerifier.verify(new FixmeTagPresenceCheck(), CheckTestUtils.getTestFile("fixme.feature"));
+ }
+
+}
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/IncompleteExamplesTableCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/IncompleteExamplesTableCheckTest.java
new file mode 100644
index 0000000..0f44c35
--- /dev/null
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/IncompleteExamplesTableCheckTest.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+public class IncompleteExamplesTableCheckTest {
+
+ @Test
+ public void test() {
+ GherkinCheckVerifier.verify(new IncompleteExamplesTableCheck(), CheckTestUtils.getTestFile("incomplete-examples-table.feature"));
+ }
+
+}
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/IndentationCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/IndentationCheckTest.java
new file mode 100644
index 0000000..d390828
--- /dev/null
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/IndentationCheckTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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;
+
+public class IndentationCheckTest {
+
+ @Test
+ public void should_not_raise_any_issue_with_default_parameter_value() {
+ GherkinCheckVerifier.verify(new IndentationCheck(), CheckTestUtils.getTestFile("indentation/indentation-default-ok.feature"));
+ }
+
+ @Test
+ public void should_raise_some_issues_with_default_parameter_value() {
+ GherkinCheckVerifier.verify(new IndentationCheck(), CheckTestUtils.getTestFile("indentation/indentation-default-ko.feature"));
+ }
+
+ @Test
+ public void should_not_raise_any_issue_with_custom_parameter_value() {
+ IndentationCheck check = new IndentationCheck();
+ check.setIndentation(4);
+ GherkinCheckVerifier.verify(check, CheckTestUtils.getTestFile("indentation/indentation-custom-ok.feature"));
+ }
+
+ @Test
+ public void should_raise_some_issues_with_custom_parameter_value() {
+ IndentationCheck check = new IndentationCheck();
+ check.setIndentation(4);
+ GherkinCheckVerifier.verify(check, CheckTestUtils.getTestFile("indentation/indentation-custom-ko.feature"));
+ }
+
+}
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/MaxNumberScenariosCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/MaxNumberScenariosCheckTest.java
new file mode 100644
index 0000000..9aa8472
--- /dev/null
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/MaxNumberScenariosCheckTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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;
+
+public class MaxNumberScenariosCheckTest {
+
+ @Test
+ public void should_not_raise_any_issue_because_the_number_of_scenarios_is_lower_than_the_default_threshold() {
+ GherkinCheckVerifier.verify(new MaxNumberScenariosCheck(), CheckTestUtils.getTestFile("max-number-scenarios/lower-than-default-threshold.feature"));
+ }
+
+ @Test
+ public void should_raise_an_issue_because_the_number_of_scenarios_is_greater_than_the_default_threshold() {
+ GherkinCheckVerifier.verify(new MaxNumberScenariosCheck(), CheckTestUtils.getTestFile("max-number-scenarios/greater-than-default-threshold.feature"));
+ }
+
+ @Test
+ public void should_not_raise_any_issue_because_the_number_of_scenarios_is_lower_than_the_custom_threshold() {
+ MaxNumberScenariosCheck check = new MaxNumberScenariosCheck();
+ check.setThreshold(4);
+ GherkinCheckVerifier.verify(check, CheckTestUtils.getTestFile("max-number-scenarios/lower-than-custom-threshold.feature"));
+ }
+
+ @Test
+ public void should_raise_an_issue_because_the_number_of_scenarios_is_greater_than_the_custom_threshold() {
+ MaxNumberScenariosCheck check = new MaxNumberScenariosCheck();
+ check.setThreshold(4);
+ GherkinCheckVerifier.verify(check, CheckTestUtils.getTestFile("max-number-scenarios/greater-than-custom-threshold.feature"));
+ }
+
+}
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/MaxNumberStepsCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/MaxNumberStepsCheckTest.java
new file mode 100644
index 0000000..f098bfa
--- /dev/null
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/MaxNumberStepsCheckTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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;
+
+public class MaxNumberStepsCheckTest {
+
+ @Test
+ public void test_with_no_background() {
+ GherkinCheckVerifier.verify(new MaxNumberStepsCheck(), CheckTestUtils.getTestFile("max-number-steps/max-number-steps-no-background.feature"));
+ }
+
+ @Test
+ public void test_with_background() {
+ GherkinCheckVerifier.verify(new MaxNumberStepsCheck(), CheckTestUtils.getTestFile("max-number-steps/max-number-steps-background.feature"));
+ }
+
+ @Test
+ public void test_custom_threshold() {
+ MaxNumberStepsCheck check = new MaxNumberStepsCheck();
+ check.setThreshold(5);
+ GherkinCheckVerifier.verify(check, CheckTestUtils.getTestFile("max-number-steps/max-number-steps-custom-threshold.feature"));
+ }
+
+}
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/MissingNewlineAtEndOfFileCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/MissingNewlineAtEndOfFileCheckTest.java
new file mode 100644
index 0000000..bb8a4fe
--- /dev/null
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/MissingNewlineAtEndOfFileCheckTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+public class MissingNewlineAtEndOfFileCheckTest {
+
+ @Test
+ public void should_contain_an_empty_new_line_at_the_end_of_the_file_and_not_raise_issues() {
+ GherkinCheckVerifier.issues(new MissingNewlineAtEndOfFileCheck(), CheckTestUtils.getTestFile("new-line-end-of-file/new-line-end-of-file.feature"))
+ .noMore();
+ }
+
+ @Test
+ public void should_not_contain_an_empty_new_line_at_the_end_of_the_file_and_raise_an_issue() {
+ GherkinCheckVerifier.issues(new MissingNewlineAtEndOfFileCheck(), CheckTestUtils.getTestFile("new-line-end-of-file/no-new-line-end-of-file.feature"))
+ .next().withMessage("Add an empty new line at the end of this file.")
+ .noMore();
+ }
+
+}
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/NoFeatureCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/NoFeatureCheckTest.java
new file mode 100644
index 0000000..794d2bb
--- /dev/null
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/NoFeatureCheckTest.java
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+public class NoFeatureCheckTest {
+
+ @Test
+ public void should_not_raise_any_issue_because_a_feature_is_defined() {
+ GherkinCheckVerifier.verify(new NoFeatureCheck(), CheckTestUtils.getTestFile("no-feature/feature.feature"));
+ }
+
+ @Test
+ public void should_raise_an_issue_because_no_feature_is_defined() {
+ GherkinCheckVerifier.verify(new NoFeatureCheck(), CheckTestUtils.getTestFile("no-feature/no-feature.feature"));
+ }
+
+}
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/NoScenarioCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/NoScenarioCheckTest.java
new file mode 100644
index 0000000..61f36a7
--- /dev/null
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/NoScenarioCheckTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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;
+
+public class NoScenarioCheckTest {
+
+ @Test
+ public void should_not_raise_any_issue_because_a_scenario_is_defined() {
+ GherkinCheckVerifier.verify(new NoScenarioCheck(), CheckTestUtils.getTestFile("no-scenario/scenario.feature"));
+ }
+
+ @Test
+ public void should_not_raise_any_issue_because_a_scenario_outline_is_defined() {
+ GherkinCheckVerifier.verify(new NoScenarioCheck(), CheckTestUtils.getTestFile("no-scenario/scenario-outline.feature"));
+ }
+
+ @Test
+ public void should_raise_an_issue_because_no_scenario_is_defined() {
+ GherkinCheckVerifier.verify(new NoScenarioCheck(), CheckTestUtils.getTestFile("no-scenario/no-scenario.feature"));
+ }
+
+}
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/StarStepPrefixCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/StarStepPrefixCheckTest.java
new file mode 100644
index 0000000..a67f76a
--- /dev/null
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/StarStepPrefixCheckTest.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+public class StarStepPrefixCheckTest {
+
+ @Test
+ public void test() {
+ GherkinCheckVerifier.verify(new StarStepPrefixCheck(), CheckTestUtils.getTestFile("star-step-prefix.feature"));
+ }
+
+}
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/TabCharacterCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/TabCharacterCheckTest.java
new file mode 100644
index 0000000..cefa065
--- /dev/null
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/TabCharacterCheckTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+public class TabCharacterCheckTest {
+
+ @Test
+ public void should_find_tab_characters_and_raise_an_issue() {
+ GherkinCheckVerifier.issues(new TabCharacterCheck(), CheckTestUtils.getTestFile("tab-character/tab-character.feature"))
+ .next().withMessage("Replace all tab characters in this file by sequences of whitespaces.")
+ .noMore();
+ }
+
+ @Test
+ public void should_not_find_tab_characters_and_not_raise_an_issue() {
+ GherkinCheckVerifier.issues(new TabCharacterCheck(), CheckTestUtils.getTestFile("tab-character/no-tab-character.feature"))
+ .noMore();
+ }
+
+}
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/TagNameCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/TagNameCheckTest.java
new file mode 100644
index 0000000..7fb4d6a
--- /dev/null
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/TagNameCheckTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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 TagNameCheckTest {
+
+ private TagNameCheck check = new TagNameCheck();
+
+ @Test
+ public void all_tags_should_follow_the_default_naming_convention_and_not_raise_any_issue() {
+ GherkinCheckVerifier.verify(check, CheckTestUtils.getTestFile("tag-name/tag-name-ok.feature"));
+ }
+
+ @Test
+ public void some_tags_should_not_follow_the_default_naming_convention_and_raise_some_issues() {
+ GherkinCheckVerifier.verify(check, CheckTestUtils.getTestFile("tag-name/tag-name-ko.feature"));
+ }
+
+ @Test
+ public void all_tags_should_follow_a_custom_naming_convention_and_not_raise_any_issue() {
+ check.setFormat("^[a-z][-a-z]*$");
+ GherkinCheckVerifier.verify(check, CheckTestUtils.getTestFile("tag-name/tag-name-custom-ok.feature"));
+ }
+
+ @Test
+ public void some_tags_should_not_follow_a_custom_naming_convention_and_raise_some_issues() {
+ check.setFormat("^[a-z]+$");
+ GherkinCheckVerifier.verify(check, CheckTestUtils.getTestFile("tag-name/tag-name-custom-ko.feature"));
+ }
+
+ @Test
+ public void should_throw_an_illegal_state_exception_as_the_format_parameter_regular_expression_is_not_valid() {
+ try {
+ check.setFormat("(");
+ GherkinCheckVerifier.issues(check, CheckTestUtils.getTestFile("tag-name/tag-name-ok.feature")).noMore();
+ } catch (IllegalStateException e) {
+ assertThat(e.getMessage()).isEqualTo("Check gherkin:tag-naming-convention (Tags should comply with a naming convention): " +
+ "format parameter \"(\" is not a valid regular expression.");
+ }
+ }
+
+}
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/TodoTagPresenceCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/TodoTagPresenceCheckTest.java
new file mode 100644
index 0000000..b456604
--- /dev/null
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/TodoTagPresenceCheckTest.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+public class TodoTagPresenceCheckTest {
+
+ @Test
+ public void test() {
+ GherkinCheckVerifier.verify(new TodoTagPresenceCheck(), CheckTestUtils.getTestFile("todo.feature"));
+ }
+
+}
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/TrailingWhitespaceCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/TrailingWhitespaceCheckTest.java
new file mode 100644
index 0000000..5cdeec8
--- /dev/null
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/TrailingWhitespaceCheckTest.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+public class TrailingWhitespaceCheckTest {
+
+ @Test
+ public void test() {
+ GherkinCheckVerifier.verify(new TrailingWhitespaceCheck(), CheckTestUtils.getTestFile("trailing-whitespace.feature"));
+ }
+
+}
diff --git a/gherkin-checks/src/test/resources/checks/allowed-tags/allowed-tags-custom.feature b/gherkin-checks/src/test/resources/checks/allowed-tags/allowed-tags-custom.feature
new file mode 100644
index 0000000..dce52ff
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/allowed-tags/allowed-tags-custom.feature
@@ -0,0 +1,16 @@
+# Noncompliant [[sc=1;ec=5]] {{Remove this tag that is not in the whitelist.}}
+@nrt
+Feature: My feature...
+
+ # Noncompliant [[sc=3;ec=23]] {{Remove this tag that is not in the whitelist.}}
+ @non-regression-test
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ @mytag @yourtag
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/allowed-tags/allowed-tags-default.feature b/gherkin-checks/src/test/resources/checks/allowed-tags/allowed-tags-default.feature
new file mode 100644
index 0000000..ed29374
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/allowed-tags/allowed-tags-default.feature
@@ -0,0 +1,16 @@
+# Noncompliant [[sc=1;ec=7]] {{Remove this tag that is not in the whitelist.}}
+@mytag
+Feature: My feature...
+
+ # Noncompliant [[sc=3;ec=23]] {{Remove this tag that is not in the whitelist.}}
+ @non-regression-test
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ @nrt @smoke
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/bom/utf16-be.feature b/gherkin-checks/src/test/resources/checks/bom/utf16-be.feature
new file mode 100644
index 0000000..01ee708
Binary files /dev/null and b/gherkin-checks/src/test/resources/checks/bom/utf16-be.feature differ
diff --git a/gherkin-checks/src/test/resources/checks/bom/utf16-le.feature b/gherkin-checks/src/test/resources/checks/bom/utf16-le.feature
new file mode 100644
index 0000000..a5054ff
Binary files /dev/null and b/gherkin-checks/src/test/resources/checks/bom/utf16-le.feature differ
diff --git a/gherkin-checks/src/test/resources/checks/bom/utf8-with-bom.feature b/gherkin-checks/src/test/resources/checks/bom/utf8-with-bom.feature
new file mode 100644
index 0000000..0f000c3
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/bom/utf8-with-bom.feature
@@ -0,0 +1,12 @@
+# Noncompliant [[sl=0]] {{Remove the Byte Order Mark (BOM).}}
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/bom/utf8.feature b/gherkin-checks/src/test/resources/checks/bom/utf8.feature
new file mode 100644
index 0000000..197f983
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/bom/utf8.feature
@@ -0,0 +1,11 @@
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/comment-convention.feature b/gherkin-checks/src/test/resources/checks/comment-convention.feature
new file mode 100644
index 0000000..21639b3
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/comment-convention.feature
@@ -0,0 +1,12 @@
+Feature: My feature...
+#My comments
+# My comments
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/comment-regular-expression.feature b/gherkin-checks/src/test/resources/checks/comment-regular-expression.feature
new file mode 100644
index 0000000..5f6051b
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/comment-regular-expression.feature
@@ -0,0 +1,13 @@
+Feature: My feature...
+ # WTF!
+ # wtf: abc
+ # Hello
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/custom-checks/forbidden-name-content.feature b/gherkin-checks/src/test/resources/checks/custom-checks/forbidden-name-content.feature
new file mode 100644
index 0000000..00bad92
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/custom-checks/forbidden-name-content.feature
@@ -0,0 +1,13 @@
+# Noncompliant [[sc=10;ec=27]] {{Remove this usage of "WTF".}}
+Feature: WTF My feature...
+
+ Scenario: Scenario #1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ # Noncompliant [[sc=13;ec=28]] {{Remove this usage of "WTF".}}
+ Scenario: Scenario WTF #2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/custom-checks/forbidden-tag.feature b/gherkin-checks/src/test/resources/checks/custom-checks/forbidden-tag.feature
new file mode 100644
index 0000000..d09bb99
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/custom-checks/forbidden-tag.feature
@@ -0,0 +1,14 @@
+@bar
+Feature: My feature...
+
+ @foo
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ @mytag
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/end-line-characters.feature b/gherkin-checks/src/test/resources/checks/end-line-characters.feature
new file mode 100644
index 0000000..197f983
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/end-line-characters.feature
@@ -0,0 +1,11 @@
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/file-name/FileNameKO.feature b/gherkin-checks/src/test/resources/checks/file-name/FileNameKO.feature
new file mode 100644
index 0000000..197f983
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/file-name/FileNameKO.feature
@@ -0,0 +1,11 @@
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/file-name/file-name-custom-ok.feature b/gherkin-checks/src/test/resources/checks/file-name/file-name-custom-ok.feature
new file mode 100644
index 0000000..197f983
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/file-name/file-name-custom-ok.feature
@@ -0,0 +1,11 @@
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/file-name/file-name-ok.feature b/gherkin-checks/src/test/resources/checks/file-name/file-name-ok.feature
new file mode 100644
index 0000000..197f983
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/file-name/file-name-ok.feature
@@ -0,0 +1,11 @@
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/file-name/file_name.kocustom.feature b/gherkin-checks/src/test/resources/checks/file-name/file_name.kocustom.feature
new file mode 100644
index 0000000..197f983
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/file-name/file_name.kocustom.feature
@@ -0,0 +1,11 @@
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/fixme.feature b/gherkin-checks/src/test/resources/checks/fixme.feature
new file mode 100644
index 0000000..16269e8
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/fixme.feature
@@ -0,0 +1,22 @@
+Feature: My feature...
+
+ # Noncompliant {{Take the required action to fix the issue indicated by this comment.}}
+ # FIXME: blabla
+
+ # Noncompliant {{Take the required action to fix the issue indicated by this comment.}}
+ #FIXME: blabla
+
+ # Noncompliant {{Take the required action to fix the issue indicated by this comment.}}
+ #[[FIXME]] blabla
+
+ # fixmeforthefirsttime
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/incomplete-examples-table.feature b/gherkin-checks/src/test/resources/checks/incomplete-examples-table.feature
new file mode 100644
index 0000000..d6571e3
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/incomplete-examples-table.feature
@@ -0,0 +1,25 @@
+Feature: My feature...
+
+ Scenario Outline: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+ # Noncompliant [[sc=5;ec=13]] {{Add a table to this "Examples" section.}}
+ Examples:
+
+ Scenario Outline: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
+ Examples:
+ # Noncompliant [[sc=7;ec=15]] {{Add data rows to this table.}}
+ | test |
+
+ Scenario Outline: Scenario 3
+ Given blabla...
+ When blabla...
+ Then blabla...
+ Examples:
+ # Noncompliant [[sc=7;ec=15]] {{Add data rows to this table or convert this "Scenario Outline" to a standard "Scenario".}}
+ | test |
+ | 1 |
diff --git a/gherkin-checks/src/test/resources/checks/indentation/indentation-custom-ko.feature b/gherkin-checks/src/test/resources/checks/indentation/indentation-custom-ko.feature
new file mode 100644
index 0000000..ab30d9e
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/indentation/indentation-custom-ko.feature
@@ -0,0 +1,60 @@
+ # Noncompliant {{Indent this line at column 0 (currently indented at column 1).}}
+ @tag
+ # Noncompliant {{Indent this line at column 0 (currently indented at column 1).}}
+ Feature: My feature...
+ blabla...
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 5).}}
+ blabla...
+
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 5).}}
+ Background: blabla...
+ blabla...
+ # Noncompliant {{Indent this line at column 8 (currently indented at column 7).}}
+ blabla...
+ # Noncompliant {{Indent this line at column 8 (currently indented at column 10).}}
+ Given blabla...
+
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 5).}}
+ @tag
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 3).}}
+ Scenario: Scenario 1
+ # Noncompliant {{Indent this line at column 8 (currently indented at column 9).}}
+ blabla...
+ blabla...
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ @tag
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 5).}}
+ @tag @tag
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 3).}}
+ Scenario Outline: Scenario 2
+ # Noncompliant {{Indent this line at column 8 (currently indented at column 7).}}
+ blabla...
+ blabla...
+ Given blabla...
+ When blabla...
+ | data |
+ # Noncompliant {{Indent this line at column 12 (currently indented at column 13).}}
+ | 2 |
+ Then blabla...
+ # Noncompliant {{Indent this line at column 12 (currently indented at column 13).}}
+ """string
+ blabla...
+ blabla...
+ """
+ # Noncompliant [[sl=-1]] {{Indent this line at column 12 (currently indented at column 11).}}
+
+ # Noncompliant {{Indent this line at column 8 (currently indented at column 9).}}
+ @tag @tag
+ # Noncompliant {{Indent this line at column 8 (currently indented at column 9).}}
+ Examples: blabla...
+ # Noncompliant {{Indent this line at column 12 (currently indented at column 13).}}
+ blabla...
+ blabla...
+ # Noncompliant {{Indent this line at column 12 (currently indented at column 11).}}
+ | data |
+ # Noncompliant {{Indent this line at column 12 (currently indented at column 13).}}
+ | 1 |
+ | 2 |
diff --git a/gherkin-checks/src/test/resources/checks/indentation/indentation-custom-ok.feature b/gherkin-checks/src/test/resources/checks/indentation/indentation-custom-ok.feature
new file mode 100644
index 0000000..b8a03a0
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/indentation/indentation-custom-ok.feature
@@ -0,0 +1,40 @@
+@tag
+@tag
+Feature: My feature...
+ blabla...
+ blabla...
+
+ Background: blabla...
+ blabla...
+ blabla...
+ Given blabla...
+
+ @tag
+ Scenario: Scenario 1
+ blabla...
+ blabla...
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ @tag @tag
+ Scenario Outline: Scenario 2
+ blabla...
+ blabla...
+ Given blabla...
+ When blabla...
+ | data |
+ | 2 |
+ Then blabla...
+ """string
+ blabla...
+ blabla...
+ """
+
+ @tag
+ @tag @tag
+ Examples: blabla...
+ blabla...
+ blabla...
+ | data |
+ | 1 |
diff --git a/gherkin-checks/src/test/resources/checks/indentation/indentation-default-ko.feature b/gherkin-checks/src/test/resources/checks/indentation/indentation-default-ko.feature
new file mode 100644
index 0000000..8dec47b
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/indentation/indentation-default-ko.feature
@@ -0,0 +1,61 @@
+# Noncompliant {{Indent this line at column 0 (currently indented at column 1).}}
+ @tag
+@tag
+ # Noncompliant {{Indent this line at column 0 (currently indented at column 2).}}
+ Feature: My feature...
+ # Noncompliant {{Indent this line at column 2 (currently indented at column 4).}}
+ blabla...
+ blabla...
+
+ # Noncompliant {{Indent this line at column 2 (currently indented at column 1).}}
+ Background: blabla...
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 2).}}
+ blabla...
+ blabla...
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 5).}}
+ Given blabla...
+
+ # Noncompliant {{Indent this line at column 2 (currently indented at column 4).}}
+ @tag
+ # Noncompliant {{Indent this line at column 2 (currently indented at column 3).}}
+ Scenario: Scenario 1
+ blabla...
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 7).}}
+ blabla...
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ @tag
+ # Noncompliant {{Indent this line at column 2 (currently indented at column 4).}}
+ @tag
+ # Noncompliant {{Indent this line at column 2 (currently indented at column 3).}}
+ Scenario Outline: Scenario 2
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 5).}}
+ blabla...
+ blabla...
+ Given blabla...
+ When blabla...
+ | data |
+ # Noncompliant {{Indent this line at column 6 (currently indented at column 7).}}
+ | 2 |
+ Then blabla...
+ # Noncompliant {{Indent this line at column 6 (currently indented at column 7).}}
+ """string
+ blabla...
+ blabla...
+ """
+ # Noncompliant [[sl=-1]] {{Indent this line at column 6 (currently indented at column 5).}}
+
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 5).}}
+ @tag
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 5).}}
+ Examples: blabla...
+ blabla...
+ # Noncompliant {{Indent this line at column 6 (currently indented at column 7).}}
+ blabla...
+ # Noncompliant {{Indent this line at column 6 (currently indented at column 5).}}
+ | data |
+ # Noncompliant {{Indent this line at column 6 (currently indented at column 7).}}
+ | 1 |
+ | 2 |
diff --git a/gherkin-checks/src/test/resources/checks/indentation/indentation-default-ok.feature b/gherkin-checks/src/test/resources/checks/indentation/indentation-default-ok.feature
new file mode 100644
index 0000000..49f0e71
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/indentation/indentation-default-ok.feature
@@ -0,0 +1,40 @@
+@tag
+@tag1
+Feature: My feature...
+ blabla...
+ blabla...
+
+ Background: blabla...
+ blabla...
+ blabla...
+ Given blabla...
+
+ @tag @tag1
+ Scenario: Scenario 1
+ blabla...
+ blabla...
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ @tag
+ Scenario Outline: Scenario 2
+ blabla...
+ blabla...
+ Given blabla...
+ When blabla...
+ | data |
+ | 2 |
+ Then blabla...
+ """string
+ blabla...
+ blabla...
+ """
+
+ @tag
+ @tag @tag
+ Examples: blabla...
+ blabla...
+ blabla...
+ | data |
+ | 1 |
diff --git a/gherkin-checks/src/test/resources/checks/max-number-scenarios/greater-than-custom-threshold.feature b/gherkin-checks/src/test/resources/checks/max-number-scenarios/greater-than-custom-threshold.feature
new file mode 100644
index 0000000..2fba9b0
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/max-number-scenarios/greater-than-custom-threshold.feature
@@ -0,0 +1,24 @@
+# Noncompliant [[sc=1;ec=8]] {{The number of scenarios (5) is greater that the maximum allowed (4). Split the scenarios into different features.}}
+Feature: My feature...
+
+ Background:
+ Given blabla...
+
+ Scenario: scenario #1
+ Given blabla...
+
+ Scenario Outline: scenario #2
+ Given blabla...
+ Examples:
+ | data |
+ | 1 |
+ | 2 |
+
+ Scenario: scenario #3
+ Given blabla...
+
+ Scenario: scenario #4
+ Given blabla...
+
+ Scenario: scenario #5
+ Given blabla...
diff --git a/gherkin-checks/src/test/resources/checks/max-number-scenarios/greater-than-default-threshold.feature b/gherkin-checks/src/test/resources/checks/max-number-scenarios/greater-than-default-threshold.feature
new file mode 100644
index 0000000..4756d3f
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/max-number-scenarios/greater-than-default-threshold.feature
@@ -0,0 +1,48 @@
+# Noncompliant [[sc=1;ec=8]] {{The number of scenarios (13) is greater that the maximum allowed (12). Split the scenarios into different features.}}
+Feature: My feature...
+
+ Background:
+ Given blabla...
+
+ Scenario: scenario #1
+ Given blabla...
+
+ Scenario Outline: scenario #2
+ Given blabla...
+ Examples:
+ | data |
+ | 1 |
+ | 2 |
+
+ Scenario: scenario #3
+ Given blabla...
+
+ Scenario: scenario #4
+ Given blabla...
+
+ Scenario: scenario #5
+ Given blabla...
+
+ Scenario: scenario #6
+ Given blabla...
+
+ Scenario: scenario #7
+ Given blabla...
+
+ Scenario: scenario #8
+ Given blabla...
+
+ Scenario: scenario #9
+ Given blabla...
+
+ Scenario: scenario #10
+ Given blabla...
+
+ Scenario: scenario #11
+ Given blabla...
+
+ Scenario: scenario #12
+ Given blabla...
+
+ Scenario: scenario #13
+ Given blabla...
diff --git a/gherkin-checks/src/test/resources/checks/max-number-scenarios/lower-than-custom-threshold.feature b/gherkin-checks/src/test/resources/checks/max-number-scenarios/lower-than-custom-threshold.feature
new file mode 100644
index 0000000..6ee15da
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/max-number-scenarios/lower-than-custom-threshold.feature
@@ -0,0 +1,20 @@
+Feature: My feature...
+
+ Background:
+ Given blabla...
+
+ Scenario: scenario #1
+ Given blabla...
+
+ Scenario Outline: scenario #2
+ Given blabla...
+ Examples:
+ | data |
+ | 1 |
+ | 2 |
+
+ Scenario: scenario #3
+ Given blabla...
+
+ Scenario: scenario #4
+ Given blabla...
diff --git a/gherkin-checks/src/test/resources/checks/max-number-scenarios/lower-than-default-threshold.feature b/gherkin-checks/src/test/resources/checks/max-number-scenarios/lower-than-default-threshold.feature
new file mode 100644
index 0000000..0203f79
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/max-number-scenarios/lower-than-default-threshold.feature
@@ -0,0 +1,44 @@
+Feature: My feature...
+
+ Background:
+ Given blabla...
+
+ Scenario: scenario #1
+ Given blabla...
+
+ Scenario Outline: scenario #2
+ Given blabla...
+ Examples:
+ | data |
+ | 1 |
+ | 2 |
+
+ Scenario: scenario #3
+ Given blabla...
+
+ Scenario: scenario #4
+ Given blabla...
+
+ Scenario: scenario #5
+ Given blabla...
+
+ Scenario: scenario #6
+ Given blabla...
+
+ Scenario: scenario #7
+ Given blabla...
+
+ Scenario: scenario #8
+ Given blabla...
+
+ Scenario: scenario #9
+ Given blabla...
+
+ Scenario: scenario #10
+ Given blabla...
+
+ Scenario: scenario #11
+ Given blabla...
+
+ Scenario: scenario #12
+ Given blabla...
diff --git a/gherkin-checks/src/test/resources/checks/max-number-steps/max-number-steps-background.feature b/gherkin-checks/src/test/resources/checks/max-number-steps/max-number-steps-background.feature
new file mode 100644
index 0000000..9f10bcb
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/max-number-steps/max-number-steps-background.feature
@@ -0,0 +1,28 @@
+Feature: My feature...
+
+ Background:
+ Given blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+
+ Scenario: scenario #1
+ Given blabla...
+ When blabla...
+ Then blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+
+ # Noncompliant [[sc=3;ec=11]] {{Reduce the number of steps (13, greater than 12 allowed)}}
+ Scenario: scenario #2
+ Given blabla...
+ When blabla...
+ Then blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
diff --git a/gherkin-checks/src/test/resources/checks/max-number-steps/max-number-steps-custom-threshold.feature b/gherkin-checks/src/test/resources/checks/max-number-steps/max-number-steps-custom-threshold.feature
new file mode 100644
index 0000000..7e8482b
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/max-number-steps/max-number-steps-custom-threshold.feature
@@ -0,0 +1,18 @@
+Feature: My feature...
+
+ Scenario: scenario #1
+ Given blabla...
+ When blabla...
+ Then blabla...
+ And blabla...
+ And blabla...
+
+ # Noncompliant [[sc=3;ec=11]] {{Reduce the number of steps (7, greater than 5 allowed)}}
+ Scenario: scenario #2
+ Given blabla...
+ When blabla...
+ Then blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
diff --git a/gherkin-checks/src/test/resources/checks/max-number-steps/max-number-steps-no-background.feature b/gherkin-checks/src/test/resources/checks/max-number-steps/max-number-steps-no-background.feature
new file mode 100644
index 0000000..778fdff
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/max-number-steps/max-number-steps-no-background.feature
@@ -0,0 +1,31 @@
+Feature: My feature...
+
+ Scenario: scenario #1
+ Given blabla...
+ When blabla...
+ Then blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+
+ # Noncompliant [[sc=3;ec=11]] {{Reduce the number of steps (13, greater than 12 allowed)}}
+ Scenario: scenario #2
+ Given blabla...
+ When blabla...
+ Then blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
diff --git a/gherkin-checks/src/test/resources/checks/new-line-end-of-file/new-line-end-of-file.feature b/gherkin-checks/src/test/resources/checks/new-line-end-of-file/new-line-end-of-file.feature
new file mode 100644
index 0000000..197f983
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/new-line-end-of-file/new-line-end-of-file.feature
@@ -0,0 +1,11 @@
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/new-line-end-of-file/no-new-line-end-of-file.feature b/gherkin-checks/src/test/resources/checks/new-line-end-of-file/no-new-line-end-of-file.feature
new file mode 100644
index 0000000..3db3cd6
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/new-line-end-of-file/no-new-line-end-of-file.feature
@@ -0,0 +1,11 @@
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
\ No newline at end of file
diff --git a/gherkin-checks/src/test/resources/checks/no-feature/feature.feature b/gherkin-checks/src/test/resources/checks/no-feature/feature.feature
new file mode 100644
index 0000000..18c0a2b
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/no-feature/feature.feature
@@ -0,0 +1,5 @@
+Feature: My feature...
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/no-feature/no-feature.feature b/gherkin-checks/src/test/resources/checks/no-feature/no-feature.feature
new file mode 100644
index 0000000..da05b93
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/no-feature/no-feature.feature
@@ -0,0 +1 @@
+# Noncompliant [[sl=0]] {{Remove this file that does not define any feature.}}
diff --git a/gherkin-checks/src/test/resources/checks/no-scenario/no-scenario.feature b/gherkin-checks/src/test/resources/checks/no-scenario/no-scenario.feature
new file mode 100644
index 0000000..c76fabe
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/no-scenario/no-scenario.feature
@@ -0,0 +1,2 @@
+# Noncompliant [[sc=1;ec=8]] {{Remove this feature that does not define any scenario.}}
+Feature: My feature...
diff --git a/gherkin-checks/src/test/resources/checks/no-scenario/scenario-outline.feature b/gherkin-checks/src/test/resources/checks/no-scenario/scenario-outline.feature
new file mode 100644
index 0000000..0f8f8e9
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/no-scenario/scenario-outline.feature
@@ -0,0 +1,9 @@
+Feature: My feature...
+ Scenario Outline: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+ Examples:
+ | data |
+ | 1 |
+ | 2 |
diff --git a/gherkin-checks/src/test/resources/checks/no-scenario/scenario.feature b/gherkin-checks/src/test/resources/checks/no-scenario/scenario.feature
new file mode 100644
index 0000000..18c0a2b
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/no-scenario/scenario.feature
@@ -0,0 +1,5 @@
+Feature: My feature...
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/star-step-prefix.feature b/gherkin-checks/src/test/resources/checks/star-step-prefix.feature
new file mode 100644
index 0000000..a910b62
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/star-step-prefix.feature
@@ -0,0 +1,12 @@
+Feature: My feature...
+
+ Scenario: My scenario 1
+ # Noncompliant [[sc=5;ec=6]] {{Replace this star prefix with Given/When/Then.}}
+ * blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/tab-character/no-tab-character.feature b/gherkin-checks/src/test/resources/checks/tab-character/no-tab-character.feature
new file mode 100644
index 0000000..197f983
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/tab-character/no-tab-character.feature
@@ -0,0 +1,11 @@
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/tab-character/tab-character.feature b/gherkin-checks/src/test/resources/checks/tab-character/tab-character.feature
new file mode 100644
index 0000000..87aaa1e
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/tab-character/tab-character.feature
@@ -0,0 +1,11 @@
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/tag-name/tag-name-custom-ko.feature b/gherkin-checks/src/test/resources/checks/tag-name/tag-name-custom-ko.feature
new file mode 100644
index 0000000..92ccf8e
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/tag-name/tag-name-custom-ko.feature
@@ -0,0 +1,14 @@
+# Noncompliant [[sc=1;ec=7]] {{Rename this tag to match the regular expression: ^[a-z]+$}}
+@myTag
+Feature: My feature...
+
+ @tag
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/tag-name/tag-name-custom-ok.feature b/gherkin-checks/src/test/resources/checks/tag-name/tag-name-custom-ok.feature
new file mode 100644
index 0000000..1e38448
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/tag-name/tag-name-custom-ok.feature
@@ -0,0 +1,13 @@
+@my-tag
+Feature: My feature...
+
+ @tag
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/tag-name/tag-name-ko.feature b/gherkin-checks/src/test/resources/checks/tag-name/tag-name-ko.feature
new file mode 100644
index 0000000..3dd4b88
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/tag-name/tag-name-ko.feature
@@ -0,0 +1,15 @@
+# Noncompliant [[sc=1;ec=7]] {{Rename this tag to match the regular expression: ^[a-z][-a-z0-9]*$}}
+@myTag @my-tag
+Feature: My feature...
+
+ # Noncompliant [[sc=3;ec=10]] {{Rename this tag to match the regular expression: ^[a-z][-a-z0-9]*$}}
+ @myTag0
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/tag-name/tag-name-ok.feature b/gherkin-checks/src/test/resources/checks/tag-name/tag-name-ok.feature
new file mode 100644
index 0000000..f12915c
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/tag-name/tag-name-ok.feature
@@ -0,0 +1,13 @@
+@mytag @my-tag
+Feature: My feature...
+
+ @my-tag1
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/todo.feature b/gherkin-checks/src/test/resources/checks/todo.feature
new file mode 100644
index 0000000..123d63e
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/todo.feature
@@ -0,0 +1,22 @@
+Feature: My feature...
+
+ # Noncompliant
+ # TODO: blabla
+
+ # Noncompliant
+ #TODO: blabla
+
+ # Noncompliant
+ #[[TODO]] blabla
+
+ # todoforthefirsttime
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-checks/src/test/resources/checks/trailing-whitespace.feature b/gherkin-checks/src/test/resources/checks/trailing-whitespace.feature
new file mode 100644
index 0000000..01f73ed
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/trailing-whitespace.feature
@@ -0,0 +1,14 @@
+# Noncompliant [[sl=3]] {{Remove the useless trailing whitespaces at the end of this line.}}
+# Noncompliant [[sl=6]] {{Remove the useless trailing whitespaces at the end of this line.}}
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
+
diff --git a/gherkin-frontend/pom.xml b/gherkin-frontend/pom.xml
new file mode 100644
index 0000000..e96222e
--- /dev/null
+++ b/gherkin-frontend/pom.xml
@@ -0,0 +1,71 @@
+
+
+ 4.0.0
+
+
+ com.racodond.sonarqube.plugin.gherkin
+ gherkin
+ 1.0-SNAPSHOT
+
+
+ gherkin-frontend
+ SonarQube Gherkin Analyzer :: Frontend
+
+
+
+ org.sonarsource.sonarqube
+ sonar-plugin-api
+ provided
+
+
+ commons-lang
+ commons-lang
+
+
+ org.sonarsource.sslr
+ sslr-core
+
+
+ org.sonarsource.sslr-squid-bridge
+ sslr-squid-bridge
+
+
+ com.google.guava
+ guava
+
+
+ junit
+ junit
+ test
+
+
+ org.sonarsource.sslr
+ sslr-testing-harness
+ test
+
+
+ org.mockito
+ mockito-all
+ test
+
+
+ org.easytesting
+ fest-assert
+ test
+
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+ ${sonar.jacoco.reportPath}
+
+
+
+
+
+
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinGrammar.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinGrammar.java
new file mode 100644
index 0000000..13cc49d
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinGrammar.java
@@ -0,0 +1,181 @@
+/*
+ * 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.parser;
+
+import com.sonar.sslr.api.typed.GrammarBuilder;
+import org.sonar.gherkin.tree.impl.InternalSyntaxToken;
+import org.sonar.plugins.gherkin.api.tree.*;
+
+public class GherkinGrammar {
+
+ private final GrammarBuilder b;
+ private final TreeFactory f;
+
+ public GherkinGrammar(GrammarBuilder b, TreeFactory f) {
+ this.b = b;
+ this.f = f;
+ }
+
+ public GherkinDocumentTree GHERKIN_DOCUMENT() {
+ return b.nonterminal(GherkinLexicalGrammar.GHERKIN_DOCUMENT).is(
+ f.gherkinDocument(
+ b.optional(b.token(GherkinLexicalGrammar.BOM)),
+ b.optional(FEATURE()),
+ b.token(GherkinLexicalGrammar.EOF)));
+ }
+
+ public FeatureTree FEATURE() {
+ return b.nonterminal(GherkinLexicalGrammar.FEATURE).is(
+ f.feature(
+ FEATURE_DECLARATION(),
+ b.optional(BACKGROUND()),
+ b.zeroOrMore(
+ b.firstOf(
+ SCENARIO_OUTLINE(),
+ SCENARIO()))));
+ }
+
+ public FeatureDeclarationTree FEATURE_DECLARATION() {
+ return b.nonterminal(GherkinLexicalGrammar.FEATURE_DECLARATION).is(
+ f.featureDeclaration(
+ b.zeroOrMore(TAG()),
+ FEATURE_PREFIX(),
+ b.token(GherkinLexicalGrammar.COLON),
+ NAME(),
+ b.optional(DESCRIPTION())));
+ }
+
+ public BackgroundTree BACKGROUND() {
+ return b.nonterminal(GherkinLexicalGrammar.BACKGROUND).is(
+ f.background(
+ BACKGROUND_PREFIX(),
+ b.token(GherkinLexicalGrammar.COLON),
+ b.optional(DESCRIPTION()),
+ b.zeroOrMore(STEP())));
+ }
+
+ public ScenarioTree SCENARIO() {
+ return b.nonterminal(GherkinLexicalGrammar.SCENARIO).is(
+ f.scenario(
+ b.zeroOrMore(TAG()),
+ SCENARIO_PREFIX(),
+ b.token(GherkinLexicalGrammar.COLON),
+ NAME(),
+ b.optional(DESCRIPTION()),
+ b.zeroOrMore(STEP())));
+ }
+
+ public ScenarioOutlineTree SCENARIO_OUTLINE() {
+ return b.nonterminal(GherkinLexicalGrammar.SCENARIO_OUTLINE).is(
+ f.scenarioOutline(
+ b.zeroOrMore(TAG()),
+ SCENARIO_OUTLINE_PREFIX(),
+ b.token(GherkinLexicalGrammar.COLON),
+ NAME(),
+ b.optional(DESCRIPTION()),
+ b.zeroOrMore(STEP()),
+ EXAMPLES()));
+ }
+
+ public ExamplesTree EXAMPLES() {
+ return b.nonterminal(GherkinLexicalGrammar.EXAMPLES).is(
+ f.examples(
+ b.zeroOrMore(TAG()),
+ EXAMPLES_PREFIX(),
+ b.token(GherkinLexicalGrammar.COLON),
+ b.optional(DESCRIPTION()),
+ b.optional(TABLE())));
+ }
+
+ public StepTree STEP() {
+ return b.nonterminal(GherkinLexicalGrammar.STEP).is(
+ f.step(
+ STEP_PREFIX(),
+ b.token(GherkinLexicalGrammar.STEP_SENTENCE),
+ b.optional(
+ b.firstOf(
+ TABLE(),
+ DOC_STRING()))));
+ }
+
+ public TagTree TAG() {
+ return b.nonterminal(GherkinLexicalGrammar.TAG).is(
+ f.tag(
+ b.token(GherkinLexicalGrammar.TAG_PREFIX),
+ b.token(GherkinLexicalGrammar.TAG_LITERAL)));
+ }
+
+ public DescriptionTree DESCRIPTION() {
+ return b.nonterminal(GherkinLexicalGrammar.DESCRIPTION).is(
+ f.description(
+ b.oneOrMore(b.token(GherkinLexicalGrammar.DESCRIPTION_SENTENCE))));
+ }
+
+ public FeaturePrefixTree FEATURE_PREFIX() {
+ return b.nonterminal(GherkinLexicalGrammar.FEATURE_PREFIX).is(
+ f.featurePrefix(b.token(GherkinLexicalGrammar.FEATURE_KEYWORD)));
+ }
+
+ public BackgroundPrefixTree BACKGROUND_PREFIX() {
+ return b.nonterminal(GherkinLexicalGrammar.BACKGROUND_PREFIX).is(
+ f.backgroundPrefix(b.token(GherkinLexicalGrammar.BACKGROUND_KEYWORD)));
+ }
+
+ public ScenarioPrefixTree SCENARIO_PREFIX() {
+ return b.nonterminal(GherkinLexicalGrammar.SCENARIO_PREFIX).is(
+ f.scenarioPrefix(b.token(GherkinLexicalGrammar.SCENARIO_KEYWORD)));
+ }
+
+ public ScenarioOutlinePrefixTree SCENARIO_OUTLINE_PREFIX() {
+ return b.nonterminal(GherkinLexicalGrammar.SCENARIO_OUTLINE_PREFIX).is(
+ f.scenarioOutlinePrefix(b.token(GherkinLexicalGrammar.SCENARIO_OUTLINE_KEYWORD)));
+ }
+
+ public ExamplesPrefixTree EXAMPLES_PREFIX() {
+ return b.nonterminal(GherkinLexicalGrammar.EXAMPLES_PREFIX).is(
+ f.examplesPrefix(b.token(GherkinLexicalGrammar.EXAMPLES_KEYWORD)));
+ }
+
+ public StepPrefixTree STEP_PREFIX() {
+ return b.nonterminal(GherkinLexicalGrammar.STEP_PREFIX).is(
+ f.stepPrefix(b.token(GherkinLexicalGrammar.STEP_KEYWORD)));
+ }
+
+ public NameTree NAME() {
+ return b.nonterminal(GherkinLexicalGrammar.NAME).is(
+ f.name(b.token(GherkinLexicalGrammar.NAME_LITERAL)));
+ }
+
+ public DocStringTree DOC_STRING() {
+ return b.nonterminal(GherkinLexicalGrammar.DOC_STRING).is(
+ f.docString(
+ b.token(GherkinLexicalGrammar.DOC_STRING_PREFIX),
+ b.optional(b.token(GherkinLexicalGrammar.DOC_STRING_CONTENT_TYPE)),
+ b.zeroOrMore(b.token(GherkinLexicalGrammar.DOC_STRING_DATA_ROW)),
+ b.token(GherkinLexicalGrammar.DOC_STRING_SUFFIX)));
+ }
+
+ public TableTree TABLE() {
+ return b.nonterminal(GherkinLexicalGrammar.TABLE).is(
+ f.table(
+ b.oneOrMore(b.token(GherkinLexicalGrammar.TABLE_DATA_ROW))));
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinLexicalGrammar.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinLexicalGrammar.java
new file mode 100644
index 0000000..2cee4da
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinLexicalGrammar.java
@@ -0,0 +1,130 @@
+/*
+ * 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.parser;
+
+import com.sonar.sslr.api.GenericTokenType;
+import org.sonar.sslr.grammar.GrammarRuleKey;
+import org.sonar.sslr.grammar.LexerlessGrammarBuilder;
+
+public enum GherkinLexicalGrammar implements GrammarRuleKey {
+ GHERKIN_DOCUMENT,
+
+ FEATURE,
+ FEATURE_DECLARATION,
+ FEATURE_PREFIX,
+ FEATURE_KEYWORD,
+
+ BACKGROUND,
+ BACKGROUND_PREFIX,
+ BACKGROUND_KEYWORD,
+
+ SCENARIO,
+ SCENARIO_PREFIX,
+ SCENARIO_KEYWORD,
+
+ SCENARIO_OUTLINE,
+ SCENARIO_OUTLINE_PREFIX,
+ SCENARIO_OUTLINE_KEYWORD,
+
+ EXAMPLES,
+ EXAMPLES_PREFIX,
+ EXAMPLES_KEYWORD,
+
+ STEP,
+ STEP_PREFIX,
+ STEP_KEYWORD,
+ STEP_SENTENCE,
+
+ COLON,
+
+ NAME,
+ NAME_LITERAL,
+
+ DESCRIPTION,
+ DESCRIPTION_SENTENCE,
+
+ TAG,
+ TAG_PREFIX,
+ TAG_LITERAL,
+
+ DOC_STRING,
+ DOC_STRING_CONTENT_TYPE,
+ DOC_STRING_PREFIX,
+ DOC_STRING_SUFFIX,
+ DOC_STRING_DATA_ROW,
+
+ TABLE,
+ TABLE_DATA_ROW,
+
+ SPACING,
+ SPACING_NO_COMMENTS,
+
+ BOM,
+ EOF;
+
+ public static LexerlessGrammarBuilder createGrammar() {
+ LexerlessGrammarBuilder b = LexerlessGrammarBuilder.create();
+ syntax(b);
+ b.setRootRule(GHERKIN_DOCUMENT);
+ return b;
+ }
+
+ private static void syntax(LexerlessGrammarBuilder b) {
+
+ String whitespaceRegex = "(? children, int startIndex, int endIndex) {
+ for (Object child : children) {
+ if (child instanceof InternalSyntaxToken) {
+ return child;
+ }
+ }
+ return new InternalSyntaxSpacing(startIndex, endIndex);
+ }
+
+ @Override
+ public Object createTerminal(Input input, int startIndex, int endIndex, List trivias, TokenType type) {
+ char[] fileChars = input.input();
+ boolean hasByteOrderMark = fileChars.length > 0 && fileChars[0] == BYTE_ORDER_MARK;
+ boolean isEof = GenericTokenType.EOF.equals(type);
+ LineColumnValue lineColumnValue = tokenPosition(input, startIndex, endIndex);
+ return new InternalSyntaxToken(
+ lineColumnValue.line,
+ column(hasByteOrderMark, lineColumnValue.line, lineColumnValue.column),
+ lineColumnValue.value,
+ createTrivias(trivias, hasByteOrderMark),
+ isEof,
+ isByteOrderMark(input, startIndex, endIndex));
+ }
+
+ private static List createTrivias(List trivias, boolean hasByteOrderMark) {
+ List result = new ArrayList<>();
+ for (Trivia trivia : trivias) {
+ Token trivialToken = trivia.getToken();
+ int column = column(hasByteOrderMark, trivialToken.getLine(), trivialToken.getColumn());
+ result.add(InternalSyntaxTrivia.create(trivialToken.getValue(), trivialToken.getLine(), column));
+ }
+ return result;
+ }
+
+ private static int column(boolean hasByteOrderMark, int line, int column) {
+ if (hasByteOrderMark && line == 1) {
+ return column - 1;
+ }
+ return column;
+ }
+
+ private static LineColumnValue tokenPosition(Input input, int startIndex, int endIndex) {
+ int[] lineAndColumn = input.lineAndColumnAt(startIndex);
+ String value = input.substring(startIndex, endIndex);
+ return new LineColumnValue(lineAndColumn[0], lineAndColumn[1] - 1, value);
+ }
+
+ private static boolean isByteOrderMark(Input input, int startIndex, int endIndex) {
+ return (Character.toString(BYTE_ORDER_MARK)).equals(input.substring(startIndex, endIndex));
+ }
+
+ private static class LineColumnValue {
+ final int line;
+ final int column;
+ final String value;
+
+ private LineColumnValue(int line, int column, String value) {
+ this.line = line;
+ this.column = column;
+ this.value = value;
+ }
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinParser.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinParser.java
new file mode 100644
index 0000000..3af3750
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinParser.java
@@ -0,0 +1,56 @@
+/*
+ * 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.parser;
+
+import com.google.common.collect.Lists;
+import com.sonar.sslr.api.typed.ActionParser;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.sslr.grammar.GrammarRuleKey;
+import org.sonar.sslr.grammar.LexerlessGrammarBuilder;
+
+import java.io.File;
+import java.nio.charset.Charset;
+
+public class GherkinParser extends ActionParser {
+
+ public GherkinParser(Charset charset, LexerlessGrammarBuilder grammarBuilder, Class grammarClass,
+ TreeFactory treeFactory, GherkinNodeBuilder nodeBuilder, GrammarRuleKey rootRule) {
+ super(charset, grammarBuilder, grammarClass, treeFactory, nodeBuilder, rootRule);
+ }
+
+ @Override
+ public Tree parse(File file) {
+ return createParentLink(super.parse(file));
+ }
+
+ private static Tree createParentLink(Tree parent) {
+ if (!parent.isLeaf()) {
+ Lists.newArrayList(parent.childrenIterator())
+ .stream()
+ .filter(nextTree -> nextTree != null)
+ .forEach(nextTree -> {
+ nextTree.setParent(parent);
+ createParentLink(nextTree);
+ });
+ }
+ return parent;
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinParserBuilder.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinParserBuilder.java
new file mode 100644
index 0000000..5a3eff9
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinParserBuilder.java
@@ -0,0 +1,51 @@
+/*
+ * 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.parser;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.sonar.sslr.grammar.GrammarRuleKey;
+
+import java.nio.charset.Charset;
+
+public class GherkinParserBuilder {
+
+ private GherkinParserBuilder() {
+ }
+
+ public static GherkinParser createParser(Charset charset) {
+ return createParser(charset, GherkinLexicalGrammar.GHERKIN_DOCUMENT);
+ }
+
+ @VisibleForTesting
+ public static GherkinParser createTestParser(Charset charset, GrammarRuleKey rootRule) {
+ return createParser(charset, rootRule);
+ }
+
+ private static GherkinParser createParser(Charset charset, GrammarRuleKey rootRule) {
+ return new GherkinParser(
+ charset,
+ GherkinLexicalGrammar.createGrammar(),
+ GherkinGrammar.class,
+ new TreeFactory(),
+ new GherkinNodeBuilder(),
+ rootRule);
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/TreeFactory.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/TreeFactory.java
new file mode 100644
index 0000000..2fa677a
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/TreeFactory.java
@@ -0,0 +1,106 @@
+/*
+ * 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.parser;
+
+import com.sonar.sslr.api.typed.Optional;
+import org.sonar.gherkin.tree.impl.*;
+import org.sonar.plugins.gherkin.api.tree.*;
+
+import java.util.List;
+
+public class TreeFactory {
+
+ public GherkinDocumentTree gherkinDocument(Optional byteOrderMark, Optional feature, SyntaxToken eof) {
+ return new GherkinDocumentTreeImpl(byteOrderMark.orNull(), feature.orNull(), eof);
+ }
+
+ public FeatureTree feature(FeatureDeclarationTree featureDeclaration, Optional background, Optional> allScenarios) {
+ return new FeatureTreeImpl(featureDeclaration, background.orNull(), allScenarios.orNull());
+ }
+
+ public FeatureDeclarationTree featureDeclaration(Optional> tags, PrefixTree prefix, SyntaxToken colon, NameTree name, Optional description) {
+ return new FeatureDeclarationTreeImpl(tags.orNull(), prefix, colon, name, description.orNull());
+ }
+
+ public BackgroundTree background(PrefixTree prefix, SyntaxToken colon, Optional description, Optional> steps) {
+ return new BackgroundTreeImpl(prefix, colon, description.orNull(), steps.orNull());
+ }
+
+ public ScenarioTree scenario(Optional> tags, PrefixTree prefix, SyntaxToken colon, NameTree name, Optional description, Optional> steps) {
+ return new ScenarioTreeImpl(tags.orNull(), prefix, colon, name, description.orNull(), steps.orNull());
+ }
+
+ public ScenarioOutlineTree scenarioOutline(Optional> tags, PrefixTree prefix, SyntaxToken colon, NameTree name, Optional description, Optional> steps, ExamplesTree examples) {
+ return new ScenarioOutlineTreeImpl(tags.orNull(), prefix, colon, name, description.orNull(), steps.orNull(), examples);
+ }
+
+ public ExamplesTree examples(Optional> tags, PrefixTree prefix, SyntaxToken colon, Optional description, Optional table) {
+ return new ExamplesTreeImpl(tags.orNull(), prefix, colon, description.orNull(), table.orNull());
+ }
+
+ public StepTree step(PrefixTree prefix, SyntaxToken sentence, Optional data) {
+ return new StepTreeImpl(prefix, sentence, data.orNull());
+ }
+
+ public TagTree tag(SyntaxToken prefix, SyntaxToken value) {
+ return new TagTreeImpl(prefix, value);
+ }
+
+ public DescriptionTree description(List descriptionLines) {
+ return new DescriptionTreeImpl(descriptionLines);
+ }
+
+ public FeaturePrefixTree featurePrefix(SyntaxToken keyword) {
+ return new FeaturePrefixTreeImpl(keyword);
+ }
+
+ public BackgroundPrefixTree backgroundPrefix(SyntaxToken keyword) {
+ return new BackgroundPrefixTreeImpl(keyword);
+ }
+
+ public ScenarioPrefixTree scenarioPrefix(SyntaxToken keyword) {
+ return new ScenarioPrefixTreeImpl(keyword);
+ }
+
+ public ScenarioOutlinePrefixTree scenarioOutlinePrefix(SyntaxToken keyword) {
+ return new ScenarioOutlinePrefixTreeImpl(keyword);
+ }
+
+ public ExamplesPrefixTree examplesPrefix(SyntaxToken keyword) {
+ return new ExamplesPrefixTreeImpl(keyword);
+ }
+
+ public StepPrefixTree stepPrefix(SyntaxToken keyword) {
+ return new StepPrefixTreeImpl(keyword);
+ }
+
+ public NameTree name(SyntaxToken name) {
+ return new NameTreeImpl(name);
+ }
+
+ public DocStringTree docString(SyntaxToken prefix, Optional contentType, Optional> data, SyntaxToken suffix) {
+ return new DocStringTreeImpl(prefix, contentType.orNull(), data.orNull(), suffix);
+ }
+
+ public TableTree table(List rows) {
+ return new TableTreeImpl(rows);
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/package-info.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/package-info.java
new file mode 100644
index 0000000..be58033
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.gherkin.parser;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/AbstractPrefixTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/AbstractPrefixTreeImpl.java
new file mode 100644
index 0000000..fa4d83c
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/AbstractPrefixTreeImpl.java
@@ -0,0 +1,52 @@
+/*
+ * 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.tree.impl;
+
+import com.google.common.collect.Iterators;
+import org.sonar.plugins.gherkin.api.tree.PrefixTree;
+import org.sonar.plugins.gherkin.api.tree.SyntaxToken;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+
+import java.util.Iterator;
+
+public abstract class AbstractPrefixTreeImpl extends GherkinTree implements PrefixTree {
+
+ private final SyntaxToken keyword;
+
+ public AbstractPrefixTreeImpl(SyntaxToken keyword) {
+ this.keyword = keyword;
+ }
+
+ @Override
+ public Iterator childrenIterator() {
+ return Iterators.singletonIterator(keyword);
+ }
+
+ @Override
+ public SyntaxToken keyword() {
+ return keyword;
+ }
+
+ @Override
+ public String text() {
+ return keyword.text();
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/BackgroundPrefixTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/BackgroundPrefixTreeImpl.java
new file mode 100644
index 0000000..67fbd9b
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/BackgroundPrefixTreeImpl.java
@@ -0,0 +1,42 @@
+/*
+ * 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.tree.impl;
+
+import org.sonar.plugins.gherkin.api.tree.BackgroundPrefixTree;
+import org.sonar.plugins.gherkin.api.tree.SyntaxToken;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+public class BackgroundPrefixTreeImpl extends AbstractPrefixTreeImpl implements BackgroundPrefixTree {
+
+ public BackgroundPrefixTreeImpl(SyntaxToken keyword) {
+ super(keyword);
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.BACKGROUND_PREFIX;
+ }
+
+ @Override
+ public void accept(DoubleDispatchVisitor visitor) {
+ visitor.visitBackgroundPrefix(this);
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/BackgroundTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/BackgroundTreeImpl.java
new file mode 100644
index 0000000..64b223a
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/BackgroundTreeImpl.java
@@ -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.tree.impl;
+
+import com.google.common.collect.Iterators;
+import org.sonar.plugins.gherkin.api.tree.*;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class BackgroundTreeImpl extends GherkinTree implements BackgroundTree {
+
+ private final PrefixTree prefix;
+ private final SyntaxToken colon;
+ private final DescriptionTree description;
+ private final List steps;
+
+ public BackgroundTreeImpl(PrefixTree prefix, SyntaxToken colon, @Nullable DescriptionTree description, @Nullable List steps) {
+ this.prefix = prefix;
+ this.colon = colon;
+ this.description = description;
+
+ if (steps != null) {
+ this.steps = steps;
+ } else {
+ this.steps = new ArrayList<>();
+ }
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.BACKGROUND;
+ }
+
+ @Override
+ public Iterator childrenIterator() {
+ return Iterators.concat(
+ Iterators.forArray(prefix, colon, description),
+ steps.iterator());
+ }
+
+ @Override
+ public PrefixTree prefix() {
+ return prefix;
+ }
+
+ @Override
+ public SyntaxToken colon() {
+ return colon;
+ }
+
+ @Override
+ @Nullable
+ public DescriptionTree description() {
+ return description;
+ }
+
+ @Override
+ public List steps() {
+ return steps;
+ }
+
+ @Override
+ public void accept(DoubleDispatchVisitor visitor) {
+ visitor.visitBackground(this);
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/DescriptionTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/DescriptionTreeImpl.java
new file mode 100644
index 0000000..4ca8619
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/DescriptionTreeImpl.java
@@ -0,0 +1,59 @@
+/*
+ * 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.tree.impl;
+
+import org.sonar.plugins.gherkin.api.tree.DescriptionTree;
+import org.sonar.plugins.gherkin.api.tree.SyntaxToken;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class DescriptionTreeImpl extends GherkinTree implements DescriptionTree {
+
+ private final List desriptionLines;
+
+ public DescriptionTreeImpl(List descriptionLines) {
+ this.desriptionLines = descriptionLines;
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.DESCRIPTION;
+ }
+
+ @Override
+ public Iterator childrenIterator() {
+ return desriptionLines.stream().map(t -> (Tree) t).collect(Collectors.toList()).iterator();
+ }
+
+ @Override
+ public List descriptionLines() {
+ return desriptionLines;
+ }
+
+ @Override
+ public void accept(DoubleDispatchVisitor visitor) {
+ visitor.visitDescription(this);
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/DocStringTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/DocStringTreeImpl.java
new file mode 100644
index 0000000..a23d7ee
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/DocStringTreeImpl.java
@@ -0,0 +1,92 @@
+/*
+ * 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.tree.impl;
+
+import com.google.common.collect.Iterators;
+import org.sonar.plugins.gherkin.api.tree.DocStringTree;
+import org.sonar.plugins.gherkin.api.tree.SyntaxToken;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class DocStringTreeImpl extends GherkinTree implements DocStringTree {
+
+ private final SyntaxToken prefix;
+ private final SyntaxToken suffix;
+ private final SyntaxToken contentType;
+ private final List data;
+
+ public DocStringTreeImpl(SyntaxToken prefix, @Nullable SyntaxToken contentType, @Nullable List data, SyntaxToken suffix) {
+ this.prefix = prefix;
+ this.contentType = contentType;
+
+ if (data != null) {
+ this.data = data;
+ } else {
+ this.data = new ArrayList<>();
+ }
+
+ this.suffix = suffix;
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.DOC_STRING;
+ }
+
+ @Override
+ public Iterator childrenIterator() {
+ return Iterators.concat(
+ Iterators.forArray(prefix, contentType),
+ data.iterator(),
+ Iterators.singletonIterator(suffix));
+ }
+
+ @Override
+ public List data() {
+ return data;
+ }
+
+ @Override
+ public SyntaxToken prefix() {
+ return prefix;
+ }
+
+ @Override
+ public SyntaxToken suffix() {
+ return suffix;
+ }
+
+ @Override
+ @Nullable
+ public SyntaxToken contentType() {
+ return contentType;
+ }
+
+ @Override
+ public void accept(DoubleDispatchVisitor visitor) {
+ visitor.visitDocString(this);
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/ExamplesPrefixTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/ExamplesPrefixTreeImpl.java
new file mode 100644
index 0000000..f492724
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/ExamplesPrefixTreeImpl.java
@@ -0,0 +1,42 @@
+/*
+ * 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.tree.impl;
+
+import org.sonar.plugins.gherkin.api.tree.ExamplesPrefixTree;
+import org.sonar.plugins.gherkin.api.tree.SyntaxToken;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+public class ExamplesPrefixTreeImpl extends AbstractPrefixTreeImpl implements ExamplesPrefixTree {
+
+ public ExamplesPrefixTreeImpl(SyntaxToken keyword) {
+ super(keyword);
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.EXAMPLES_PREFIX;
+ }
+
+ @Override
+ public void accept(DoubleDispatchVisitor visitor) {
+ visitor.visitExamplesPrefix(this);
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/ExamplesTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/ExamplesTreeImpl.java
new file mode 100644
index 0000000..7d7204f
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/ExamplesTreeImpl.java
@@ -0,0 +1,96 @@
+/*
+ * 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.tree.impl;
+
+import com.google.common.collect.Iterators;
+import org.sonar.plugins.gherkin.api.tree.*;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class ExamplesTreeImpl extends GherkinTree implements ExamplesTree {
+
+ private final List tags;
+ private final PrefixTree prefix;
+ private final SyntaxToken colon;
+ private final DescriptionTree description;
+ private final TableTree table;
+
+ public ExamplesTreeImpl(@Nullable List tags, PrefixTree prefix, SyntaxToken colon, @Nullable DescriptionTree description, @Nullable TableTree table) {
+ if (tags != null) {
+ this.tags = tags;
+ } else {
+ this.tags = new ArrayList<>();
+ }
+
+ this.prefix = prefix;
+ this.colon = colon;
+ this.description = description;
+ this.table = table;
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.EXAMPLES;
+ }
+
+ @Override
+ public Iterator childrenIterator() {
+ return Iterators.concat(
+ tags.iterator(),
+ Iterators.forArray(prefix, colon, description, table));
+ }
+
+ @Override
+ public List tags() {
+ return tags;
+ }
+
+ @Override
+ public PrefixTree prefix() {
+ return prefix;
+ }
+
+ @Override
+ public SyntaxToken colon() {
+ return colon;
+ }
+
+ @Override
+ @Nullable
+ public DescriptionTree description() {
+ return description;
+ }
+
+ @Override
+ @Nullable
+ public TableTree table() {
+ return table;
+ }
+
+ @Override
+ public void accept(DoubleDispatchVisitor visitor) {
+ visitor.visitExamples(this);
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/FeatureDeclarationTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/FeatureDeclarationTreeImpl.java
new file mode 100644
index 0000000..4e7fae4
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/FeatureDeclarationTreeImpl.java
@@ -0,0 +1,95 @@
+/*
+ * 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.tree.impl;
+
+import com.google.common.collect.Iterators;
+import org.sonar.plugins.gherkin.api.tree.*;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class FeatureDeclarationTreeImpl extends GherkinTree implements FeatureDeclarationTree {
+
+ private final List tags;
+ private final PrefixTree prefix;
+ private final SyntaxToken colon;
+ private final NameTree name;
+ private final DescriptionTree description;
+
+ public FeatureDeclarationTreeImpl(@Nullable List tags, PrefixTree prefix, SyntaxToken colon, NameTree name, @Nullable DescriptionTree description) {
+ if (tags != null) {
+ this.tags = tags;
+ } else {
+ this.tags = new ArrayList<>();
+ }
+
+ this.prefix = prefix;
+ this.colon = colon;
+ this.name = name;
+ this.description = description;
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.FEATURE_DECLARATION;
+ }
+
+ @Override
+ public Iterator childrenIterator() {
+ return Iterators.concat(
+ tags.iterator(),
+ Iterators.forArray(prefix, colon, name, description));
+ }
+
+ @Override
+ public List tags() {
+ return tags;
+ }
+
+ @Override
+ public PrefixTree prefix() {
+ return prefix;
+ }
+
+ @Override
+ public SyntaxToken colon() {
+ return colon;
+ }
+
+ @Override
+ public NameTree name() {
+ return name;
+ }
+
+ @Override
+ @Nullable
+ public DescriptionTree description() {
+ return description;
+ }
+
+ @Override
+ public void accept(DoubleDispatchVisitor visitor) {
+ visitor.visitFeatureDeclaration(this);
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/FeaturePrefixTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/FeaturePrefixTreeImpl.java
new file mode 100644
index 0000000..b13b0cf
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/FeaturePrefixTreeImpl.java
@@ -0,0 +1,42 @@
+/*
+ * 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.tree.impl;
+
+import org.sonar.plugins.gherkin.api.tree.FeaturePrefixTree;
+import org.sonar.plugins.gherkin.api.tree.SyntaxToken;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+public class FeaturePrefixTreeImpl extends AbstractPrefixTreeImpl implements FeaturePrefixTree {
+
+ public FeaturePrefixTreeImpl(SyntaxToken keyword) {
+ super(keyword);
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.FEATURE_PREFIX;
+ }
+
+ @Override
+ public void accept(DoubleDispatchVisitor visitor) {
+ visitor.visitFeaturePrefix(this);
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/FeatureTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/FeatureTreeImpl.java
new file mode 100644
index 0000000..c148ee1
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/FeatureTreeImpl.java
@@ -0,0 +1,96 @@
+/*
+ * 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.tree.impl;
+
+import com.google.common.collect.Iterators;
+import org.sonar.plugins.gherkin.api.tree.*;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class FeatureTreeImpl extends GherkinTree implements FeatureTree {
+
+ private final FeatureDeclarationTree declaration;
+ private final BackgroundTree background;
+ private final List allScenarios;
+ private final List scenarios;
+ private final List scenarioOutlines;
+
+ public FeatureTreeImpl(FeatureDeclarationTree declaration, @Nullable BackgroundTree background, @Nullable List allScenarios) {
+ this.declaration = declaration;
+ this.background = background;
+
+ if (allScenarios != null) {
+ this.allScenarios = allScenarios;
+ } else {
+ this.allScenarios = new ArrayList<>();
+ }
+
+ this.scenarios = TreeListUtils.allElementsOfType(allScenarios, ScenarioTree.class);
+ this.scenarioOutlines = TreeListUtils.allElementsOfType(allScenarios, ScenarioOutlineTree.class);
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.FEATURE;
+ }
+
+ @Override
+ public Iterator childrenIterator() {
+ return Iterators.concat(
+ Iterators.forArray(declaration, background),
+ allScenarios.iterator());
+ }
+
+ @Override
+ public FeatureDeclarationTree declaration() {
+ return declaration;
+ }
+
+ @Override
+ @Nullable
+ public BackgroundTree background() {
+ return background;
+ }
+
+ @Override
+ public List scenarios() {
+ return scenarios;
+ }
+
+ @Override
+ public List scenarioOutlines() {
+ return scenarioOutlines;
+ }
+
+ @Override
+ public List allScenarios() {
+ return allScenarios;
+ }
+
+ @Override
+ public void accept(DoubleDispatchVisitor visitor) {
+ visitor.visitFeature(this);
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/GherkinDocumentTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/GherkinDocumentTreeImpl.java
new file mode 100644
index 0000000..a08950d
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/GherkinDocumentTreeImpl.java
@@ -0,0 +1,70 @@
+/*
+ * 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.tree.impl;
+
+import com.google.common.collect.Iterators;
+import org.sonar.plugins.gherkin.api.tree.GherkinDocumentTree;
+import org.sonar.plugins.gherkin.api.tree.FeatureTree;
+import org.sonar.plugins.gherkin.api.tree.SyntaxToken;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+import javax.annotation.Nullable;
+import java.util.Iterator;
+
+public class GherkinDocumentTreeImpl extends GherkinTree implements GherkinDocumentTree {
+
+ private final SyntaxToken byteOrderMark;
+ private final FeatureTree feature;
+ private final SyntaxToken eof;
+
+ public GherkinDocumentTreeImpl(@Nullable SyntaxToken byteOrderMark, @Nullable FeatureTree feature, SyntaxToken eof) {
+ this.byteOrderMark = byteOrderMark;
+ this.feature = feature;
+ this.eof = eof;
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.GHERKIN_DOCUMENT;
+ }
+
+ @Override
+ public Iterator childrenIterator() {
+ return Iterators.forArray(byteOrderMark, feature, eof);
+ }
+
+ @Override
+ public boolean hasByteOrderMark() {
+ return byteOrderMark != null;
+ }
+
+ @Override
+ @Nullable
+ public FeatureTree feature() {
+ return feature;
+ }
+
+ @Override
+ public void accept(DoubleDispatchVisitor visitor) {
+ visitor.visitGherkinDocument(this);
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/GherkinTree.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/GherkinTree.java
new file mode 100644
index 0000000..4d07b2d
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/GherkinTree.java
@@ -0,0 +1,89 @@
+/*
+ * 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.tree.impl;
+
+import org.sonar.plugins.gherkin.api.tree.SyntaxToken;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+
+import java.util.Iterator;
+
+public abstract class GherkinTree implements Tree {
+
+ private Tree parent;
+
+ @Override
+ public Tree parent() {
+ return parent;
+ }
+
+ @Override
+ public void setParent(Tree parent) {
+ this.parent = parent;
+ }
+
+ public int getLine() {
+ return getFirstToken().line();
+ }
+
+ @Override
+ public final boolean is(Kind... kind) {
+ if (getKind() != null) {
+ for (Kind kindIter : kind) {
+ if (getKind() == kindIter) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isLeaf() {
+ return false;
+ }
+
+ public SyntaxToken getLastToken() {
+ SyntaxToken lastToken = null;
+ Iterator childrenIterator = childrenIterator();
+ while (childrenIterator.hasNext()) {
+ GherkinTree child = (GherkinTree) childrenIterator.next();
+ if (child != null) {
+ SyntaxToken childLastToken = child.getLastToken();
+ if (childLastToken != null) {
+ lastToken = childLastToken;
+ }
+ }
+ }
+ return lastToken;
+ }
+
+ public SyntaxToken getFirstToken() {
+ Iterator childrenIterator = childrenIterator();
+ Tree child;
+ do {
+ if (childrenIterator.hasNext()) {
+ child = childrenIterator.next();
+ } else {
+ throw new IllegalStateException("Tree has no non-null children " + getKind());
+ }
+ } while (child == null);
+ return ((GherkinTree) child).getFirstToken();
+ }
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/InternalSyntaxSpacing.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/InternalSyntaxSpacing.java
new file mode 100644
index 0000000..4d0b784
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/InternalSyntaxSpacing.java
@@ -0,0 +1,85 @@
+/*
+ * 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.tree.impl;
+
+import org.sonar.plugins.gherkin.api.tree.SyntaxSpacing;
+import org.sonar.plugins.gherkin.api.tree.SyntaxTrivia;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+public class InternalSyntaxSpacing extends GherkinTree implements SyntaxSpacing {
+
+ public InternalSyntaxSpacing(int startIndex, int endIndex) {
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.SPACING;
+ }
+
+ @Override
+ public boolean isLeaf() {
+ return true;
+ }
+
+ @Override
+ public Iterator childrenIterator() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void accept(DoubleDispatchVisitor visitor) {
+ // Do nothing at the moment. Spacings are dropped anyway.
+ }
+
+ @Override
+ public int column() {
+ return 0;
+ }
+
+ @Override
+ public int line() {
+ return 0;
+ }
+
+ @Override
+ public int endColumn() {
+ return 0;
+ }
+
+ @Override
+ public int endLine() {
+ return 0;
+ }
+
+ @Override
+ public String text() {
+ return "";
+ }
+
+ @Override
+ public List trivias() {
+ return Collections.emptyList();
+ }
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/InternalSyntaxToken.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/InternalSyntaxToken.java
new file mode 100644
index 0000000..728eadc
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/InternalSyntaxToken.java
@@ -0,0 +1,129 @@
+/*
+ * 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.tree.impl;
+
+import org.sonar.plugins.gherkin.api.tree.SyntaxToken;
+import org.sonar.plugins.gherkin.api.tree.SyntaxTrivia;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+import java.util.Iterator;
+import java.util.List;
+
+public class InternalSyntaxToken extends GherkinTree implements SyntaxToken {
+
+ private final int line;
+ private final int column;
+ private final String value;
+ private final boolean isEOF;
+ private final boolean isBOM;
+ private int endLine;
+ private int endColumn;
+ private final List trivias;
+
+ public InternalSyntaxToken(int line, int column, String value, List trivias, boolean isEOF, boolean isBOM) {
+ this.value = value;
+ this.trivias = trivias;
+ this.line = line;
+ this.column = column;
+ this.isEOF = isEOF;
+ this.isBOM = isBOM;
+ calculateEndOffsets();
+ }
+
+ private void calculateEndOffsets() {
+ String[] lines = value.split("\r\n|\n|\r", -1);
+ endColumn = column + value.length();
+ endLine = line + lines.length - 1;
+
+ if (endLine != line) {
+ endColumn = lines[lines.length - 1].length();
+ }
+ }
+
+ @Override
+ public int endLine() {
+ return endLine;
+ }
+
+ @Override
+ public int endColumn() {
+ return endColumn;
+ }
+
+ @Override
+ public int line() {
+ return line;
+ }
+
+ @Override
+ public int column() {
+ return column;
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.TOKEN;
+ }
+
+ @Override
+ public boolean isLeaf() {
+ return true;
+ }
+
+ public boolean isEOF() {
+ return isEOF;
+ }
+
+ public boolean isBOM() {
+ return isBOM;
+ }
+
+ @Override
+ public Iterator childrenIterator() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void accept(DoubleDispatchVisitor visitor) {
+ visitor.visitToken(this);
+ }
+
+ @Override
+ public SyntaxToken getFirstToken() {
+ return this;
+ }
+
+ @Override
+ public SyntaxToken getLastToken() {
+ return this;
+ }
+
+ @Override
+ public String text() {
+ return value;
+ }
+
+ @Override
+ public List trivias() {
+ return trivias;
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/InternalSyntaxTrivia.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/InternalSyntaxTrivia.java
new file mode 100644
index 0000000..1d20be8
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/InternalSyntaxTrivia.java
@@ -0,0 +1,124 @@
+/*
+ * 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.tree.impl;
+
+import org.sonar.plugins.gherkin.api.tree.SyntaxToken;
+import org.sonar.plugins.gherkin.api.tree.SyntaxTrivia;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+public class InternalSyntaxTrivia extends GherkinTree implements SyntaxTrivia {
+
+ private final String comment;
+ private final int column;
+ private int startLine;
+ private int endLine;
+ private int endColumn;
+
+ public InternalSyntaxTrivia(String comment, int startLine, int column) {
+ this.comment = comment;
+ this.startLine = startLine;
+ this.column = column;
+ calculateEndOffsets();
+ }
+
+ private void calculateEndOffsets() {
+ String[] lines = comment.split("\r\n|\n|\r", -1);
+ endColumn = column + comment.length();
+ endLine = startLine + lines.length - 1;
+
+ if (endLine != startLine) {
+ endColumn = lines[lines.length - 1].length();
+ }
+ }
+
+ @Override
+ public int endLine() {
+ return endLine;
+ }
+
+ @Override
+ public int endColumn() {
+ return endColumn;
+ }
+
+ @Override
+ public String text() {
+ return comment;
+ }
+
+ @Override
+ public List trivias() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public int line() {
+ return startLine;
+ }
+
+ @Override
+ public int column() {
+ return column;
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.TRIVIA;
+ }
+
+ @Override
+ public boolean isLeaf() {
+ return true;
+ }
+
+ @Override
+ public Iterator childrenIterator() {
+ throw new UnsupportedOperationException();
+ }
+
+ public static SyntaxTrivia create(String comment, int startLine, int column) {
+ return new InternalSyntaxTrivia(comment, startLine, column);
+ }
+
+ @Override
+ public int getLine() {
+ return startLine;
+ }
+
+ @Override
+ public void accept(DoubleDispatchVisitor visitor) {
+ visitor.visitComment(this);
+ }
+
+ @Override
+ public SyntaxToken getFirstToken() {
+ return this;
+ }
+
+ @Override
+ public SyntaxToken getLastToken() {
+ return this;
+ }
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/NameTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/NameTreeImpl.java
new file mode 100644
index 0000000..8e96346
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/NameTreeImpl.java
@@ -0,0 +1,63 @@
+/*
+ * 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.tree.impl;
+
+import com.google.common.collect.Iterators;
+import org.sonar.plugins.gherkin.api.tree.SyntaxToken;
+import org.sonar.plugins.gherkin.api.tree.NameTree;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+import java.util.Iterator;
+
+public class NameTreeImpl extends GherkinTree implements NameTree {
+
+ private final SyntaxToken name;
+
+ public NameTreeImpl(SyntaxToken name) {
+ this.name = name;
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.NAME;
+ }
+
+ @Override
+ public Iterator childrenIterator() {
+ return Iterators.singletonIterator(name);
+ }
+
+ @Override
+ public SyntaxToken value() {
+ return name;
+ }
+
+ @Override
+ public String text() {
+ return name.text();
+ }
+
+ @Override
+ public void accept(DoubleDispatchVisitor visitor) {
+ visitor.visitName(this);
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/ScenarioOutlinePrefixTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/ScenarioOutlinePrefixTreeImpl.java
new file mode 100644
index 0000000..b700ba2
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/ScenarioOutlinePrefixTreeImpl.java
@@ -0,0 +1,42 @@
+/*
+ * 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.tree.impl;
+
+import org.sonar.plugins.gherkin.api.tree.ScenarioOutlinePrefixTree;
+import org.sonar.plugins.gherkin.api.tree.SyntaxToken;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+public class ScenarioOutlinePrefixTreeImpl extends AbstractPrefixTreeImpl implements ScenarioOutlinePrefixTree {
+
+ public ScenarioOutlinePrefixTreeImpl(SyntaxToken keyword) {
+ super(keyword);
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.SCENARIO_OUTLINE_PREFIX;
+ }
+
+ @Override
+ public void accept(DoubleDispatchVisitor visitor) {
+ visitor.visitScenarioOutlinePrefix(this);
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/ScenarioOutlineTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/ScenarioOutlineTreeImpl.java
new file mode 100644
index 0000000..6d52d06
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/ScenarioOutlineTreeImpl.java
@@ -0,0 +1,117 @@
+/*
+ * 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.tree.impl;
+
+import com.google.common.collect.Iterators;
+import org.sonar.plugins.gherkin.api.tree.*;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class ScenarioOutlineTreeImpl extends GherkinTree implements ScenarioOutlineTree {
+
+ private final List tags;
+ private final PrefixTree prefix;
+ private final SyntaxToken colon;
+ private final NameTree name;
+ private final DescriptionTree description;
+ private final List steps;
+ private final ExamplesTree examples;
+
+ public ScenarioOutlineTreeImpl(@Nullable List tags, PrefixTree prefix, SyntaxToken colon, NameTree name, @Nullable DescriptionTree description, @Nullable List steps, ExamplesTree examples) {
+ if (tags != null) {
+ this.tags = tags;
+ } else {
+ this.tags = new ArrayList<>();
+ }
+
+ this.prefix = prefix;
+ this.colon = colon;
+ this.name = name;
+ this.description = description;
+
+ if (steps != null) {
+ this.steps = steps;
+ } else {
+ this.steps = new ArrayList<>();
+ }
+
+ this.examples = examples;
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.SCENARIO_OUTLINE;
+ }
+
+ @Override
+ public Iterator childrenIterator() {
+ return Iterators.concat(
+ tags.iterator(),
+ Iterators.forArray(prefix, colon, name, description),
+ steps.iterator(),
+ Iterators.singletonIterator(examples));
+ }
+
+ @Override
+ public List tags() {
+ return tags;
+ }
+
+ @Override
+ public PrefixTree prefix() {
+ return prefix;
+ }
+
+ @Override
+ public SyntaxToken colon() {
+ return colon;
+ }
+
+ @Override
+ public NameTree name() {
+ return name;
+ }
+
+ @Override
+ @Nullable
+ public DescriptionTree description() {
+ return description;
+ }
+
+ @Override
+ public List steps() {
+ return steps;
+ }
+
+ @Override
+ public ExamplesTree examples() {
+ return examples;
+ }
+
+ @Override
+ public void accept(DoubleDispatchVisitor visitor) {
+ visitor.visitScenarioOutline(this);
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/ScenarioPrefixTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/ScenarioPrefixTreeImpl.java
new file mode 100644
index 0000000..ff2f5dc
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/ScenarioPrefixTreeImpl.java
@@ -0,0 +1,42 @@
+/*
+ * 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.tree.impl;
+
+import org.sonar.plugins.gherkin.api.tree.ScenarioPrefixTree;
+import org.sonar.plugins.gherkin.api.tree.SyntaxToken;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+public class ScenarioPrefixTreeImpl extends AbstractPrefixTreeImpl implements ScenarioPrefixTree {
+
+ public ScenarioPrefixTreeImpl(SyntaxToken keyword) {
+ super(keyword);
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.SCENARIO_PREFIX;
+ }
+
+ @Override
+ public void accept(DoubleDispatchVisitor visitor) {
+ visitor.visitScenarioPrefix(this);
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/ScenarioTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/ScenarioTreeImpl.java
new file mode 100644
index 0000000..96a62c4
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/ScenarioTreeImpl.java
@@ -0,0 +1,107 @@
+/*
+ * 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.tree.impl;
+
+import com.google.common.collect.Iterators;
+import org.sonar.plugins.gherkin.api.tree.*;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class ScenarioTreeImpl extends GherkinTree implements ScenarioTree {
+
+ private final List tags;
+ private final PrefixTree prefix;
+ private final SyntaxToken colon;
+ private final NameTree name;
+ private final DescriptionTree description;
+ private final List steps;
+
+ public ScenarioTreeImpl(@Nullable List tags, PrefixTree prefix, SyntaxToken colon, NameTree name, @Nullable DescriptionTree description, @Nullable List steps) {
+ if (tags != null) {
+ this.tags = tags;
+ } else {
+ this.tags = new ArrayList<>();
+ }
+ this.prefix = prefix;
+ this.colon = colon;
+ this.name = name;
+ this.description = description;
+
+ if (steps != null) {
+ this.steps = steps;
+ } else {
+ this.steps = new ArrayList<>();
+ }
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.SCENARIO;
+ }
+
+ @Override
+ public Iterator childrenIterator() {
+ return Iterators.concat(
+ tags.iterator(),
+ Iterators.forArray(prefix, colon, name, description),
+ steps.iterator());
+ }
+
+ @Override
+ public List tags() {
+ return tags;
+ }
+
+ @Override
+ public PrefixTree prefix() {
+ return prefix;
+ }
+
+ @Override
+ public SyntaxToken colon() {
+ return colon;
+ }
+
+ @Override
+ public NameTree name() {
+ return name;
+ }
+
+ @Override
+ @Nullable
+ public DescriptionTree description() {
+ return description;
+ }
+
+ @Override
+ public List steps() {
+ return steps;
+ }
+
+ @Override
+ public void accept(DoubleDispatchVisitor visitor) {
+ visitor.visitScenario(this);
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/StepPrefixTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/StepPrefixTreeImpl.java
new file mode 100644
index 0000000..62d187f
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/StepPrefixTreeImpl.java
@@ -0,0 +1,42 @@
+/*
+ * 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.tree.impl;
+
+import org.sonar.plugins.gherkin.api.tree.StepPrefixTree;
+import org.sonar.plugins.gherkin.api.tree.SyntaxToken;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+public class StepPrefixTreeImpl extends AbstractPrefixTreeImpl implements StepPrefixTree {
+
+ public StepPrefixTreeImpl(SyntaxToken keyword) {
+ super(keyword);
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.STEP_PREFIX;
+ }
+
+ @Override
+ public void accept(DoubleDispatchVisitor visitor) {
+ visitor.visitStepPrefix(this);
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/StepTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/StepTreeImpl.java
new file mode 100644
index 0000000..5cafcb2
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/StepTreeImpl.java
@@ -0,0 +1,93 @@
+/*
+ * 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.tree.impl;
+
+import com.google.common.collect.Iterators;
+import org.sonar.plugins.gherkin.api.tree.*;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+import javax.annotation.Nullable;
+import java.util.Iterator;
+
+public class StepTreeImpl extends GherkinTree implements StepTree {
+
+ private final PrefixTree prefix;
+ private final SyntaxToken sentence;
+ private final DocStringTree docString;
+ private final TableTree table;
+
+ public StepTreeImpl(PrefixTree prefix, SyntaxToken sentence, @Nullable Tree data) {
+ this.prefix = prefix;
+ this.sentence = sentence;
+
+ if (data != null) {
+ if (data instanceof DocStringTree) {
+ docString = (DocStringTree) data;
+ table = null;
+ } else if (data instanceof TableTree) {
+ table = (TableTree) data;
+ docString = null;
+ } else {
+ throw new IllegalStateException("Unsupported data tree: " + data.toString());
+ }
+ } else {
+ docString = null;
+ table = null;
+ }
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.STEP;
+ }
+
+ @Override
+ public Iterator childrenIterator() {
+ return Iterators.forArray(prefix, sentence, docString, table);
+ }
+
+ @Override
+ public PrefixTree prefix() {
+ return prefix;
+ }
+
+ @Override
+ public SyntaxToken sentence() {
+ return sentence;
+ }
+
+ @Override
+ @Nullable
+ public DocStringTree docString() {
+ return docString;
+ }
+
+ @Override
+ @Nullable
+ public TableTree table() {
+ return table;
+ }
+
+ @Override
+ public void accept(DoubleDispatchVisitor visitor) {
+ visitor.visitStep(this);
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/TableTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/TableTreeImpl.java
new file mode 100644
index 0000000..77e17d0
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/TableTreeImpl.java
@@ -0,0 +1,59 @@
+/*
+ * 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.tree.impl;
+
+import org.sonar.plugins.gherkin.api.tree.SyntaxToken;
+import org.sonar.plugins.gherkin.api.tree.TableTree;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class TableTreeImpl extends GherkinTree implements TableTree {
+
+ private final List rows;
+
+ public TableTreeImpl(List rows) {
+ this.rows = rows;
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.TABLE;
+ }
+
+ @Override
+ public Iterator childrenIterator() {
+ return rows.stream().map(t -> (Tree) t).collect(Collectors.toList()).iterator();
+ }
+
+ @Override
+ public List rows() {
+ return rows;
+ }
+
+ @Override
+ public void accept(DoubleDispatchVisitor visitor) {
+ visitor.visitTable(this);
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/TagTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/TagTreeImpl.java
new file mode 100644
index 0000000..2526d7d
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/TagTreeImpl.java
@@ -0,0 +1,70 @@
+/*
+ * 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.tree.impl;
+
+import com.google.common.collect.Iterators;
+import org.sonar.plugins.gherkin.api.tree.SyntaxToken;
+import org.sonar.plugins.gherkin.api.tree.TagTree;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+import java.util.Iterator;
+
+public class TagTreeImpl extends GherkinTree implements TagTree {
+
+ private final SyntaxToken prefix;
+ private final SyntaxToken value;
+
+ public TagTreeImpl(SyntaxToken prefix, SyntaxToken value) {
+ this.prefix = prefix;
+ this.value = value;
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.TAG;
+ }
+
+ @Override
+ public Iterator childrenIterator() {
+ return Iterators.forArray(prefix, value);
+ }
+
+ @Override
+ public SyntaxToken prefix() {
+ return prefix;
+ }
+
+ @Override
+ public SyntaxToken value() {
+ return value;
+ }
+
+ @Override
+ public String text() {
+ return value.text().substring(0, value.text().length());
+ }
+
+ @Override
+ public void accept(DoubleDispatchVisitor visitor) {
+ visitor.visitTag(this);
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/TreeListUtils.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/TreeListUtils.java
new file mode 100644
index 0000000..f678b46
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/TreeListUtils.java
@@ -0,0 +1,45 @@
+/*
+ * 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.tree.impl;
+
+import org.sonar.plugins.gherkin.api.tree.Tree;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class TreeListUtils {
+
+ private TreeListUtils() {
+ }
+
+ public static List allElementsOfType(@Nullable List extends Tree> trees, Class treeType) {
+ if (trees != null) {
+ return trees.stream()
+ .filter(e -> treeType.isAssignableFrom(e.getClass()))
+ .map(treeType::cast)
+ .collect(Collectors.toList());
+ } else {
+ return new ArrayList<>();
+ }
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/package-info.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/package-info.java
new file mode 100644
index 0000000..d8b6053
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.gherkin.tree.impl;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/CharsetAwareVisitor.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/CharsetAwareVisitor.java
new file mode 100644
index 0000000..f107b93
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/CharsetAwareVisitor.java
@@ -0,0 +1,28 @@
+/*
+ * 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.visitors;
+
+import java.nio.charset.Charset;
+
+public interface CharsetAwareVisitor {
+
+ void setCharset(Charset charset);
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/GherkinVisitorContext.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/GherkinVisitorContext.java
new file mode 100644
index 0000000..28b7e7b
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/GherkinVisitorContext.java
@@ -0,0 +1,47 @@
+/*
+ * 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.visitors;
+
+import org.sonar.plugins.gherkin.api.tree.GherkinDocumentTree;
+import org.sonar.plugins.gherkin.api.visitors.TreeVisitorContext;
+
+import java.io.File;
+
+public class GherkinVisitorContext implements TreeVisitorContext {
+
+ private final GherkinDocumentTree tree;
+ private final File file;
+
+ public GherkinVisitorContext(GherkinDocumentTree tree, File file) {
+ this.tree = tree;
+ this.file = file;
+ }
+
+ @Override
+ public GherkinDocumentTree getTopTree() {
+ return tree;
+ }
+
+ @Override
+ public File getFile() {
+ return file;
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/Issues.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/Issues.java
new file mode 100644
index 0000000..f3ce155
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/Issues.java
@@ -0,0 +1,66 @@
+/*
+ * 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.visitors;
+
+import org.sonar.plugins.gherkin.api.GherkinCheck;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.issue.*;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class Issues {
+
+ private List issueList;
+ private GherkinCheck check;
+
+ public Issues(GherkinCheck check) {
+ this.check = check;
+ this.issueList = new ArrayList<>();
+ }
+
+ public PreciseIssue addPreciseIssue(File file, Tree tree, String message) {
+ PreciseIssue issue = new PreciseIssue(check, new IssueLocation(file, tree, message));
+ issueList.add(issue);
+ return issue;
+ }
+
+ public FileIssue addFileIssue(File file, String message) {
+ FileIssue issue = new FileIssue(check, file, message);
+ issueList.add(issue);
+ return issue;
+ }
+
+ public LineIssue addLineIssue(File file, int line, String message) {
+ LineIssue issue = new LineIssue(check, file, line, message);
+ issueList.add(issue);
+ return issue;
+ }
+
+ public List getList() {
+ return issueList;
+ }
+
+ public void reset() {
+ issueList.clear();
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/SyntaxHighlighterVisitor.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/SyntaxHighlighterVisitor.java
new file mode 100644
index 0000000..b5780eb
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/SyntaxHighlighterVisitor.java
@@ -0,0 +1,113 @@
+/*
+ * 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.visitors;
+
+import com.google.common.collect.ImmutableList;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
+import org.sonar.api.batch.sensor.highlighting.TypeOfText;
+import org.sonar.gherkin.tree.impl.InternalSyntaxToken;
+import org.sonar.plugins.gherkin.api.tree.*;
+import org.sonar.plugins.gherkin.api.visitors.SubscriptionVisitor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SyntaxHighlighterVisitor extends SubscriptionVisitor {
+
+ private final SensorContext sensorContext;
+ private final FileSystem fileSystem;
+ private NewHighlighting highlighting;
+
+ public SyntaxHighlighterVisitor(SensorContext sensorContext) {
+ this.sensorContext = sensorContext;
+ fileSystem = sensorContext.fileSystem();
+ }
+
+ @Override
+ public List nodesToVisit() {
+ return ImmutableList.builder()
+ .add(Tree.Kind.TAG)
+ .add(Tree.Kind.FEATURE_PREFIX)
+ .add(Tree.Kind.BACKGROUND_PREFIX)
+ .add(Tree.Kind.SCENARIO_PREFIX)
+ .add(Tree.Kind.SCENARIO_OUTLINE_PREFIX)
+ .add(Tree.Kind.EXAMPLES_PREFIX)
+ .add(Tree.Kind.STEP_PREFIX)
+ .add(Tree.Kind.NAME)
+ .add(Tree.Kind.TOKEN)
+ .build();
+ }
+
+ @Override
+ public void visitFile(Tree tree) {
+ highlighting = sensorContext.newHighlighting().onFile(fileSystem.inputFile(fileSystem.predicates().is(getContext().getFile())));
+ }
+
+ @Override
+ public void leaveFile(Tree scriptTree) {
+ highlighting.save();
+ }
+
+ @Override
+ public void visitNode(Tree tree) {
+ List tokens = new ArrayList<>();
+ TypeOfText code = null;
+
+ if (tree.is(Tree.Kind.TAG)) {
+ tokens.add(((TagTree) tree).prefix());
+ tokens.add(((TagTree) tree).value());
+ code = TypeOfText.ANNOTATION;
+
+ } else if (tree.is(Tree.Kind.FEATURE_PREFIX,
+ Tree.Kind.BACKGROUND_PREFIX,
+ Tree.Kind.SCENARIO_PREFIX,
+ Tree.Kind.SCENARIO_OUTLINE_PREFIX,
+ Tree.Kind.EXAMPLES_PREFIX,
+ Tree.Kind.STEP_PREFIX)) {
+
+ tokens.add(((PrefixTree) tree).keyword());
+ code = TypeOfText.KEYWORD;
+
+ } else if (tree.is(Tree.Kind.NAME)) {
+ tokens.add(((NameTree) tree).value());
+ code = TypeOfText.STRING;
+
+ } else if (tree.is(Tree.Kind.TOKEN)) {
+ highlightComments((InternalSyntaxToken) tree);
+ }
+
+ for (SyntaxToken token : tokens) {
+ highlight(token, code);
+ }
+ }
+
+ private void highlightComments(InternalSyntaxToken token) {
+ for (SyntaxTrivia trivia : token.trivias()) {
+ highlight(trivia, TypeOfText.COMMENT);
+ }
+ }
+
+ private void highlight(SyntaxToken token, TypeOfText type) {
+ highlighting.highlight(token.line(), token.column(), token.endLine(), token.endColumn(), type);
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/ClassesVisitor.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/ClassesVisitor.java
new file mode 100644
index 0000000..d317ec3
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/ClassesVisitor.java
@@ -0,0 +1,51 @@
+/*
+ * 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.visitors.metrics;
+
+import com.google.common.collect.ImmutableList;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.SubscriptionVisitor;
+
+import java.util.List;
+
+public class ClassesVisitor extends SubscriptionVisitor {
+
+ private int classes;
+
+ public ClassesVisitor(Tree tree) {
+ classes = 0;
+ scanTree(tree);
+ }
+
+ @Override
+ public List nodesToVisit() {
+ return ImmutableList.of(Tree.Kind.FEATURE);
+ }
+
+ @Override
+ public void visitNode(Tree tree) {
+ classes++;
+ }
+
+ public int getNumberOfClasses() {
+ return classes;
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/CommentLinesVisitor.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/CommentLinesVisitor.java
new file mode 100644
index 0000000..0db523d
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/CommentLinesVisitor.java
@@ -0,0 +1,56 @@
+/*
+ * 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.visitors.metrics;
+
+import com.google.common.collect.ImmutableList;
+import org.sonar.plugins.gherkin.api.tree.SyntaxToken;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.SubscriptionVisitor;
+
+import java.util.List;
+
+public class CommentLinesVisitor extends SubscriptionVisitor {
+
+ private int commentLines;
+ private GherkinCommentAnalyser commentAnalyser = new GherkinCommentAnalyser();
+
+ public CommentLinesVisitor(Tree tree) {
+ commentLines = 0;
+ scanTree(tree);
+ }
+
+ @Override
+ public List nodesToVisit() {
+ return ImmutableList.of(Tree.Kind.TOKEN);
+ }
+
+ @Override
+ public void visitNode(Tree tree) {
+ ((SyntaxToken) tree).trivias()
+ .stream()
+ .filter(t -> !commentAnalyser.isBlank(commentAnalyser.getContents(t.text())))
+ .forEach(t -> commentLines++);
+ }
+
+ public int getNumberOfCommentLines() {
+ return commentLines;
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/FunctionsVisitor.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/FunctionsVisitor.java
new file mode 100644
index 0000000..010fb50
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/FunctionsVisitor.java
@@ -0,0 +1,54 @@
+/*
+ * 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.visitors.metrics;
+
+import com.google.common.collect.ImmutableList;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.SubscriptionVisitor;
+
+import java.util.List;
+
+public class FunctionsVisitor extends SubscriptionVisitor {
+
+ private int functions;
+
+ public FunctionsVisitor(Tree tree) {
+ functions = 0;
+ scanTree(tree);
+ }
+
+ @Override
+ public List nodesToVisit() {
+ return ImmutableList.of(
+ Tree.Kind.BACKGROUND,
+ Tree.Kind.SCENARIO,
+ Tree.Kind.SCENARIO_OUTLINE);
+ }
+
+ @Override
+ public void visitNode(Tree tree) {
+ functions++;
+ }
+
+ public int getNumberOfFunctions() {
+ return functions;
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/GherkinCommentAnalyser.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/GherkinCommentAnalyser.java
new file mode 100644
index 0000000..d4c1a8a
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/GherkinCommentAnalyser.java
@@ -0,0 +1,45 @@
+/*
+ * 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.visitors.metrics;
+
+import org.sonar.squidbridge.CommentAnalyser;
+
+public class GherkinCommentAnalyser extends CommentAnalyser {
+
+ @Override
+ public boolean isBlank(String line) {
+ for (int i = 0; i < line.length(); i++) {
+ if (Character.isLetterOrDigit(line.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public String getContents(String comment) {
+ if (comment.startsWith("#")) {
+ return comment.substring(1, comment.length());
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/LinesOfCodeVisitor.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/LinesOfCodeVisitor.java
new file mode 100644
index 0000000..65e1d30
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/LinesOfCodeVisitor.java
@@ -0,0 +1,57 @@
+/*
+ * 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.visitors.metrics;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+import org.sonar.gherkin.tree.impl.InternalSyntaxToken;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.SubscriptionVisitor;
+
+import java.util.List;
+import java.util.Set;
+
+public class LinesOfCodeVisitor extends SubscriptionVisitor {
+
+ private Set linesOfCode = Sets.newHashSet();
+
+ public LinesOfCodeVisitor(Tree tree) {
+ linesOfCode.clear();
+ scanTree(tree);
+ }
+
+ @Override
+ public List nodesToVisit() {
+ return ImmutableList.of(Tree.Kind.TOKEN);
+ }
+
+ @Override
+ public void visitNode(Tree tree) {
+ InternalSyntaxToken token = (InternalSyntaxToken) tree;
+ if (!token.isEOF() && !token.isBOM()) {
+ linesOfCode.add(token.line());
+ }
+ }
+
+ public int getNumberOfLinesOfCode() {
+ return linesOfCode.size();
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/MetricsVisitor.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/MetricsVisitor.java
new file mode 100644
index 0000000..8589867
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/MetricsVisitor.java
@@ -0,0 +1,81 @@
+/*
+ * 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.visitors.metrics;
+
+import com.google.common.collect.ImmutableList;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Metric;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.SubscriptionVisitor;
+
+import java.io.Serializable;
+import java.util.List;
+
+public class MetricsVisitor extends SubscriptionVisitor {
+
+ private final SensorContext sensorContext;
+ private final FileSystem fileSystem;
+ private InputFile inputFile;
+
+ public MetricsVisitor(SensorContext sensorContext) {
+ this.sensorContext = sensorContext;
+ this.fileSystem = sensorContext.fileSystem();
+ }
+
+ @Override
+ public List nodesToVisit() {
+ return ImmutableList.of(Tree.Kind.GHERKIN_DOCUMENT);
+ }
+
+ @Override
+ public void visitFile(Tree tree) {
+ this.inputFile = fileSystem.inputFile(fileSystem.predicates().is(getContext().getFile()));
+ }
+
+ @Override
+ public void leaveFile(Tree tree) {
+ LinesOfCodeVisitor linesOfCodeVisitor = new LinesOfCodeVisitor(tree);
+ saveMetricOnFile(CoreMetrics.NCLOC, linesOfCodeVisitor.getNumberOfLinesOfCode());
+
+ CommentLinesVisitor commentLinesVisitor = new CommentLinesVisitor(tree);
+ saveMetricOnFile(CoreMetrics.COMMENT_LINES, commentLinesVisitor.getNumberOfCommentLines());
+
+ StatementsVisitor statementsVisitor = new StatementsVisitor(tree);
+ saveMetricOnFile(CoreMetrics.STATEMENTS, statementsVisitor.getNumberOfStatements());
+
+ FunctionsVisitor functionsVisitor = new FunctionsVisitor(tree);
+ saveMetricOnFile(CoreMetrics.FUNCTIONS, functionsVisitor.getNumberOfFunctions());
+
+ ClassesVisitor classesVisitor = new ClassesVisitor(tree);
+ saveMetricOnFile(CoreMetrics.CLASSES, classesVisitor.getNumberOfClasses());
+ }
+
+ private void saveMetricOnFile(Metric metric, T value) {
+ sensorContext.newMeasure()
+ .withValue(value)
+ .forMetric(metric)
+ .on(inputFile)
+ .save();
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/StatementsVisitor.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/StatementsVisitor.java
new file mode 100644
index 0000000..b6343c6
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/StatementsVisitor.java
@@ -0,0 +1,51 @@
+/*
+ * 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.visitors.metrics;
+
+import com.google.common.collect.ImmutableList;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.SubscriptionVisitor;
+
+import java.util.List;
+
+public class StatementsVisitor extends SubscriptionVisitor {
+
+ private int statements;
+
+ public StatementsVisitor(Tree tree) {
+ statements = 0;
+ scanTree(tree);
+ }
+
+ @Override
+ public List nodesToVisit() {
+ return ImmutableList.of(Tree.Kind.STEP);
+ }
+
+ @Override
+ public void visitNode(Tree tree) {
+ statements++;
+ }
+
+ public int getNumberOfStatements() {
+ return statements;
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/package-info.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/package-info.java
new file mode 100644
index 0000000..af4c687
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/metrics/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.gherkin.visitors.metrics;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/package-info.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/package-info.java
new file mode 100644
index 0000000..fbf9a09
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/visitors/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.gherkin.visitors;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/CustomGherkinRulesDefinition.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/CustomGherkinRulesDefinition.java
new file mode 100644
index 0000000..a3d7a93
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/CustomGherkinRulesDefinition.java
@@ -0,0 +1,61 @@
+/*
+ * 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.plugins.gherkin.api;
+
+import com.google.common.collect.ImmutableList;
+import org.sonar.api.ExtensionPoint;
+import org.sonar.api.batch.BatchSide;
+import org.sonar.api.server.rule.RulesDefinition;
+import org.sonar.squidbridge.annotations.AnnotationBasedRulesDefinition;
+
+/**
+ * Extension point to create custom rule repository for Gherkin.
+ */
+@ExtensionPoint
+@BatchSide
+public abstract class CustomGherkinRulesDefinition implements RulesDefinition {
+
+ /**
+ * Defines rule repository with check metadata from check classes' annotations.
+ * This method should be overridden if check metadata are provided via another format,
+ * e.g: XMl, JSON.
+ */
+ @Override
+ public void define(Context context) {
+ NewRepository repo = context.createRepository(repositoryKey(), "gherkin").setName(repositoryName());
+ new AnnotationBasedRulesDefinition(repo, "gherkin").addRuleClasses(false, ImmutableList.copyOf(checkClasses()));
+ repo.done();
+ }
+
+ /**
+ * Name of the custom rule repository.
+ */
+ public abstract String repositoryName();
+
+ /**
+ * Key of the custom rule repository.
+ */
+ public abstract String repositoryKey();
+
+ /**
+ * Array of the custom rules classes.
+ */
+ public abstract Class[] checkClasses();
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/GherkinCheck.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/GherkinCheck.java
new file mode 100644
index 0000000..721f5a4
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/GherkinCheck.java
@@ -0,0 +1,43 @@
+/*
+ * 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.plugins.gherkin.api;
+
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.TreeVisitorContext;
+import org.sonar.plugins.gherkin.api.visitors.issue.FileIssue;
+import org.sonar.plugins.gherkin.api.visitors.issue.Issue;
+import org.sonar.plugins.gherkin.api.visitors.issue.LineIssue;
+import org.sonar.plugins.gherkin.api.visitors.issue.PreciseIssue;
+
+import java.util.List;
+
+public interface GherkinCheck {
+
+ PreciseIssue addPreciseIssue(Tree tree, String message);
+
+ FileIssue addFileIssue(String message);
+
+ LineIssue addLineIssue(int line, String message);
+
+ List scanFile(TreeVisitorContext context);
+
+ void validateParameters();
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/package-info.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/package-info.java
new file mode 100644
index 0000000..4cb5444
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.plugins.gherkin.api;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/BackgroundPrefixTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/BackgroundPrefixTree.java
new file mode 100644
index 0000000..a295106
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/BackgroundPrefixTree.java
@@ -0,0 +1,23 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+public interface BackgroundPrefixTree extends PrefixTree {
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/BackgroundTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/BackgroundTree.java
new file mode 100644
index 0000000..fa5e50b
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/BackgroundTree.java
@@ -0,0 +1,32 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+import java.util.List;
+
+public interface BackgroundTree extends Tree, Descriptionable {
+
+ PrefixTree prefix();
+
+ SyntaxToken colon();
+
+ List steps();
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/BasicScenarioTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/BasicScenarioTree.java
new file mode 100644
index 0000000..b6fbb82
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/BasicScenarioTree.java
@@ -0,0 +1,34 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+import java.util.List;
+
+public interface BasicScenarioTree extends Tree, Taggable, Descriptionable {
+
+ PrefixTree prefix();
+
+ SyntaxToken colon();
+
+ NameTree name();
+
+ List steps();
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/DescriptionTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/DescriptionTree.java
new file mode 100644
index 0000000..98f7860
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/DescriptionTree.java
@@ -0,0 +1,28 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+import java.util.List;
+
+public interface DescriptionTree extends Tree {
+
+ List descriptionLines();
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/Descriptionable.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/Descriptionable.java
new file mode 100644
index 0000000..2c6c625
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/Descriptionable.java
@@ -0,0 +1,29 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+import javax.annotation.Nullable;
+
+public interface Descriptionable {
+
+ @Nullable
+ DescriptionTree description();
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/DocStringTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/DocStringTree.java
new file mode 100644
index 0000000..4ac24f5
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/DocStringTree.java
@@ -0,0 +1,36 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+import javax.annotation.Nullable;
+import java.util.List;
+
+public interface DocStringTree extends Tree {
+
+ SyntaxToken prefix();
+
+ SyntaxToken suffix();
+
+ List data();
+
+ @Nullable
+ SyntaxToken contentType();
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/ExamplesPrefixTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/ExamplesPrefixTree.java
new file mode 100644
index 0000000..2439324
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/ExamplesPrefixTree.java
@@ -0,0 +1,23 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+public interface ExamplesPrefixTree extends PrefixTree {
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/ExamplesTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/ExamplesTree.java
new file mode 100644
index 0000000..f88f9ee
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/ExamplesTree.java
@@ -0,0 +1,33 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+import javax.annotation.Nullable;
+
+public interface ExamplesTree extends Tree, Taggable, Descriptionable {
+
+ PrefixTree prefix();
+
+ SyntaxToken colon();
+
+ @Nullable
+ TableTree table();
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/FeatureDeclarationTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/FeatureDeclarationTree.java
new file mode 100644
index 0000000..245e604
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/FeatureDeclarationTree.java
@@ -0,0 +1,30 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+public interface FeatureDeclarationTree extends Tree, Taggable, Descriptionable {
+
+ PrefixTree prefix();
+
+ SyntaxToken colon();
+
+ NameTree name();
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/FeaturePrefixTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/FeaturePrefixTree.java
new file mode 100644
index 0000000..4cdabfa
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/FeaturePrefixTree.java
@@ -0,0 +1,23 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+public interface FeaturePrefixTree extends PrefixTree {
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/FeatureTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/FeatureTree.java
new file mode 100644
index 0000000..ca9ec23
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/FeatureTree.java
@@ -0,0 +1,38 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+import javax.annotation.Nullable;
+import java.util.List;
+
+public interface FeatureTree extends Tree {
+
+ FeatureDeclarationTree declaration();
+
+ @Nullable
+ BackgroundTree background();
+
+ List scenarioOutlines();
+
+ List scenarios();
+
+ List allScenarios();
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/GherkinDocumentTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/GherkinDocumentTree.java
new file mode 100644
index 0000000..3ce2aec
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/GherkinDocumentTree.java
@@ -0,0 +1,31 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+import javax.annotation.Nullable;
+
+public interface GherkinDocumentTree extends Tree {
+
+ boolean hasByteOrderMark();
+
+ @Nullable
+ FeatureTree feature();
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/NameTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/NameTree.java
new file mode 100644
index 0000000..f3a777c
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/NameTree.java
@@ -0,0 +1,28 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+public interface NameTree extends Tree {
+
+ SyntaxToken value();
+
+ String text();
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/PrefixTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/PrefixTree.java
new file mode 100644
index 0000000..7d684d6
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/PrefixTree.java
@@ -0,0 +1,28 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+public interface PrefixTree extends Tree {
+
+ SyntaxToken keyword();
+
+ String text();
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/ScenarioOutlinePrefixTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/ScenarioOutlinePrefixTree.java
new file mode 100644
index 0000000..cfff57b
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/ScenarioOutlinePrefixTree.java
@@ -0,0 +1,23 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+public interface ScenarioOutlinePrefixTree extends PrefixTree {
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/ScenarioOutlineTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/ScenarioOutlineTree.java
new file mode 100644
index 0000000..bbf5a88
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/ScenarioOutlineTree.java
@@ -0,0 +1,26 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+public interface ScenarioOutlineTree extends BasicScenarioTree {
+
+ ExamplesTree examples();
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/ScenarioPrefixTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/ScenarioPrefixTree.java
new file mode 100644
index 0000000..c348b03
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/ScenarioPrefixTree.java
@@ -0,0 +1,23 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+public interface ScenarioPrefixTree extends PrefixTree {
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/ScenarioTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/ScenarioTree.java
new file mode 100644
index 0000000..41fc9ea
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/ScenarioTree.java
@@ -0,0 +1,23 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+public interface ScenarioTree extends BasicScenarioTree {
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/StepPrefixTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/StepPrefixTree.java
new file mode 100644
index 0000000..48a45e8
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/StepPrefixTree.java
@@ -0,0 +1,23 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+public interface StepPrefixTree extends PrefixTree {
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/StepTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/StepTree.java
new file mode 100644
index 0000000..16427b0
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/StepTree.java
@@ -0,0 +1,36 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+import javax.annotation.Nullable;
+
+public interface StepTree extends Tree {
+
+ PrefixTree prefix();
+
+ SyntaxToken sentence();
+
+ @Nullable
+ DocStringTree docString();
+
+ @Nullable
+ TableTree table();
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/SyntaxSpacing.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/SyntaxSpacing.java
new file mode 100644
index 0000000..6007795
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/SyntaxSpacing.java
@@ -0,0 +1,38 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+import java.util.List;
+
+public interface SyntaxSpacing extends Tree {
+
+ String text();
+
+ List trivias();
+
+ int line();
+
+ int column();
+
+ int endLine();
+
+ int endColumn();
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/SyntaxToken.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/SyntaxToken.java
new file mode 100644
index 0000000..6a3e023
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/SyntaxToken.java
@@ -0,0 +1,38 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+import java.util.List;
+
+public interface SyntaxToken extends Tree {
+
+ String text();
+
+ List trivias();
+
+ int line();
+
+ int column();
+
+ int endLine();
+
+ int endColumn();
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/SyntaxTrivia.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/SyntaxTrivia.java
new file mode 100644
index 0000000..fca93e8
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/SyntaxTrivia.java
@@ -0,0 +1,23 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+public interface SyntaxTrivia extends SyntaxToken {
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/TableTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/TableTree.java
new file mode 100644
index 0000000..b18c73a
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/TableTree.java
@@ -0,0 +1,28 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+import java.util.List;
+
+public interface TableTree extends Tree {
+
+ List rows();
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/TagTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/TagTree.java
new file mode 100644
index 0000000..793a4d5
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/TagTree.java
@@ -0,0 +1,30 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+public interface TagTree extends Tree {
+
+ SyntaxToken prefix();
+
+ SyntaxToken value();
+
+ String text();
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/Taggable.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/Taggable.java
new file mode 100644
index 0000000..a2da641
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/Taggable.java
@@ -0,0 +1,28 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+import java.util.List;
+
+public interface Taggable {
+
+ List tags();
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/Tree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/Tree.java
new file mode 100644
index 0000000..d98d376
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/Tree.java
@@ -0,0 +1,81 @@
+/*
+ * 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.plugins.gherkin.api.tree;
+
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+import org.sonar.sslr.grammar.GrammarRuleKey;
+
+import javax.annotation.Nullable;
+import java.util.Iterator;
+
+public interface Tree {
+
+ boolean is(Kind... kind);
+
+ void accept(DoubleDispatchVisitor visitor);
+
+ Iterator childrenIterator();
+
+ @Nullable
+ Tree parent();
+
+ void setParent(Tree parent);
+
+ boolean isLeaf();
+
+ Kind getKind();
+
+ enum Kind implements GrammarRuleKey {
+
+ GHERKIN_DOCUMENT(GherkinDocumentTree.class),
+ FEATURE(FeatureTree.class),
+ FEATURE_DECLARATION(FeatureDeclarationTree.class),
+ BACKGROUND(BackgroundTree.class),
+ SCENARIO(ScenarioTree.class),
+ SCENARIO_OUTLINE(ScenarioOutlineTree.class),
+ EXAMPLES(ExamplesTree.class),
+ STEP(StepTree.class),
+ FEATURE_PREFIX(FeaturePrefixTree.class),
+ BACKGROUND_PREFIX(BackgroundPrefixTree.class),
+ SCENARIO_PREFIX(ScenarioPrefixTree.class),
+ SCENARIO_OUTLINE_PREFIX(ScenarioOutlinePrefixTree.class),
+ EXAMPLES_PREFIX(ExamplesPrefixTree.class),
+ STEP_PREFIX(StepPrefixTree.class),
+ TAG(TagTree.class),
+ NAME(TagTree.class),
+ DESCRIPTION(DescriptionTree.class),
+ TABLE(TableTree.class),
+ DOC_STRING(DocStringTree.class),
+ TOKEN(SyntaxToken.class),
+ TRIVIA(SyntaxTrivia.class),
+ SPACING(SyntaxSpacing.class);
+
+ final Class extends Tree> associatedInterface;
+
+ Kind(Class extends Tree> associatedInterface) {
+ this.associatedInterface = associatedInterface;
+ }
+
+ public Class extends Tree> getAssociatedInterface() {
+ return associatedInterface;
+ }
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/package-info.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/package-info.java
new file mode 100644
index 0000000..f0fc73c
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.plugins.gherkin.api.tree;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/DoubleDispatchVisitor.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/DoubleDispatchVisitor.java
new file mode 100644
index 0000000..11d54cd
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/DoubleDispatchVisitor.java
@@ -0,0 +1,153 @@
+/*
+ * 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.plugins.gherkin.api.visitors;
+
+import com.google.common.base.Preconditions;
+import org.sonar.plugins.gherkin.api.tree.*;
+
+import javax.annotation.Nullable;
+import java.util.Iterator;
+import java.util.List;
+
+public abstract class DoubleDispatchVisitor implements TreeVisitor {
+
+ private TreeVisitorContext context = null;
+
+ @Override
+ public TreeVisitorContext getContext() {
+ Preconditions.checkState(context != null, "this#scanTree(context) should be called to initialised the context before accessing it");
+ return context;
+ }
+
+ @Override
+ public final void scanTree(TreeVisitorContext context) {
+ this.context = context;
+ scan(context.getTopTree());
+ }
+
+ protected void scan(@Nullable Tree tree) {
+ if (tree != null) {
+ tree.accept(this);
+ }
+ }
+
+ protected void scanChildren(Tree tree) {
+ Iterator childrenIterator = tree.childrenIterator();
+ Tree child;
+
+ while (childrenIterator.hasNext()) {
+ child = childrenIterator.next();
+ if (child != null) {
+ child.accept(this);
+ }
+ }
+ }
+
+ protected void scan(List trees) {
+ trees.forEach(this::scan);
+ }
+
+ public void visitGherkinDocument(GherkinDocumentTree tree) {
+ scanChildren(tree);
+ }
+
+ public void visitFeature(FeatureTree tree) {
+ scanChildren(tree);
+ }
+
+ public void visitFeatureDeclaration(FeatureDeclarationTree tree) {
+ scanChildren(tree);
+ }
+
+ public void visitBackground(BackgroundTree tree) {
+ scanChildren(tree);
+ }
+
+ public void visitScenario(ScenarioTree tree) {
+ scanChildren(tree);
+ }
+
+ public void visitScenarioOutline(ScenarioOutlineTree tree) {
+ scanChildren(tree);
+ }
+
+ public void visitExamples(ExamplesTree tree) {
+ scanChildren(tree);
+ }
+
+ public void visitStep(StepTree tree) {
+ scanChildren(tree);
+ }
+
+ public void visitDescription(DescriptionTree tree) {
+ scanChildren(tree);
+ }
+
+ public void visitTag(TagTree tree) {
+ scanChildren(tree);
+ }
+
+ public void visitName(NameTree tree) {
+ scanChildren(tree);
+ }
+
+ public void visitFeaturePrefix(FeaturePrefixTree tree) {
+ scanChildren(tree);
+ }
+
+ public void visitBackgroundPrefix(BackgroundPrefixTree tree) {
+ scanChildren(tree);
+ }
+
+ public void visitScenarioPrefix(ScenarioPrefixTree tree) {
+ scanChildren(tree);
+ }
+
+ public void visitScenarioOutlinePrefix(ScenarioOutlinePrefixTree tree) {
+ scanChildren(tree);
+ }
+
+ public void visitExamplesPrefix(ExamplesPrefixTree tree) {
+ scanChildren(tree);
+ }
+
+ public void visitStepPrefix(StepPrefixTree tree) {
+ scanChildren(tree);
+ }
+
+ public void visitTable(TableTree tree) {
+ scanChildren(tree);
+ }
+
+ public void visitDocString(DocStringTree tree) {
+ scanChildren(tree);
+ }
+
+ public void visitToken(SyntaxToken token) {
+ for (SyntaxTrivia syntaxTrivia : token.trivias()) {
+ syntaxTrivia.accept(this);
+ }
+ }
+
+ public void visitComment(SyntaxTrivia commentToken) {
+ // no sub-tree
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/DoubleDispatchVisitorCheck.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/DoubleDispatchVisitorCheck.java
new file mode 100644
index 0000000..110f19e
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/DoubleDispatchVisitorCheck.java
@@ -0,0 +1,64 @@
+/*
+ * 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.plugins.gherkin.api.visitors;
+
+import org.sonar.gherkin.visitors.Issues;
+import org.sonar.plugins.gherkin.api.GherkinCheck;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.issue.FileIssue;
+import org.sonar.plugins.gherkin.api.visitors.issue.Issue;
+import org.sonar.plugins.gherkin.api.visitors.issue.LineIssue;
+import org.sonar.plugins.gherkin.api.visitors.issue.PreciseIssue;
+
+import java.util.List;
+
+public abstract class DoubleDispatchVisitorCheck extends DoubleDispatchVisitor implements GherkinCheck {
+
+ private Issues issues = new Issues(this);
+
+ @Override
+ public List scanFile(TreeVisitorContext context) {
+ validateParameters();
+ issues.reset();
+ scanTree(context);
+ return issues.getList();
+ }
+
+ @Override
+ public PreciseIssue addPreciseIssue(Tree tree, String message) {
+ return issues.addPreciseIssue(getContext().getFile(), tree, message);
+ }
+
+ @Override
+ public FileIssue addFileIssue(String message) {
+ return issues.addFileIssue(getContext().getFile(), message);
+ }
+
+ @Override
+ public LineIssue addLineIssue(int line, String message) {
+ return issues.addLineIssue(getContext().getFile(), line, message);
+ }
+
+ @Override
+ public void validateParameters() {
+ // Default behavior: do nothing
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/SubscriptionVisitor.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/SubscriptionVisitor.java
new file mode 100644
index 0000000..93ac481
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/SubscriptionVisitor.java
@@ -0,0 +1,101 @@
+/*
+ * 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.plugins.gherkin.api.visitors;
+
+import com.google.common.base.Preconditions;
+import org.sonar.gherkin.tree.impl.GherkinTree;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+public abstract class SubscriptionVisitor implements TreeVisitor {
+
+ private TreeVisitorContext context;
+ private Collection nodesToVisit;
+
+ public abstract List nodesToVisit();
+
+ public void visitNode(Tree tree) {
+ // Default behavior: do nothing.
+ }
+
+ public void leaveNode(Tree tree) {
+ // Default behavior: do nothing.
+ }
+
+ public void visitFile(Tree tree) {
+ // Default behavior: do nothing.
+ }
+
+ public void leaveFile(Tree tree) {
+ // Default behavior: do nothing.
+ }
+
+ @Override
+ public TreeVisitorContext getContext() {
+ Preconditions.checkState(context != null, "this#scanTree(context) should be called to initialised the context before accessing it");
+ return context;
+ }
+
+ @Override
+ public final void scanTree(TreeVisitorContext context) {
+ this.context = context;
+ visitFile(context.getTopTree());
+ scanTree(context.getTopTree());
+ leaveFile(context.getTopTree());
+ }
+
+ public void scanTree(Tree tree) {
+ nodesToVisit = nodesToVisit();
+ visit(tree);
+ }
+
+ private void visit(Tree tree) {
+ boolean isSubscribed = isSubscribed(tree);
+ if (isSubscribed) {
+ visitNode(tree);
+ }
+ visitChildren(tree);
+ if (isSubscribed) {
+ leaveNode(tree);
+ }
+ }
+
+ protected boolean isSubscribed(Tree tree) {
+ return nodesToVisit.contains(tree.getKind());
+ }
+
+ private void visitChildren(Tree tree) {
+ GherkinTree gherkinTree = (GherkinTree) tree;
+
+ if (!gherkinTree.isLeaf()) {
+ for (Iterator iter = gherkinTree.childrenIterator(); iter.hasNext(); ) {
+ Tree next = iter.next();
+
+ if (next != null) {
+ visit(next);
+ }
+ }
+ }
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/SubscriptionVisitorCheck.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/SubscriptionVisitorCheck.java
new file mode 100644
index 0000000..624db8e
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/SubscriptionVisitorCheck.java
@@ -0,0 +1,64 @@
+/*
+ * 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.plugins.gherkin.api.visitors;
+
+import org.sonar.gherkin.visitors.Issues;
+import org.sonar.plugins.gherkin.api.GherkinCheck;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.issue.FileIssue;
+import org.sonar.plugins.gherkin.api.visitors.issue.Issue;
+import org.sonar.plugins.gherkin.api.visitors.issue.LineIssue;
+import org.sonar.plugins.gherkin.api.visitors.issue.PreciseIssue;
+
+import java.util.List;
+
+public abstract class SubscriptionVisitorCheck extends SubscriptionVisitor implements GherkinCheck {
+
+ private Issues issues = new Issues(this);
+
+ @Override
+ public List scanFile(TreeVisitorContext context) {
+ validateParameters();
+ issues.reset();
+ scanTree(context);
+ return issues.getList();
+ }
+
+ @Override
+ public PreciseIssue addPreciseIssue(Tree tree, String message) {
+ return issues.addPreciseIssue(getContext().getFile(), tree, message);
+ }
+
+ @Override
+ public FileIssue addFileIssue(String message) {
+ return issues.addFileIssue(getContext().getFile(), message);
+ }
+
+ @Override
+ public LineIssue addLineIssue(int line, String message) {
+ return issues.addLineIssue(getContext().getFile(), line, message);
+ }
+
+ @Override
+ public void validateParameters() {
+ // Default behavior: do nothing
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/TreeVisitor.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/TreeVisitor.java
new file mode 100644
index 0000000..a35db30
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/TreeVisitor.java
@@ -0,0 +1,28 @@
+/*
+ * 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.plugins.gherkin.api.visitors;
+
+public interface TreeVisitor {
+
+ TreeVisitorContext getContext();
+
+ void scanTree(TreeVisitorContext context);
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/TreeVisitorContext.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/TreeVisitorContext.java
new file mode 100644
index 0000000..b4e0afc
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/TreeVisitorContext.java
@@ -0,0 +1,38 @@
+/*
+ * 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.plugins.gherkin.api.visitors;
+
+import org.sonar.plugins.gherkin.api.tree.GherkinDocumentTree;
+
+import java.io.File;
+
+public interface TreeVisitorContext {
+
+ /**
+ * @return the top tree node of the current file AST representation.
+ */
+ GherkinDocumentTree getTopTree();
+
+ /**
+ * @return the current file
+ */
+ File getFile();
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/issue/FileIssue.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/issue/FileIssue.java
new file mode 100644
index 0000000..e7e25da
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/issue/FileIssue.java
@@ -0,0 +1,85 @@
+/*
+ * 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.plugins.gherkin.api.visitors.issue;
+
+import org.sonar.plugins.gherkin.api.GherkinCheck;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+
+import javax.annotation.Nullable;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FileIssue implements Issue {
+
+ private final GherkinCheck check;
+ private final File file;
+ private Double cost;
+ private final String message;
+ private final List secondaryLocations;
+
+ public FileIssue(GherkinCheck check, File file, String message) {
+ this.check = check;
+ this.file = file;
+ this.message = message;
+ this.secondaryLocations = new ArrayList<>();
+ this.cost = null;
+ }
+
+ public String message() {
+ return message;
+ }
+
+ @Override
+ public GherkinCheck check() {
+ return check;
+ }
+
+ public File file() {
+ return file;
+ }
+
+ @Nullable
+ @Override
+ public Double cost() {
+ return cost;
+ }
+
+ @Override
+ public Issue cost(double cost) {
+ this.cost = cost;
+ return this;
+ }
+
+ public List secondaryLocations() {
+ return secondaryLocations;
+ }
+
+ public FileIssue secondary(Tree tree, String message) {
+ secondaryLocations.add(new IssueLocation(file, tree, message));
+ return this;
+ }
+
+ public FileIssue secondary(File file, Tree tree, String message) {
+ secondaryLocations.add(new IssueLocation(file, tree, message));
+ return this;
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/issue/Issue.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/issue/Issue.java
new file mode 100644
index 0000000..29335ad
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/issue/Issue.java
@@ -0,0 +1,35 @@
+/*
+ * 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.plugins.gherkin.api.visitors.issue;
+
+import org.sonar.plugins.gherkin.api.GherkinCheck;
+
+import javax.annotation.Nullable;
+
+public interface Issue {
+
+ GherkinCheck check();
+
+ @Nullable
+ Double cost();
+
+ Issue cost(double cost);
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/issue/IssueLocation.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/issue/IssueLocation.java
new file mode 100644
index 0000000..e5a1665
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/issue/IssueLocation.java
@@ -0,0 +1,72 @@
+/*
+ * 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.plugins.gherkin.api.visitors.issue;
+
+import org.sonar.gherkin.tree.impl.GherkinTree;
+import org.sonar.plugins.gherkin.api.tree.SyntaxToken;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+
+import javax.annotation.Nullable;
+import java.io.File;
+
+public class IssueLocation {
+
+ private final File file;
+ private final SyntaxToken firstToken;
+ private final SyntaxToken lastToken;
+ private final String message;
+
+ public IssueLocation(File file, Tree tree, @Nullable String message) {
+ this(file, tree, tree, message);
+ }
+
+ public IssueLocation(File file, Tree firstTree, Tree lastTree, @Nullable String message) {
+ this.file = file;
+ this.firstToken = ((GherkinTree) firstTree).getFirstToken();
+ this.lastToken = ((GherkinTree) lastTree).getLastToken();
+ this.message = message;
+ }
+
+ public File file() {
+ return file;
+ }
+
+ @Nullable
+ public String message() {
+ return message;
+ }
+
+ public int startLine() {
+ return firstToken.line();
+ }
+
+ public int startLineOffset() {
+ return firstToken.column();
+ }
+
+ public int endLine() {
+ return lastToken.endLine();
+ }
+
+ public int endLineOffset() {
+ return lastToken.endColumn();
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/issue/LineIssue.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/issue/LineIssue.java
new file mode 100644
index 0000000..a682d63
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/issue/LineIssue.java
@@ -0,0 +1,74 @@
+/*
+ * 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.plugins.gherkin.api.visitors.issue;
+
+import com.google.common.base.Preconditions;
+import org.sonar.plugins.gherkin.api.GherkinCheck;
+
+import javax.annotation.Nullable;
+import java.io.File;
+
+public class LineIssue implements Issue {
+
+ private final GherkinCheck check;
+ private final File file;
+ private Double cost;
+ private final String message;
+ private final int line;
+
+ public LineIssue(GherkinCheck check, File file, int line, String message) {
+ Preconditions.checkArgument(line > 0);
+ this.check = check;
+ this.file = file;
+ this.message = message;
+ this.line = line;
+ this.cost = null;
+ }
+
+ public String message() {
+ return message;
+ }
+
+ public int line() {
+ return line;
+ }
+
+ @Override
+ public GherkinCheck check() {
+ return check;
+ }
+
+ public File file() {
+ return file;
+ }
+
+ @Nullable
+ @Override
+ public Double cost() {
+ return cost;
+ }
+
+ @Override
+ public Issue cost(double cost) {
+ this.cost = cost;
+ return this;
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/issue/PreciseIssue.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/issue/PreciseIssue.java
new file mode 100644
index 0000000..e909bf9
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/issue/PreciseIssue.java
@@ -0,0 +1,79 @@
+/*
+ * 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.plugins.gherkin.api.visitors.issue;
+
+import org.sonar.plugins.gherkin.api.GherkinCheck;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+
+import javax.annotation.Nullable;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class PreciseIssue implements Issue {
+
+ private GherkinCheck check;
+ private Double cost;
+ private IssueLocation primaryLocation;
+ private List secondaryLocations;
+
+ public PreciseIssue(GherkinCheck check, IssueLocation primaryLocation) {
+ this.check = check;
+ this.primaryLocation = primaryLocation;
+ this.secondaryLocations = new ArrayList<>();
+ this.cost = null;
+ }
+
+ @Override
+ public GherkinCheck check() {
+ return check;
+ }
+
+ @Nullable
+ @Override
+ public Double cost() {
+ return cost;
+ }
+
+ @Override
+ public PreciseIssue cost(double cost) {
+ this.cost = cost;
+ return this;
+ }
+
+ public IssueLocation primaryLocation() {
+ return primaryLocation;
+ }
+
+ public List secondaryLocations() {
+ return secondaryLocations;
+ }
+
+ public PreciseIssue secondary(Tree tree, String message) {
+ secondaryLocations.add(new IssueLocation(primaryLocation.file(), tree, message));
+ return this;
+ }
+
+ public PreciseIssue secondary(File file, Tree tree, String message) {
+ secondaryLocations.add(new IssueLocation(file, tree, message));
+ return this;
+ }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/issue/package-info.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/issue/package-info.java
new file mode 100644
index 0000000..b9f2888
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/issue/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.plugins.gherkin.api.visitors.issue;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/package-info.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/package-info.java
new file mode 100644
index 0000000..1215329
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/visitors/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.plugins.gherkin.api.visitors;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/BackgroundPrefixTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/BackgroundPrefixTreeTest.java
new file mode 100644
index 0000000..f4f91f1
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/BackgroundPrefixTreeTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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.parser;
+
+import org.junit.Test;
+
+public class BackgroundPrefixTreeTest extends PrefixTreeTest {
+
+ public BackgroundPrefixTreeTest() {
+ super(GherkinLexicalGrammar.BACKGROUND_PREFIX);
+ }
+
+ @Test
+ public void backgroundPrefix() throws Exception {
+ checkParsed("Background", "Background");
+ checkParsed(" Background", "Background");
+ }
+
+ @Test
+ public void notBackgroundPrefix() throws Exception {
+ checkNotParsed("background");
+ checkNotParsed("BACKGROUND");
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/BackgroundTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/BackgroundTreeTest.java
new file mode 100644
index 0000000..e011f02
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/BackgroundTreeTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.parser;
+
+import org.junit.Test;
+import org.sonar.plugins.gherkin.api.tree.BackgroundTree;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class BackgroundTreeTest extends GherkinTreeTest {
+
+ public BackgroundTreeTest() {
+ super(GherkinLexicalGrammar.BACKGROUND);
+ }
+
+ @Test
+ public void background() throws Exception {
+ BackgroundTree tree;
+
+ tree = checkParsed("Background:");
+ assertThat(tree.steps()).hasSize(0);
+ assertThat(tree.description()).isNull();
+
+ tree = checkParsed("Background: blabla...");
+ assertThat(tree.steps()).hasSize(0);
+ assertThat(tree.description()).isNotNull();
+
+ tree = checkParsed("Background: blabla...\nGiven ...");
+ assertThat(tree.steps()).hasSize(1);
+ assertThat(tree.description()).isNotNull();
+
+ tree = checkParsed("Background:\nGiven ...\nAnd ...");
+ assertThat(tree.steps()).hasSize(2);
+ assertThat(tree.description()).isNull();
+ }
+
+ @Test
+ public void notBackground() throws Exception {
+ checkNotParsed("Background");
+ checkNotParsed("@mytag\nBackground:");
+ }
+
+ private BackgroundTree checkParsed(String toParse) {
+ BackgroundTree tree = (BackgroundTree) parser().parse(toParse);
+ assertThat(tree).isNotNull();
+ assertThat(tree.prefix()).isNotNull();
+ assertThat(tree.colon()).isNotNull();
+ assertThat(tree.steps()).isNotNull();
+ return tree;
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/DescriptionTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/DescriptionTreeTest.java
new file mode 100644
index 0000000..4949ebf
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/DescriptionTreeTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.parser;
+
+import org.junit.Test;
+import org.sonar.plugins.gherkin.api.tree.DescriptionTree;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class DescriptionTreeTest extends GherkinTreeTest {
+
+ public DescriptionTreeTest() {
+ super(GherkinLexicalGrammar.DESCRIPTION);
+ }
+
+ @Test
+ public void description() throws Exception {
+ checkParsed("blabla...", 1);
+ checkParsed("blabla...\nblabla...", 2);
+ checkParsed("blabla...\n\nblabla...", 2);
+ }
+
+ private void checkParsed(String toParse, int size) {
+ DescriptionTree tree = (DescriptionTree) parser().parse(toParse);
+ assertThat(tree).isNotNull();
+ assertThat(tree.descriptionLines()).isNotNull();
+ assertThat(tree.descriptionLines().size()).isEqualTo(size);
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/DocStringTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/DocStringTreeTest.java
new file mode 100644
index 0000000..e89ac45
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/DocStringTreeTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.parser;
+
+import org.junit.Test;
+import org.sonar.plugins.gherkin.api.tree.DocStringTree;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class DocStringTreeTest extends GherkinTreeTest {
+
+ public DocStringTreeTest() {
+ super(GherkinLexicalGrammar.DOC_STRING);
+ }
+
+ @Test
+ public void docString() throws Exception {
+ DocStringTree tree;
+
+ tree = checkParsed("\"\"\"\n\"\"\"");
+ assertThat(tree.contentType()).isNull();
+ assertThat(tree.data()).hasSize(0);
+
+ tree = checkParsed(" \"\"\"\n \"\"\"");
+ assertThat(tree.contentType()).isNull();
+ assertThat(tree.data()).hasSize(0);
+
+ tree = checkParsed("\"\"\"\nblabla\n\"\"\"");
+ assertThat(tree.contentType()).isNull();
+ assertThat(tree.data()).hasSize(1);
+
+ tree = checkParsed("\"\"\"\nblabla\nblabla\n\"\"\"");
+ assertThat(tree.contentType()).isNull();
+ assertThat(tree.data()).hasSize(2);
+
+ tree = checkParsed("\"\"\"\n blabla\n blabla\n\"\"\"");
+ assertThat(tree.contentType()).isNull();
+ assertThat(tree.data()).hasSize(2);
+
+ tree = checkParsed("\"\"\"type\n blabla\n blabla\n\"\"\"");
+ assertThat(tree.contentType()).isNotNull();
+ assertThat(tree.contentType().text()).isEqualTo("type");
+ assertThat(tree.data()).hasSize(2);
+
+ tree = checkParsed("\"\"\"type\n # blabla\n blabla\n\"\"\"");
+ assertThat(tree.contentType()).isNotNull();
+ assertThat(tree.contentType().text()).isEqualTo("type");
+ assertThat(tree.data()).hasSize(2);
+ }
+
+ @Test
+ public void notDocString() throws Exception {
+ checkNotParsed("\"\"");
+ checkNotParsed("\"\"\"\"\"\"");
+ }
+
+ private DocStringTree checkParsed(String toParse) {
+ DocStringTree tree = (DocStringTree) parser().parse(toParse);
+ assertThat(tree).isNotNull();
+ assertThat(tree.prefix()).isNotNull();
+ assertThat(tree.suffix()).isNotNull();
+ assertThat(tree.data()).isNotNull();
+ return tree;
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/ExamplesPrefixTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/ExamplesPrefixTreeTest.java
new file mode 100644
index 0000000..1fac98e
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/ExamplesPrefixTreeTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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.parser;
+
+import org.junit.Test;
+
+public class ExamplesPrefixTreeTest extends PrefixTreeTest {
+
+ public ExamplesPrefixTreeTest() {
+ super(GherkinLexicalGrammar.EXAMPLES_PREFIX);
+ }
+
+ @Test
+ public void examplesPrefix() throws Exception {
+ checkParsed("Examples", "Examples");
+ checkParsed(" Examples", "Examples");
+ }
+
+ @Test
+ public void notExamplesPrefix() throws Exception {
+ checkNotParsed("examples");
+ checkNotParsed("EXAMPLES");
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/ExamplesTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/ExamplesTreeTest.java
new file mode 100644
index 0000000..8fd26af
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/ExamplesTreeTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.parser;
+
+import org.junit.Test;
+import org.sonar.plugins.gherkin.api.tree.ExamplesTree;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class ExamplesTreeTest extends GherkinTreeTest {
+
+ public ExamplesTreeTest() {
+ super(GherkinLexicalGrammar.EXAMPLES);
+ }
+
+ @Test
+ public void examples() throws Exception {
+ ExamplesTree tree;
+
+ tree = checkParsed("Examples:");
+ assertThat(tree.tags()).hasSize(0);
+ assertThat(tree.description()).isNull();
+ assertThat(tree.table()).isNull();
+
+ tree = checkParsed("@mytag @my-tag\nExamples:");
+ assertThat(tree.tags()).hasSize(2);
+ assertThat(tree.description()).isNull();
+ assertThat(tree.table()).isNull();
+
+ tree = checkParsed("@mytag @my-tag\nExamples:\nblabla...\nblabla...");
+ assertThat(tree.tags()).hasSize(2);
+ assertThat(tree.description()).isNotNull();
+ assertThat(tree.table()).isNull();
+
+ tree = checkParsed("@mytag @my-tag\nExamples:\nblabla...\nblabla...\n|d1|d2|");
+ assertThat(tree.tags()).hasSize(2);
+ assertThat(tree.description()).isNotNull();
+ assertThat(tree.table().rows()).hasSize(1);
+
+ tree = checkParsed("@mytag @my-tag\nExamples:\n|d1|d2|\n|1|2|");
+ assertThat(tree.tags()).hasSize(2);
+ assertThat(tree.description()).isNull();
+ assertThat(tree.table().rows()).hasSize(2);
+ }
+
+ @Test
+ public void notExamples() throws Exception {
+ checkNotParsed("Examples");
+ checkNotParsed("Examples :");
+ checkNotParsed("Examples :\n|aa");
+ }
+
+ private ExamplesTree checkParsed(String toParse) {
+ ExamplesTree tree = (ExamplesTree) parser().parse(toParse);
+ assertThat(tree).isNotNull();
+ assertThat(tree.prefix()).isNotNull();
+ assertThat(tree.colon()).isNotNull();
+ assertThat(tree.tags()).isNotNull();
+ return tree;
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/FeatureDeclarationTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/FeatureDeclarationTreeTest.java
new file mode 100644
index 0000000..a54188d
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/FeatureDeclarationTreeTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.parser;
+
+import org.junit.Test;
+import org.sonar.plugins.gherkin.api.tree.FeatureDeclarationTree;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class FeatureDeclarationTreeTest extends GherkinTreeTest {
+
+ public FeatureDeclarationTreeTest() {
+ super(GherkinLexicalGrammar.FEATURE_DECLARATION);
+ }
+
+ @Test
+ public void featureDeclaration() throws Exception {
+ FeatureDeclarationTree tree;
+
+ tree = checkParsed("Feature: my feature");
+ assertThat(tree.name().text()).isEqualTo("my feature");
+ assertThat(tree.tags()).hasSize(0);
+ assertThat(tree.description()).isNull();
+
+ tree = checkParsed("Feature: my feature\nblabla...\nblabla...");
+ assertThat(tree.name().text()).isEqualTo("my feature");
+ assertThat(tree.tags()).hasSize(0);
+ assertThat(tree.description()).isNotNull();
+
+ tree = checkParsed("@mytag @my-tag\nFeature: my feature\nblabla...\nblabla...");
+ assertThat(tree.name().text()).isEqualTo("my feature");
+ assertThat(tree.tags()).hasSize(2);
+ assertThat(tree.description()).isNotNull();
+ }
+
+ @Test
+ public void notFeatureDeclaration() throws Exception {
+ checkNotParsed("feature");
+ checkNotParsed("Feature");
+ checkNotParsed("Feature:");
+ }
+
+ private FeatureDeclarationTree checkParsed(String toParse) {
+ FeatureDeclarationTree tree = (FeatureDeclarationTree) parser().parse(toParse);
+ assertThat(tree).isNotNull();
+ assertThat(tree.tags()).isNotNull();
+ assertThat(tree.name()).isNotNull();
+ return tree;
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/FeaturePrefixTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/FeaturePrefixTreeTest.java
new file mode 100644
index 0000000..aca74ef
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/FeaturePrefixTreeTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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.parser;
+
+import org.junit.Test;
+
+public class FeaturePrefixTreeTest extends PrefixTreeTest {
+
+ public FeaturePrefixTreeTest() {
+ super(GherkinLexicalGrammar.FEATURE_PREFIX);
+ }
+
+ @Test
+ public void featurePrefix() throws Exception {
+ checkParsed("Feature", "Feature");
+ checkParsed(" Feature", "Feature");
+ }
+
+ @Test
+ public void notFeaturePrefix() throws Exception {
+ checkNotParsed("feature");
+ checkNotParsed("FEATURE");
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/FeatureTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/FeatureTreeTest.java
new file mode 100644
index 0000000..4b91c12
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/FeatureTreeTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.parser;
+
+import org.junit.Test;
+import org.sonar.plugins.gherkin.api.tree.FeatureTree;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class FeatureTreeTest extends GherkinTreeTest {
+
+ public FeatureTreeTest() {
+ super(GherkinLexicalGrammar.FEATURE);
+ }
+
+ @Test
+ public void feature() throws Exception {
+ FeatureTree tree;
+
+ tree = checkParsed("Feature: my feature");
+ assertThat(tree.declaration().name().text()).isEqualTo("my feature");
+ assertThat(tree.declaration().tags()).hasSize(0);
+ assertThat(tree.declaration().description()).isNull();
+ assertThat(tree.background()).isNull();
+ assertThat(tree.scenarioOutlines()).hasSize(0);
+ assertThat(tree.scenarios()).hasSize(0);
+
+ tree = checkParsed("Feature: my feature\nblabla...\nblabla...");
+ assertThat(tree.declaration().name().text()).isEqualTo("my feature");
+ assertThat(tree.declaration().tags()).hasSize(0);
+ assertThat(tree.declaration().description()).isNotNull();
+ assertThat(tree.background()).isNull();
+ assertThat(tree.scenarioOutlines()).hasSize(0);
+ assertThat(tree.scenarios()).hasSize(0);
+
+ tree = checkParsed("@mytag @my-tag\nFeature: my feature\nblabla...\nblabla...");
+ assertThat(tree.declaration().name().text()).isEqualTo("my feature");
+ assertThat(tree.declaration().tags()).hasSize(2);
+ assertThat(tree.declaration().description()).isNotNull();
+ assertThat(tree.background()).isNull();
+ assertThat(tree.scenarioOutlines()).hasSize(0);
+ assertThat(tree.scenarios()).hasSize(0);
+
+ tree = checkParsed(
+ "@mytag @my-tag\n" +
+ "Feature:my feature\n" +
+ "blabla...\n" +
+ "blabla...\n" +
+ "Background:");
+ assertThat(tree.declaration().name().text()).isEqualTo("my feature");
+ assertThat(tree.declaration().tags()).hasSize(2);
+ assertThat(tree.declaration().description()).isNotNull();
+ assertThat(tree.background()).isNotNull();
+ assertThat(tree.scenarioOutlines()).hasSize(0);
+ assertThat(tree.scenarios()).hasSize(0);
+
+ tree = checkParsed(
+ "@mytag @my-tag\n" +
+ "Feature:my feature\n" +
+ "blabla...\n" +
+ "blabla...\n" +
+ "Background:\n" +
+ "Given ...\n" +
+ "And ...\n" +
+ "\n" +
+ "Scenario: name1\n" +
+ "Scenario: name2\n" +
+ "Given ...\n" +
+ "And ...\n" +
+ "When ...\n" +
+ "Then ...\n" +
+ "Scenario Outline: name3\n" +
+ "* ...\n" +
+ "When ...\n" +
+ "Then ...\n" +
+ "\n" +
+ "@mytag\n" +
+ "Examples:\n" +
+ "|d1|\n");
+ assertThat(tree.declaration().name().text()).isEqualTo("my feature");
+ assertThat(tree.declaration().tags()).hasSize(2);
+ assertThat(tree.declaration().description()).isNotNull();
+ assertThat(tree.background()).isNotNull();
+ assertThat(tree.scenarioOutlines()).hasSize(1);
+ assertThat(tree.scenarios()).hasSize(2);
+ }
+
+ @Test
+ public void notFeature() throws Exception {
+ checkNotParsed("feature");
+ checkNotParsed("Feature");
+ checkNotParsed("Feature:");
+ }
+
+ private FeatureTree checkParsed(String toParse) {
+ FeatureTree tree = (FeatureTree) parser().parse(toParse);
+ assertThat(tree).isNotNull();
+ assertThat(tree.declaration()).isNotNull();
+ assertThat(tree.scenarios()).isNotNull();
+ assertThat(tree.scenarioOutlines()).isNotNull();
+ return tree;
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinDocumentTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinDocumentTreeTest.java
new file mode 100644
index 0000000..c8c2eef
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinDocumentTreeTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.parser;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+import org.junit.Test;
+import org.sonar.plugins.gherkin.api.tree.GherkinDocumentTree;
+
+import java.io.File;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class GherkinDocumentTreeTest extends GherkinTreeTest {
+
+ public GherkinDocumentTreeTest() {
+ super(GherkinLexicalGrammar.GHERKIN_DOCUMENT);
+ }
+
+ @Test
+ public void gherkinDocument() throws Exception {
+ GherkinDocumentTree tree;
+
+ tree = checkParsed(new File("src/test/resources/parser/empty.feature"));
+ assertThat(tree.feature()).isNull();
+
+ tree = checkParsed(new File("src/test/resources/parser/parse.feature"));
+ assertThat(tree.hasByteOrderMark()).isEqualTo(false);
+ assertThat(tree.feature()).isNotNull();
+ assertThat(tree.feature().scenarioOutlines()).hasSize(1);
+ assertThat(tree.feature().scenarios()).hasSize(2);
+ assertThat(tree.feature().background()).isNotNull();
+
+ tree = checkParsed(new File("src/test/resources/parser/parse-bom.feature"));
+ assertThat(tree.hasByteOrderMark()).isEqualTo(true);
+ assertThat(tree.feature()).isNotNull();
+ assertThat(tree.feature().scenarioOutlines()).hasSize(1);
+ assertThat(tree.feature().scenarios()).hasSize(2);
+ assertThat(tree.feature().background()).isNotNull();
+ }
+
+ @Test
+ public void notGherkinDocument() throws Exception {
+ checkNotParsed(new File("src/test/resources/parser/two-backgrounds.feature"));
+ }
+
+ private GherkinDocumentTree checkParsed(File file) throws Exception {
+ GherkinDocumentTree tree = (GherkinDocumentTree) parser().parse(Files.toString(file, Charsets.UTF_8));
+ assertThat(tree).isNotNull();
+ return tree;
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinGrammarTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinGrammarTest.java
new file mode 100644
index 0000000..5ddad3c
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinGrammarTest.java
@@ -0,0 +1,35 @@
+/*
+ * 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.parser;
+
+import com.google.common.base.Charsets;
+import com.sonar.sslr.api.RecognitionException;
+import org.junit.Test;
+
+public class GherkinGrammarTest {
+
+ @Test(expected = RecognitionException.class)
+ public void parse_error() throws Exception {
+ GherkinParserBuilder
+ .createTestParser(Charsets.UTF_8, GherkinLexicalGrammar.GHERKIN_DOCUMENT)
+ .parse("blabla");
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinTreeTest.java
new file mode 100644
index 0000000..7e43fc5
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinTreeTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.parser;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+import com.sonar.sslr.api.typed.ActionParser;
+import org.sonar.gherkin.parser.GherkinLexicalGrammar;
+import org.sonar.gherkin.parser.GherkinParserBuilder;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+
+import java.io.File;
+
+import static org.junit.Assert.fail;
+
+public abstract class GherkinTreeTest {
+
+ private final ActionParser parser;
+
+ public GherkinTreeTest(GherkinLexicalGrammar ruleKey) {
+ parser = GherkinParserBuilder.createTestParser(Charsets.UTF_8, ruleKey);
+ }
+
+ public ActionParser parser() {
+ return parser;
+ }
+
+ public void checkNotParsed(String toParse) {
+ try {
+ parser.parse(toParse);
+ } catch (Exception e) {
+ return;
+ }
+ fail("Did not throw a RecognitionException as expected.");
+ }
+
+ public void checkNotParsed(File file) {
+ try {
+ parser.parse(Files.toString(file, Charsets.UTF_8));
+ } catch (Exception e) {
+ return;
+ }
+ fail("Did not throw a RecognitionException as expected.");
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/NameTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/NameTreeTest.java
new file mode 100644
index 0000000..e20ed33
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/NameTreeTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.parser;
+
+import org.junit.Test;
+import org.sonar.plugins.gherkin.api.tree.NameTree;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class NameTreeTest extends GherkinTreeTest {
+
+ public NameTreeTest() {
+ super(GherkinLexicalGrammar.NAME);
+ }
+
+ @Test
+ public void name() throws Exception {
+ checkParsed("abc", "abc");
+ checkParsed("abc ", "abc ");
+ checkParsed(" abc", "abc");
+ checkParsed("blabla... blabla...", "blabla... blabla...");
+ }
+
+ private void checkParsed(String toParse, String name) {
+ NameTree tree = (NameTree) parser().parse(toParse);
+ assertThat(tree).isNotNull();
+ assertThat(tree.text()).isEqualTo(name);
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/PrefixTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/PrefixTreeTest.java
new file mode 100644
index 0000000..70f9f6a
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/PrefixTreeTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.parser;
+
+import org.sonar.plugins.gherkin.api.tree.PrefixTree;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public abstract class PrefixTreeTest extends GherkinTreeTest {
+
+ public PrefixTreeTest(GherkinLexicalGrammar ruleKey) {
+ super(ruleKey);
+ }
+
+ public void checkParsed(String toParse, String prefix) {
+ PrefixTree tree = (PrefixTree) parser().parse(toParse);
+ assertThat(tree).isNotNull();
+ assertThat(tree.keyword()).isNotNull();
+ assertThat(tree.text()).isNotNull();
+ assertThat(tree.text()).isEqualTo(prefix);
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/ScenarioOutlinePrefixTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/ScenarioOutlinePrefixTreeTest.java
new file mode 100644
index 0000000..a30331c
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/ScenarioOutlinePrefixTreeTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.parser;
+
+import org.junit.Test;
+
+public class ScenarioOutlinePrefixTreeTest extends PrefixTreeTest {
+
+ public ScenarioOutlinePrefixTreeTest() {
+ super(GherkinLexicalGrammar.SCENARIO_OUTLINE_PREFIX);
+ }
+
+ @Test
+ public void scenarioOutlinePrefix() throws Exception {
+ checkParsed("Scenario Outline", "Scenario Outline");
+ checkParsed(" Scenario Outline", "Scenario Outline");
+ }
+
+ @Test
+ public void notScenarioOutlinePrefix() throws Exception {
+ checkNotParsed("scenario outline");
+ checkNotParsed("SCENARIO OUTLINE");
+ checkNotParsed("Scenario Outline");
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/ScenarioOutlineTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/ScenarioOutlineTreeTest.java
new file mode 100644
index 0000000..fe7a1c4
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/ScenarioOutlineTreeTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.parser;
+
+import org.junit.Test;
+import org.sonar.plugins.gherkin.api.tree.ScenarioOutlineTree;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class ScenarioOutlineTreeTest extends GherkinTreeTest {
+
+ public ScenarioOutlineTreeTest() {
+ super(GherkinLexicalGrammar.SCENARIO_OUTLINE);
+ }
+
+ @Test
+ public void scenarioOutline() throws Exception {
+ ScenarioOutlineTree tree;
+
+ tree = checkParsed("Scenario Outline: name...\nExamples:");
+ assertThat(tree.steps()).hasSize(0);
+ assertThat(tree.tags()).hasSize(0);
+ assertThat(tree.description()).isNull();
+ assertThat(tree.name().text()).isEqualTo("name...");
+ assertThat(tree.examples().table()).isNull();
+
+ tree = checkParsed("Scenario Outline: name...\nblabla...\nExamples:");
+ assertThat(tree.steps()).hasSize(0);
+ assertThat(tree.tags()).hasSize(0);
+ assertThat(tree.description()).isNotNull();
+ assertThat(tree.name().text()).isEqualTo("name...");
+ assertThat(tree.examples().table()).isNull();
+
+ tree = checkParsed("Scenario Outline: name...\nblabla...\nGiven blabla...\n\nExamples:");
+ assertThat(tree.steps()).hasSize(1);
+ assertThat(tree.tags()).hasSize(0);
+ assertThat(tree.description()).isNotNull();
+ assertThat(tree.name().text()).isEqualTo("name...");
+ assertThat(tree.examples().table()).isNull();
+
+ tree = checkParsed("Scenario Outline: name...\nblabla...\nGiven blabla...\n\nExamples:\n|d1|d2|");
+ assertThat(tree.steps()).hasSize(1);
+ assertThat(tree.tags()).hasSize(0);
+ assertThat(tree.description()).isNotNull();
+ assertThat(tree.name().text()).isEqualTo("name...");
+ assertThat(tree.examples().table().rows()).hasSize(1);
+
+ tree = checkParsed("@mytag\nScenario Outline: name...\nblabla...\nGiven blabla...\n\nExamples:\n|d1|d2|");
+ assertThat(tree.steps()).hasSize(1);
+ assertThat(tree.tags()).hasSize(1);
+ assertThat(tree.description()).isNotNull();
+ assertThat(tree.name().text()).isEqualTo("name...");
+ assertThat(tree.examples().table().rows()).hasSize(1);
+
+ tree = checkParsed("@mytag\nScenario Outline: name...\nblabla...\nGiven blabla...\n\n@mytag\nExamples:\n|d1|d2|");
+ assertThat(tree.steps()).hasSize(1);
+ assertThat(tree.tags()).hasSize(1);
+ assertThat(tree.description()).isNotNull();
+ assertThat(tree.name().text()).isEqualTo("name...");
+ assertThat(tree.examples().table().rows()).hasSize(1);
+ assertThat(tree.examples().tags()).hasSize(1);
+ }
+
+ @Test
+ public void notScenarioOutline() throws Exception {
+ checkNotParsed("Scenario");
+ checkNotParsed("Scenario Outline");
+ checkNotParsed("Scenario Outline:");
+ checkNotParsed("Scenario Outline: name...");
+ checkNotParsed("Scenario Outline:\nExamples:");
+ }
+
+ private ScenarioOutlineTree checkParsed(String toParse) {
+ ScenarioOutlineTree tree = (ScenarioOutlineTree) parser().parse(toParse);
+ assertThat(tree).isNotNull();
+ assertThat(tree.prefix()).isNotNull();
+ assertThat(tree.colon()).isNotNull();
+ assertThat(tree.steps()).isNotNull();
+ assertThat(tree.tags()).isNotNull();
+ assertThat(tree.name()).isNotNull();
+ assertThat(tree.examples()).isNotNull();
+ return tree;
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/ScenarioPrefixTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/ScenarioPrefixTreeTest.java
new file mode 100644
index 0000000..cb52259
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/ScenarioPrefixTreeTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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.parser;
+
+import org.junit.Test;
+
+public class ScenarioPrefixTreeTest extends PrefixTreeTest {
+
+ public ScenarioPrefixTreeTest() {
+ super(GherkinLexicalGrammar.SCENARIO_PREFIX);
+ }
+
+ @Test
+ public void scenarioPrefix() throws Exception {
+ checkParsed("Scenario", "Scenario");
+ checkParsed(" Scenario", "Scenario");
+ }
+
+ @Test
+ public void notScenarioPrefix() throws Exception {
+ checkNotParsed("scenario");
+ checkNotParsed("SCENARIO");
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/ScenarioTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/ScenarioTreeTest.java
new file mode 100644
index 0000000..198e84b
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/ScenarioTreeTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.parser;
+
+import org.junit.Test;
+import org.sonar.plugins.gherkin.api.tree.ScenarioTree;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class ScenarioTreeTest extends GherkinTreeTest {
+
+ public ScenarioTreeTest() {
+ super(GherkinLexicalGrammar.SCENARIO);
+ }
+
+ @Test
+ public void scenario() throws Exception {
+ ScenarioTree tree;
+
+ tree = checkParsed("Scenario: name...");
+ assertThat(tree.steps()).hasSize(0);
+ assertThat(tree.tags()).hasSize(0);
+ assertThat(tree.description()).isNull();
+ assertThat(tree.name().text()).isEqualTo("name...");
+
+ tree = checkParsed("@mytag @my-tag\nScenario: name...");
+ assertThat(tree.steps()).hasSize(0);
+ assertThat(tree.tags()).hasSize(2);
+ assertThat(tree.description()).isNull();
+ assertThat(tree.name().text()).isEqualTo("name...");
+
+ tree = checkParsed("@mytag @my-tag\nScenario: name...\nblabal...\nblabla...");
+ assertThat(tree.steps()).hasSize(0);
+ assertThat(tree.tags()).hasSize(2);
+ assertThat(tree.description()).isNotNull();
+ assertThat(tree.name().text()).isEqualTo("name...");
+
+ tree = checkParsed("@mytag @my-tag\nScenario: name...\nGiven ...\nWhen ...");
+ assertThat(tree.steps()).hasSize(2);
+ assertThat(tree.tags()).hasSize(2);
+ assertThat(tree.description()).isNull();
+ assertThat(tree.name().text()).isEqualTo("name...");
+ }
+
+ @Test
+ public void notScenario() throws Exception {
+ checkNotParsed("Scenario");
+ checkNotParsed("Scenario:");
+ checkNotParsed("Scenario : name...");
+ }
+
+ private ScenarioTree checkParsed(String toParse) {
+ ScenarioTree tree = (ScenarioTree) parser().parse(toParse);
+ assertThat(tree).isNotNull();
+ assertThat(tree.prefix()).isNotNull();
+ assertThat(tree.colon()).isNotNull();
+ assertThat(tree.steps()).isNotNull();
+ assertThat(tree.tags()).isNotNull();
+ assertThat(tree.name()).isNotNull();
+ return tree;
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/StepPrefixTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/StepPrefixTreeTest.java
new file mode 100644
index 0000000..8347083
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/StepPrefixTreeTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.parser;
+
+import org.junit.Test;
+
+public class StepPrefixTreeTest extends PrefixTreeTest {
+
+ public StepPrefixTreeTest() {
+ super(GherkinLexicalGrammar.STEP_PREFIX);
+ }
+
+ @Test
+ public void stepPrefix() throws Exception {
+ checkParsed("Given", "Given");
+ checkParsed(" Given", "Given");
+ checkParsed("When", "When");
+ checkParsed(" When", "When");
+ checkParsed("Then", "Then");
+ checkParsed(" Then", "Then");
+ checkParsed("*", "*");
+ checkParsed(" *", "*");
+ checkParsed("And", "And");
+ checkParsed(" And", "And");
+ checkParsed("But", "But");
+ checkParsed(" But", "But");
+ }
+
+ @Test
+ public void notStepPrefix() throws Exception {
+ checkNotParsed("given");
+ checkNotParsed("GIVEN");
+ checkNotParsed("when");
+ checkNotParsed("WHEN");
+ checkNotParsed("then");
+ checkNotParsed("THEN");
+ checkNotParsed("but");
+ checkNotParsed("BUT");
+ checkNotParsed("and");
+ checkNotParsed("AND");
+ checkNotParsed("blabla");
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/StepTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/StepTreeTest.java
new file mode 100644
index 0000000..bd20c68
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/StepTreeTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.parser;
+
+import org.junit.Test;
+import org.sonar.plugins.gherkin.api.tree.StepTree;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class StepTreeTest extends GherkinTreeTest {
+
+ public StepTreeTest() {
+ super(GherkinLexicalGrammar.STEP);
+ }
+
+ @Test
+ public void step() throws Exception {
+ StepTree tree;
+
+ checkParsed("Given I am a customer", "Given", "I am a customer");
+ checkParsed(" Given I am a customer", "Given", "I am a customer");
+ checkParsed(" Given I am a customer ", "Given", "I am a customer ");
+
+ checkParsed("When I add a product to my cart", "When", "I add a product to my cart");
+ checkParsed(" When I add a product to my cart", "When", "I add a product to my cart");
+ checkParsed(" When I add a product to my cart ", "When", "I add a product to my cart ");
+
+ checkParsed("Then I should see the product in my cart", "Then", "I should see the product in my cart");
+ checkParsed(" Then I should see the product in my cart", "Then", "I should see the product in my cart");
+ checkParsed(" Then I should see the product in my cart ", "Then", "I should see the product in my cart ");
+
+ checkParsed("* I should see the product in my cart", "*", "I should see the product in my cart");
+ checkParsed(" * I should see the product in my cart", "*", "I should see the product in my cart");
+ checkParsed(" * I should see the product in my cart ", "*", "I should see the product in my cart ");
+
+ tree = checkParsed("Given I am a customer:\n\"\"\"\nblabla...\nblabla...\n\"\"\"", "Given", "I am a customer:");
+ assertThat(tree.table()).isNull();
+ assertThat(tree.docString()).isNotNull();
+ assertThat(tree.docString().data()).hasSize(2);
+
+ tree = checkParsed("Given I am a customer:\n| abc |\n|2|", "Given", "I am a customer:");
+ assertThat(tree.table()).isNotNull();
+ assertThat(tree.docString()).isNull();
+ assertThat(tree.table().rows()).hasSize(2);
+ }
+
+ @Test
+ public void notStep() throws Exception {
+ checkNotParsed("blabla...");
+ checkNotParsed("GIVEN I am a customer");
+ checkNotParsed("given I am a customer");
+ checkNotParsed("WHEN I add a product to my cart");
+ checkNotParsed("when I add a product to my cart");
+ checkNotParsed("THEN I should see the product in my cart");
+ checkNotParsed("then I should see the product in my cart");
+ }
+
+ private StepTree checkParsed(String toParse, String prefix, String sentence) {
+ StepTree tree = (StepTree) parser().parse(toParse);
+ assertThat(tree).isNotNull();
+ assertThat(tree.prefix()).isNotNull();
+ assertThat(tree.sentence()).isNotNull();
+ assertThat(tree.prefix().keyword()).isNotNull();
+ assertThat(tree.prefix().text()).isNotNull();
+ assertThat(tree.prefix().text()).isEqualTo(prefix);
+ assertThat(tree.sentence().text()).isEqualTo(sentence);
+ return tree;
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/TableTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/TableTreeTest.java
new file mode 100644
index 0000000..e6ef8d9
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/TableTreeTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.parser;
+
+import org.junit.Test;
+import org.sonar.plugins.gherkin.api.tree.TableTree;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class TableTreeTest extends GherkinTreeTest {
+
+ public TableTreeTest() {
+ super(GherkinLexicalGrammar.TABLE);
+ }
+
+ @Test
+ public void table() throws Exception {
+ TableTree tree;
+
+ tree = checkParsed("||");
+ assertThat(tree.rows()).hasSize(1);
+
+ tree = checkParsed(" ||");
+ assertThat(tree.rows()).hasSize(1);
+
+ tree = checkParsed(" | |");
+ assertThat(tree.rows()).hasSize(1);
+
+ tree = checkParsed("| test|");
+ assertThat(tree.rows()).hasSize(1);
+
+ tree = checkParsed("| test| \n | 2 |");
+ assertThat(tree.rows()).hasSize(2);
+
+ tree = checkParsed("| test| \n | 2 | \n | sqfqsf|");
+ assertThat(tree.rows()).hasSize(3);
+ }
+
+ @Test
+ public void notTable() throws Exception {
+ checkNotParsed("abc");
+ checkNotParsed("|abc");
+ checkNotParsed("| abc");
+ }
+
+ private TableTree checkParsed(String toParse) {
+ TableTree tree = (TableTree) parser().parse(toParse);
+ assertThat(tree).isNotNull();
+ assertThat(tree.rows()).isNotNull();
+ return tree;
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/TagTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/TagTreeTest.java
new file mode 100644
index 0000000..067bfc3
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/TagTreeTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.parser;
+
+import org.junit.Test;
+import org.sonar.plugins.gherkin.api.tree.TagTree;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class TagTreeTest extends GherkinTreeTest {
+
+ public TagTreeTest() {
+ super(GherkinLexicalGrammar.TAG);
+ }
+
+ @Test
+ public void tag() throws Exception {
+ checkParsed("@mytag", "mytag");
+ checkParsed("@my-tag ", "my-tag");
+ checkParsed(" @my-tag ", "my-tag");
+ }
+
+ @Test
+ public void notTag() throws Exception {
+ checkNotParsed("@");
+ checkNotParsed("@@mytag");
+ checkNotParsed("mytag");
+ }
+
+ private void checkParsed(String toParse, String tag) {
+ TagTree tree = (TagTree) parser().parse(toParse);
+ assertThat(tree).isNotNull();
+ assertThat(tree.text()).isEqualTo(tag);
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/SyntaxHighlighterVisitorTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/SyntaxHighlighterVisitorTest.java
new file mode 100644
index 0000000..6aed816
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/SyntaxHighlighterVisitorTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.visitors;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.sensor.highlighting.TypeOfText;
+import org.sonar.api.batch.sensor.internal.SensorContextTester;
+import org.sonar.gherkin.parser.GherkinParserBuilder;
+import org.sonar.plugins.gherkin.api.tree.GherkinDocumentTree;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.TreeVisitorContext;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.api.batch.sensor.highlighting.TypeOfText.*;
+
+public class SyntaxHighlighterVisitorTest {
+
+ private SyntaxHighlighterVisitor highlighterVisitor;
+ private SensorContextTester sensorContext;
+ private File file;
+ private DefaultInputFile inputFile;
+ private TreeVisitorContext visitorContext;
+
+ @Rule
+ public final TemporaryFolder tempFolder = new TemporaryFolder();
+
+ @Before
+ public void setUp() throws IOException {
+ DefaultFileSystem fileSystem = new DefaultFileSystem(tempFolder.getRoot());
+ fileSystem.setEncoding(Charsets.UTF_8);
+ file = tempFolder.newFile();
+ inputFile = new DefaultInputFile("moduleKey", file.getName())
+ .setLanguage("gherkin")
+ .setType(InputFile.Type.MAIN);
+ fileSystem.add(inputFile);
+
+ sensorContext = SensorContextTester.create(tempFolder.getRoot());
+ sensorContext.setFileSystem(fileSystem);
+ visitorContext = mock(TreeVisitorContext.class);
+ highlighterVisitor = new SyntaxHighlighterVisitor(sensorContext);
+ when(visitorContext.getFile()).thenReturn(file);
+ }
+
+ @Test
+ public void tag() throws Exception {
+ highlight("@my-tag\nFeature: my feature...");
+ assertHighlighting(1, 0, 7, ANNOTATION);
+ }
+
+ @Test
+ public void name() throws Exception {
+ highlight("@my-tag\nFeature: my feature...");
+ assertHighlighting(2, 9, 13, STRING);
+ }
+
+ @Test
+ public void feature_prefix() throws Exception {
+ highlight("Feature: my feature...");
+ assertHighlighting(1, 0, 7, KEYWORD);
+ }
+
+ @Test
+ public void background_prefix() throws Exception {
+ highlight("Feature: my feature...\nBackground:");
+ assertHighlighting(2, 0, 10, KEYWORD);
+ }
+
+ @Test
+ public void scenario_prefix() throws Exception {
+ highlight("Feature: my feature...\nScenario: my scenario...");
+ assertHighlighting(2, 0, 8, KEYWORD);
+ }
+
+ @Test
+ public void scenario_outline_prefix() throws Exception {
+ highlight("Feature: my feature...\nScenario Outline: my scenario...\nExamples:");
+ assertHighlighting(2, 0, 16, KEYWORD);
+ }
+
+ @Test
+ public void examples_prefix() throws Exception {
+ highlight("Feature: my feature...\nScenario Outline: my scenario...\nExamples:");
+ assertHighlighting(3, 0, 8, KEYWORD);
+ }
+
+ @Test
+ public void step_prefix() throws Exception {
+ highlight("Feature: my feature...\nScenario: my scenario...\nGiven blabla...\nWhen blabla...\nThen blabla...\n* blabla...");
+ assertHighlighting(3, 0, 5, KEYWORD);
+ assertHighlighting(4, 0, 4, KEYWORD);
+ assertHighlighting(5, 0, 4, KEYWORD);
+ assertHighlighting(6, 0, 1, KEYWORD);
+ }
+
+ @Test
+ public void comment1() throws Exception {
+ highlight("# blabla\nFeature: my feature...");
+ assertHighlighting(1, 0, 8, COMMENT);
+ }
+
+ @Test
+ public void comment2() throws Exception {
+ highlight("#blabla\nFeature: my feature...");
+ assertHighlighting(1, 0, 7, COMMENT);
+ }
+
+ @Test
+ public void byte_order_mark() throws Exception {
+ highlight("\ufeffFeature: my feature...");
+ assertHighlighting(1, 0, 7, KEYWORD);
+ }
+
+ private void highlight(String string) throws Exception {
+ inputFile.initMetadata(string);
+ Tree tree = GherkinParserBuilder.createParser(Charsets.UTF_8).parse(string);
+ when(visitorContext.getTopTree()).thenReturn((GherkinDocumentTree) tree);
+
+ Files.write(string, file, Charsets.UTF_8);
+ highlighterVisitor.scanTree(visitorContext);
+ }
+
+ private void assertHighlighting(int line, int column, int length, TypeOfText type) {
+ for (int i = column; i < column + length; i++) {
+ List typeOfTexts = sensorContext.highlightingTypeAt("moduleKey:" + file.getName(), line, i);
+ assertThat(typeOfTexts).hasSize(1);
+ assertThat(typeOfTexts.get(0)).isEqualTo(type);
+ }
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/metrics/GherkinCommentAnalyserTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/metrics/GherkinCommentAnalyserTest.java
new file mode 100644
index 0000000..126e6f9
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/metrics/GherkinCommentAnalyserTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.visitors.metrics;
+
+import org.junit.Test;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class GherkinCommentAnalyserTest {
+
+ private GherkinCommentAnalyser analyser = new GherkinCommentAnalyser();
+
+ @Test
+ public void content() {
+ assertThat(analyser.getContents("# comment")).isEqualTo(" comment");
+ assertThat(analyser.getContents("#comment")).isEqualTo("comment");
+ }
+
+ @Test
+ public void blank() {
+ assertThat(analyser.isBlank(" ")).isTrue();
+ assertThat(analyser.isBlank(" ")).isTrue();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void unknown_type_of_comments() {
+ analyser.getContents("");
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/metrics/MetricsTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/metrics/MetricsTest.java
new file mode 100644
index 0000000..09d5097
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/metrics/MetricsTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.visitors.metrics;
+
+import com.google.common.base.Charsets;
+import org.junit.Test;
+import org.sonar.gherkin.parser.GherkinParserBuilder;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+
+import java.io.File;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class MetricsTest {
+
+ @Test
+ public void metrics_UTF8_file() {
+ String path = "src/test/resources/metrics/metrics.feature";
+ Tree tree = GherkinParserBuilder.createParser(Charsets.UTF_8).parse(new File(path));
+ assertMetrics(tree);
+ }
+
+ @Test
+ public void metrics_UTF8_file_with_BOM() {
+ String path = "src/test/resources/metrics/metrics-bom.feature";
+ Tree tree = GherkinParserBuilder.createParser(Charsets.UTF_8).parse(new File(path));
+ assertMetrics(tree);
+ }
+
+ private void assertMetrics(Tree tree) {
+ assertThat(new LinesOfCodeVisitor(tree).getNumberOfLinesOfCode()).isEqualTo(22);
+ assertThat(new StatementsVisitor(tree).getNumberOfStatements()).isEqualTo(10);
+ assertThat(new CommentLinesVisitor(tree).getNumberOfCommentLines()).isEqualTo(2);
+ assertThat(new FunctionsVisitor(tree).getNumberOfFunctions()).isEqualTo(4);
+ assertThat(new ClassesVisitor(tree).getNumberOfClasses()).isEqualTo(1);
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/metrics/MetricsVisitorTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/metrics/MetricsVisitorTest.java
new file mode 100644
index 0000000..761fecf
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/metrics/MetricsVisitorTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.visitors.metrics;
+
+import com.google.common.base.Charsets;
+import org.junit.Test;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.sensor.internal.SensorContextTester;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.gherkin.parser.GherkinParserBuilder;
+import org.sonar.plugins.gherkin.api.tree.GherkinDocumentTree;
+import org.sonar.plugins.gherkin.api.visitors.TreeVisitorContext;
+
+import java.io.File;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class MetricsVisitorTest {
+
+ @Test
+ public void test() {
+ File moduleBaseDir = new File("src/test/resources/metrics/");
+ SensorContextTester context = SensorContextTester.create(moduleBaseDir);
+
+ DefaultInputFile inputFile = new DefaultInputFile("moduleKey", "metrics.feature")
+ .setModuleBaseDir(moduleBaseDir.toPath())
+ .setLanguage("gherkin")
+ .setType(InputFile.Type.MAIN);
+
+ context.fileSystem().add(inputFile);
+
+ MetricsVisitor metricsVisitor = new MetricsVisitor(context);
+
+ TreeVisitorContext treeVisitorContext = mock(TreeVisitorContext.class);
+ when(treeVisitorContext.getFile()).thenReturn(inputFile.file());
+ when(treeVisitorContext.getTopTree()).thenReturn((GherkinDocumentTree) GherkinParserBuilder.createParser(Charsets.UTF_8).parse(inputFile.file()));
+
+ metricsVisitor.scanTree(treeVisitorContext);
+
+ String componentKey = "moduleKey:metrics.feature";
+ assertThat(context.measure(componentKey, CoreMetrics.NCLOC).value()).isEqualTo(22);
+ assertThat(context.measure(componentKey, CoreMetrics.STATEMENTS).value()).isEqualTo(10);
+ assertThat(context.measure(componentKey, CoreMetrics.COMMENT_LINES).value()).isEqualTo(2);
+ assertThat(context.measure(componentKey, CoreMetrics.FUNCTIONS).value()).isEqualTo(4);
+ assertThat(context.measure(componentKey, CoreMetrics.CLASSES).value()).isEqualTo(1);
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/plugins/gherkin/api/CustomGherkinRulesDefinitionTest.java b/gherkin-frontend/src/test/java/org/sonar/plugins/gherkin/api/CustomGherkinRulesDefinitionTest.java
new file mode 100644
index 0000000..983cca9
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/plugins/gherkin/api/CustomGherkinRulesDefinitionTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.plugins.gherkin.api;
+
+import org.junit.Test;
+import org.sonar.api.server.rule.RulesDefinition;
+import org.sonar.check.Rule;
+import org.sonar.check.RuleProperty;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class CustomGherkinRulesDefinitionTest {
+
+ private static final String REPOSITORY_NAME = "Custom Rule Repository";
+ private static final String REPOSITORY_KEY = "CustomRuleRepository";
+
+ private static final String RULE_NAME = "This is my custom rule";
+ private static final String RULE_KEY = "MyCustomRule";
+
+ @Test
+ public void test() {
+ MyCustomGherkinRulesDefinition rulesDefinition = new MyCustomGherkinRulesDefinition();
+ RulesDefinition.Context context = new RulesDefinition.Context();
+ rulesDefinition.define(context);
+ RulesDefinition.Repository repository = context.repository(REPOSITORY_KEY);
+
+ assertThat(repository.name()).isEqualTo(REPOSITORY_NAME);
+ assertThat(repository.language()).isEqualTo("gherkin");
+ assertThat(repository.rules()).hasSize(1);
+
+ RulesDefinition.Rule customRule = repository.rule(RULE_KEY);
+ assertThat(customRule).isNotNull();
+ assertThat(customRule.key()).isEqualTo(RULE_KEY);
+ assertThat(customRule.name()).isEqualTo(RULE_NAME);
+
+ RulesDefinition.Param param = repository.rules().get(0).params().get(0);
+ assertThat(param.key()).isEqualTo("customParam");
+ assertThat(param.description()).isEqualTo("Custom parameter");
+ assertThat(param.defaultValue()).isEqualTo("Default value");
+ }
+
+ @Rule(
+ key = RULE_KEY,
+ name = RULE_NAME,
+ description = "desc",
+ tags = {"bug"})
+ public class MyCustomRule extends DoubleDispatchVisitor {
+ @RuleProperty(
+ key = "customParam",
+ description = "Custom parameter",
+ defaultValue = "Default value")
+ public String customParam = "value";
+ }
+
+ public static class MyCustomGherkinRulesDefinition extends CustomGherkinRulesDefinition {
+
+ @Override
+ public String repositoryName() {
+ return REPOSITORY_NAME;
+ }
+
+ @Override
+ public String repositoryKey() {
+ return REPOSITORY_KEY;
+ }
+
+ @Override
+ public Class[] checkClasses() {
+ return new Class[]{MyCustomRule.class};
+ }
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/plugins/gherkin/api/visitors/IssueLocationTest.java b/gherkin-frontend/src/test/java/org/sonar/plugins/gherkin/api/visitors/IssueLocationTest.java
new file mode 100644
index 0000000..7c456aa
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/plugins/gherkin/api/visitors/IssueLocationTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.plugins.gherkin.api.visitors;
+
+import org.junit.Test;
+import org.sonar.gherkin.tree.impl.InternalSyntaxToken;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.issue.IssueLocation;
+
+import java.io.File;
+import java.util.Collections;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class IssueLocationTest {
+
+ private static final File FILE = mock(File.class);
+ private static final String MESSAGE = "message";
+
+ @Test
+ public void several_lines_tokens() throws Exception {
+ String tokenValue = "blabla\\\nblabla...";
+
+ IssueLocation location = new IssueLocation(FILE, createToken(3, 2, tokenValue), MESSAGE);
+ assertThat(location.startLine()).isEqualTo(3);
+ assertThat(location.endLine()).isEqualTo(4);
+ assertThat(location.startLineOffset()).isEqualTo(2);
+ assertThat(location.endLineOffset()).isEqualTo(9);
+ assertThat(location.file()).isEqualTo(FILE);
+ }
+
+ private Tree createToken(int line, int column, String tokenValue) {
+ return new InternalSyntaxToken(line, column, tokenValue, Collections.emptyList(), false, false);
+ }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/plugins/gherkin/api/visitors/IssueTest.java b/gherkin-frontend/src/test/java/org/sonar/plugins/gherkin/api/visitors/IssueTest.java
new file mode 100644
index 0000000..f4c1f6e
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/plugins/gherkin/api/visitors/IssueTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.plugins.gherkin.api.visitors;
+
+import org.junit.Test;
+import org.sonar.gherkin.tree.impl.InternalSyntaxToken;
+import org.sonar.plugins.gherkin.api.GherkinCheck;
+import org.sonar.plugins.gherkin.api.visitors.issue.FileIssue;
+import org.sonar.plugins.gherkin.api.visitors.issue.IssueLocation;
+import org.sonar.plugins.gherkin.api.visitors.issue.LineIssue;
+import org.sonar.plugins.gherkin.api.visitors.issue.PreciseIssue;
+
+import java.io.File;
+import java.util.Collections;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class IssueTest {
+
+ private static final GherkinCheck CHECK = new DoubleDispatchVisitorCheck() {
+ };
+ private static final String MESSAGE = "message";
+ private static final InternalSyntaxToken TOKEN = new InternalSyntaxToken(5, 1, "value", Collections.emptyList(), false, false);
+ private static final File FILE = mock(File.class);
+
+ @Test
+ public void test_file_issue() throws Exception {
+ FileIssue fileIssue = new FileIssue(CHECK, FILE, MESSAGE);
+
+ assertThat(fileIssue.check()).isEqualTo(CHECK);
+ assertThat(fileIssue.cost()).isNull();
+ assertThat(fileIssue.message()).isEqualTo(MESSAGE);
+ assertThat(fileIssue.file()).isEqualTo(FILE);
+
+ fileIssue.cost(42);
+ assertThat(fileIssue.cost()).isEqualTo(42);
+
+ fileIssue.secondary(FILE, TOKEN, "secondary message");
+ assertThat(fileIssue.secondaryLocations()).hasSize(1);
+ assertThat(fileIssue.secondaryLocations().get(0).message()).isEqualTo("secondary message");
+ assertThat(fileIssue.secondaryLocations().get(0).file()).isEqualTo(FILE);
+
+ fileIssue.secondary(TOKEN, "new secondary message");
+ assertThat(fileIssue.secondaryLocations()).hasSize(2);
+ assertThat(fileIssue.secondaryLocations().get(1).message()).isEqualTo("new secondary message");
+ }
+
+ @Test
+ public void test_line_issue() throws Exception {
+ LineIssue lineIssue = new LineIssue(CHECK, FILE, 42, MESSAGE);
+
+ assertThat(lineIssue.check()).isEqualTo(CHECK);
+ assertThat(lineIssue.cost()).isNull();
+ assertThat(lineIssue.message()).isEqualTo(MESSAGE);
+ assertThat(lineIssue.line()).isEqualTo(42);
+ assertThat(lineIssue.file()).isEqualTo(FILE);
+
+ lineIssue.cost(42);
+ assertThat(lineIssue.cost()).isEqualTo(42);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void test_negative_line_issue() throws Exception {
+ new LineIssue(CHECK, FILE, -1, MESSAGE);
+ }
+
+ @Test
+ public void test_precise_issue() throws Exception {
+ IssueLocation primaryLocation = new IssueLocation(FILE, TOKEN, MESSAGE);
+ PreciseIssue preciseIssue = new PreciseIssue(CHECK, primaryLocation);
+
+ assertThat(preciseIssue.check()).isEqualTo(CHECK);
+ assertThat(preciseIssue.cost()).isNull();
+ assertThat(preciseIssue.primaryLocation()).isEqualTo(primaryLocation);
+ assertThat(preciseIssue.secondaryLocations()).isEmpty();
+
+ preciseIssue.cost(42);
+ assertThat(preciseIssue.cost()).isEqualTo(42);
+
+ assertThat(primaryLocation.startLine()).isEqualTo(5);
+ assertThat(primaryLocation.endLine()).isEqualTo(5);
+ assertThat(primaryLocation.startLineOffset()).isEqualTo(1);
+ assertThat(primaryLocation.endLineOffset()).isEqualTo(6);
+ assertThat(primaryLocation.message()).isEqualTo(MESSAGE);
+
+ preciseIssue.secondary(FILE, TOKEN, "secondary message");
+ assertThat(preciseIssue.secondaryLocations()).hasSize(1);
+ assertThat(preciseIssue.secondaryLocations().get(0).message()).isEqualTo("secondary message");
+ assertThat(preciseIssue.secondaryLocations().get(0).file()).isEqualTo(FILE);
+
+ preciseIssue.secondary(TOKEN, "new secondary message");
+ assertThat(preciseIssue.secondaryLocations()).hasSize(2);
+ assertThat(preciseIssue.secondaryLocations().get(1).message()).isEqualTo("new secondary message");
+ }
+
+}
diff --git a/gherkin-frontend/src/test/resources/metrics/metrics-bom.feature b/gherkin-frontend/src/test/resources/metrics/metrics-bom.feature
new file mode 100644
index 0000000..0d30677
--- /dev/null
+++ b/gherkin-frontend/src/test/resources/metrics/metrics-bom.feature
@@ -0,0 +1,30 @@
+Feature: My feature...
+ blaba...
+ blabla...
+
+ # blabla...
+ #blabla...
+
+ Background:
+ Given blabla...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ @tag
+ Scenario Outline: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Examples:
+ | var |
+ | 1 |
+ | 2 |
+
+ Scenario: Scenario 3
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-frontend/src/test/resources/metrics/metrics.feature b/gherkin-frontend/src/test/resources/metrics/metrics.feature
new file mode 100644
index 0000000..5e3d783
--- /dev/null
+++ b/gherkin-frontend/src/test/resources/metrics/metrics.feature
@@ -0,0 +1,30 @@
+Feature: My feature...
+ blaba...
+ blabla...
+
+ # blabla...
+ #blabla...
+
+ Background:
+ Given blabla...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ @tag
+ Scenario Outline: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Examples:
+ | var |
+ | 1 |
+ | 2 |
+
+ Scenario: Scenario 3
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-frontend/src/test/resources/parser/empty.feature b/gherkin-frontend/src/test/resources/parser/empty.feature
new file mode 100644
index 0000000..e9f5340
--- /dev/null
+++ b/gherkin-frontend/src/test/resources/parser/empty.feature
@@ -0,0 +1 @@
+#blabla...
diff --git a/gherkin-frontend/src/test/resources/parser/parse-bom.feature b/gherkin-frontend/src/test/resources/parser/parse-bom.feature
new file mode 100644
index 0000000..adc035d
--- /dev/null
+++ b/gherkin-frontend/src/test/resources/parser/parse-bom.feature
@@ -0,0 +1,31 @@
+@mytag
+Feature: My feature...
+ blaba...
+ blabla...
+
+ # blabla...
+ #blabla...
+
+ Background:
+ Given blabla...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ @tag
+ Scenario Outline: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Examples:
+ | var |
+ | 1 |
+ | 2 |
+
+ Scenario: Scenario 3
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-frontend/src/test/resources/parser/parse.feature b/gherkin-frontend/src/test/resources/parser/parse.feature
new file mode 100644
index 0000000..2d5ab23
--- /dev/null
+++ b/gherkin-frontend/src/test/resources/parser/parse.feature
@@ -0,0 +1,31 @@
+@mytag
+Feature: My feature...
+ blaba...
+ blabla...
+
+ # blabla...
+ #blabla...
+
+ Background:
+ Given blabla...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ @tag
+ Scenario Outline: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Examples:
+ | var |
+ | 1 |
+ | 2 |
+
+ Scenario: Scenario 3
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/gherkin-frontend/src/test/resources/parser/two-backgrounds.feature b/gherkin-frontend/src/test/resources/parser/two-backgrounds.feature
new file mode 100644
index 0000000..57d3a83
--- /dev/null
+++ b/gherkin-frontend/src/test/resources/parser/two-backgrounds.feature
@@ -0,0 +1,33 @@
+Feature: My feature...
+ blaba...
+ blabla...
+
+ # blabla...
+ #blabla...
+
+ Background:
+ Given blabla...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Background:
+ Given blabla...
+
+ @tag
+ Scenario Outline: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Examples:
+ | var |
+ | 1 |
+ | 2 |
+
+ Scenario: Scenario 3
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/plugin/plugins/gherkin-custom-rules-plugin/pom.xml b/its/plugin/plugins/gherkin-custom-rules-plugin/pom.xml
new file mode 100644
index 0000000..d362ba5
--- /dev/null
+++ b/its/plugin/plugins/gherkin-custom-rules-plugin/pom.xml
@@ -0,0 +1,54 @@
+
+
+ 4.0.0
+
+
+ com.racodond.sonarqube.plugin.gherkin
+ gherkin-its-plugin-plugins
+ 1.0-SNAPSHOT
+
+
+ gherkin-custom-rules-plugin
+ sonar-plugin
+
+ SonarQube Gherkin Analyzer :: Integration Tests :: Plugin :: Plugins :: Custom Rules
+
+
+
+ org.sonarsource.sonarqube
+ sonar-plugin-api
+ provided
+
+
+ com.racodond.sonarqube.plugin.gherkin
+ sonar-gherkin-plugin
+ sonar-plugin
+
+
+
+
+
+
+ org.sonarsource.sonar-packaging-maven-plugin
+ sonar-packaging-maven-plugin
+ 1.15
+ true
+
+ org.sonar.gherkin.MyGherkinCustomRulesPlugin
+ gherkin
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.5.1
+
+
+ 1.8
+
+
+
+
+
+
diff --git a/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/java/org/sonar/gherkin/MyGherkinCustomRulesDefinition.java b/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/java/org/sonar/gherkin/MyGherkinCustomRulesDefinition.java
new file mode 100644
index 0000000..91ac7e9
--- /dev/null
+++ b/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/java/org/sonar/gherkin/MyGherkinCustomRulesDefinition.java
@@ -0,0 +1,57 @@
+/*
+ * 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;
+
+import org.sonar.gherkin.checks.ForbiddenTagCheck;
+import org.sonar.gherkin.checks.ForbiddenNameContentCheck;
+import org.sonar.plugins.gherkin.api.CustomGherkinRulesDefinition;
+
+/**
+ * Extension point to define a Gherkin rule repository.
+ */
+public class MyGherkinCustomRulesDefinition extends CustomGherkinRulesDefinition {
+
+ /**
+ * Provide the repository name.
+ */
+ @Override
+ public String repositoryName() {
+ return "My Gherkin Custom Repository";
+ }
+
+ /**
+ * Provide the repository key.
+ */
+ @Override
+ public String repositoryKey() {
+ return "custom-gherkin";
+ }
+
+ /**
+ * Provide the list of classes implementing rules.
+ */
+ @Override
+ public Class[] checkClasses() {
+ return new Class[]{
+ ForbiddenTagCheck.class,
+ ForbiddenNameContentCheck.class
+ };
+ }
+}
diff --git a/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/java/org/sonar/gherkin/MyGherkinCustomRulesPlugin.java b/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/java/org/sonar/gherkin/MyGherkinCustomRulesPlugin.java
new file mode 100644
index 0000000..6dcd9ca
--- /dev/null
+++ b/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/java/org/sonar/gherkin/MyGherkinCustomRulesPlugin.java
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+import com.google.common.collect.ImmutableList;
+import org.sonar.api.Plugin;
+
+/**
+ * Extension point to define a SonarQube plugin.
+ */
+public class MyGherkinCustomRulesPlugin implements Plugin {
+
+ @Override
+ public void define(Context context) {
+ context.addExtensions(
+ ImmutableList.of(
+ MyGherkinCustomRulesDefinition.class));
+ }
+
+}
diff --git a/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/java/org/sonar/gherkin/checks/ForbiddenNameContentCheck.java b/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/java/org/sonar/gherkin/checks/ForbiddenNameContentCheck.java
new file mode 100644
index 0000000..2747374
--- /dev/null
+++ b/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/java/org/sonar/gherkin/checks/ForbiddenNameContentCheck.java
@@ -0,0 +1,54 @@
+/*
+ * 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.collect.ImmutableList;
+import org.sonar.check.Priority;
+import org.sonar.check.Rule;
+import org.sonar.plugins.gherkin.api.tree.NameTree;
+import org.sonar.plugins.gherkin.api.tree.Tree;
+import org.sonar.plugins.gherkin.api.visitors.SubscriptionVisitorCheck;
+import org.sonar.squidbridge.annotations.SqaleConstantRemediation;
+
+import java.util.List;
+
+@Rule(
+ key = "forbidden-name-content",
+ priority = Priority.CRITICAL,
+ name = "'WTF' should never appear in name",
+ tags = {"bug"})
+@SqaleConstantRemediation("5min")
+public class ForbiddenNameContentCheck extends SubscriptionVisitorCheck {
+
+ private static final String FORBIDDEN_NAME_CONTENT = "wtf";
+
+ @Override
+ public List nodesToVisit() {
+ return ImmutableList.of(Tree.Kind.NAME);
+ }
+
+ @Override
+ public void visitNode(Tree tree) {
+ if (((NameTree) tree).text().toLowerCase().contains(FORBIDDEN_NAME_CONTENT)) {
+ addPreciseIssue(tree, "Remove this usage of \"WTF\".");
+ }
+ }
+
+}
diff --git a/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/java/org/sonar/gherkin/checks/ForbiddenTagCheck.java b/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/java/org/sonar/gherkin/checks/ForbiddenTagCheck.java
new file mode 100644
index 0000000..278ef40
--- /dev/null
+++ b/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/java/org/sonar/gherkin/checks/ForbiddenTagCheck.java
@@ -0,0 +1,50 @@
+/*
+ * 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.collect.ImmutableSet;
+import org.sonar.check.Priority;
+import org.sonar.check.Rule;
+import org.sonar.plugins.gherkin.api.tree.TagTree;
+import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitorCheck;
+import org.sonar.squidbridge.annotations.SqaleConstantRemediation;
+
+import java.util.Set;
+
+@Rule(
+ key = "forbidden-tag",
+ priority = Priority.MAJOR,
+ name = "Forbidden tag should not be used",
+ tags = {"convention"})
+@SqaleConstantRemediation("5min")
+public class ForbiddenTagCheck extends DoubleDispatchVisitorCheck {
+
+ private static final Set FORBIDDEN_TAGS = ImmutableSet.of("foo", "bar");
+
+ @Override
+ public void visitTag(TagTree tree) {
+ if (FORBIDDEN_TAGS.contains(tree.text().toLowerCase())) {
+ addPreciseIssue(tree, "Remove this usage of the forbidden \"" + tree.text() + "\" tag.");
+ }
+ // super method must be called in order to visit what is under the key node in the syntax tree
+ super.visitTag(tree);
+ }
+
+}
diff --git a/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/java/org/sonar/gherkin/checks/package-info.java b/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/java/org/sonar/gherkin/checks/package-info.java
new file mode 100644
index 0000000..9c4e378
--- /dev/null
+++ b/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/java/org/sonar/gherkin/checks/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.gherkin.checks;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
diff --git a/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/java/org/sonar/gherkin/package-info.java b/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/java/org/sonar/gherkin/package-info.java
new file mode 100644
index 0000000..9c82628
--- /dev/null
+++ b/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/java/org/sonar/gherkin/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.gherkin;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
diff --git a/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/resources/org/sonar/l10n/gherkin/rules/custom-gherkin/forbidden-name-content.html b/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/resources/org/sonar/l10n/gherkin/rules/custom-gherkin/forbidden-name-content.html
new file mode 100644
index 0000000..cba95e8
--- /dev/null
+++ b/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/resources/org/sonar/l10n/gherkin/rules/custom-gherkin/forbidden-name-content.html
@@ -0,0 +1,3 @@
+
+ "WTF" should not appear in names.
+
diff --git a/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/resources/org/sonar/l10n/gherkin/rules/custom-gherkin/forbidden-tag.html b/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/resources/org/sonar/l10n/gherkin/rules/custom-gherkin/forbidden-tag.html
new file mode 100644
index 0000000..df91588
--- /dev/null
+++ b/its/plugin/plugins/gherkin-custom-rules-plugin/src/main/resources/org/sonar/l10n/gherkin/rules/custom-gherkin/forbidden-tag.html
@@ -0,0 +1,7 @@
+
+ The following tags should not be used:
+
+
+
foo
+
bar
+
diff --git a/its/plugin/plugins/gherkin-custom-rules-plugin/src/test/java/org/sonar/gherkin/MyGherkinCustomRulesDefinitionTest.java b/its/plugin/plugins/gherkin-custom-rules-plugin/src/test/java/org/sonar/gherkin/MyGherkinCustomRulesDefinitionTest.java
new file mode 100644
index 0000000..9fe725b
--- /dev/null
+++ b/its/plugin/plugins/gherkin-custom-rules-plugin/src/test/java/org/sonar/gherkin/MyGherkinCustomRulesDefinitionTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+import org.junit.Test;
+import org.sonar.api.server.rule.RulesDefinition;
+import org.sonar.check.Rule;
+import org.sonar.gherkin.checks.ForbiddenTagCheck;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class MyGherkinCustomRulesDefinitionTest {
+
+ @Test
+ public void test() {
+ MyGherkinCustomRulesDefinition rulesDefinition = new MyGherkinCustomRulesDefinition();
+
+ RulesDefinition.Context context = new RulesDefinition.Context();
+ rulesDefinition.define(context);
+
+ RulesDefinition.Repository repository = context.repository("custom-gherkin");
+
+ assertThat(repository.name()).isEqualTo("My Gherkin Custom Repository");
+ assertThat(repository.language()).isEqualTo("gherkin");
+ assertThat(repository.rules()).hasSize(2);
+
+ RulesDefinition.Rule forbiddenKeysRule = repository.rule(ForbiddenTagCheck.class.getAnnotation(Rule.class).key());
+ assertThat(forbiddenKeysRule).isNotNull();
+ assertThat(forbiddenKeysRule.name()).isEqualTo(ForbiddenTagCheck.class.getAnnotation(Rule.class).name());
+ }
+
+}
diff --git a/its/plugin/plugins/gherkin-custom-rules-plugin/src/test/java/org/sonar/gherkin/MyGherkinCustomRulesPluginTest.java b/its/plugin/plugins/gherkin-custom-rules-plugin/src/test/java/org/sonar/gherkin/MyGherkinCustomRulesPluginTest.java
new file mode 100644
index 0000000..82b9e80
--- /dev/null
+++ b/its/plugin/plugins/gherkin-custom-rules-plugin/src/test/java/org/sonar/gherkin/MyGherkinCustomRulesPluginTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+import org.junit.Test;
+import org.sonar.api.Plugin;
+import org.sonar.api.utils.Version;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class MyGherkinCustomRulesPluginTest {
+
+ @Test
+ public void should_get_the_right_version() {
+ Plugin.Context context = new Plugin.Context(Version.create(5, 6));
+ new MyGherkinCustomRulesPlugin().define(context);
+ assertThat(context.getSonarQubeVersion().major()).isEqualTo(5);
+ assertThat(context.getSonarQubeVersion().minor()).isEqualTo(6);
+ }
+
+ @Test
+ public void should_get_the_right_number_of_extensions() {
+ Plugin.Context context = new Plugin.Context(Version.create(5, 6));
+ new MyGherkinCustomRulesPlugin().define(context);
+ assertThat(context.getExtensions()).hasSize(1);
+ }
+
+}
diff --git a/its/plugin/plugins/gherkin-custom-rules-plugin/src/test/java/org/sonar/gherkin/checks/ForbiddenNameContentCheckTest.java b/its/plugin/plugins/gherkin-custom-rules-plugin/src/test/java/org/sonar/gherkin/checks/ForbiddenNameContentCheckTest.java
new file mode 100644
index 0000000..305cacc
--- /dev/null
+++ b/its/plugin/plugins/gherkin-custom-rules-plugin/src/test/java/org/sonar/gherkin/checks/ForbiddenNameContentCheckTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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 java.io.File;
+
+public class ForbiddenNameContentCheckTest {
+
+ @Test
+ public void test() {
+ GherkinCheckVerifier.verify(new ForbiddenNameContentCheck(), new File("src/test/resources/checks/forbidden-name-content.feature"));
+ }
+
+}
diff --git a/its/plugin/plugins/gherkin-custom-rules-plugin/src/test/java/org/sonar/gherkin/checks/ForbiddenTagCheckTest.java b/its/plugin/plugins/gherkin-custom-rules-plugin/src/test/java/org/sonar/gherkin/checks/ForbiddenTagCheckTest.java
new file mode 100644
index 0000000..6efd6bd
--- /dev/null
+++ b/its/plugin/plugins/gherkin-custom-rules-plugin/src/test/java/org/sonar/gherkin/checks/ForbiddenTagCheckTest.java
@@ -0,0 +1,37 @@
+/*
+ * 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 java.io.File;
+
+public class ForbiddenTagCheckTest {
+
+ @Test
+ public void test() {
+ GherkinCheckVerifier.issues(new ForbiddenTagCheck(), new File("src/test/resources/checks/forbidden-tag.feature"))
+ .next().atLine(1).withMessage("Remove this usage of the forbidden \"bar\" tag.")
+ .next().atLine(4).withMessage("Remove this usage of the forbidden \"foo\" tag.")
+ .noMore();
+ }
+
+}
diff --git a/its/plugin/plugins/gherkin-custom-rules-plugin/src/test/resources/checks/forbidden-name-content.feature b/its/plugin/plugins/gherkin-custom-rules-plugin/src/test/resources/checks/forbidden-name-content.feature
new file mode 100644
index 0000000..00bad92
--- /dev/null
+++ b/its/plugin/plugins/gherkin-custom-rules-plugin/src/test/resources/checks/forbidden-name-content.feature
@@ -0,0 +1,13 @@
+# Noncompliant [[sc=10;ec=27]] {{Remove this usage of "WTF".}}
+Feature: WTF My feature...
+
+ Scenario: Scenario #1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ # Noncompliant [[sc=13;ec=28]] {{Remove this usage of "WTF".}}
+ Scenario: Scenario WTF #2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/plugin/plugins/gherkin-custom-rules-plugin/src/test/resources/checks/forbidden-tag.feature b/its/plugin/plugins/gherkin-custom-rules-plugin/src/test/resources/checks/forbidden-tag.feature
new file mode 100644
index 0000000..d09bb99
--- /dev/null
+++ b/its/plugin/plugins/gherkin-custom-rules-plugin/src/test/resources/checks/forbidden-tag.feature
@@ -0,0 +1,14 @@
+@bar
+Feature: My feature...
+
+ @foo
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ @mytag
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/plugin/plugins/pom.xml b/its/plugin/plugins/pom.xml
new file mode 100644
index 0000000..9e2aecb
--- /dev/null
+++ b/its/plugin/plugins/pom.xml
@@ -0,0 +1,20 @@
+
+
+ 4.0.0
+
+
+ com.racodond.sonarqube.plugin.gherkin
+ gherkin-its-plugin
+ 1.0-SNAPSHOT
+
+
+ gherkin-its-plugin-plugins
+ SonarQube Gherkin Analyzer :: Integration Tests :: Plugin :: Plugins
+ pom
+
+
+ gherkin-custom-rules-plugin
+
+
+
diff --git a/its/plugin/pom.xml b/its/plugin/pom.xml
new file mode 100644
index 0000000..77b99bc
--- /dev/null
+++ b/its/plugin/pom.xml
@@ -0,0 +1,21 @@
+
+
+ 4.0.0
+
+
+ com.racodond.sonarqube.plugin.gherkin
+ gherkin-its
+ 1.0-SNAPSHOT
+
+
+ gherkin-its-plugin
+ SonarQube Gherkin Analyzer :: Integration Tests :: Plugin
+ pom
+
+
+ plugins
+ tests
+
+
+
diff --git a/its/plugin/projects/custom-rules/src/forbidden-name-content.feature b/its/plugin/projects/custom-rules/src/forbidden-name-content.feature
new file mode 100644
index 0000000..1e4b933
--- /dev/null
+++ b/its/plugin/projects/custom-rules/src/forbidden-name-content.feature
@@ -0,0 +1,11 @@
+Feature: My feature...
+
+ Scenario: Scenario #1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario WTF #2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/plugin/projects/custom-rules/src/forbidden-tag.feature b/its/plugin/projects/custom-rules/src/forbidden-tag.feature
new file mode 100644
index 0000000..e2a355d
--- /dev/null
+++ b/its/plugin/projects/custom-rules/src/forbidden-tag.feature
@@ -0,0 +1,12 @@
+@bar
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/plugin/projects/issues/src/dir/file2.feature b/its/plugin/projects/issues/src/dir/file2.feature
new file mode 100644
index 0000000..3c08d07
--- /dev/null
+++ b/its/plugin/projects/issues/src/dir/file2.feature
@@ -0,0 +1,23 @@
+@mytag
+Feature: My feature...
+
+ # blabla...
+ # blabla...
+ @nrt
+ Scenario: Scenario #1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario #2
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario Outline: Scenario #2
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Examples:
+ |d1|
diff --git a/its/plugin/projects/issues/src/file1.feature b/its/plugin/projects/issues/src/file1.feature
new file mode 100644
index 0000000..7fd41df
--- /dev/null
+++ b/its/plugin/projects/issues/src/file1.feature
@@ -0,0 +1,18 @@
+@mytag @nrt
+Feature: My feature...
+
+ # blabla...
+ # blabla...
+ Scenario: Scenario #1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ @nrt
+ Scenario Outline: Scenario #3
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Examples:
+ |d1|
diff --git a/its/plugin/projects/metrics/src/dir/file2.feature b/its/plugin/projects/metrics/src/dir/file2.feature
new file mode 100644
index 0000000..23ded22
--- /dev/null
+++ b/its/plugin/projects/metrics/src/dir/file2.feature
@@ -0,0 +1,21 @@
+Feature: My feature...
+
+ # blabla...
+ # blabla...
+ Scenario: Scenario #1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario #2
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario Outline: Scenario #2
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Examples:
+ |d1|
diff --git a/its/plugin/projects/metrics/src/file1.feature b/its/plugin/projects/metrics/src/file1.feature
new file mode 100644
index 0000000..a95a69d
--- /dev/null
+++ b/its/plugin/projects/metrics/src/file1.feature
@@ -0,0 +1,16 @@
+Feature: My feature...
+
+ # blabla...
+ # blabla...
+ Scenario: Scenario #1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario Outline: Scenario #3
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Examples:
+ |d1|
diff --git a/its/plugin/tests/pom.xml b/its/plugin/tests/pom.xml
new file mode 100644
index 0000000..1f20bd2
--- /dev/null
+++ b/its/plugin/tests/pom.xml
@@ -0,0 +1,54 @@
+
+
+ 4.0.0
+
+
+ com.racodond.sonarqube.plugin.gherkin
+ gherkin-its-plugin
+ 1.0-SNAPSHOT
+
+
+ gherkin-its-plugin-tests
+ SonarQube Gherkin Analyzer :: Integration Tests :: Plugin :: Tests
+
+
+
+ org.sonarsource.sonarqube
+ sonar-plugin-api
+ provided
+
+
+ org.sonarsource.orchestrator
+ sonar-orchestrator
+ test
+
+
+ org.easytesting
+ fest-assert
+ test
+
+
+ junit
+ junit
+ test
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ org/sonar/gherkin/its/Tests.java
+
+
+
+
+
+
+
+
diff --git a/its/plugin/tests/src/test/java/org/sonar/gherkin/its/CustomRulesTest.java b/its/plugin/tests/src/test/java/org/sonar/gherkin/its/CustomRulesTest.java
new file mode 100644
index 0000000..3ae7ddd
--- /dev/null
+++ b/its/plugin/tests/src/test/java/org/sonar/gherkin/its/CustomRulesTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.its;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.sonar.wsclient.issue.Issue;
+import org.sonar.wsclient.issue.IssueClient;
+import org.sonar.wsclient.issue.IssueQuery;
+
+import java.io.File;
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class CustomRulesTest {
+
+ @org.junit.ClassRule
+ public static Orchestrator orchestrator = Tests.ORCHESTRATOR;
+
+ private static final String PROJECT_KEY = "custom-rules";
+ private static IssueClient issueClient;
+
+ @BeforeClass
+ public static void init() {
+ orchestrator.resetData();
+
+ SonarScanner build = Tests.createSonarScannerBuild()
+ .setProjectDir(new File("../projects/custom-rules/"))
+ .setProjectKey(PROJECT_KEY)
+ .setProjectName(PROJECT_KEY);
+
+ orchestrator.getServer().provisionProject(PROJECT_KEY, PROJECT_KEY);
+ Tests.setProfile("gherkin-custom-rules-profile", PROJECT_KEY);
+ orchestrator.executeBuild(build);
+
+ issueClient = orchestrator.getServer().wsClient().issueClient();
+ }
+
+ @Test
+ public void issues_against_rule_forbidden_tag() {
+ List issues = issueClient.find(IssueQuery.create().rules("custom-gherkin:forbidden-tag")).list();
+
+ assertThat(issues).hasSize(1);
+
+ Issue issue = issues.get(0);
+ assertThat(issue.line()).isEqualTo(1);
+ assertThat(issue.message()).isEqualTo("Remove this usage of the forbidden \"bar\" tag.");
+ assertThat(issue.debt()).isEqualTo("5min");
+ assertThat(issue.severity()).isEqualTo("MAJOR");
+ }
+
+ @Test
+ public void issues_against_rule_forbidden_name_content() {
+ List issues = issueClient.find(IssueQuery.create().rules("custom-gherkin:forbidden-name-content")).list();
+
+ assertThat(issues).hasSize(1);
+
+ Issue issue = issues.get(0);
+ assertThat(issue.line()).isEqualTo(8);
+ assertThat(issue.message()).isEqualTo("Remove this usage of \"WTF\".");
+ assertThat(issue.debt()).isEqualTo("5min");
+ assertThat(issue.severity()).isEqualTo("CRITICAL");
+ }
+
+}
diff --git a/its/plugin/tests/src/test/java/org/sonar/gherkin/its/IssuesTest.java b/its/plugin/tests/src/test/java/org/sonar/gherkin/its/IssuesTest.java
new file mode 100644
index 0000000..4930dc3
--- /dev/null
+++ b/its/plugin/tests/src/test/java/org/sonar/gherkin/its/IssuesTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.its;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import org.junit.Test;
+import org.sonar.wsclient.issue.Issue;
+import org.sonar.wsclient.issue.IssueQuery;
+
+import java.io.File;
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class IssuesTest {
+
+ @org.junit.ClassRule
+ public static Orchestrator orchestrator = Tests.ORCHESTRATOR;
+
+ private static final String PROJECT_KEY = "issues";
+
+ @org.junit.BeforeClass
+ public static void init() {
+ orchestrator.resetData();
+
+ SonarScanner build = Tests.createSonarScannerBuild()
+ .setProjectDir(new File("../projects/issues/"))
+ .setProjectKey(PROJECT_KEY)
+ .setProjectName(PROJECT_KEY);
+
+ orchestrator.getServer().provisionProject(PROJECT_KEY, PROJECT_KEY);
+ Tests.setProfile("allowed-tags-only-profile", PROJECT_KEY);
+ orchestrator.executeBuild(build);
+ }
+
+ @Test
+ public void issue_for_rule_allowed_tags() {
+ List issues = orchestrator.getServer().wsClient().issueClient().find(IssueQuery.create()).list();
+
+ assertThat(issues).hasSize(2);
+
+ assertThat(issues.get(0).ruleKey()).isEqualTo("gherkin:allowed-tags");
+ assertThat(issues.get(0).severity()).isEqualTo("MAJOR");
+ assertThat(issues.get(0).line()).isEqualTo(1);
+
+ assertThat(issues.get(1).ruleKey()).isEqualTo("gherkin:allowed-tags");
+ assertThat(issues.get(1).severity()).isEqualTo("MAJOR");
+ assertThat(issues.get(1).line()).isEqualTo(1);
+ }
+
+}
diff --git a/its/plugin/tests/src/test/java/org/sonar/gherkin/its/MetricsTest.java b/its/plugin/tests/src/test/java/org/sonar/gherkin/its/MetricsTest.java
new file mode 100644
index 0000000..1da719f
--- /dev/null
+++ b/its/plugin/tests/src/test/java/org/sonar/gherkin/its/MetricsTest.java
@@ -0,0 +1,185 @@
+/*
+ * 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.its;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.sonar.wsclient.Sonar;
+import org.sonar.wsclient.services.Measure;
+import org.sonar.wsclient.services.Resource;
+import org.sonar.wsclient.services.ResourceQuery;
+
+import java.io.File;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class MetricsTest {
+
+ @ClassRule
+ public static Orchestrator orchestrator = Tests.ORCHESTRATOR;
+
+ private static final String PROJECT_KEY = "metrics";
+ private static Sonar wsClient;
+
+ @BeforeClass
+ public static void init() {
+ orchestrator.resetData();
+
+ SonarScanner build = Tests.createSonarScannerBuild()
+ .setProjectDir(new File("../projects/metrics/"))
+ .setProjectKey(PROJECT_KEY)
+ .setProjectName(PROJECT_KEY);
+
+ orchestrator.getServer().provisionProject(PROJECT_KEY, PROJECT_KEY);
+ Tests.setProfile("empty-profile", PROJECT_KEY);
+ orchestrator.executeBuild(build);
+
+ wsClient = orchestrator.getServer().getWsClient();
+ }
+
+ @Test
+ public void project_measures() {
+ assertThat(getProjectMeasure("lines").getIntValue()).isEqualTo(39);
+ assertThat(getProjectMeasure("ncloc").getIntValue()).isEqualTo(26);
+ assertThat(getProjectMeasure("classes").getIntValue()).isEqualTo(2);
+ assertThat(getProjectMeasure("functions").getIntValue()).isEqualTo(5);
+ assertThat(getProjectMeasure("statements").getIntValue()).isEqualTo(15);
+ assertThat(getProjectMeasure("files").getIntValue()).isEqualTo(2);
+ assertThat(getProjectMeasure("directories").getIntValue()).isEqualTo(2);
+
+ assertThat(getProjectMeasure("comment_lines").getIntValue()).isEqualTo(4);
+ assertThat(getProjectMeasure("comment_lines_density").getValue()).isEqualTo(13.3);
+
+ assertThat(getProjectMeasure("complexity")).isNull();
+ assertThat(getProjectMeasure("complexity_in_functions")).isNull();
+ assertThat(getProjectMeasure("function_complexity")).isNull();
+ assertThat(getProjectMeasure("function_complexity_distribution")).isNull();
+ assertThat(getProjectMeasure("file_complexity")).isNull();
+
+ assertThat(getProjectMeasure("duplicated_lines").getIntValue()).isEqualTo(0);
+ assertThat(getProjectMeasure("duplicated_files").getIntValue()).isEqualTo(0);
+ assertThat(getProjectMeasure("duplicated_blocks").getIntValue()).isEqualTo(0);
+ assertThat(getProjectMeasure("duplicated_lines_density").getValue()).isEqualTo(0.0);
+
+ assertThat(getProjectMeasure("violations").getIntValue()).isEqualTo(0);
+ }
+
+ @Test
+ public void dir_measures() {
+ assertThat(getDirMeasure("lines").getIntValue()).isEqualTo(22);
+ assertThat(getDirMeasure("ncloc").getIntValue()).isEqualTo(15);
+ assertThat(getDirMeasure("classes").getIntValue()).isEqualTo(1);
+ assertThat(getDirMeasure("functions").getIntValue()).isEqualTo(3);
+ assertThat(getDirMeasure("statements").getIntValue()).isEqualTo(9);
+ assertThat(getDirMeasure("files").getIntValue()).isEqualTo(1);
+ assertThat(getDirMeasure("directories").getIntValue()).isEqualTo(1);
+
+ assertThat(getDirMeasure("comment_lines").getIntValue()).isEqualTo(2);
+ assertThat(getDirMeasure("comment_lines_density").getValue()).isEqualTo(11.8);
+
+ assertThat(getDirMeasure("complexity")).isNull();
+ assertThat(getDirMeasure("complexity_in_functions")).isNull();
+ assertThat(getDirMeasure("function_complexity")).isNull();
+ assertThat(getDirMeasure("function_complexity_distribution")).isNull();
+ assertThat(getDirMeasure("file_complexity")).isNull();
+
+ assertThat(getDirMeasure("duplicated_lines").getIntValue()).isEqualTo(0);
+ assertThat(getDirMeasure("duplicated_files").getIntValue()).isEqualTo(0);
+ assertThat(getDirMeasure("duplicated_blocks").getIntValue()).isEqualTo(0);
+ assertThat(getDirMeasure("duplicated_lines_density").getValue()).isEqualTo(0.0);
+
+ assertThat(getDirMeasure("violations").getIntValue()).isEqualTo(0);
+ }
+
+ @Test
+ public void file1_measures() {
+ assertThat(getFile1Measure("lines").getIntValue()).isEqualTo(17);
+ assertThat(getFile1Measure("ncloc").getIntValue()).isEqualTo(11);
+ assertThat(getFile1Measure("classes").getIntValue()).isEqualTo(1);
+ assertThat(getFile1Measure("functions").getIntValue()).isEqualTo(2);
+ assertThat(getFile1Measure("statements").getIntValue()).isEqualTo(6);
+
+ assertThat(getFile1Measure("comment_lines").getIntValue()).isEqualTo(2);
+ assertThat(getFile1Measure("comment_lines_density").getValue()).isEqualTo(15.4);
+
+ assertThat(getFile1Measure("complexity")).isNull();
+ assertThat(getFile1Measure("complexity_in_functions")).isNull();
+ assertThat(getFile1Measure("function_complexity")).isNull();
+ assertThat(getFile1Measure("function_complexity_distribution")).isNull();
+ assertThat(getFile1Measure("file_complexity")).isNull();
+
+ assertThat(getFile1Measure("duplicated_lines")).isNull();
+ assertThat(getFile1Measure("duplicated_blocks")).isNull();
+ assertThat(getFile1Measure("duplicated_lines_density")).isNull();
+
+ assertThat(getFile1Measure("violations")).isNull();
+ }
+
+ @Test
+ public void file2_measures() {
+ assertThat(getFile2Measure("lines").getIntValue()).isEqualTo(22);
+ assertThat(getFile2Measure("ncloc").getIntValue()).isEqualTo(15);
+ assertThat(getFile2Measure("classes").getIntValue()).isEqualTo(1);
+ assertThat(getFile2Measure("functions").getIntValue()).isEqualTo(3);
+ assertThat(getFile2Measure("statements").getIntValue()).isEqualTo(9);
+
+ assertThat(getFile2Measure("comment_lines").getIntValue()).isEqualTo(2);
+ assertThat(getFile2Measure("comment_lines_density").getValue()).isEqualTo(11.8);
+
+ assertThat(getFile2Measure("complexity")).isNull();
+ assertThat(getFile2Measure("complexity_in_functions")).isNull();
+ assertThat(getFile2Measure("function_complexity")).isNull();
+ assertThat(getFile2Measure("function_complexity_distribution")).isNull();
+ assertThat(getFile2Measure("file_complexity")).isNull();
+
+ assertThat(getFile2Measure("duplicated_lines")).isNull();
+ assertThat(getFile2Measure("duplicated_blocks")).isNull();
+ assertThat(getFile2Measure("duplicated_lines_density")).isNull();
+
+ assertThat(getFile2Measure("violations")).isNull();
+ }
+
+ private Measure getProjectMeasure(String metricKey) {
+ Resource resource = wsClient.find(ResourceQuery.createForMetrics(PROJECT_KEY, metricKey));
+ return resource == null ? null : resource.getMeasure(metricKey);
+ }
+
+ private Measure getDirMeasure(String metricKey) {
+ Resource resource = wsClient.find(ResourceQuery.createForMetrics(PROJECT_KEY + ":src/dir", metricKey));
+ return resource == null ? null : resource.getMeasure(metricKey);
+ }
+
+ private Measure getFileMeasure(String metricKey, String fileKey) {
+ Resource resource = wsClient.find(ResourceQuery.createForMetrics(fileKey, metricKey));
+ return resource == null ? null : resource.getMeasure(metricKey);
+ }
+
+ private Measure getFile1Measure(String metricKey) {
+ return getFileMeasure(metricKey, PROJECT_KEY + ":src/file1.feature");
+ }
+
+ private Measure getFile2Measure(String metricKey) {
+ return getFileMeasure(metricKey, PROJECT_KEY + ":src/dir/file2.feature");
+ }
+
+}
diff --git a/its/plugin/tests/src/test/java/org/sonar/gherkin/its/Tests.java b/its/plugin/tests/src/test/java/org/sonar/gherkin/its/Tests.java
new file mode 100644
index 0000000..03e8169
--- /dev/null
+++ b/its/plugin/tests/src/test/java/org/sonar/gherkin/its/Tests.java
@@ -0,0 +1,59 @@
+/*
+ * 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.its;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import com.sonar.orchestrator.locator.FileLocation;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+import java.io.File;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+ CustomRulesTest.class,
+ MetricsTest.class,
+ IssuesTest.class,
+})
+public class Tests {
+
+ @org.junit.ClassRule
+ public static final Orchestrator ORCHESTRATOR = Orchestrator.builderEnv()
+ .addPlugin(FileLocation.byWildcardFilename(new File("../../../sonar-gherkin-plugin/target"), "sonar-gherkin-plugin-*-SNAPSHOT.jar"))
+ .addPlugin(FileLocation.byWildcardFilename(new File("../plugins/gherkin-custom-rules-plugin/target"), "gherkin-custom-rules-plugin-*-SNAPSHOT.jar"))
+ .restoreProfileAtStartup(FileLocation.ofClasspath("/org/sonar/gherkin/its/profiles/allowed-tags-only-profile.xml"))
+ .restoreProfileAtStartup(FileLocation.ofClasspath("/org/sonar/gherkin/its/profiles/empty-profile.xml"))
+ .restoreProfileAtStartup(FileLocation.ofClasspath("/org/sonar/gherkin/its/profiles/gherkin-custom-rules-profile.xml"))
+ .build();
+
+ public static SonarScanner createSonarScannerBuild() {
+ SonarScanner build = SonarScanner.create();
+ build.setProjectVersion("1.0");
+ build.setSourceEncoding("UTF-8");
+ build.setSourceDirs("src");
+ return build;
+ }
+
+ public static void setProfile(String profileName, String projectKey) {
+ ORCHESTRATOR.getServer().associateProjectToQualityProfile(projectKey, "gherkin", profileName);
+ }
+
+}
diff --git a/its/plugin/tests/src/test/resources/org/sonar/gherkin/its/profiles/allowed-tags-only-profile.xml b/its/plugin/tests/src/test/resources/org/sonar/gherkin/its/profiles/allowed-tags-only-profile.xml
new file mode 100644
index 0000000..aca94cb
--- /dev/null
+++ b/its/plugin/tests/src/test/resources/org/sonar/gherkin/its/profiles/allowed-tags-only-profile.xml
@@ -0,0 +1,16 @@
+
+
+ allowed-tags-only-profile
+ gherkin
+
+
+ gherkin
+ allowed-tags
+ MAJOR
+
+ allowedTags
+ smoke,nrt
+
+
+
+
diff --git a/its/plugin/tests/src/test/resources/org/sonar/gherkin/its/profiles/empty-profile.xml b/its/plugin/tests/src/test/resources/org/sonar/gherkin/its/profiles/empty-profile.xml
new file mode 100644
index 0000000..5cde9e2
--- /dev/null
+++ b/its/plugin/tests/src/test/resources/org/sonar/gherkin/its/profiles/empty-profile.xml
@@ -0,0 +1,6 @@
+
+
+ empty-profile
+ gherkin
+
+
diff --git a/its/plugin/tests/src/test/resources/org/sonar/gherkin/its/profiles/gherkin-custom-rules-profile.xml b/its/plugin/tests/src/test/resources/org/sonar/gherkin/its/profiles/gherkin-custom-rules-profile.xml
new file mode 100644
index 0000000..58200a8
--- /dev/null
+++ b/its/plugin/tests/src/test/resources/org/sonar/gherkin/its/profiles/gherkin-custom-rules-profile.xml
@@ -0,0 +1,19 @@
+
+
+ gherkin-custom-rules-profile
+ gherkin
+
+
+ custom-gherkin
+ forbidden-tag
+ MAJOR
+
+
+
+ custom-gherkin
+ forbidden-name-content
+ CRITICAL
+
+
+
+
diff --git a/its/pom.xml b/its/pom.xml
new file mode 100644
index 0000000..7eef9b4
--- /dev/null
+++ b/its/pom.xml
@@ -0,0 +1,44 @@
+
+
+ 4.0.0
+
+
+ com.racodond.sonarqube.plugin.gherkin
+ gherkin
+ 1.0-SNAPSHOT
+
+
+ gherkin-its
+ SonarQube Gherkin Analyzer :: Integration Tests
+ pom
+
+ Integration tests of the SonarQube Gherkin Analyzer
+ 2016
+
+ David RACODON
+ mailto:david.racodon@gmail.com
+
+
+
+ GNU LGPL 3
+ http://www.gnu.org/licenses/lgpl.txt
+ repo
+
+
+
+
+
+ racodond
+ David RACODON
+ david.racodon@gmail.com
+ https://www.linkedin.com/pub/david-racodon/11/62/283
+
+
+
+
+ plugin
+ ruling
+
+
+
diff --git a/its/ruling/pom.xml b/its/ruling/pom.xml
new file mode 100644
index 0000000..6e505eb
--- /dev/null
+++ b/its/ruling/pom.xml
@@ -0,0 +1,20 @@
+
+
+ 4.0.0
+
+
+ com.racodond.sonarqube.plugin.gherkin
+ gherkin-its
+ 1.0-SNAPSHOT
+
+
+ gherkin-its-ruling
+ SonarQube Gherkin Analyzer :: Integration Tests :: Ruling
+ pom
+
+
+ tests
+
+
+
diff --git a/its/ruling/projects/custom/allowed-tags/allowed-tags-custom.feature b/its/ruling/projects/custom/allowed-tags/allowed-tags-custom.feature
new file mode 100644
index 0000000..dce52ff
--- /dev/null
+++ b/its/ruling/projects/custom/allowed-tags/allowed-tags-custom.feature
@@ -0,0 +1,16 @@
+# Noncompliant [[sc=1;ec=5]] {{Remove this tag that is not in the whitelist.}}
+@nrt
+Feature: My feature...
+
+ # Noncompliant [[sc=3;ec=23]] {{Remove this tag that is not in the whitelist.}}
+ @non-regression-test
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ @mytag @yourtag
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/allowed-tags/allowed-tags-default.feature b/its/ruling/projects/custom/allowed-tags/allowed-tags-default.feature
new file mode 100644
index 0000000..ed29374
--- /dev/null
+++ b/its/ruling/projects/custom/allowed-tags/allowed-tags-default.feature
@@ -0,0 +1,16 @@
+# Noncompliant [[sc=1;ec=7]] {{Remove this tag that is not in the whitelist.}}
+@mytag
+Feature: My feature...
+
+ # Noncompliant [[sc=3;ec=23]] {{Remove this tag that is not in the whitelist.}}
+ @non-regression-test
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ @nrt @smoke
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/bom/utf16-be.feature b/its/ruling/projects/custom/bom/utf16-be.feature
new file mode 100644
index 0000000..01ee708
Binary files /dev/null and b/its/ruling/projects/custom/bom/utf16-be.feature differ
diff --git a/its/ruling/projects/custom/bom/utf16-le.feature b/its/ruling/projects/custom/bom/utf16-le.feature
new file mode 100644
index 0000000..a5054ff
Binary files /dev/null and b/its/ruling/projects/custom/bom/utf16-le.feature differ
diff --git a/its/ruling/projects/custom/bom/utf8-with-bom.feature b/its/ruling/projects/custom/bom/utf8-with-bom.feature
new file mode 100644
index 0000000..0f000c3
--- /dev/null
+++ b/its/ruling/projects/custom/bom/utf8-with-bom.feature
@@ -0,0 +1,12 @@
+# Noncompliant [[sl=0]] {{Remove the Byte Order Mark (BOM).}}
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/bom/utf8.feature b/its/ruling/projects/custom/bom/utf8.feature
new file mode 100644
index 0000000..197f983
--- /dev/null
+++ b/its/ruling/projects/custom/bom/utf8.feature
@@ -0,0 +1,11 @@
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/comment-convention.feature b/its/ruling/projects/custom/comment-convention.feature
new file mode 100644
index 0000000..21639b3
--- /dev/null
+++ b/its/ruling/projects/custom/comment-convention.feature
@@ -0,0 +1,12 @@
+Feature: My feature...
+#My comments
+# My comments
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/comment-regular-expression.feature b/its/ruling/projects/custom/comment-regular-expression.feature
new file mode 100644
index 0000000..5f6051b
--- /dev/null
+++ b/its/ruling/projects/custom/comment-regular-expression.feature
@@ -0,0 +1,13 @@
+Feature: My feature...
+ # WTF!
+ # wtf: abc
+ # Hello
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/custom-checks/forbidden-name-content.feature b/its/ruling/projects/custom/custom-checks/forbidden-name-content.feature
new file mode 100644
index 0000000..00bad92
--- /dev/null
+++ b/its/ruling/projects/custom/custom-checks/forbidden-name-content.feature
@@ -0,0 +1,13 @@
+# Noncompliant [[sc=10;ec=27]] {{Remove this usage of "WTF".}}
+Feature: WTF My feature...
+
+ Scenario: Scenario #1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ # Noncompliant [[sc=13;ec=28]] {{Remove this usage of "WTF".}}
+ Scenario: Scenario WTF #2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/custom-checks/forbidden-tag.feature b/its/ruling/projects/custom/custom-checks/forbidden-tag.feature
new file mode 100644
index 0000000..d09bb99
--- /dev/null
+++ b/its/ruling/projects/custom/custom-checks/forbidden-tag.feature
@@ -0,0 +1,14 @@
+@bar
+Feature: My feature...
+
+ @foo
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ @mytag
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/end-line-characters.feature b/its/ruling/projects/custom/end-line-characters.feature
new file mode 100644
index 0000000..197f983
--- /dev/null
+++ b/its/ruling/projects/custom/end-line-characters.feature
@@ -0,0 +1,11 @@
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/file-name/FileNameKO.feature b/its/ruling/projects/custom/file-name/FileNameKO.feature
new file mode 100644
index 0000000..197f983
--- /dev/null
+++ b/its/ruling/projects/custom/file-name/FileNameKO.feature
@@ -0,0 +1,11 @@
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/file-name/file-name-custom-ok.feature b/its/ruling/projects/custom/file-name/file-name-custom-ok.feature
new file mode 100644
index 0000000..197f983
--- /dev/null
+++ b/its/ruling/projects/custom/file-name/file-name-custom-ok.feature
@@ -0,0 +1,11 @@
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/file-name/file-name-ok.feature b/its/ruling/projects/custom/file-name/file-name-ok.feature
new file mode 100644
index 0000000..197f983
--- /dev/null
+++ b/its/ruling/projects/custom/file-name/file-name-ok.feature
@@ -0,0 +1,11 @@
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/file-name/file_name.kocustom.feature b/its/ruling/projects/custom/file-name/file_name.kocustom.feature
new file mode 100644
index 0000000..197f983
--- /dev/null
+++ b/its/ruling/projects/custom/file-name/file_name.kocustom.feature
@@ -0,0 +1,11 @@
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/fixme.feature b/its/ruling/projects/custom/fixme.feature
new file mode 100644
index 0000000..16269e8
--- /dev/null
+++ b/its/ruling/projects/custom/fixme.feature
@@ -0,0 +1,22 @@
+Feature: My feature...
+
+ # Noncompliant {{Take the required action to fix the issue indicated by this comment.}}
+ # FIXME: blabla
+
+ # Noncompliant {{Take the required action to fix the issue indicated by this comment.}}
+ #FIXME: blabla
+
+ # Noncompliant {{Take the required action to fix the issue indicated by this comment.}}
+ #[[FIXME]] blabla
+
+ # fixmeforthefirsttime
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/incomplete-examples-table.feature b/its/ruling/projects/custom/incomplete-examples-table.feature
new file mode 100644
index 0000000..d6571e3
--- /dev/null
+++ b/its/ruling/projects/custom/incomplete-examples-table.feature
@@ -0,0 +1,25 @@
+Feature: My feature...
+
+ Scenario Outline: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+ # Noncompliant [[sc=5;ec=13]] {{Add a table to this "Examples" section.}}
+ Examples:
+
+ Scenario Outline: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
+ Examples:
+ # Noncompliant [[sc=7;ec=15]] {{Add data rows to this table.}}
+ | test |
+
+ Scenario Outline: Scenario 3
+ Given blabla...
+ When blabla...
+ Then blabla...
+ Examples:
+ # Noncompliant [[sc=7;ec=15]] {{Add data rows to this table or convert this "Scenario Outline" to a standard "Scenario".}}
+ | test |
+ | 1 |
diff --git a/its/ruling/projects/custom/indentation/indentation-custom-ko.feature b/its/ruling/projects/custom/indentation/indentation-custom-ko.feature
new file mode 100644
index 0000000..ab30d9e
--- /dev/null
+++ b/its/ruling/projects/custom/indentation/indentation-custom-ko.feature
@@ -0,0 +1,60 @@
+ # Noncompliant {{Indent this line at column 0 (currently indented at column 1).}}
+ @tag
+ # Noncompliant {{Indent this line at column 0 (currently indented at column 1).}}
+ Feature: My feature...
+ blabla...
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 5).}}
+ blabla...
+
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 5).}}
+ Background: blabla...
+ blabla...
+ # Noncompliant {{Indent this line at column 8 (currently indented at column 7).}}
+ blabla...
+ # Noncompliant {{Indent this line at column 8 (currently indented at column 10).}}
+ Given blabla...
+
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 5).}}
+ @tag
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 3).}}
+ Scenario: Scenario 1
+ # Noncompliant {{Indent this line at column 8 (currently indented at column 9).}}
+ blabla...
+ blabla...
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ @tag
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 5).}}
+ @tag @tag
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 3).}}
+ Scenario Outline: Scenario 2
+ # Noncompliant {{Indent this line at column 8 (currently indented at column 7).}}
+ blabla...
+ blabla...
+ Given blabla...
+ When blabla...
+ | data |
+ # Noncompliant {{Indent this line at column 12 (currently indented at column 13).}}
+ | 2 |
+ Then blabla...
+ # Noncompliant {{Indent this line at column 12 (currently indented at column 13).}}
+ """string
+ blabla...
+ blabla...
+ """
+ # Noncompliant [[sl=-1]] {{Indent this line at column 12 (currently indented at column 11).}}
+
+ # Noncompliant {{Indent this line at column 8 (currently indented at column 9).}}
+ @tag @tag
+ # Noncompliant {{Indent this line at column 8 (currently indented at column 9).}}
+ Examples: blabla...
+ # Noncompliant {{Indent this line at column 12 (currently indented at column 13).}}
+ blabla...
+ blabla...
+ # Noncompliant {{Indent this line at column 12 (currently indented at column 11).}}
+ | data |
+ # Noncompliant {{Indent this line at column 12 (currently indented at column 13).}}
+ | 1 |
+ | 2 |
diff --git a/its/ruling/projects/custom/indentation/indentation-custom-ok.feature b/its/ruling/projects/custom/indentation/indentation-custom-ok.feature
new file mode 100644
index 0000000..b8a03a0
--- /dev/null
+++ b/its/ruling/projects/custom/indentation/indentation-custom-ok.feature
@@ -0,0 +1,40 @@
+@tag
+@tag
+Feature: My feature...
+ blabla...
+ blabla...
+
+ Background: blabla...
+ blabla...
+ blabla...
+ Given blabla...
+
+ @tag
+ Scenario: Scenario 1
+ blabla...
+ blabla...
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ @tag @tag
+ Scenario Outline: Scenario 2
+ blabla...
+ blabla...
+ Given blabla...
+ When blabla...
+ | data |
+ | 2 |
+ Then blabla...
+ """string
+ blabla...
+ blabla...
+ """
+
+ @tag
+ @tag @tag
+ Examples: blabla...
+ blabla...
+ blabla...
+ | data |
+ | 1 |
diff --git a/its/ruling/projects/custom/indentation/indentation-default-ko.feature b/its/ruling/projects/custom/indentation/indentation-default-ko.feature
new file mode 100644
index 0000000..8dec47b
--- /dev/null
+++ b/its/ruling/projects/custom/indentation/indentation-default-ko.feature
@@ -0,0 +1,61 @@
+# Noncompliant {{Indent this line at column 0 (currently indented at column 1).}}
+ @tag
+@tag
+ # Noncompliant {{Indent this line at column 0 (currently indented at column 2).}}
+ Feature: My feature...
+ # Noncompliant {{Indent this line at column 2 (currently indented at column 4).}}
+ blabla...
+ blabla...
+
+ # Noncompliant {{Indent this line at column 2 (currently indented at column 1).}}
+ Background: blabla...
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 2).}}
+ blabla...
+ blabla...
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 5).}}
+ Given blabla...
+
+ # Noncompliant {{Indent this line at column 2 (currently indented at column 4).}}
+ @tag
+ # Noncompliant {{Indent this line at column 2 (currently indented at column 3).}}
+ Scenario: Scenario 1
+ blabla...
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 7).}}
+ blabla...
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ @tag
+ # Noncompliant {{Indent this line at column 2 (currently indented at column 4).}}
+ @tag
+ # Noncompliant {{Indent this line at column 2 (currently indented at column 3).}}
+ Scenario Outline: Scenario 2
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 5).}}
+ blabla...
+ blabla...
+ Given blabla...
+ When blabla...
+ | data |
+ # Noncompliant {{Indent this line at column 6 (currently indented at column 7).}}
+ | 2 |
+ Then blabla...
+ # Noncompliant {{Indent this line at column 6 (currently indented at column 7).}}
+ """string
+ blabla...
+ blabla...
+ """
+ # Noncompliant [[sl=-1]] {{Indent this line at column 6 (currently indented at column 5).}}
+
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 5).}}
+ @tag
+ # Noncompliant {{Indent this line at column 4 (currently indented at column 5).}}
+ Examples: blabla...
+ blabla...
+ # Noncompliant {{Indent this line at column 6 (currently indented at column 7).}}
+ blabla...
+ # Noncompliant {{Indent this line at column 6 (currently indented at column 5).}}
+ | data |
+ # Noncompliant {{Indent this line at column 6 (currently indented at column 7).}}
+ | 1 |
+ | 2 |
diff --git a/its/ruling/projects/custom/indentation/indentation-default-ok.feature b/its/ruling/projects/custom/indentation/indentation-default-ok.feature
new file mode 100644
index 0000000..49f0e71
--- /dev/null
+++ b/its/ruling/projects/custom/indentation/indentation-default-ok.feature
@@ -0,0 +1,40 @@
+@tag
+@tag1
+Feature: My feature...
+ blabla...
+ blabla...
+
+ Background: blabla...
+ blabla...
+ blabla...
+ Given blabla...
+
+ @tag @tag1
+ Scenario: Scenario 1
+ blabla...
+ blabla...
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ @tag
+ Scenario Outline: Scenario 2
+ blabla...
+ blabla...
+ Given blabla...
+ When blabla...
+ | data |
+ | 2 |
+ Then blabla...
+ """string
+ blabla...
+ blabla...
+ """
+
+ @tag
+ @tag @tag
+ Examples: blabla...
+ blabla...
+ blabla...
+ | data |
+ | 1 |
diff --git a/its/ruling/projects/custom/max-number-scenarios/greater-than-custom-threshold.feature b/its/ruling/projects/custom/max-number-scenarios/greater-than-custom-threshold.feature
new file mode 100644
index 0000000..2fba9b0
--- /dev/null
+++ b/its/ruling/projects/custom/max-number-scenarios/greater-than-custom-threshold.feature
@@ -0,0 +1,24 @@
+# Noncompliant [[sc=1;ec=8]] {{The number of scenarios (5) is greater that the maximum allowed (4). Split the scenarios into different features.}}
+Feature: My feature...
+
+ Background:
+ Given blabla...
+
+ Scenario: scenario #1
+ Given blabla...
+
+ Scenario Outline: scenario #2
+ Given blabla...
+ Examples:
+ | data |
+ | 1 |
+ | 2 |
+
+ Scenario: scenario #3
+ Given blabla...
+
+ Scenario: scenario #4
+ Given blabla...
+
+ Scenario: scenario #5
+ Given blabla...
diff --git a/its/ruling/projects/custom/max-number-scenarios/greater-than-default-threshold.feature b/its/ruling/projects/custom/max-number-scenarios/greater-than-default-threshold.feature
new file mode 100644
index 0000000..4756d3f
--- /dev/null
+++ b/its/ruling/projects/custom/max-number-scenarios/greater-than-default-threshold.feature
@@ -0,0 +1,48 @@
+# Noncompliant [[sc=1;ec=8]] {{The number of scenarios (13) is greater that the maximum allowed (12). Split the scenarios into different features.}}
+Feature: My feature...
+
+ Background:
+ Given blabla...
+
+ Scenario: scenario #1
+ Given blabla...
+
+ Scenario Outline: scenario #2
+ Given blabla...
+ Examples:
+ | data |
+ | 1 |
+ | 2 |
+
+ Scenario: scenario #3
+ Given blabla...
+
+ Scenario: scenario #4
+ Given blabla...
+
+ Scenario: scenario #5
+ Given blabla...
+
+ Scenario: scenario #6
+ Given blabla...
+
+ Scenario: scenario #7
+ Given blabla...
+
+ Scenario: scenario #8
+ Given blabla...
+
+ Scenario: scenario #9
+ Given blabla...
+
+ Scenario: scenario #10
+ Given blabla...
+
+ Scenario: scenario #11
+ Given blabla...
+
+ Scenario: scenario #12
+ Given blabla...
+
+ Scenario: scenario #13
+ Given blabla...
diff --git a/its/ruling/projects/custom/max-number-scenarios/lower-than-custom-threshold.feature b/its/ruling/projects/custom/max-number-scenarios/lower-than-custom-threshold.feature
new file mode 100644
index 0000000..6ee15da
--- /dev/null
+++ b/its/ruling/projects/custom/max-number-scenarios/lower-than-custom-threshold.feature
@@ -0,0 +1,20 @@
+Feature: My feature...
+
+ Background:
+ Given blabla...
+
+ Scenario: scenario #1
+ Given blabla...
+
+ Scenario Outline: scenario #2
+ Given blabla...
+ Examples:
+ | data |
+ | 1 |
+ | 2 |
+
+ Scenario: scenario #3
+ Given blabla...
+
+ Scenario: scenario #4
+ Given blabla...
diff --git a/its/ruling/projects/custom/max-number-scenarios/lower-than-default-threshold.feature b/its/ruling/projects/custom/max-number-scenarios/lower-than-default-threshold.feature
new file mode 100644
index 0000000..0203f79
--- /dev/null
+++ b/its/ruling/projects/custom/max-number-scenarios/lower-than-default-threshold.feature
@@ -0,0 +1,44 @@
+Feature: My feature...
+
+ Background:
+ Given blabla...
+
+ Scenario: scenario #1
+ Given blabla...
+
+ Scenario Outline: scenario #2
+ Given blabla...
+ Examples:
+ | data |
+ | 1 |
+ | 2 |
+
+ Scenario: scenario #3
+ Given blabla...
+
+ Scenario: scenario #4
+ Given blabla...
+
+ Scenario: scenario #5
+ Given blabla...
+
+ Scenario: scenario #6
+ Given blabla...
+
+ Scenario: scenario #7
+ Given blabla...
+
+ Scenario: scenario #8
+ Given blabla...
+
+ Scenario: scenario #9
+ Given blabla...
+
+ Scenario: scenario #10
+ Given blabla...
+
+ Scenario: scenario #11
+ Given blabla...
+
+ Scenario: scenario #12
+ Given blabla...
diff --git a/its/ruling/projects/custom/max-number-steps/max-number-steps-background.feature b/its/ruling/projects/custom/max-number-steps/max-number-steps-background.feature
new file mode 100644
index 0000000..9f10bcb
--- /dev/null
+++ b/its/ruling/projects/custom/max-number-steps/max-number-steps-background.feature
@@ -0,0 +1,28 @@
+Feature: My feature...
+
+ Background:
+ Given blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+
+ Scenario: scenario #1
+ Given blabla...
+ When blabla...
+ Then blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+
+ # Noncompliant [[sc=3;ec=11]] {{Reduce the number of steps (13, greater than 12 allowed)}}
+ Scenario: scenario #2
+ Given blabla...
+ When blabla...
+ Then blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
diff --git a/its/ruling/projects/custom/max-number-steps/max-number-steps-custom-threshold.feature b/its/ruling/projects/custom/max-number-steps/max-number-steps-custom-threshold.feature
new file mode 100644
index 0000000..7e8482b
--- /dev/null
+++ b/its/ruling/projects/custom/max-number-steps/max-number-steps-custom-threshold.feature
@@ -0,0 +1,18 @@
+Feature: My feature...
+
+ Scenario: scenario #1
+ Given blabla...
+ When blabla...
+ Then blabla...
+ And blabla...
+ And blabla...
+
+ # Noncompliant [[sc=3;ec=11]] {{Reduce the number of steps (7, greater than 5 allowed)}}
+ Scenario: scenario #2
+ Given blabla...
+ When blabla...
+ Then blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
diff --git a/its/ruling/projects/custom/max-number-steps/max-number-steps-no-background.feature b/its/ruling/projects/custom/max-number-steps/max-number-steps-no-background.feature
new file mode 100644
index 0000000..778fdff
--- /dev/null
+++ b/its/ruling/projects/custom/max-number-steps/max-number-steps-no-background.feature
@@ -0,0 +1,31 @@
+Feature: My feature...
+
+ Scenario: scenario #1
+ Given blabla...
+ When blabla...
+ Then blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+
+ # Noncompliant [[sc=3;ec=11]] {{Reduce the number of steps (13, greater than 12 allowed)}}
+ Scenario: scenario #2
+ Given blabla...
+ When blabla...
+ Then blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
+ And blabla...
diff --git a/its/ruling/projects/custom/new-line-end-of-file/new-line-end-of-file.feature b/its/ruling/projects/custom/new-line-end-of-file/new-line-end-of-file.feature
new file mode 100644
index 0000000..197f983
--- /dev/null
+++ b/its/ruling/projects/custom/new-line-end-of-file/new-line-end-of-file.feature
@@ -0,0 +1,11 @@
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/new-line-end-of-file/no-new-line-end-of-file.feature b/its/ruling/projects/custom/new-line-end-of-file/no-new-line-end-of-file.feature
new file mode 100644
index 0000000..3db3cd6
--- /dev/null
+++ b/its/ruling/projects/custom/new-line-end-of-file/no-new-line-end-of-file.feature
@@ -0,0 +1,11 @@
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
\ No newline at end of file
diff --git a/its/ruling/projects/custom/no-feature/feature.feature b/its/ruling/projects/custom/no-feature/feature.feature
new file mode 100644
index 0000000..18c0a2b
--- /dev/null
+++ b/its/ruling/projects/custom/no-feature/feature.feature
@@ -0,0 +1,5 @@
+Feature: My feature...
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/no-feature/no-feature.feature b/its/ruling/projects/custom/no-feature/no-feature.feature
new file mode 100644
index 0000000..da05b93
--- /dev/null
+++ b/its/ruling/projects/custom/no-feature/no-feature.feature
@@ -0,0 +1 @@
+# Noncompliant [[sl=0]] {{Remove this file that does not define any feature.}}
diff --git a/its/ruling/projects/custom/no-scenario/no-scenario.feature b/its/ruling/projects/custom/no-scenario/no-scenario.feature
new file mode 100644
index 0000000..c76fabe
--- /dev/null
+++ b/its/ruling/projects/custom/no-scenario/no-scenario.feature
@@ -0,0 +1,2 @@
+# Noncompliant [[sc=1;ec=8]] {{Remove this feature that does not define any scenario.}}
+Feature: My feature...
diff --git a/its/ruling/projects/custom/no-scenario/scenario-outline.feature b/its/ruling/projects/custom/no-scenario/scenario-outline.feature
new file mode 100644
index 0000000..0f8f8e9
--- /dev/null
+++ b/its/ruling/projects/custom/no-scenario/scenario-outline.feature
@@ -0,0 +1,9 @@
+Feature: My feature...
+ Scenario Outline: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+ Examples:
+ | data |
+ | 1 |
+ | 2 |
diff --git a/its/ruling/projects/custom/no-scenario/scenario.feature b/its/ruling/projects/custom/no-scenario/scenario.feature
new file mode 100644
index 0000000..18c0a2b
--- /dev/null
+++ b/its/ruling/projects/custom/no-scenario/scenario.feature
@@ -0,0 +1,5 @@
+Feature: My feature...
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/star-step-prefix.feature b/its/ruling/projects/custom/star-step-prefix.feature
new file mode 100644
index 0000000..a910b62
--- /dev/null
+++ b/its/ruling/projects/custom/star-step-prefix.feature
@@ -0,0 +1,12 @@
+Feature: My feature...
+
+ Scenario: My scenario 1
+ # Noncompliant [[sc=5;ec=6]] {{Replace this star prefix with Given/When/Then.}}
+ * blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/tab-character/no-tab-character.feature b/its/ruling/projects/custom/tab-character/no-tab-character.feature
new file mode 100644
index 0000000..197f983
--- /dev/null
+++ b/its/ruling/projects/custom/tab-character/no-tab-character.feature
@@ -0,0 +1,11 @@
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/tab-character/tab-character.feature b/its/ruling/projects/custom/tab-character/tab-character.feature
new file mode 100644
index 0000000..87aaa1e
--- /dev/null
+++ b/its/ruling/projects/custom/tab-character/tab-character.feature
@@ -0,0 +1,11 @@
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/tag-name/tag-name-custom-ko.feature b/its/ruling/projects/custom/tag-name/tag-name-custom-ko.feature
new file mode 100644
index 0000000..92ccf8e
--- /dev/null
+++ b/its/ruling/projects/custom/tag-name/tag-name-custom-ko.feature
@@ -0,0 +1,14 @@
+# Noncompliant [[sc=1;ec=7]] {{Rename this tag to match the regular expression: ^[a-z]+$}}
+@myTag
+Feature: My feature...
+
+ @tag
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/tag-name/tag-name-custom-ok.feature b/its/ruling/projects/custom/tag-name/tag-name-custom-ok.feature
new file mode 100644
index 0000000..1e38448
--- /dev/null
+++ b/its/ruling/projects/custom/tag-name/tag-name-custom-ok.feature
@@ -0,0 +1,13 @@
+@my-tag
+Feature: My feature...
+
+ @tag
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/tag-name/tag-name-ko.feature b/its/ruling/projects/custom/tag-name/tag-name-ko.feature
new file mode 100644
index 0000000..3dd4b88
--- /dev/null
+++ b/its/ruling/projects/custom/tag-name/tag-name-ko.feature
@@ -0,0 +1,15 @@
+# Noncompliant [[sc=1;ec=7]] {{Rename this tag to match the regular expression: ^[a-z][-a-z0-9]*$}}
+@myTag @my-tag
+Feature: My feature...
+
+ # Noncompliant [[sc=3;ec=10]] {{Rename this tag to match the regular expression: ^[a-z][-a-z0-9]*$}}
+ @myTag0
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/tag-name/tag-name-ok.feature b/its/ruling/projects/custom/tag-name/tag-name-ok.feature
new file mode 100644
index 0000000..f12915c
--- /dev/null
+++ b/its/ruling/projects/custom/tag-name/tag-name-ok.feature
@@ -0,0 +1,13 @@
+@mytag @my-tag
+Feature: My feature...
+
+ @my-tag1
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/todo.feature b/its/ruling/projects/custom/todo.feature
new file mode 100644
index 0000000..123d63e
--- /dev/null
+++ b/its/ruling/projects/custom/todo.feature
@@ -0,0 +1,22 @@
+Feature: My feature...
+
+ # Noncompliant
+ # TODO: blabla
+
+ # Noncompliant
+ #TODO: blabla
+
+ # Noncompliant
+ #[[TODO]] blabla
+
+ # todoforthefirsttime
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
diff --git a/its/ruling/projects/custom/trailing-whitespace.feature b/its/ruling/projects/custom/trailing-whitespace.feature
new file mode 100644
index 0000000..01f73ed
--- /dev/null
+++ b/its/ruling/projects/custom/trailing-whitespace.feature
@@ -0,0 +1,14 @@
+# Noncompliant [[sl=3]] {{Remove the useless trailing whitespaces at the end of this line.}}
+# Noncompliant [[sl=6]] {{Remove the useless trailing whitespaces at the end of this line.}}
+Feature: My feature...
+
+ Scenario: Scenario 1
+ Given blabla...
+ When blabla...
+ Then blabla...
+
+ Scenario: Scenario 2
+ Given blabla...
+ When blabla...
+ Then blabla...
+
diff --git a/its/ruling/tests/pom.xml b/its/ruling/tests/pom.xml
new file mode 100644
index 0000000..efd3ec0
--- /dev/null
+++ b/its/ruling/tests/pom.xml
@@ -0,0 +1,50 @@
+
+
+ 4.0.0
+
+
+ com.racodond.sonarqube.plugin.gherkin
+ gherkin-its-ruling
+ 1.0-SNAPSHOT
+
+
+ gherkin-its-ruling-tests
+ SonarQube Gherkin Analyzer :: Integration Tests :: Ruling :: Tests
+
+
+
+ org.sonarsource.orchestrator
+ sonar-orchestrator
+
+
+ com.google.guava
+ guava
+
+
+ junit
+ junit
+
+
+ org.easytesting
+ fest-assert
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ org/sonar/gherkin/its/GherkinRulingTest.java
+
+
+
+
+
+
+
+
diff --git a/its/ruling/tests/src/test/expected/gherkin-S1131.json b/its/ruling/tests/src/test/expected/gherkin-S1131.json
new file mode 100644
index 0000000..a113c9f
--- /dev/null
+++ b/its/ruling/tests/src/test/expected/gherkin-S1131.json
@@ -0,0 +1,6 @@
+{
+'project:custom/trailing-whitespace.feature':[
+3,
+6,
+],
+}
diff --git a/its/ruling/tests/src/test/expected/gherkin-S1134.json b/its/ruling/tests/src/test/expected/gherkin-S1134.json
new file mode 100644
index 0000000..35f4656
--- /dev/null
+++ b/its/ruling/tests/src/test/expected/gherkin-S1134.json
@@ -0,0 +1,7 @@
+{
+'project:custom/fixme.feature':[
+4,
+7,
+10,
+],
+}
diff --git a/its/ruling/tests/src/test/expected/gherkin-S1135.json b/its/ruling/tests/src/test/expected/gherkin-S1135.json
new file mode 100644
index 0000000..b2aff7e
--- /dev/null
+++ b/its/ruling/tests/src/test/expected/gherkin-S1135.json
@@ -0,0 +1,7 @@
+{
+'project:custom/todo.feature':[
+4,
+7,
+10,
+],
+}
diff --git a/its/ruling/tests/src/test/expected/gherkin-S1578.json b/its/ruling/tests/src/test/expected/gherkin-S1578.json
new file mode 100644
index 0000000..9d7ca4e
--- /dev/null
+++ b/its/ruling/tests/src/test/expected/gherkin-S1578.json
@@ -0,0 +1,8 @@
+{
+'project:custom/file-name/FileNameKO.feature':[
+0,
+],
+'project:custom/file-name/file_name.kocustom.feature':[
+0,
+],
+}
diff --git a/its/ruling/tests/src/test/expected/gherkin-S2260.json b/its/ruling/tests/src/test/expected/gherkin-S2260.json
new file mode 100644
index 0000000..bef655e
--- /dev/null
+++ b/its/ruling/tests/src/test/expected/gherkin-S2260.json
@@ -0,0 +1,8 @@
+{
+'project:custom/bom/utf16-be.feature':[
+1,
+],
+'project:custom/bom/utf16-le.feature':[
+1,
+],
+}
diff --git a/its/ruling/tests/src/test/expected/gherkin-allowed-tags.json b/its/ruling/tests/src/test/expected/gherkin-allowed-tags.json
new file mode 100644
index 0000000..f0855be
--- /dev/null
+++ b/its/ruling/tests/src/test/expected/gherkin-allowed-tags.json
@@ -0,0 +1,71 @@
+{
+'project:custom/allowed-tags/allowed-tags-custom.feature':[
+6,
+12,
+12,
+],
+'project:custom/allowed-tags/allowed-tags-default.feature':[
+2,
+6,
+],
+'project:custom/custom-checks/forbidden-tag.feature':[
+1,
+4,
+10,
+],
+'project:custom/indentation/indentation-custom-ko.feature':[
+2,
+18,
+28,
+30,
+30,
+50,
+50,
+],
+'project:custom/indentation/indentation-custom-ok.feature':[
+1,
+2,
+12,
+20,
+20,
+34,
+35,
+35,
+],
+'project:custom/indentation/indentation-default-ko.feature':[
+2,
+3,
+19,
+29,
+31,
+51,
+],
+'project:custom/indentation/indentation-default-ok.feature':[
+1,
+2,
+12,
+12,
+20,
+34,
+35,
+35,
+],
+'project:custom/tag-name/tag-name-custom-ko.feature':[
+2,
+5,
+],
+'project:custom/tag-name/tag-name-custom-ok.feature':[
+1,
+4,
+],
+'project:custom/tag-name/tag-name-ko.feature':[
+2,
+2,
+6,
+],
+'project:custom/tag-name/tag-name-ok.feature':[
+1,
+1,
+4,
+],
+}
diff --git a/its/ruling/tests/src/test/expected/gherkin-bom-utf8-files.json b/its/ruling/tests/src/test/expected/gherkin-bom-utf8-files.json
new file mode 100644
index 0000000..4178cd7
--- /dev/null
+++ b/its/ruling/tests/src/test/expected/gherkin-bom-utf8-files.json
@@ -0,0 +1,5 @@
+{
+'project:custom/bom/utf8-with-bom.feature':[
+0,
+],
+}
diff --git a/its/ruling/tests/src/test/expected/gherkin-comment-convention.json b/its/ruling/tests/src/test/expected/gherkin-comment-convention.json
new file mode 100644
index 0000000..f0bc1d8
--- /dev/null
+++ b/its/ruling/tests/src/test/expected/gherkin-comment-convention.json
@@ -0,0 +1,13 @@
+{
+'project:custom/comment-convention.feature':[
+2,
+],
+'project:custom/fixme.feature':[
+7,
+10,
+],
+'project:custom/todo.feature':[
+7,
+10,
+],
+}
diff --git a/its/ruling/tests/src/test/expected/gherkin-empty-line-end-of-file.json b/its/ruling/tests/src/test/expected/gherkin-empty-line-end-of-file.json
new file mode 100644
index 0000000..22b98de
--- /dev/null
+++ b/its/ruling/tests/src/test/expected/gherkin-empty-line-end-of-file.json
@@ -0,0 +1,5 @@
+{
+'project:custom/new-line-end-of-file/no-new-line-end-of-file.feature':[
+0,
+],
+}
diff --git a/its/ruling/tests/src/test/expected/gherkin-incomplete-examples-table.json b/its/ruling/tests/src/test/expected/gherkin-incomplete-examples-table.json
new file mode 100644
index 0000000..8aa2577
--- /dev/null
+++ b/its/ruling/tests/src/test/expected/gherkin-incomplete-examples-table.json
@@ -0,0 +1,13 @@
+{
+'project:custom/incomplete-examples-table.feature':[
+8,
+16,
+24,
+],
+'project:custom/indentation/indentation-custom-ok.feature':[
+39,
+],
+'project:custom/indentation/indentation-default-ok.feature':[
+39,
+],
+}
diff --git a/its/ruling/tests/src/test/expected/gherkin-indentation.json b/its/ruling/tests/src/test/expected/gherkin-indentation.json
new file mode 100644
index 0000000..63633aa
--- /dev/null
+++ b/its/ruling/tests/src/test/expected/gherkin-indentation.json
@@ -0,0 +1,96 @@
+{
+'project:custom/indentation/indentation-custom-ko.feature':[
+2,
+4,
+5,
+7,
+10,
+11,
+13,
+15,
+18,
+20,
+22,
+23,
+24,
+25,
+26,
+28,
+30,
+32,
+34,
+35,
+36,
+37,
+38,
+40,
+41,
+43,
+46,
+50,
+52,
+54,
+55,
+57,
+59,
+60,
+],
+'project:custom/indentation/indentation-custom-ok.feature':[
+4,
+5,
+7,
+8,
+9,
+10,
+12,
+13,
+14,
+15,
+16,
+17,
+18,
+20,
+21,
+22,
+23,
+24,
+25,
+26,
+27,
+28,
+29,
+32,
+34,
+35,
+36,
+37,
+38,
+39,
+40,
+],
+'project:custom/indentation/indentation-default-ko.feature':[
+2,
+5,
+7,
+11,
+13,
+16,
+19,
+21,
+24,
+31,
+33,
+35,
+41,
+44,
+47,
+51,
+53,
+56,
+58,
+60,
+],
+'project:custom/tab-character/tab-character.feature':[
+4,
+],
+}
diff --git a/its/ruling/tests/src/test/expected/gherkin-max-number-scenarios.json b/its/ruling/tests/src/test/expected/gherkin-max-number-scenarios.json
new file mode 100644
index 0000000..e494c20
--- /dev/null
+++ b/its/ruling/tests/src/test/expected/gherkin-max-number-scenarios.json
@@ -0,0 +1,5 @@
+{
+'project:custom/max-number-scenarios/greater-than-default-threshold.feature':[
+2,
+],
+}
diff --git a/its/ruling/tests/src/test/expected/gherkin-max-number-steps.json b/its/ruling/tests/src/test/expected/gherkin-max-number-steps.json
new file mode 100644
index 0000000..6ef6ff5
--- /dev/null
+++ b/its/ruling/tests/src/test/expected/gherkin-max-number-steps.json
@@ -0,0 +1,8 @@
+{
+'project:custom/max-number-steps/max-number-steps-background.feature':[
+20,
+],
+'project:custom/max-number-steps/max-number-steps-no-background.feature':[
+18,
+],
+}
diff --git a/its/ruling/tests/src/test/expected/gherkin-no-feature.json b/its/ruling/tests/src/test/expected/gherkin-no-feature.json
new file mode 100644
index 0000000..c445383
--- /dev/null
+++ b/its/ruling/tests/src/test/expected/gherkin-no-feature.json
@@ -0,0 +1,5 @@
+{
+'project:custom/no-feature/no-feature.feature':[
+0,
+],
+}
diff --git a/its/ruling/tests/src/test/expected/gherkin-no-scenario.json b/its/ruling/tests/src/test/expected/gherkin-no-scenario.json
new file mode 100644
index 0000000..bd25c02
--- /dev/null
+++ b/its/ruling/tests/src/test/expected/gherkin-no-scenario.json
@@ -0,0 +1,5 @@
+{
+'project:custom/no-scenario/no-scenario.feature':[
+2,
+],
+}
diff --git a/its/ruling/tests/src/test/expected/gherkin-star-step-prefix.json b/its/ruling/tests/src/test/expected/gherkin-star-step-prefix.json
new file mode 100644
index 0000000..9d54bc7
--- /dev/null
+++ b/its/ruling/tests/src/test/expected/gherkin-star-step-prefix.json
@@ -0,0 +1,5 @@
+{
+'project:custom/star-step-prefix.feature':[
+5,
+],
+}
diff --git a/its/ruling/tests/src/test/expected/gherkin-tab-character.json b/its/ruling/tests/src/test/expected/gherkin-tab-character.json
new file mode 100644
index 0000000..ef52464
--- /dev/null
+++ b/its/ruling/tests/src/test/expected/gherkin-tab-character.json
@@ -0,0 +1,5 @@
+{
+'project:custom/tab-character/tab-character.feature':[
+0,
+],
+}
diff --git a/its/ruling/tests/src/test/expected/gherkin-tag-naming-convention.json b/its/ruling/tests/src/test/expected/gherkin-tag-naming-convention.json
new file mode 100644
index 0000000..368db12
--- /dev/null
+++ b/its/ruling/tests/src/test/expected/gherkin-tag-naming-convention.json
@@ -0,0 +1,9 @@
+{
+'project:custom/tag-name/tag-name-custom-ko.feature':[
+2,
+],
+'project:custom/tag-name/tag-name-ko.feature':[
+2,
+6,
+],
+}
diff --git a/its/ruling/tests/src/test/java/org/sonar/gherkin/its/GherkinRulingTest.java b/its/ruling/tests/src/test/java/org/sonar/gherkin/its/GherkinRulingTest.java
new file mode 100644
index 0000000..51aed7d
--- /dev/null
+++ b/its/ruling/tests/src/test/java/org/sonar/gherkin/its/GherkinRulingTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.its;
+
+import com.google.common.io.Files;
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import com.sonar.orchestrator.locator.FileLocation;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class GherkinRulingTest {
+
+ @ClassRule
+ public static Orchestrator orchestrator = Orchestrator.builderEnv()
+ .addPlugin(FileLocation.byWildcardMavenFilename(new File("../../../sonar-gherkin-plugin/target"), "sonar-gherkin-plugin-*-SNAPSHOT.jar"))
+ .setOrchestratorProperty("litsVersion", "0.6")
+ .addPlugin("lits")
+ .build();
+
+ @Before
+ public void setUp() throws Exception {
+ ProfileGenerator.generateProfile(orchestrator);
+ }
+
+ @Test
+ public void test() throws Exception {
+ File litsDifferencesFile = FileLocation.of("target/differences").getFile();
+ orchestrator.getServer().provisionProject("project", "project");
+ orchestrator.getServer().associateProjectToQualityProfile("project", "gherkin", "rules");
+ SonarScanner build = SonarScanner.create(FileLocation.of("../projects").getFile())
+ .setProjectKey("project")
+ .setProjectName("project")
+ .setProjectVersion("1.0")
+ .setLanguage("gherkin")
+ .setSourceDirs("./")
+ .setSourceEncoding("UTF-8")
+ .setProperty("sonar.analysis.mode", "preview")
+ .setProperty("sonar.issuesReport.html.enable", "true")
+ .setProperty("dump.old", FileLocation.of("src/test/expected").getFile().getAbsolutePath())
+ .setProperty("dump.new", FileLocation.of("target/actual").getFile().getAbsolutePath())
+ .setProperty("lits.differences", litsDifferencesFile.getAbsolutePath())
+ .setProperty("sonar.cpd.skip", "true");
+ orchestrator.executeBuild(build);
+
+ assertThat(Files.toString(litsDifferencesFile, StandardCharsets.UTF_8)).isEmpty();
+ }
+
+}
diff --git a/its/ruling/tests/src/test/java/org/sonar/gherkin/its/ProfileGenerator.java b/its/ruling/tests/src/test/java/org/sonar/gherkin/its/ProfileGenerator.java
new file mode 100644
index 0000000..f8aa0ba
--- /dev/null
+++ b/its/ruling/tests/src/test/java/org/sonar/gherkin/its/ProfileGenerator.java
@@ -0,0 +1,107 @@
+/*
+ * 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.its;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Multimap;
+import com.google.common.io.Files;
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.locator.FileLocation;
+import org.sonar.wsclient.internal.HttpRequestFactory;
+import org.sonar.wsclient.jsonsimple.JSONValue;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+
+public class ProfileGenerator {
+
+ private static Multimap parameters = ImmutableListMultimap.builder()
+ .build();
+
+ public static void generateProfile(Orchestrator orchestrator) {
+ try {
+ StringBuilder sb = new StringBuilder()
+ .append("")
+ .append("rules")
+ .append("gherkin")
+ .append("");
+
+ Set ruleKeys = getRuleKeysFromRepository(orchestrator);
+
+ for (String key : ruleKeys) {
+ sb.append("")
+ .append("gherkin")
+ .append("").append(key).append("")
+ .append("INFO");
+
+ Collection parameters = ProfileGenerator.parameters.get(key);
+ if (!parameters.isEmpty()) {
+ sb.append("");
+ for (Parameter parameter : parameters) {
+ sb.append("")
+ .append("").append(parameter.parameterKey).append("")
+ .append("").append(parameter.parameterValue).append("")
+ .append("");
+ }
+ sb.append("");
+ }
+
+ sb.append("");
+ }
+ sb.append("")
+ .append("");
+
+ File file = File.createTempFile("profile", ".xml");
+ Files.write(sb, file, Charsets.UTF_8);
+ orchestrator.getServer().restoreProfile(FileLocation.of(file));
+ file.delete();
+ } catch (IOException e) {
+ throw Throwables.propagate(e);
+ }
+ }
+
+ private static Set getRuleKeysFromRepository(Orchestrator orchestrator) {
+ Set ruleKeys = new HashSet<>();
+ String json = new HttpRequestFactory(orchestrator.getServer().getUrl())
+ .get("/api/rules/search", ImmutableMap.of("languages", "gherkin", "repositories", "gherkin", "ps", "1000"));
+ @SuppressWarnings("unchecked")
+ List