Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clojure native #138

Closed
wants to merge 13 commits into from
87 changes: 61 additions & 26 deletions clojure/pom.xml
Original file line number Diff line number Diff line change
@@ -1,31 +1,66 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>info.cukes</groupId>
<artifactId>cucumber-jvm</artifactId>
<relativePath>../pom.xml</relativePath>
<version>1.0.0.RC2-SNAPSHOT</version>
</parent>
<parent>
<groupId>info.cukes</groupId>
<artifactId>cucumber-jvm</artifactId>
<relativePath>../pom.xml</relativePath>
<version>1.0.0.RC2-SNAPSHOT</version>
</parent>

<artifactId>cucumber-clojure</artifactId>
<packaging>jar</packaging>
<name>Cucumber-JVM: Clojure</name>
<artifactId>cucumber-clojure</artifactId>
<packaging>jar</packaging>

<dependencies>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-core</artifactId>
</dependency>
<dependency>
<groupId>org.clojure</groupId>
<artifactId>clojure</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<name>Cucumber-JVM: Clojure</name>

<dependencies>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-core</artifactId>
</dependency>
<dependency>
<groupId>org.clojure</groupId>
<artifactId>clojure</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.theoryinpractise</groupId>
<artifactId>clojure-maven-plugin</artifactId>
<version>1.3.6</version>
<configuration>
<namespaces>
<namespace>cucumber.*</namespace>
</namespaces>
<sourceDirectories>
<sourceDirectory>src/main/clj</sourceDirectory>
</sourceDirectories>
<compileDeclaredNamespaceOnly>true</compileDeclaredNamespaceOnly>
</configuration>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
112 changes: 112 additions & 0 deletions clojure/src/main/clj/cucumber/runtime/clj.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
(ns cucumber.runtime.clj
(:import (cucumber.runtime CucumberException
JdkPatternArgumentMatcher
StepDefinition
HookDefinition)
(gherkin TagExpression)
(clojure.lang RT))
(:gen-class :name cucumber.runtime.clj.Backend
:implements [cucumber.runtime.Backend]
:constructors
{[cucumber.io.ResourceLoader] []}
:init init
:state state))

(def world (atom nil))

(defn load-script [path]
(try
(RT/load (str "cucumber/" (.replaceAll path ".clj$" "")) true)
(catch Throwable t
(throw (CucumberException. t)))))

(defn- -init [resource-loader]
[[] (atom {:resource-loader resource-loader})])

(defn- -buildWorld [cljb glue-paths a-world]
(reset! world a-world)
(doseq [path glue-paths
resource (.resources (:resource-loader @(.state cljb)) path ".clj")]
;; scripts are loaded with this namespace as the current namespace
;; to give access to the macros defined below
;; you can still use (ns ...) as normal
(binding [*ns* (create-ns 'cucumber.runtime.clj)]
(load-script (.getPath resource)))))

(defn- -disposeWorld [cljb])

;; Snippets don't have a nice interface to extend :(
(defn- -getSnippet [cljb step]
"snippet!")

(defn add-step-definition [pattern fun location]
(.addStepDefinition
@world
(reify
StepDefinition
(matchedArguments [_ step]
(.argumentsFrom (JdkPatternArgumentMatcher. pattern)
(.getName step)))
(getLocation [_]
(str (:file location) ":" (:line location)))
(getParameterTypes [_]
nil)
(execute [_ locale args]
(apply fun args))
(isDefinedAt [_ stack-trace-element]
(and (= (.getLineNumber stack-trace-element)
(:line location))
(= (.getFileName stack-trace-element)
(:file location))))
(getPattern [_]
pattern))))

(defmulti add-hook-definition (fn [t & _] t))

(defmethod add-hook-definition :before [_ tag-expression hook-fun]
(let [te (TagExpression. (list* (seq tag-expression)))]
(.addBeforeHook
@world
(reify
HookDefinition
(execute [hd scenario-result]
(hook-fun))
(matches [hd tags]
(.eval te tags))
(getOrder [hd] 0)))))

(defmethod add-hook-definition :after [_ tag-expression hook-fun]
(let [te (TagExpression. (list* (seq tag-expression)))
max-parameter-count (->> hook-fun class .getDeclaredMethods
(filter #(= "invoke" (.getName %)))
(map #(count (.getParameterTypes %)))
(apply max))]
(.addBeforeHook
@world
(reify
HookDefinition
(execute [hd scenario-result]
(if (zero? max-parameter-count)
(hook-fun)
(hook-fun scenario-result)))
(matches [hd tags]
(.eval te tags))
(getOrder [hd] 0)))))

;; TODO: before and after hooks
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO is done?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is, I forgot to delete it


(defmacro step-macros [& names]
(cons 'do
(for [name names]
`(defmacro ~name [pattern# fun#]
`(add-step-definition ~pattern# ~fun#
'~{:file *file*
:line (:line (meta ~'&form))})))))
(step-macros
Given When Then And But)

(defmacro Before [fun]
`(add-hook-definition :before [] ~fun))

(defmacro After [fun]
`(add-hook-definition :after [] ~fun))
28 changes: 28 additions & 0 deletions clojure/src/main/clj/leiningen/cuke.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
(ns leiningen.cuke
(:require [leiningen.compile :as lc]
[leiningen.core :as c]))

(defn cuke
"runs cucumber"
[project]
;; basically a reimplimentation of cli.Main that doesn't annoyingly
;; call System/exit
(lc/eval-in-project
project
`(let [~'runtime (cucumber.runtime.Runtime.
(list* ["test/cucumber"])
(cucumber.io.FileResourceLoader.) false)
mformatter# (doto (cucumber.formatter.MultiFormatter.)
(.add (.createFormatter
(cucumber.formatter.FormatterFactory.)
"pretty" System/out)))
formatter# (.formatterProxy mformatter#)]
(.run ~'runtime
(list* ["test/cucumber/features"])
(list)
formatter#
(.reporterProxy mformatter#))
(.done formatter#)
(println)
~(when-not c/*interactive?*
`(System/exit (.exitStatus ~'runtime))))))
90 changes: 0 additions & 90 deletions clojure/src/main/java/cucumber/runtime/clojure/ClojureBackend.java

This file was deleted.

18 changes: 0 additions & 18 deletions clojure/src/main/java/cucumber/runtime/clojure/ClojureHook.java

This file was deleted.

This file was deleted.

Loading