diff --git a/.idea/modules.xml b/.idea/modules.xml
index a79a75c66b68..0e7013fe25da 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -19,20 +19,23 @@
+
-
+
+
+
@@ -118,7 +121,6 @@
-
diff --git a/build/cucumber-test-runner/cucumber-test-runner.iml b/build/cucumber-test-runner/cucumber-test-runner.iml
new file mode 100644
index 000000000000..dc0cd4905ca3
--- /dev/null
+++ b/build/cucumber-test-runner/cucumber-test-runner.iml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/cucumber-test-runner/src/com/intellij/cucumber/CucumberMain.java b/build/cucumber-test-runner/src/com/intellij/cucumber/CucumberMain.java
new file mode 100644
index 000000000000..f68c30693561
--- /dev/null
+++ b/build/cucumber-test-runner/src/com/intellij/cucumber/CucumberMain.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.cucumber;
+
+import com.intellij.idea.IdeaTestApplication;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.Ref;
+import cucumber.io.MultiLoader;
+import cucumber.runtime.Runtime;
+import cucumber.runtime.RuntimeOptions;
+
+/**
+ * @author Dennis.Ushakov
+ */
+public class CucumberMain {
+ static {
+ // Radar #5755208: Command line Java applications need a way to launch without a Dock icon.
+ System.setProperty("apple.awt.UIElement", "true");
+ }
+
+ public static void main(final String[] args) {
+ IdeaTestApplication.getInstance(null);
+ final Ref errorRef = new Ref();
+ final Ref runtimeRef = new Ref();
+ ApplicationManager.getApplication().invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ RuntimeOptions runtimeOptions = new RuntimeOptions(System.getProperties(), args);
+ cucumber.runtime.Runtime runtime = new Runtime(new MultiLoader(classLoader), classLoader, runtimeOptions);
+ runtimeRef.set(runtime);
+ runtime.writeStepdefsJson();
+ runtime.run();
+ } catch (Throwable throwable) {
+ errorRef.set(throwable);
+ Logger.getInstance(CucumberMain.class).error(throwable);
+ }
+ }
+ }, ApplicationManager.getApplication().getDefaultModalityState());
+ final Throwable throwable = errorRef.get();
+ if (throwable != null) {
+ throwable.printStackTrace();
+ }
+ System.err.println("Failed tests :");
+ for (Throwable error : runtimeRef.get().getErrors()) {
+ error.printStackTrace();
+ System.err.println("=============================");
+ }
+ System.exit(throwable != null ? 1 : 0);
+ }
+}
diff --git a/build/scripts/common_tests.gant b/build/scripts/common_tests.gant
index c942c974e12b..466446b0bb92 100644
--- a/build/scripts/common_tests.gant
+++ b/build/scripts/common_tests.gant
@@ -16,8 +16,9 @@
import static org.jetbrains.jps.idea.IdeaProjectLoader.guessHome
includeTargets << new File("${guessHome(this)}/build/scripts/utils.gant")
+includeTargets << new File("${guessHome(this)}/build/scripts/cucumber-tests.gant")
-requireProperty("out", "$home/out/classes")
+requireProperty("out", "$home/out")
target(compile: "Compile project") {
ant.delete(failonerror: false) {
@@ -38,7 +39,7 @@ private pass(String prop) {
}
}
-target('default': 'The default target') {
+target('run_tests': 'Run java tests') {
depends([compile])
ant.junit(fork: "yes", showoutput: "true", logfailedtests:false) {
@@ -75,3 +76,7 @@ target('default': 'The default target') {
}
}
}
+
+target('default' : "Run all tests") {
+ depends([compile, run_tests, run_cucumber_tests])
+}
\ No newline at end of file
diff --git a/build/scripts/cucumber-tests.gant b/build/scripts/cucumber-tests.gant
new file mode 100644
index 000000000000..7eb4e4bf40b8
--- /dev/null
+++ b/build/scripts/cucumber-tests.gant
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+target('run_cucumber_tests': "Run cucumber tests") {
+ if (!isDefined("cucumber_test_dirs")) {
+ return
+ }
+
+ cucumber_test_dirs.each {
+ def m = findModule(it[0])
+ def platformPrefix = it[1]
+ def testsDir = it[2]
+ def stepDefsPackage = it[3]
+
+
+ ant.java(failonerror: "true", classname: "com.intellij.cucumber.CucumberMain", fork: "true", dir: testsDir) {
+ jvmarg(line: "-Xmx512m")
+ jvmarg(line: "-XX:MaxPermSize=350m")
+ jvmarg(line: "-XX:+HeapDumpOnOutOfMemoryError")
+ jvmarg(line: "-ea")
+ jvmarg(line: "-Didea.platform.prefix=$platformPrefix")
+
+ System.getProperties().entrySet().each {
+ if (it.key.startsWith("pass.")) {
+ def trimmed = it.key.substring("pass.".length());
+ jvmarg(value: "-D${trimmed}=${it.value}");
+ };
+ }
+
+ arg(value: "--format")
+ arg(value: "org.jetbrains.plugins.cucumber.java.run.CucumberJvmSMFormatter")
+ arg(value: "--glue")
+ arg(value: stepDefsPackage)
+ arg(value: ".")
+
+ classpath() {
+ projectBuilder.moduleRuntimeClasspath(findModule("cucumber-test-runner"), false).each {
+ pathelement(location: it)
+ }
+ projectBuilder.moduleRuntimeClasspath(m, true).each {
+ pathelement(location: it)
+ }
+ }
+ }
+
+ }
+
+}
diff --git a/build/scripts/tests.gant b/build/scripts/tests.gant
index 9fc565ee0e1c..14bd38a5a2ea 100644
--- a/build/scripts/tests.gant
+++ b/build/scripts/tests.gant
@@ -3,6 +3,7 @@ import static org.jetbrains.jps.idea.IdeaProjectLoader.guessHome
includeTargets << new File("${guessHome(this)}/build/scripts/common_tests.gant")
setProperty("testcases", ["com.intellij.AllTests"])
+setProperty("cucumber_test_dirs", [["git4idea", "Idea", "$home/plugins/git4idea/test-features/git4idea", "git4idea"]])
def isMac = System.getProperty("os.name").toLowerCase().startsWith("mac")
def args = [
diff --git a/plugins/cucumber-jvm-formatter/cucumber-jvm-formatter.iml b/plugins/cucumber-jvm-formatter/cucumber-jvm-formatter.iml
new file mode 100644
index 000000000000..acf56ba72944
--- /dev/null
+++ b/plugins/cucumber-jvm-formatter/cucumber-jvm-formatter.iml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/cucumber-jvm-formatter/src/org/jetbrains/plugins/cucumber/java/run/CucumberJvmSMFormatter.java b/plugins/cucumber-jvm-formatter/src/org/jetbrains/plugins/cucumber/java/run/CucumberJvmSMFormatter.java
new file mode 100644
index 000000000000..6fc3befcb1b2
--- /dev/null
+++ b/plugins/cucumber-jvm-formatter/src/org/jetbrains/plugins/cucumber/java/run/CucumberJvmSMFormatter.java
@@ -0,0 +1,349 @@
+package org.jetbrains.plugins.cucumber.java.run;
+
+import gherkin.formatter.Formatter;
+import gherkin.formatter.Reporter;
+import gherkin.formatter.model.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.SimpleDateFormat;
+import java.util.ArrayDeque;
+import java.util.Date;
+import java.util.List;
+import java.util.Queue;
+
+/**
+ * User: Andrey.Vokin
+ * Date: 8/10/12
+ */
+public class CucumberJvmSMFormatter implements Formatter, Reporter {
+ private int scenarioCount;
+ private int passedScenarioCount;
+ private boolean scenarioPassed = true;
+
+ private int stepCount;
+ private int passedStepCount;
+ private int skippedStepCount;
+ private int pendingStepCount;
+ private int failedStepCount;
+ private int undefinedStepCount;
+
+ private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSSZ");
+
+ private static final String TEMPLATE_TEST_STARTED =
+ "##teamcity[testStarted timestamp = '%s' locationHint = 'file:///%s' captureStandardOutput = 'true' name = '%s']";
+ private static final String TEMPLATE_TEST_FAILED =
+ "##teamcity[testFailed timestamp = '%s' details = '%s' message = '%s' name = '%s' %s]";
+ private static final String TEMPLATE_SCENARIO_FAILED = "##teamcity[customProgressStatus timestamp='%s' type='testFailed']";
+ private static final String TEMPLATE_TEST_PENDING =
+ "##teamcity[testIgnored name = '%s' message = 'Skipped step' timestamp = '%s']";
+
+ private static final String TEMPLATE_TEST_FINISHED =
+ "##teamcity[testFinished timestamp = '%s' diagnosticInfo = 'cucumber f/s=(1344855950447, 1344855950447), duration=0, time.now=%s' duration = '0' name = '%s']";
+
+ private static final String TEMPLATE_ENTER_THE_MATRIX = "##teamcity[enteredTheMatrix timestamp = '%s']";
+
+ private static final String TEMPLATE_TEST_SUITE_STARTED =
+ "##teamcity[testSuiteStarted timestamp = '%s' locationHint = 'file://%s' name = '%s']";
+ private static final String TEMPLATE_TEST_SUITE_FINISHED = "##teamcity[testSuiteFinished timestamp = '%s' name = '%s']";
+
+ private static final String TEMPLATE_SCENARIO_COUNTING_STARTED =
+ "##teamcity[customProgressStatus testsCategory = 'Scenarios' count = '0' timestamp = '%s']";
+ private static final String TEMPLATE_SCENARIO_COUNTING_FINISHED =
+ "##teamcity[customProgressStatus testsCategory = '' count = '0' timestamp = '%s']";
+ private static final String TEMPLATE_SCENARIO_STARTED = "##teamcity[customProgressStatus type = 'testStarted' timestamp = '%s']";
+
+ public static final String RESULT_STATUS_PENDING = "pending";
+
+ private Appendable appendable;
+
+ private Queue queue;
+
+ private String uri;
+ private String currentFeatureName;
+
+ private boolean beforeExampleSection;
+
+ private ScenarioOutline currentScenarioOutline;
+
+ private Scenario currentScenario;
+
+ private Queue currentSteps;
+
+ @SuppressWarnings("UnusedDeclaration")
+ public CucumberJvmSMFormatter(Appendable appendable) {
+ this.appendable = System.err;
+ queue = new ArrayDeque();
+ currentSteps = new ArrayDeque();
+ outCommand(String.format(TEMPLATE_ENTER_THE_MATRIX, getCurrentTime()));
+ outCommand(String.format(TEMPLATE_SCENARIO_COUNTING_STARTED, getCurrentTime()));
+ }
+
+ @Override
+ public void feature(Feature feature) {
+ if (currentFeatureName != null) {
+ done();
+ }
+ currentFeatureName = "Feature: " + getName(feature);
+ outCommand(String.format(TEMPLATE_TEST_SUITE_STARTED, getCurrentTime(), uri + ":" + feature.getLine(), currentFeatureName));
+ }
+
+ private boolean isRealScenario(final Scenario scenario) {
+ return scenario.getKeyword().equals("Scenario");
+ }
+
+ @Override
+ public void scenario(Scenario scenario) {
+ closeScenario();
+ if (isRealScenario(scenario)) {
+ scenarioCount++;
+ outCommand(String.format(TEMPLATE_SCENARIO_STARTED, getCurrentTime()));
+ closeScenarioOutline();
+ currentSteps.clear();
+ }
+ currentScenario = scenario;
+ beforeExampleSection = false;
+ outCommand(String.format(TEMPLATE_TEST_SUITE_STARTED, getCurrentTime(), uri + ":" + scenario.getLine(), getName(currentScenario)));
+
+ while (queue.size() > 0) {
+ String smMessage = queue.poll();
+ outCommand(smMessage);
+ }
+ }
+
+ @Override
+ public void scenarioOutline(ScenarioOutline outline) {
+ scenarioCount++;
+ outCommand(String.format(TEMPLATE_SCENARIO_STARTED, getCurrentTime()));
+ queue.clear();
+ currentSteps.clear();
+
+ closePreviousScenarios();
+ currentScenarioOutline = outline;
+ currentScenario = null;
+ beforeExampleSection = true;
+ outCommand(
+ String.format(TEMPLATE_TEST_SUITE_STARTED, getCurrentTime(), uri + ":" + outline.getLine(), getName(currentScenarioOutline)));
+ }
+
+ @Override
+ public void examples(Examples examples) {
+ beforeExampleSection = false;
+ outCommand(String.format(TEMPLATE_TEST_SUITE_STARTED, getCurrentTime(), uri + ":" + examples.getLine(), "Examples:"));
+ }
+
+ @Override
+ public void step(Step step) {
+ if (beforeExampleSection) {
+ return;
+ }
+ currentSteps.add(step);
+ }
+
+ @Override
+ public void result(Result result) {
+ stepCount++;
+ Step currentStep = currentSteps.poll();
+ outCommand(String.format(TEMPLATE_TEST_STARTED, getCurrentTime(), uri + ":" + currentStep.getLine(), getName(currentStep)), true);
+ String stepFullName = getName(currentStep);
+ if (result.getStatus().equals(Result.FAILED)) {
+ failedStepCount++;
+ scenarioPassed = false;
+ String fullMessage = result.getErrorMessage().replace("\r", "").replace("\t", " ");
+ String[] messageInfo = fullMessage.split("\n", 2);
+ final String message;
+ final String details;
+ if (messageInfo.length == 2) {
+ message = messageInfo[0].trim();
+ details = messageInfo[1].trim();
+ }
+ else {
+ message = fullMessage;
+ details = "";
+ }
+
+ outCommand(String.format(TEMPLATE_TEST_FAILED, getCurrentTime(), escape(details), escape(message), stepFullName, ""), true);
+ outCommand(String.format(TEMPLATE_SCENARIO_FAILED, getCurrentTime()), true);
+ }
+ else if (result.getStatus().equals(RESULT_STATUS_PENDING)) {
+ pendingStepCount++;
+ scenarioPassed = false;
+ outCommand(String.format(TEMPLATE_TEST_PENDING, stepFullName, getCurrentTime()), true);
+ }
+ else if (result.equals(Result.UNDEFINED)) {
+ undefinedStepCount++;
+ scenarioPassed = false;
+ String message = "Undefined step: " + getName(currentStep);
+ String details = "";
+ outCommand(String.format(TEMPLATE_TEST_FAILED, getCurrentTime(), escape(details), escape(message), stepFullName, "error = 'true'"), true);
+ outCommand(String.format(TEMPLATE_SCENARIO_FAILED, getCurrentTime()), true);
+ }
+ else if (result.equals(Result.SKIPPED)) {
+ skippedStepCount++;
+ scenarioPassed = false;
+ outCommand(String.format(TEMPLATE_TEST_PENDING, stepFullName, getCurrentTime()), true);
+ }
+ else {
+ passedStepCount++;
+ }
+
+ String currentTime = getCurrentTime();
+ outCommand(String.format(TEMPLATE_TEST_FINISHED, currentTime, currentTime, stepFullName), true);
+ }
+
+ private void closeScenario() {
+ if (currentScenario != null) {
+ if (isRealScenario(currentScenario)) {
+ if (scenarioPassed) {
+ passedScenarioCount++;
+ }
+ }
+ outCommand(String.format(TEMPLATE_TEST_SUITE_FINISHED, getCurrentTime(), getName(currentScenario)));
+ }
+ currentScenario = null;
+ }
+
+ private void closeScenarioOutline() {
+ if (currentScenarioOutline != null) {
+ if (scenarioPassed) {
+ passedScenarioCount++;
+ }
+ if (!beforeExampleSection) {
+ outCommand(String.format(TEMPLATE_TEST_SUITE_FINISHED, getCurrentTime(), "Examples:"));
+ }
+ outCommand(String.format(TEMPLATE_TEST_SUITE_FINISHED, getCurrentTime(), getName(currentScenarioOutline)));
+ }
+ currentScenarioOutline = null;
+ }
+
+ private void closePreviousScenarios() {
+ closeScenario();
+ closeScenarioOutline();
+ }
+
+ @Override
+ public void background(Background background) {
+ closeScenario();
+ currentScenario = null;
+ }
+
+ @Override
+ public void done() {
+ closePreviousScenarios();
+ outCommand(String.format(TEMPLATE_TEST_SUITE_FINISHED, getCurrentTime(), currentFeatureName));
+ }
+
+ @Override
+ public void uri(String s) {
+ String currentDir = System.getenv().get("current_dir");
+ if (currentDir != null) {
+ uri = currentDir + File.separator + s;
+ }
+ else {
+ uri = s;
+ }
+ }
+
+ @Override
+ public void eof() {
+ }
+
+ @Override
+ public void syntaxError(String s, String s1, List strings, String s2, Integer integer) {
+ outCommand("Syntax error\n");
+ }
+
+ @Override
+ public void after(Match match, Result result) {
+ outCommand("after\n");
+ }
+
+ @Override
+ public void match(Match match) {
+ }
+
+ @SuppressWarnings("UnusedDeclaration")
+ public void embedding(String mimeType, byte[] data) {
+ outCommand("embedding\n");
+ }
+
+ @SuppressWarnings("UnusedDeclaration")
+ public void embedding(String s, InputStream inputStream) {
+ outCommand("embedding\n");
+ }
+
+ @Override
+ public void write(String s) {
+ out(s);
+ }
+
+ @Override
+ public void close() {
+ outCommand(String.format(TEMPLATE_SCENARIO_COUNTING_FINISHED, getCurrentTime()));
+
+ outCommand(scenarioCount + " scenario (" + passedScenarioCount + " passed)\n");
+ outCommand(stepCount + " steps (" + passedStepCount + " passed)\n");
+ }
+
+ @Override
+ public void before(Match match, Result result) {
+ outCommand("before\n");
+ }
+
+ private String getCurrentTime() {
+ return DATE_FORMAT.format(new Date());
+ }
+
+ private String escape(String source) {
+ return source.replace("|", "||").replace("\n", "|n").replace("\r", "|r").replace("'", "|'").replace("[", "|[").replace("]", "|]");
+ }
+
+ private void outCommand(String s) {
+ outCommand(s, false);
+ }
+
+ private void outCommand(String s, boolean waitForScenario) {
+ if (currentScenario == null && waitForScenario) {
+ queue.add(s);
+ }
+ else {
+ try {
+ appendable.append("\n");
+ appendable.append(s);
+ appendable.append("\n");
+ }
+ catch (IOException ignored) {
+ }
+ }
+ }
+
+ private void out(String s) {
+ try {
+ appendable.append(s);
+ }
+ catch (IOException ignored) {
+ }
+ }
+
+ private String getName(Scenario scenario) {
+ if (scenario.getKeyword().equals("Scenario Outline")) {
+ return escape("Scenario: Line: " + scenario.getLine());
+ }
+ else {
+ return escape("Scenario: " + scenario.getName());
+ }
+ }
+
+ private String getName(ScenarioOutline outline) {
+ return escape("Scenario Outline: " + outline.getName());
+ }
+
+ private String getName(Step step) {
+ return escape(step.getKeyword() + " " + step.getName());
+ }
+
+ private String getName(Feature feature) {
+ return escape(feature.getName());
+ }
+}