summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordickmao <rchiang@cs.stonybrook.edu>2018-08-28 21:48:31 -0400
committerdickmao <dick.r.chiang@gmail.com>2018-08-30 00:14:42 -0400
commit800dd7677276f9e425c5db37b3e31e56ab1dad32 (patch)
tree09eb145ffcbaf89d873c89e8346ee8d38a88ebbe
parent5bd1e7248bb9bf3b335cbd6bec250866bd165af1 (diff)
Add tests for castling legality and ics response handling.
Fixes misinterpretation of "e1-b1" as a valid queenside castle, and makes more sense of ics basic response handling #21. `make test` should run the historic.pgn test and the new ecukes tests.
-rw-r--r--Cask29
-rw-r--r--Makefile.am5
-rw-r--r--chess-engine.el9
-rw-r--r--chess-ics.el24
-rw-r--r--chess-module.el2
-rw-r--r--chess-ply.el14
-rw-r--r--features/castling.feature7
-rw-r--r--features/ics.feature5
-rw-r--r--features/step-definitions/emacs-chess-steps.el47
-rw-r--r--features/support/env.el43
10 files changed, 165 insertions, 20 deletions
diff --git a/Cask b/Cask
new file mode 100644
index 0000000..6cb2749
--- /dev/null
+++ b/Cask
@@ -0,0 +1,29 @@
+;;-*- Mode: Emacs-Lisp -*-
+;; Cask is a package manager for emacs lisp projects. It runs
+;; tests and can generate the _pkg.el file.
+;;
+;; See http://cask.readthedocs.org/en/latest/guide/dsl.html for more
+;; information about Cask.
+;;
+;; cask pkg-file
+;;
+;; cask exec ecukes
+;; cask install
+;;
+;; are particularly useful commands (update/install is for flycheck-cask).
+;;
+;;; Code:
+
+(source gnu)
+(source melpa)
+
+(package-file "chess.el")
+
+(development
+ ;; optional dependencies (used in the tests)
+ (depends-on "f")
+ (depends-on "ert-runner")
+ (depends-on "ecukes")
+ (depends-on "espuds"))
+
+;;; Cash ends here
diff --git a/Makefile.am b/Makefile.am
index 9153f9f..7ce0e93 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -108,6 +108,11 @@ chess-test:
echo "$(EMACS) -batch -L $(srcdir) -l chess-test.el -f chess-test '$(DATABASE)' $(START) $(COUNT); rm -f $(top_builddir)/chess-test" > $@
chmod u+x $@
+.PHONY: test
+test: $(TESTS) $(ELCFILES)
+ $(top_builddir)/chess-test
+ cask exec ecukes
+
test/twic.pgn:
(set -e; f=$$(pwd)/$@; tmp_dir=`mktemp -d`; \
cd $$tmp_dir; \
diff --git a/chess-engine.el b/chess-engine.el
index 0fef4b0..0ac7541 100644
--- a/chess-engine.el
+++ b/chess-engine.el
@@ -136,7 +136,7 @@ If conversion fails, this function fired an 'illegal event."
((eq event 'match)
(if (chess-game-data game 'active)
(chess-engine-command nil 'busy)
- (let ((name (and (> (length (car args)) 0) (car args))))
+ (let ((name (> (length (car args)) 0)))
(if (y-or-n-p (if name
(chess-string 'want-to-play (car args))
(chess-string 'want-to-play-a)))
@@ -291,9 +291,10 @@ If conversion fails, this function fired an 'illegal event."
(chess-game-run-hooks game 'flag-fell))))
((eq event 'flag-fell)
- (chess-message 'opp-flag-fell)
- (chess-game-end game :flag-fell)
- (chess-game-set-data game 'active nil))
+ (let ((chess-engine-handling-event t))
+ (chess-message 'opp-flag-fell)
+ (chess-game-end game :flag-fell)
+ (chess-game-set-data game 'active nil)))
((eq event 'kibitz)
(let ((chess-engine-handling-event t))
diff --git a/chess-ics.el b/chess-ics.el
index 24162be..51ce1e0 100644
--- a/chess-ics.el
+++ b/chess-ics.el
@@ -314,7 +314,12 @@ standard position). In those cases, this variable should be set to nil.")
(white (match-string-no-properties 2))
(black (match-string-no-properties 3)))
(message "Creating game %d (%s vs. %s)" game-number white black)
- (chess-ics-game game-number :White white :Black black)))))
+ ; chess-module-set-game* would add event handlers
+ ; to the game as if it were an engine game
+ ; We just need (chess-engine-game nil) to return the game.
+ (setq chess-engine-opponent-name (if (string= white chess-ics-handle) black white))
+ (setq chess-module-game (chess-ics-game game-number :White white :Black black))
+))))
(cons "^<10>$" (function (lambda () (chess-ics-send "style 12\nrefresh"))))
(cons "^Game \\([0-9]+\\): \\S-+ backs up \\([0-9]+\\).$"
(function
@@ -369,17 +374,17 @@ standard position). In those cases, this variable should be set to nil.")
(cons "\\S-+ would like to take back \\([0-9]+\\) half move(s)."
(function
(lambda ()
- (funcall chess-engine-response-handler 'undo
+ (funcall #'chess-engine-default-handler 'undo
(string-to-number (match-string 1))))))
(cons "The game has been aborted on move [^.]+\\."
(function
(lambda ()
(let ((chess-engine-pending-offer 'abort))
- (funcall chess-engine-response-handler 'accept)))))
+ (funcall #'chess-engine-default-handler 'accept)))))
(cons "\\S-+ accepts the takeback request\\."
(function
(lambda ()
- (funcall chess-engine-response-handler 'accept))))
+ (funcall #'chess-engine-default-handler 'accept))))
(cons ;; resign announcement
"{Game \\([0-9]+\\) (\\(\\S-+\\) vs\\. \\(\\S-+\\)) \\(\\S-+\\) resigns}"
(function
@@ -391,7 +396,7 @@ standard position). In those cases, this variable should be set to nil.")
:Black (match-string 3))))
(with-current-buffer (chess-game-data game 'engine)
(if opponent-p
- (funcall chess-engine-response-handler 'resign)
+ (funcall #'chess-engine-default-handler 'resign)
(unless (chess-game-status game)
(chess-game-end game :resign))))
t))))
@@ -399,19 +404,19 @@ standard position). In those cases, this variable should be set to nil.")
(function
(lambda ()
(if (string= (match-string 1) chess-engine-opponent-name)
- (funcall chess-engine-response-handler 'flag-fell)
- (funcall chess-engine-response-handler 'call-flag t)))))
+ (funcall #'chess-engine-default-handler 'flag-fell)
+ (funcall #'chess-engine-default-handler 'call-flag t)))))
(cons "Illegal move (\\([^)]+\\))\\."
(function
(lambda ()
- (funcall chess-engine-response-handler 'illegal
+ (funcall #'chess-engine-default-handler 'illegal
(match-string 1)))))
(cons "Challenge: \\(\\S-+\\) \\S-+ \\S-+ \\S-+ .+"
(function
(lambda ()
(let ((opponent (match-string 1)))
(if (y-or-n-p (chess-string 'want-to-play opponent))
- (chess-ics-send (concat "accept " opponent))
+ (chess-ics-send (concat "accept " opponent))
(chess-ics-send "decline match"))))))
;; Buttonize URLs.
(cons "\"?\\(\\(https?\\|ftp\\)://[^ \t\n\r\"]+\\)\"?"
@@ -470,7 +475,6 @@ See `chess-ics-game'.")
(chess-game-set-data game 'ics-game-number game-number)
(chess-game-set-data game 'ics-buffer (current-buffer))
(chess-game-set-tag game "Site" chess-ics-server)
- (chess-engine-set-response-handler (current-buffer))
(while tags
(cl-assert (keywordp (car tags)))
(chess-game-set-tag
diff --git a/chess-module.el b/chess-module.el
index c581844..cfcae66 100644
--- a/chess-module.el
+++ b/chess-module.el
@@ -91,7 +91,7 @@
(chess-module-detach-game nil))
(setq chess-module-game game)
(chess-game-add-hook game 'chess-module-event-handler
- (or module (current-buffer)))))
+ (or module (current-buffer)))))
(defsubst chess-module-leader-p (module)
(chess-with-current-buffer module
diff --git a/chess-ply.el b/chess-ply.el
index f49c7ad..165befa 100644
--- a/chess-ply.el
+++ b/chess-ply.el
@@ -209,7 +209,7 @@ maneuver."
;; validate that `changes' can be legally applied to the given
;; position
(when (or valid-p
- (chess-legal-plies position :any :index (car changes)
+ (chess-legal-plies position :index (car changes)
:target (cadr changes)))
(unless chess-ply-checking-mate
(setq piece (chess-pos-piece position (car changes)))
@@ -445,16 +445,20 @@ position object passed in."
candidate)))
(if changes
(if chess-ply-throw-if-any
- (throw 'any-found t)
- (push (cons position changes) plies)))))
+ (throw 'any-found t)
+ (if (or (not specific-target)
+ (= specific-target (cadr changes)))
+ (push (cons position changes) plies))))))
(if (chess-pos-can-castle position (if color ?Q ?q))
(let ((changes (chess-ply-castling-changes position t
candidate)))
(if changes
(if chess-ply-throw-if-any
- (throw 'any-found t)
- (push (cons position changes) plies)))))))
+ (throw 'any-found t)
+ (if (or (not specific-target)
+ (= specific-target (cadr changes)))
+ (push (cons position changes) plies))))))))
;; the knight is a zesty little piece; there may be more than
;; one, but at only one possible square in each direction
diff --git a/features/castling.feature b/features/castling.feature
new file mode 100644
index 0000000..8cbcf02
--- /dev/null
+++ b/features/castling.feature
@@ -0,0 +1,7 @@
+Scenario: e1-b1 is misinterpreted as O-O-O when it should be illegal
+ Given game with fen "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/R3KBNR w KQkq -"
+ Then the move "e1-b1" is illegal
+
+Scenario: e8-b8 is misinterpreted as O-O-O when it should be illegal
+ Given game with fen "r3kbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b KQkq -"
+ Then the move "e8-b8" is illegal
diff --git a/features/ics.feature b/features/ics.feature
new file mode 100644
index 0000000..5a49dfe
--- /dev/null
+++ b/features/ics.feature
@@ -0,0 +1,5 @@
+Scenario: Echo area should indicate opponent ran out of time.
+ Given ics session
+ When new game
+ And opponent forfeits on time
+ Then I should see message "Your opponent has forfeited the game on time"
diff --git a/features/step-definitions/emacs-chess-steps.el b/features/step-definitions/emacs-chess-steps.el
new file mode 100644
index 0000000..5e1b6cc
--- /dev/null
+++ b/features/step-definitions/emacs-chess-steps.el
@@ -0,0 +1,47 @@
+;; This file contains your project specific step definitions. All
+;; files in this directory whose names end with "-steps.el" will be
+;; loaded automatically by Ecukes.
+
+(Given "^ics session$"
+ (lambda ()
+ (let ((test-fn (expand-file-name (make-temp-name "test-fifo") (or small-temporary-file-directory temporary-file-directory))))
+ (setq test-fifo (start-process-shell-command "test-fifo" nil (format "mkfifo %s && cat >> %s" test-fn test-fn)))
+ (process-send-string test-fifo "login:\n")
+ (process-send-string test-fifo "Logging you in as \"GuestME\"\n")
+ (process-send-string test-fifo "fics% \n")
+ (sleep-for 3)
+ (chess-ics "nowhere.org" 5000 nil nil "sh" "-c" (format "cat %s" test-fn))
+
+ )))
+
+(When "^new game$"
+ (lambda ()
+ (process-send-string test-fifo (format "{Game 42 (GuestYOU vs. GuestME) Creating unrated blitz match.}\n"))
+ (process-send-string test-fifo (format "<12> rnbqkbnr pppppppp -------- -------- -------- -------- PPPPPPPP RNBQKBNR W -1 1 1 1 1 0 23 GuestYOU GuestME -1 5 5 39 39 300 300 1 none (0:00) none 1 0 0\n"))
+ (process-send-string test-fifo (format "<12> rnbqkbnr pppppppp -------- -------- --P----- -------- PP-PPPPP RNBQKBNR B 2 1 1 1 1 0 23 GuestYOU GuestME 1 5 5 39 39 300 300 1 P/c2-c4 (0:00) c4 1 0 0\n"))
+ (process-send-string test-fifo (format "<12> rnbqkbnr pppp-ppp -------- ----p--- --P----- -------- PP-PPPPP RNBQKBNR W 4 1 1 1 1 0 23 GuestYOU GuestME -1 5 5 39 39 300 300 2 P/e7-e5 (0:00) e5 1 1 0\n"))
+ (sleep-for 3)
+ )
+ )
+
+(When "^opponent forfeits on time$"
+ (lambda ()
+ (process-send-string test-fifo (format "{Game 42 (GuestYOU vs. GuestME) GuestYOU forfeits on time} 0-1\n"))
+ (sleep-for 3)
+ ))
+
+(Given "^game with fen \"\\(.+\\)\"$"
+ (lambda (fen)
+ (let ((game (chess-game-create (chess-fen-to-pos fen))) objects)
+ (setq test-display (car (chess-create-modules (list '(chess-plain))
+ 'chess--create-display
+ game t t))))))
+
+(Then "^the move \"\\([a-h][1-8]\\)-\\([a-h][1-8]\\)\" is illegal$"
+ (lambda (source target)
+ (let ((position (chess-display-position test-display)))
+ (assert (null (chess-ply-create position nil
+ (chess-coord-to-index source)
+ (chess-coord-to-index target)))))))
+
+
diff --git a/features/support/env.el b/features/support/env.el
new file mode 100644
index 0000000..58ed42f
--- /dev/null
+++ b/features/support/env.el
@@ -0,0 +1,43 @@
+(require 'f)
+
+(defvar emacs-chess-support-path
+ (f-dirname load-file-name))
+
+(defvar emacs-chess-features-path
+ (f-parent emacs-chess-support-path))
+
+(defvar emacs-chess-root-path
+ (f-parent emacs-chess-features-path))
+
+(add-to-list 'load-path emacs-chess-root-path)
+
+(require 'chess-ics)
+(require 'espuds)
+(require 'ert)
+(require 'cl)
+
+(Setup
+ (defvar test-display nil)
+ (defvar test-fifo nil)
+)
+
+(Before
+ ;; Before each scenario is run
+ )
+
+(After
+ ;; After each scenario is run
+ (when test-display
+ (chess-module-destroy test-display)
+ (setq test-display nil))
+ (when (process-status "*chess-ics*")
+ (delete-process "*chess-ics*"))
+ (when test-fifo
+ (process-send-eof test-fifo)
+ (delete-process test-fifo)
+ (setq test-fifo nil))
+)
+
+(Teardown
+ ;; After when everything has been run
+ )