clucumber 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +20 -0
- data/README.rdoc +54 -0
- data/lib/clucumber/clucumber.asd +5 -0
- data/lib/clucumber/packages.lisp +17 -0
- data/lib/clucumber/server.lisp +242 -0
- data/lib/clucumber.rb +71 -0
- data/test/helper.rb +10 -0
- data/test/test_clucumber.rb +7 -0
- metadata +94 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Andreas Fuchs
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
Clucumber, the CL Cucumber adapter
|
2
|
+
==================================
|
3
|
+
|
4
|
+
This is a (somewhat complete) implementation of the [cucumber wire
|
5
|
+
protocol](http://wiki.github.com/aslakhellesoy/cucumber/wire-protocol) in (as portable as possible) Common Lisp. This means you can
|
6
|
+
write [cucumber](http://cukes.info/) features, and write lisp code to execute
|
7
|
+
your steps.
|
8
|
+
|
9
|
+
Getting started
|
10
|
+
---------------
|
11
|
+
|
12
|
+
First, you write your cucumber features like you would any other.
|
13
|
+
|
14
|
+
Then you define cucumber steps in CL: Just place them in
|
15
|
+
features/step_definitions/*.lisp.
|
16
|
+
|
17
|
+
If your application needs any support code, place that in
|
18
|
+
support/*.lisp.
|
19
|
+
|
20
|
+
Files in support and step_definitions/ are loaded (not file-compiled)
|
21
|
+
in alphabetical order, with support/ files being loaded before step
|
22
|
+
definitions.
|
23
|
+
|
24
|
+
Running tests
|
25
|
+
-------------
|
26
|
+
|
27
|
+
In your `features/support/env.rb`, you load the clucumber.rb included in this distribution. Then, you run something like this:
|
28
|
+
|
29
|
+
begin
|
30
|
+
@main_clucumber = ClucumberSubprocess.new(File.expand_path("../", File.dirname(__FILE__)),
|
31
|
+
:port => 42428)
|
32
|
+
at_exit do
|
33
|
+
@main_clucumber.kill
|
34
|
+
end
|
35
|
+
|
36
|
+
@main_clucumber.start <<-LISP
|
37
|
+
;; Put code here that loads your application.
|
38
|
+
LISP
|
39
|
+
rescue PTY::ChildExited
|
40
|
+
puts(@main_clucumber && @main_clucumber.output)
|
41
|
+
end
|
42
|
+
|
43
|
+
This will launch a lisp with clucumber loaded (pass :lisp parameter to `ClucumberSubprocess.new` to specify which lisp, it defaults to sbcl), and start listening on port 42428.
|
44
|
+
|
45
|
+
Then, on the command line, you run cucumber:
|
46
|
+
|
47
|
+
$ cucumber
|
48
|
+
|
49
|
+
And you watch the lines zip by.
|
50
|
+
|
51
|
+
That should be all (-:
|
52
|
+
|
53
|
+
Over the next few days, I hope to fill out the test suite with more
|
54
|
+
interesting examples that you can use as a reference.
|
@@ -0,0 +1,17 @@
|
|
1
|
+
(cl:defpackage #:clucumber-external
|
2
|
+
(:export #:start))
|
3
|
+
|
4
|
+
(cl:defpackage #:clucumber-steps
|
5
|
+
(:export #:define-test-package #:*test-package*
|
6
|
+
#:Given* #:When* #:Then*
|
7
|
+
#:Before #:After
|
8
|
+
#:pending #:fail
|
9
|
+
#:var)
|
10
|
+
(:use #:cl #:cl-interpol))
|
11
|
+
|
12
|
+
(cl:defpackage #:clucumber-user
|
13
|
+
(:export)
|
14
|
+
(:use #:cl #:clucumber-steps))
|
15
|
+
|
16
|
+
(cl:defpackage #:clucumber
|
17
|
+
(:use #:cl #:clucumber-steps #:clucumber-external #:usocket #:st-json))
|
@@ -0,0 +1,242 @@
|
|
1
|
+
(cl:in-package #:clucumber)
|
2
|
+
|
3
|
+
(defvar clucumber-steps:*test-package* (find-package :clucumber-user))
|
4
|
+
|
5
|
+
(defvar *print-backtraces* t)
|
6
|
+
|
7
|
+
(defvar *base-pathname*)
|
8
|
+
|
9
|
+
(defparameter *default-step-regex-delimiter* #\{)
|
10
|
+
|
11
|
+
(defparameter *default-step-regex-close-delimiter* #\})
|
12
|
+
|
13
|
+
(defun load-definitions (base-pathname)
|
14
|
+
(let ((support-files (directory (merge-pathnames (make-pathname :directory '(:relative "support"
|
15
|
+
:wild-inferiors)
|
16
|
+
:name :wild
|
17
|
+
:type "lisp")
|
18
|
+
base-pathname)))
|
19
|
+
(step-files (directory (merge-pathnames (make-pathname :directory '(:relative "step_definitions"
|
20
|
+
:wild-inferiors)
|
21
|
+
:name :wild
|
22
|
+
:type "lisp")
|
23
|
+
base-pathname))))
|
24
|
+
(dolist (files (list support-files step-files))
|
25
|
+
(let ((*readtable* (copy-readtable))
|
26
|
+
(*package* *test-package*)
|
27
|
+
(cl-interpol::*regex-delimiters* (cons *default-step-regex-delimiter*
|
28
|
+
cl-interpol::*regex-delimiters*)))
|
29
|
+
(cl-interpol:enable-interpol-syntax)
|
30
|
+
(mapc #'load (sort files #'string<
|
31
|
+
:key (lambda (path)
|
32
|
+
(enough-namestring path base-pathname))))))))
|
33
|
+
|
34
|
+
(defun serve-cucumber-requests (socket &aux (stream (socket-stream socket)))
|
35
|
+
(handler-case
|
36
|
+
(loop
|
37
|
+
(let* ((line (read-line stream))
|
38
|
+
(message (read-json line nil))
|
39
|
+
(reply (call-wire-protocol-method message)))
|
40
|
+
(st-json:write-json reply stream)
|
41
|
+
(terpri stream)
|
42
|
+
(finish-output stream)))
|
43
|
+
(end-of-file nil nil)))
|
44
|
+
|
45
|
+
|
46
|
+
;;; Step definitions
|
47
|
+
|
48
|
+
|
49
|
+
(defparameter *steps* (make-array 0 :adjustable t :fill-pointer t))
|
50
|
+
|
51
|
+
(defclass step-definition ()
|
52
|
+
((regex :initarg :regex :accessor regex)
|
53
|
+
(cont :initarg :continuation :accessor continuation)
|
54
|
+
(scanner :accessor scanner)
|
55
|
+
(definition-file :initform *load-truename* :accessor definition-file)))
|
56
|
+
|
57
|
+
(defmethod initialize-instance :after ((o step-definition) &key regex &allow-other-keys)
|
58
|
+
(setf (scanner o) (cl-ppcre:create-scanner regex)))
|
59
|
+
|
60
|
+
(defun add-step (regex function)
|
61
|
+
(let ((existing-step (find regex *steps* :key #'regex :test #'string=)))
|
62
|
+
(if existing-step
|
63
|
+
(setf (continuation existing-step) function
|
64
|
+
(definition-file existing-step) *load-truename*)
|
65
|
+
(vector-push-extend
|
66
|
+
(make-instance 'step-definition :regex regex :continuation function)
|
67
|
+
*steps*)))
|
68
|
+
*steps*)
|
69
|
+
|
70
|
+
(defmacro clucumber-steps:Given* (regex args &body body)
|
71
|
+
`(eval-when (:compile-toplevel :load-toplevel :execute)
|
72
|
+
(add-step ,regex (lambda (,@args) ,@body))))
|
73
|
+
|
74
|
+
(defmacro clucumber-steps:Then* (regex args &body body)
|
75
|
+
`(eval-when (:compile-toplevel :load-toplevel :execute)
|
76
|
+
(add-step ,regex (lambda (,@args) ,@body))))
|
77
|
+
|
78
|
+
(defmacro clucumber-steps:When* (regex args &body body)
|
79
|
+
`(eval-when (:compile-toplevel :load-toplevel :execute)
|
80
|
+
(add-step ,regex (lambda (,@args) ,@body))))
|
81
|
+
|
82
|
+
;;; Packages
|
83
|
+
|
84
|
+
(defun clucumber-external:start (*base-pathname* host port &key quit)
|
85
|
+
(setf (fill-pointer *steps*) 0)
|
86
|
+
(load-definitions *base-pathname*)
|
87
|
+
(let ((server (usocket:socket-listen host port :reuse-address t)))
|
88
|
+
(unwind-protect
|
89
|
+
(loop
|
90
|
+
(let ((socket (usocket:socket-accept server :element-type 'character)))
|
91
|
+
(serve-cucumber-requests socket))
|
92
|
+
(when quit (return)))
|
93
|
+
(usocket:socket-close server))))
|
94
|
+
|
95
|
+
(defmacro clucumber-steps:define-test-package (name &rest defpackage-arguments)
|
96
|
+
`(eval-when (:compile-toplevel :load-toplevel :execute)
|
97
|
+
(defpackage ,name
|
98
|
+
(:use #:clucumber)
|
99
|
+
,@defpackage-arguments)
|
100
|
+
(setf *test-package* (find-package ',name))))
|
101
|
+
|
102
|
+
;;; Before / after hooks:
|
103
|
+
|
104
|
+
;; I'm not sure if these can handle tags. It would certainly be nice if they could.
|
105
|
+
|
106
|
+
(defvar *before-hooks* (make-array 0 :adjustable t :fill-pointer t))
|
107
|
+
(defvar *after-hooks* (make-array 0 :adjustable t :fill-pointer t))
|
108
|
+
|
109
|
+
(defmacro clucumber-steps:Before (&body body)
|
110
|
+
`(vector-push-extend (lambda () ,@body) *before-hooks*))
|
111
|
+
|
112
|
+
(defmacro clucumber-steps:After (&body body)
|
113
|
+
`(vector-push-extend (lambda () ,@body) *before-hooks*))
|
114
|
+
|
115
|
+
;;; Wire protocol
|
116
|
+
|
117
|
+
(defvar *wire-protocol-methods* (make-hash-table :test #'equal))
|
118
|
+
|
119
|
+
(defmacro define-wire-protocol-method (name args &body body)
|
120
|
+
(let ((params (gensym)))
|
121
|
+
`(setf (gethash ,name *wire-protocol-methods*)
|
122
|
+
(lambda (,params)
|
123
|
+
(declare (ignorable ,params))
|
124
|
+
(catch 'wire-protocol-method
|
125
|
+
(let (,@(mapcar (lambda (arg-spec)
|
126
|
+
(destructuring-bind (arg-name &optional
|
127
|
+
(jso-name (string-downcase arg-name)))
|
128
|
+
(if (listp arg-spec) arg-spec (list arg-spec))
|
129
|
+
`(,arg-name (getjso ,jso-name ,params))))
|
130
|
+
args))
|
131
|
+
,@body))))))
|
132
|
+
|
133
|
+
(defun call-wire-protocol-method (wire-protocol-message)
|
134
|
+
(let ((method (gethash (first wire-protocol-message) *wire-protocol-methods*)))
|
135
|
+
(if method
|
136
|
+
(funcall method
|
137
|
+
(second wire-protocol-message))
|
138
|
+
(list "fail"))))
|
139
|
+
|
140
|
+
(defun clucumber-steps:fail (message &key format-args exception backtrace)
|
141
|
+
(throw 'wire-protocol-method
|
142
|
+
(list "fail"
|
143
|
+
(apply #'jso
|
144
|
+
`("message" ,(apply #'format nil message format-args)
|
145
|
+
,@(when exception
|
146
|
+
`("exception" ,exception))
|
147
|
+
,@(when backtrace
|
148
|
+
`("backtrace" ,backtrace)))))))
|
149
|
+
|
150
|
+
(defun clucumber-steps:pending (&optional message)
|
151
|
+
(throw 'wire-protocol-method
|
152
|
+
`("pending" ,@(when message
|
153
|
+
(list message)))))
|
154
|
+
|
155
|
+
(defun backtrace-for (condition)
|
156
|
+
(if *print-backtraces*
|
157
|
+
(trivial-backtrace:print-backtrace condition :output nil)
|
158
|
+
""))
|
159
|
+
|
160
|
+
(defmacro with-error-handling (&body body)
|
161
|
+
`(let ((*debugger-hook* (lambda (condition prev-hook)
|
162
|
+
(declare (ignore prev-hook))
|
163
|
+
(fail "Non-error condition invoked the debugger"
|
164
|
+
:exception (princ-to-string condition)
|
165
|
+
:backtrace (backtrace-for condition)))))
|
166
|
+
(handler-case
|
167
|
+
(progn ,@body)
|
168
|
+
(error (condition)
|
169
|
+
(fail "Caught an error"
|
170
|
+
:exception (princ-to-string condition)
|
171
|
+
:backtrace (backtrace-for condition))))))
|
172
|
+
|
173
|
+
(define-wire-protocol-method "begin_scenario" ()
|
174
|
+
(with-error-handling
|
175
|
+
(map nil 'funcall *before-hooks*)
|
176
|
+
(list "success")))
|
177
|
+
|
178
|
+
(define-wire-protocol-method "end_scenario" ()
|
179
|
+
(with-error-handling
|
180
|
+
(map nil 'funcall *after-hooks*)
|
181
|
+
(reset-state)
|
182
|
+
(list "success")))
|
183
|
+
|
184
|
+
(define-wire-protocol-method "step_matches" ((name-to-match "name_to_match"))
|
185
|
+
(list "success"
|
186
|
+
(loop for posn from 0
|
187
|
+
for step across *steps*
|
188
|
+
for scanner = (scanner step)
|
189
|
+
for (matchp end starts ends) = (multiple-value-list (cl-ppcre:scan scanner name-to-match))
|
190
|
+
for arguments = (map 'list (lambda (start end)
|
191
|
+
(jso "val" (subseq name-to-match start end)
|
192
|
+
"pos" start))
|
193
|
+
starts ends)
|
194
|
+
if matchp collect (jso "id" posn "args" arguments
|
195
|
+
"regexp" (regex step)
|
196
|
+
"source" (enough-namestring (definition-file step)
|
197
|
+
*base-pathname*)))))
|
198
|
+
|
199
|
+
(define-wire-protocol-method "invoke" (id args)
|
200
|
+
(let ((step (elt *steps* id)))
|
201
|
+
(if step
|
202
|
+
(with-error-handling
|
203
|
+
(apply (continuation step) args)
|
204
|
+
(list "success"))
|
205
|
+
(fail "Step ~S is undefined" :format-args `(,id)))))
|
206
|
+
|
207
|
+
|
208
|
+
(defun make-dwim-step-regex (step)
|
209
|
+
(let* ((escaped (cl-ppcre:regex-replace-all "[][}{)(]" step "\\\\\\&"))
|
210
|
+
(count 0))
|
211
|
+
(values (or (cl-ppcre:regex-replace-all "\"[^\"]+\"" escaped
|
212
|
+
(lambda (&rest _)
|
213
|
+
(declare (ignore _))
|
214
|
+
(incf count)
|
215
|
+
"\"([^\"]*)\""))
|
216
|
+
escaped)
|
217
|
+
count)))
|
218
|
+
|
219
|
+
(define-wire-protocol-method "snippet_text" ((keyword "step_keyword") (step-name "step_name"))
|
220
|
+
;; TODO: figure out multiline_arg_class
|
221
|
+
(list "success"
|
222
|
+
(multiple-value-bind (step-re group-count) (make-dwim-step-regex step-name)
|
223
|
+
(let ((group-vars (loop for i from 0 below group-count
|
224
|
+
collect (format nil "group-~D" i))))
|
225
|
+
(format nil "(~A* #?~C^~A$~C (~{~A~^ ~})~% ~
|
226
|
+
;; express the regexp above with the code you wish you had~% ~
|
227
|
+
(pending))" (string-trim '(#\Space) keyword) *default-step-regex-delimiter*
|
228
|
+
step-re *default-step-regex-close-delimiter*
|
229
|
+
group-vars)))))
|
230
|
+
|
231
|
+
;;; Sharing state between steps:
|
232
|
+
|
233
|
+
(defvar *variables* (make-hash-table :test #'eql))
|
234
|
+
|
235
|
+
(defun clucumber-steps:var (name &optional default)
|
236
|
+
(gethash name *variables* default))
|
237
|
+
|
238
|
+
(defun (setf clucumber-steps:var) (new-val name &optional default)
|
239
|
+
(setf (gethash name *variables* default) new-val))
|
240
|
+
|
241
|
+
(defun reset-state ()
|
242
|
+
(clrhash *variables*))
|
data/lib/clucumber.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'pty'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
class ClucumberSubprocess
|
5
|
+
class LaunchFailed < RuntimeError; end
|
6
|
+
|
7
|
+
attr_reader :output
|
8
|
+
|
9
|
+
def initialize(dir, options={})
|
10
|
+
@dir = dir
|
11
|
+
lisp = options[:lisp] || ENV['LISP'] || 'sbcl --disable-debugger'
|
12
|
+
@port = options[:port] || raise("Need a port to run clucumber on.")
|
13
|
+
@output = ""
|
14
|
+
|
15
|
+
Dir.chdir(@dir) do
|
16
|
+
@out, @in, @pid = PTY.spawn(lisp)
|
17
|
+
end
|
18
|
+
@reader = Thread.start {
|
19
|
+
record_output
|
20
|
+
}
|
21
|
+
@in.puts(<<-LISP)
|
22
|
+
(require :asdf)
|
23
|
+
(load #p"#{File.expand_path("clucumber/clucumber.asd", File.dirname(__FILE__))}")
|
24
|
+
LISP
|
25
|
+
end
|
26
|
+
|
27
|
+
def start(additional_forms="")
|
28
|
+
@in.puts <<-LISP
|
29
|
+
#{additional_forms}
|
30
|
+
(asdf:oos 'asdf:load-op :clucumber)
|
31
|
+
(clucumber-external:start #p"./" "localhost" #{@port})
|
32
|
+
LISP
|
33
|
+
until socket = TCPSocket.new("localhost", @port) rescue nil
|
34
|
+
raise LaunchFailed, "Couldn't start clucumber:\n#{@output}" unless alive?
|
35
|
+
sleep 0.01
|
36
|
+
end
|
37
|
+
File.open(File.join(@dir, "step_definitions", "clucumber.wire"), "w") do |out|
|
38
|
+
YAML.dump({'host' => "localhost", 'port' => @port}, out)
|
39
|
+
end
|
40
|
+
socket.close
|
41
|
+
end
|
42
|
+
|
43
|
+
def record_output
|
44
|
+
begin
|
45
|
+
while line = @out.readline
|
46
|
+
@output << line
|
47
|
+
end
|
48
|
+
rescue PTY::ChildExited
|
49
|
+
STDOUT.puts "child exited, stopping."
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def kill
|
55
|
+
if @pid
|
56
|
+
FileUtils.rm_f File.join(@dir, "step_definitions", "clucumber.wire")
|
57
|
+
@reader.terminate!
|
58
|
+
Process.kill("TERM", @pid)
|
59
|
+
Process.waitpid(@pid)
|
60
|
+
@pid = nil
|
61
|
+
end
|
62
|
+
rescue PTY::ChildExited
|
63
|
+
@pid = nil
|
64
|
+
end
|
65
|
+
|
66
|
+
def alive?
|
67
|
+
if !@pid.nil?
|
68
|
+
(Process.kill("CONT", @pid) && true) rescue false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/test/helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: clucumber
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Andreas Fuchs
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-05-03 00:00:00 +02:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: aruba
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :development
|
31
|
+
version_requirements: *id001
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: cucumber
|
34
|
+
prerelease: false
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
segments:
|
40
|
+
- 0
|
41
|
+
version: "0"
|
42
|
+
type: :development
|
43
|
+
version_requirements: *id002
|
44
|
+
description: |-
|
45
|
+
A cucumber extension that lets you write your step definitions in Common Lisp.
|
46
|
+
Set internal state in your Hunchentoot web app or your library, and use the full power of Cucumber and its other extensions.
|
47
|
+
email: asf@boinkor.net
|
48
|
+
executables: []
|
49
|
+
|
50
|
+
extensions: []
|
51
|
+
|
52
|
+
extra_rdoc_files:
|
53
|
+
- LICENSE
|
54
|
+
- README.rdoc
|
55
|
+
files:
|
56
|
+
- lib/clucumber.rb
|
57
|
+
- lib/clucumber/clucumber.asd
|
58
|
+
- lib/clucumber/packages.lisp
|
59
|
+
- lib/clucumber/server.lisp
|
60
|
+
- LICENSE
|
61
|
+
- README.rdoc
|
62
|
+
has_rdoc: true
|
63
|
+
homepage: http://github.com/antifuchs/clucumber
|
64
|
+
licenses: []
|
65
|
+
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options:
|
68
|
+
- --charset=UTF-8
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
segments:
|
83
|
+
- 0
|
84
|
+
version: "0"
|
85
|
+
requirements: []
|
86
|
+
|
87
|
+
rubyforge_project:
|
88
|
+
rubygems_version: 1.3.6
|
89
|
+
signing_key:
|
90
|
+
specification_version: 3
|
91
|
+
summary: Test drive your Common Lisp application from Cucumber
|
92
|
+
test_files:
|
93
|
+
- test/helper.rb
|
94
|
+
- test/test_clucumber.rb
|