forked from antifuchs/clucumber
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial work on Clucumber, the CL adapter to Cucumber.
- Loading branch information
0 parents
commit 16e3171
Showing
5 changed files
with
148 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
(asdf:defsystem clucumber | ||
:depends-on (:cl-interpol :cl-ppcre :trivial-backtrace :usocket) | ||
:serial t | ||
:components ((:file "packages") | ||
(:file "server"))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
Feature: Load order of files | ||
|
||
As a user of Clucumber, I want the files I specify to be loaded in a | ||
specific order, so that I can rely on definitions in one file to be | ||
present in the other. | ||
|
||
Scenario: Support files are loaded first | ||
|
||
Given I start clucumber in fixtures/load-order/ | ||
When I load the clucumber-specific files | ||
Then support/a.lisp should be loaded before step_definitions/a.lisp | ||
|
||
Scenario: Files are loaded in alphabetical order | ||
|
||
Given I start clucumber in fixtures/load-order/ | ||
When I load the clucumber-specific files | ||
|
||
Then support/a.lisp should be loaded before support/b.lisp | ||
And support/b.lisp should be loaded before support/c.lisp | ||
|
||
And step_definitions/a.lisp should be loaded before step_definitions/b.lisp | ||
And step_definitions/b.lisp should be loaded before step_definitions/c.lisp | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
Feature: Packages | ||
|
||
As a user of clucumber, I want to define a package that includes | ||
clucumber functionality, so that I can refer to that package more | ||
conveniently from step definitions. | ||
|
||
Scenario: Defining a package in support | ||
|
||
When I start clucumber in fixtures/packages/ | ||
Then the current package should be default-package | ||
|
||
When I define some-other-package as the test package | ||
Then the current package should be some-other-package | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
(cl:defpackage #:clucumber-external | ||
(:export #:start)) | ||
|
||
(cl:defpackage #:clucumber-steps | ||
(:export #:define-test-package #:*test-package* | ||
#:Given #:When #:Then) | ||
(:use #:cl #:cl-interpol)) | ||
|
||
(cl:defpackage #:clucumber-user | ||
(:export) | ||
(:use #:cl #:clucumber-steps)) | ||
|
||
(cl:defpackage #:clucumber | ||
(:use #:cl #:clucumber-steps #:clucumber-external)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
(cl:in-package #:clucumber) | ||
|
||
(defun load-definitions (base-pathname) | ||
(let ((support-files (directory (merge-pathnames (make-pathname :directory '(:relative "support" | ||
:wild-inferiors) | ||
:name :wild | ||
:type "lisp") | ||
base-pathname))) | ||
(step-files (directory (merge-pathnames (make-pathname :directory '(:relative "step_definitions" | ||
:wild-inferiors) | ||
:name :wild | ||
:type "lisp") | ||
base-pathname)))) | ||
(dolist (files (list support-files step-files)) | ||
(let ((*readtable* (copy-readtable)) | ||
(*package* *test-package*)) | ||
(cl-interpol:enable-interpol-syntax) | ||
(mapc #'load (sort files #'string> | ||
:key (lambda (path) | ||
(enough-namestring path base-pathname)))))))) | ||
|
||
(defun serve-cucumber-requests (socket) | ||
(let ((stream (usocket:socket-stream socket))) | ||
(unwind-protect | ||
(loop with eof-value = (gensym) | ||
for line = (read-line stream nil eof-value) | ||
until (eql line eof-value) | ||
do (catch 'exited | ||
(let ((did-not-unwind nil) | ||
(*debugger-hook* (lambda (condition prev-hook) | ||
(declare (ignore prev-hook)) | ||
(print :debugger stream) | ||
(prin1 (trivial-backtrace:print-backtrace condition | ||
:output nil)) | ||
(throw 'exited nil)))) | ||
(unwind-protect | ||
(progn | ||
(call-step line) | ||
(print :ok stream) | ||
(setf did-not-unwind t)) | ||
(unless did-not-unwind | ||
(print :unwind stream) | ||
(terpri stream))))))))) | ||
|
||
;;; Step definitions | ||
|
||
|
||
(defparameter *steps* ()) | ||
|
||
(defun call-step (line) | ||
(let ((matches (remove-if-not (lambda (regexp) | ||
(cl-ppcre:scan regexp line)) | ||
*steps* :key #'car))) | ||
(unless (= 1 (length matches)) | ||
(error "Ambiguous step definitions matching ~S: ~S" line matches)) | ||
(let ((regex (car (first matches))) | ||
(function (cdr (first matches)))) | ||
(apply function (coerce (nth-value 1 (cl-ppcre:scan-to-strings regex line)) 'list))))) | ||
|
||
(defun add-step (regex function) | ||
(let ((existing-step (find regex *steps* :key #'car :test #'string=))) | ||
(if existing-step | ||
(setf (cdr existing-step) function) | ||
(setf *steps* (nconc *steps* | ||
(list (cons regex function)))))) | ||
*steps*) | ||
|
||
(defmacro Given (regex args &body body) | ||
`(eval-when (:compile-toplevel :load-toplevel :execute) | ||
(add-step ,regex (lambda (,@args) ,@body)))) | ||
|
||
(defmacro Then (regex args &body body) | ||
`(eval-when (:compile-toplevel :load-toplevel :execute) | ||
(add-step ,regex (lambda (,@args) ,@body)))) | ||
|
||
;;; TODO: Given, When, Then. | ||
|
||
;;; Packages | ||
|
||
(defun clucumber-external:start (base-pathname host port) | ||
(load-definitions base-pathname) | ||
(let ((socket (usocket:socket-connect host port))) | ||
(serve-cucumber-requests socket))) | ||
|
||
(defvar clucumber-steps:*test-package* (find-package :clucumber-user)) | ||
|
||
(defmacro clucumber-steps:define-test-package (name &rest defpackage-arguments) | ||
`(eval-when (:compile-toplevel :load-toplevel :execute) | ||
(defpackage ,name | ||
(:use #:clucumber) | ||
,@defpackage-arguments) | ||
(setf *test-package* (find-package ',name)))) |