git.fiddlerwoaroof.com
Browse code

Refactoring watch-stack for testability

Ed Langley authored on 13/04/2018 06:33:35
Showing 3 changed files
... ...
@@ -23,7 +23,8 @@
23 23
    #:extract-timeline
24 24
    #:extract-list
25 25
    #:extract-stack
26
-   #:timeline))
26
+   #:timeline
27
+   #:tagged-kv-formatter))
27 28
 (in-package :cloud-watcher.aws-result)
28 29
 
29 30
 (defun find-all-indices (pred str &optional accum (start (or (car accum) 0)))
... ...
@@ -55,13 +56,6 @@
55 56
                                                                  (list (length v)))))))
56 57
     (serapeum:string-join parts "-")))
57 58
 
58
-(deftest decamelize ()
59
-  (should be equal "a" (decamelize "A"))
60
-  (should be equal "a" (decamelize "a"))
61
-  (should be equal "outputs" (decamelize "Outputs"))
62
-  (should be equal "outputs-outputs" (decamelize "OutputsOutputs"))
63
-  (should be equal "a-b-c" (decamelize "ABC")))
64
-
65 59
 
66 60
 (defmacro tagged-kv-formatter (tag)
67 61
   `(formatter ,(format nil "~a ~~a ~~a~~%" tag)))
... ...
@@ -156,3 +150,11 @@
156 150
     (setf s-sdt (initialize-date s-sdt)
157 151
           s-edt (initialize-date s-edt)
158 152
           s-cdt (initialize-date s-cdt))))
153
+
154
+
155
+(deftest decamelize ()
156
+  (should be equal "a" (decamelize "A"))
157
+  (should be equal "a" (decamelize "a"))
158
+  (should be equal "outputs" (decamelize "Outputs"))
159
+  (should be equal "outputs-outputs" (decamelize "OutputsOutputs"))
160
+  (should be equal "a-b-c" (decamelize "ABC")))
... ...
@@ -3,7 +3,7 @@
3 3
   (:import-from :cloud-watcher.aws-result :start-date-time :end-date-time)
4 4
   (:import-from :serapeum :op)
5 5
   (:import-from :clon :defsynopsis :group :flag :stropt)
6
-  (:use :cl)
6
+  (:use :cl :st)
7 7
   (:export options
8 8
            #:*cloud-watcher-synopsis*
9 9
            #:dump))
... ...
@@ -20,6 +20,8 @@
20 20
            (stropt :long-name "aws-region" :default-value "us-west-2")
21 21
            (flag :short-name "u" :long-name "update"))
22 22
     (group (:header "misc")
23
+           (flag :long-name "rebuild")
24
+           (flag :long-name "self-test")
23 25
            (flag :long-name "help"))))
24 26
 
25 27
 (defun stack-parameters-main (name)
... ...
@@ -28,6 +30,23 @@
28 30
 (defun stack-outputs-main (name)
29 31
   (stack-outputs (stack-for-name name)))
30 32
 
33
+(defun run-tests ()
34
+  (st:test :package (find-package :cloud-watcher.aws-result))
35
+  (st:test :package (find-package :cloud-watcher.main))
36
+  (st:test :package (find-package :cloud-watcher.cli)))
37
+
38
+(defun dump ()
39
+  "Create an executable with the command-line interface defined above."
40
+  (handler-bind ((sb-ext:name-conflict (lambda (c)
41
+                                         (declare (ignore c))
42
+                                         (invoke-restart-interactively 'sb-ext:resolve-conflict))))
43
+    (let ((sb-ext:*on-package-variance* '(:warn (:cloud-watcher.aws-result
44
+                                                 :cloud-watcher.main
45
+                                                 :cloud-watcher.cli)
46
+                                          :error t)))
47
+      (asdf:load-system :cloud-watcher :force t)))
48
+  (clon:dump "cloud-watcher" main))
49
+
31 50
 (defun main ()
32 51
   (let* ((context (net.didierverna.clon:make-context :synopsis *cloud-watcher-synopsis*))
33 52
          (files (clon:remainder :context context))
... ...
@@ -39,8 +58,6 @@
39 58
     (cond ((clon:getopt :long-name "help") (clon:help))
40 59
           ((clon:getopt :long-name "watch") (cloud-watcher.main:watch-stack (car files)))
41 60
           ((clon:getopt :long-name "outputs") (stack-outputs-main (car files)))
42
-          ((clon:getopt :long-name "parameters") (stack-parameters-main (car files))))))
43
-
44
-(defun dump ()
45
-  "Create an executable with the command-line interface defined above."
46
-  (clon:dump "cloud-watcher" main))
61
+          ((clon:getopt :long-name "parameters") (stack-parameters-main (car files)))
62
+          ((clon:getopt :long-name "self-test") (run-tests))
63
+          ((clon:getopt :long-name "rebuild") (dump)))))
... ...
@@ -43,27 +43,50 @@
43 43
     (local-time-duration:timestamp-difference (end-date-time timeline)
44 44
                                               (start-date-time timeline))))
45 45
 
46
+(defun every-five-seconds (cb args &optional (delay-function #'sleep))
47
+  (loop
48
+     while (apply cb args)
49
+     do (funcall delay-function 5)))
50
+
51
+(deftest every-five-seconds ()
52
+  (let ((counter 0)
53
+        delay)
54
+    (flet ((fake-delay (num)
55
+             (incf counter)
56
+             (setf delay num))
57
+           (work ()
58
+             (< counter 10)))
59
+      (every-five-seconds #'work () #'fake-delay)
60
+      (should be = 10 counter)
61
+      (should be = 5 delay))))
62
+
63
+(defclass stack-watcher ()
64
+  ())
65
+
46 66
 (defun watch-stack (name)
47 67
   (format t "~&Watching ~s~2%" name)
48
-  (let ((done? nil)
49
-        (old-status nil))
50
-    (loop until done?
51
-       for the-stack = (stack-for-name name)
52
-       do
53
-         (unless old-status
54
-           (format t "~& PARAMETERS ~%============~%")
55
-           (stack-parameters the-stack)
56
-           (format t "~&============~2%"))
57
-
58
-         (unless (eql old-status (stack-status the-stack))
59
-           (format t "~&STATUS ~a~%" (stack-status the-stack))
60
-           (setf old-status (stack-status the-stack)))
61
-
62
-         (if (ends-with-subseq "COMPLETE" (symbol-name (stack-status the-stack)))
63
-             (progn
64
-               (format t "~2& OUTPUTS ~%=========~%")
65
-               (stack-outputs the-stack)
66
-               (format t "~&=========~%")
67
-               (return)))
68
-         (sleep 5)))
68
+  (block nil
69
+    (let ((old-status nil))
70
+      (every-five-seconds
71
+       (lambda ()
72
+         (let* ((the-stack (stack-for-name name))
73
+                (current-status (stack-status the-stack)))
74
+           (unless old-status
75
+             (format t "~& PARAMETERS ~%============~%")
76
+             (stack-parameters the-stack)
77
+             (format t "~&============~2%"))
78
+
79
+           (unless (eql old-status current-status)
80
+             (format t "~&STATUS ~a~%" current-status)
81
+             (setf old-status current-status))
82
+
83
+           (if (ends-with-subseq "COMPLETE" (symbol-name current-status))
84
+               (progn
85
+                 (format t "~2& OUTPUTS ~%=========~%")
86
+                 (stack-outputs the-stack)
87
+                 (format t "~&=========~%")
88
+                 nil)
89
+               t)))
90
+       ())))
69 91
   (fresh-line))
92
+