Skip to content

Instantly share code, notes, and snippets.

@geofflane
Created January 5, 2015 14:31
Show Gist options
  • Save geofflane/ee8283f7f990e9fe2d6e to your computer and use it in GitHub Desktop.
Save geofflane/ee8283f7f990e9fe2d6e to your computer and use it in GitHub Desktop.
key-drone
(ns key-drone.core-test
(:use clj-drone.core)
(:require [clojure.test :refer :all]
[key-drone.core :refer :all]))
(deftest key-handler-test
(def navigation-keymap
{
\h :tilt-left
\H :tilt-left
\l :tilt-right
\L :tilt-right
\u :up
\U :up
\d :down
\D :down
\j :tilt-back
\J :tilt-back
\k :tilt-front
\K :tilt-front
\t :take-off
\T :take-off
\g :land
\G :land
\s :spin-right
\S :spin-right
\A :spin-left
\a :spin-left
\E :emergency
\e :emergency
}
)
(doseq [keymap navigation-keymap]
(let [key-char (first keymap)
expected-nav-plan (second keymap)]
(testing (str "should navigate " expected-nav-plan " with " key-char)
(with-redefs [drone (fn [actual-nav-plan & args] (is (= expected-nav-plan actual-nav-plan)))]
(is (= :continue (key-handler (int key-char))))))))
(testing "should quit program"
(is (= :quit (key-handler (int \q))))
(is (= :quit (key-handler (int \Q)))))
(testing "should handle invalid keystroke"
(is (= :invalid (key-handler (int \X))))))
(deftest navigate-test
(testing "should catch io exceptions and log message"
(with-redefs [drone (fn [nav-plan & args] (throw (java.io.IOException. "OOPS: expected exception")))]
(navigate :up 1))))
(deftest control-loop-test
(testing "exit after quit is returned"
(control-loop (fn [] :quit))))
(ns key-drone.core
(:require [clj-drone.core :refer :all]
[clojure.java.io :as io])
(:import [jline.console ConsoleReader])
(:gen-class :main true))
(defn log
[message]
(println message))
(defn navigate
[nav-plan & args]
(try
(log (str "Drone " nav-plan " at " args))
(apply drone nav-plan args)
(catch java.io.IOException e
(log (str "caught exception: " (.getMessage e))))))
(defn- navigate-and-continue
[plan & args]
(do
(navigate plan args)
:continue))
(defn- invalid-key
[]
(do
(log "Invalid key pressed")
:invalid))
(defn- quit
[]
(do
(log "Closing down")
:quit))
;input: ascii code of key
;transform: apply function associated with key
;output: :continue, :quit
;implements interfaces:
; - control handler (output :quit or anything else)
; - key press handler (input asciiCode, output: true/false)
(defn key-handler
[asciiCode]
(let [keypressed (Character/toUpperCase (char asciiCode))]
(cond
(= keypressed \T) (navigate-and-continue :take-off)
(= keypressed \G) (navigate-and-continue :land)
(= keypressed \H) (navigate-and-continue :tilt-left 1)
(= keypressed \L) (navigate-and-continue :tilt-right 1)
(= keypressed \K) (navigate-and-continue :tilt-front 1)
(= keypressed \J) (navigate-and-continue :tilt-back 1)
(= keypressed \S) (navigate-and-continue :spin-right 1)
(= keypressed \A) (navigate-and-continue :spin-left 1)
(= keypressed \U) (navigate-and-continue :up 1)
(= keypressed \D) (navigate-and-continue :down 1)
(= keypressed \E) (navigate-and-continue :emergency)
(= keypressed \Q) (quit)
:else (invalid-key))))
;input: key press handler
;transform: input key strokes
;output; true/false from handler
;requires interface:
; - key press handler
(defn key-reader
"generates keystrokes"
[key-press-handler]
(let [reader (ConsoleReader.)]
(fn []
(let [asciiCode (.readCharacter reader)]
(key-press-handler asciiCode)))))
;input: control handler
;transform: call handler until quit is pressed
;output: false
;requires interface:
; - control handler
(defn control-loop
[key-reader-fn]
(let [result (key-reader-fn)]
(if (= result :quit)
0
(recur key-reader-fn))))
;input: none
;transform: started application into drone control
;output: exit code 0 for success else failure
(defn -main
"Command line entry point for key-drone"
[& args]
(do
(log "Starting Key Drone (q to quit)")
(drone-initialize)
(control-loop (key-reader key-handler))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment