Skip to content

Commit 1d7e3b4

Browse files
committed
Move helper functions to own file, fix test issue with client conn
Changes 1. Created integration-test-utils.el to collect increasing number of integration helper functions. 1. A `with-cider-test-sandbox` macro is introduced to restore cider state after test has finished. 2. The `cider-itu-poll-until` helper function is introduced to replace `nrepl-tests-sleep-until`, this fn now throws if condition does not become true after the timeout has elapsed. 3. There was an issue with the previous attempt to add a hook and return a value for the test to poll when he client is connected to the nREPL. The hook was naively attached to the temp buffer, which is not necessarily in scope when the hook is run. A new `cider-itu-nrepl-client-connected-ref-make!` replaces that fn, that now returns a global variable ref, and has to be called inside `with-cider-test-sandbox` so that the hook variable is restored after the test finishes. 4. Introduced tests to test the new integration tests helper functions. 5. All integration tests are updated to use the new functions mentioned above. 6. Rename `connected?` to `is-connected` as per review comments. 7. Restore `nrepl-client-tests.el` update which was overwritten with previous commit.
1 parent 1622cc2 commit 1d7e3b4

File tree

3 files changed

+195
-101
lines changed

3 files changed

+195
-101
lines changed
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
;;; integration-test-utils.el -*- lexical-binding: t; -*-
2+
3+
;; Copyright © 2022 Ioannis Kappas
4+
5+
;; This file is NOT part of GNU Emacs.
6+
7+
;; This program is free software: you can redistribute it and/or
8+
;; modify it under the terms of the GNU General Public License as
9+
;; published by the Free Software Foundation, either version 3 of the
10+
;; License, or (at your option) any later version.
11+
;;
12+
;; This program is distributed in the hope that it will be useful, but
13+
;; WITHOUT ANY WARRANTY; without even the implied warranty of
14+
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
;; General Public License for more details.
16+
;;
17+
;; You should have received a copy of the GNU General Public License
18+
;; along with this program. If not, see `http://www.gnu.org/licenses/'.
19+
20+
;;; Commentary:
21+
22+
;; Helper utils for use by integration tests.
23+
;;
24+
;; All helper functions should begin with `cider-itu-`.
25+
26+
;; This file is part of CIDER
27+
28+
;;; Code:
29+
30+
(require 'buttercup)
31+
(require 'cider)
32+
(require 'cl-lib)
33+
34+
(defmacro with-cider-test-sandbox (&rest body)
35+
"Run BODY inside sandbox, with key cider global vars restored on exit.
36+
37+
Only the following variables are currently restored, please add more as the
38+
test coverage increases:
39+
40+
`cider-connected-hook`."
41+
(declare (indent 0))
42+
;; for dynamic vars, just use a binding under the same name.
43+
`(let ((cider-connected-hook cider-connected-hook))
44+
,@body))
45+
46+
;; https://emacs.stackexchange.com/a/55031
47+
(defmacro with-temp-dir (temp-dir &rest body)
48+
"Create a temporary directory and bind it to TEMP-DIR while evaluating BODY.
49+
Remove the temp directory at the end of evaluation."
50+
(declare (indent 1))
51+
`(let ((,temp-dir (make-temp-file "" t)))
52+
(unwind-protect
53+
(progn
54+
,@body)
55+
(condition-case err
56+
(delete-directory ,temp-dir t)
57+
(error
58+
(message ":with-temp-dir-error :cannot-remove-temp-dir %S" err))))))
59+
60+
(defmacro cider-itu-poll-until (condition timeout-secs)
61+
"Poll every 0.2 secs until CONDITION becomes true or error out if TIMEOUT-SECS elapses."
62+
(let* ((interval-secs 0.2)
63+
(count (truncate (/ timeout-secs interval-secs))))
64+
`(cl-loop repeat ,count
65+
for condition = ,condition
66+
if condition
67+
return condition
68+
else
69+
do (sleep-for ,interval-secs)
70+
finally (error ":cider-itu-poll-until-errored :timed-out-after-secs %d :waiting-for %S"
71+
,timeout-secs (quote ,condition)))))
72+
73+
(defun cider-itu-nrepl-client-connected-ref-make! ()
74+
"Returns a gv ref to signal when the client is connected to the nREPL server.
75+
This is done by adding a hook to `cider-connected-hook' and must be run
76+
inside `with-cider-test-sandbox'.
77+
78+
Use `gv-deref' to deref the value.
79+
80+
The generalized variable can have the following values
81+
82+
'!connected the client has not yet connected to the nREPL server.
83+
'connected the client has connected to the nREPL server."
84+
(let ((is-connected '!connected))
85+
(add-hook 'cider-connected-hook
86+
(lambda ()
87+
(setq is-connected 'connected)))
88+
(gv-ref is-connected)))
89+
90+
(describe "in integration utils test"
91+
(it "that cider-itu-poll-until works when sexpr eventually becomes true."
92+
(let ((stack '(nil 123 456)))
93+
(expect (cider-itu-poll-until (progn (message ":looping... %S" stack) (pop stack)) 1) :to-equal 123)
94+
(expect stack :to-equal '(456)))
95+
(expect (cider-itu-poll-until nil 1) :to-throw 'error))
96+
97+
(it "that sand box can restore CIDER global vars."
98+
(let ((count (length cider-connected-hook)))
99+
(with-cider-test-sandbox
100+
(add-hook 'cider-connected-hook (lambda ()))
101+
(expect (length cider-connected-hook) :to-be (1+ count)))
102+
(expect (length cider-connected-hook) :to-be count)))
103+
104+
(it "that `cider-itu-nrepl-client-connected-ref-make!' return ref changes value when client is connected."
105+
(with-cider-test-sandbox
106+
(let ((is-connected* (cider-itu-nrepl-client-connected-ref-make!)))
107+
(expect (gv-deref is-connected*) :to-equal '!connected)
108+
(run-hooks 'cider-connected-hook)
109+
(expect (gv-deref is-connected*) :to-equal 'connected)))))
110+
111+
112+
(provide 'integration-test-utils)
113+
114+
;;; integration-test-utils.el ends here

test/integration/integration-tests.el

Lines changed: 73 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -29,41 +29,18 @@
2929
(require 'cider)
3030
(require 'nrepl-dict)
3131
(require 'nrepl-tests-utils "test/utils/nrepl-tests-utils")
32-
33-
;; https://emacs.stackexchange.com/a/55031
34-
(defmacro with-temp-dir (temp-dir &rest body)
35-
"Create a temporary directory and bind it to TEMP-DIR while evaluating BODY.
36-
Remove the temp directory at the end of evaluation."
37-
`(let ((,temp-dir (make-temp-file "" t)))
38-
(unwind-protect
39-
(progn
40-
,@body)
41-
(condition-case err
42-
(delete-directory ,temp-dir t)
43-
(error
44-
(message ":with-temp-dir-error :cannot-remove-temp-dir %S" err))))))
45-
46-
(defun nrepl-client-connected?-ref-make! ()
47-
"Return a reference to indicate when the client is connected to nREPL server.
48-
This is done by adding a hook to `cider-connected-hook` and is only active
49-
in the scope of the current buffer."
50-
(let (connected?)
51-
(add-hook 'cider-connected-hook
52-
(lambda ()
53-
(setq connected? t))
54-
nil
55-
;; only set in the current buffer scope.
56-
t)))
57-
32+
(require 'integration-test-utils)
5833

5934
(describe "jack in"
60-
;; See "bb" case for basic commentary
35+
;; See "babashka" case for commentary on the base template used by all other
36+
;; tests.
6137
;;
6238
;; It has been observed that some REPLs (Clojure cli, shadow) might take a
6339
;; very long time to bring up/respond/shutdown, and thus sleep duration values
6440
;; are set rather high.
6541

6642
(it "to babashka"
43+
(with-cider-test-sandbox
6744
(with-temp-dir temp-dir
6845
;; set up a project directory in temp
6946
(let* ((project-dir temp-dir)
@@ -75,18 +52,20 @@ in the scope of the current buffer."
7552
(setq-local default-directory project-dir)
7653

7754
(unwind-protect
78-
;; jack in and get repl buffer
79-
(let* ((client-connected?* (nrepl-client-connected?-ref-make!))
55+
(let* (;; Get a gv reference so as to poll if the client has
56+
;; connected to the nREPL server.
57+
(client-is-connected* (cider-itu-nrepl-client-connected-ref-make!))
58+
59+
;; jack in and get repl buffer
8060
(nrepl-proc (cider-jack-in-clj '()))
8161
(nrepl-buf (process-buffer nrepl-proc)))
8262

8363
;; wait until the client has successfully connected to the
8464
;; nREPL server.
85-
(nrepl-tests-sleep-until 5 client-connected?*)
86-
(expect client-connected?*)
65+
(cider-itu-poll-until (eq (gv-deref client-is-connected*) 'connected) 5)
8766

8867
;; give it some time to setup the clj REPL
89-
(nrepl-tests-sleep-until 5 (cider-repls 'clj nil))
68+
(cider-itu-poll-until (cider-repls 'clj nil) 5)
9069

9170
;; send command to the REPL, and stdout/stderr to
9271
;; corresponding eval- variables.
@@ -107,7 +86,7 @@ in the scope of the current buffer."
10786
(when out (push out eval-out)))) )
10887

10988
;; wait for the response to come back.
110-
(nrepl-tests-sleep-until 5 eval-out)
89+
(cider-itu-poll-until eval-out 5)
11190

11291
;; ensure there are no errors and response is as expected.
11392
(expect eval-err :to-equal '())
@@ -117,30 +96,29 @@ in the scope of the current buffer."
11796
(cider-quit repl-buffer)
11897

11998
;; wait for the REPL to exit
120-
(nrepl-tests-sleep-until 5 (not (eq (process-status nrepl-proc) 'run)))
99+
(cider-itu-poll-until (not (eq (process-status nrepl-proc) 'run)) 5)
121100
(expect (member (process-status nrepl-proc) '(exit signal)))))
122101

123102
;; useful for debugging on errors
124103
(when-let ((nrepl-error-buffer (get-buffer "*nrepl-error*")))
125104
(with-current-buffer nrepl-error-buffer
126-
(message ":*nrepl-error* %S" (substring-no-properties (buffer-string))))))))))
105+
(message ":*nrepl-error* %S" (substring-no-properties (buffer-string)))))))))))
127106

128107
(it "to clojure tools cli"
108+
(with-cider-test-sandbox
129109
(with-temp-dir temp-dir
130110
(let* ((project-dir temp-dir)
131111
(deps-edn (expand-file-name "deps.edn" project-dir)))
132112
(write-region "{}" nil deps-edn)
133113
(with-temp-buffer
134114
(setq-local default-directory project-dir)
135115
(unwind-protect
136-
(let* ((client-connected?* (nrepl-client-connected?-ref-make!))
116+
(let* ((client-is-connected* (cider-itu-nrepl-client-connected-ref-make!))
137117
(nrepl-proc (cider-jack-in-clj `()))
138118
(nrepl-buf (process-buffer nrepl-proc)))
139-
(nrepl-tests-sleep-until 5 client-connected?*)
140-
(expect client-connected?*)
141-
142119
;; high duration since on windows it takes a long time to startup
143-
(nrepl-tests-sleep-until 90 (cider-repls 'clj nil))
120+
(cider-itu-poll-until (eq (gv-deref client-is-connected*) 'connected) 90)
121+
(cider-itu-poll-until (cider-repls 'clj nil) 90)
144122
(let ((repl-buffer (cider-current-repl))
145123
(eval-err '())
146124
(eval-out '()))
@@ -153,17 +131,18 @@ in the scope of the current buffer."
153131
(out err)
154132
(when err (push err eval-err))
155133
(when out (push out eval-out)))) )
156-
(nrepl-tests-sleep-until 10 eval-out)
134+
(cider-itu-poll-until eval-out 10)
157135
(expect eval-err :to-equal '())
158136
(expect eval-out :to-equal '(":clojure? true"))
159137
(cider-quit repl-buffer)
160-
(nrepl-tests-sleep-until 15 (not (eq (process-status nrepl-proc) 'run)))
138+
(cider-itu-poll-until (not (eq (process-status nrepl-proc) 'run)) 15)
161139
(expect (member (process-status nrepl-proc) '(exit signal)))))
162140
(when-let ((nrepl-error-buffer (get-buffer "*nrepl-error*")))
163141
(with-current-buffer nrepl-error-buffer
164-
(message ":*nrepl-error* %S" (substring-no-properties (buffer-string))))))))))
142+
(message ":*nrepl-error* %S" (substring-no-properties (buffer-string)))))))))))
165143

166144
(it "to leiningen"
145+
(with-cider-test-sandbox
167146
(with-temp-dir temp-dir
168147
(let* ((project-dir temp-dir)
169148
(project-clj (expand-file-name "project.clj" project-dir)))
@@ -173,12 +152,11 @@ in the scope of the current buffer."
173152
(with-temp-buffer
174153
(setq-local default-directory project-dir)
175154
(unwind-protect
176-
(let* ((client-connected?* (nrepl-client-connected?-ref-make!))
155+
(let* ((client-is-connected* (cider-itu-nrepl-client-connected-ref-make!))
177156
(nrepl-proc (cider-jack-in-clj `()))
178157
(nrepl-buf (process-buffer nrepl-proc)))
179-
(nrepl-tests-sleep-until 5 client-connected?*)
180-
(expect client-connected?*)
181-
(nrepl-tests-sleep-until 90 (cider-repls 'clj nil))
158+
(cider-itu-poll-until (eq (gv-deref client-is-connected*) 'connected) 90)
159+
(cider-itu-poll-until (cider-repls 'clj nil) 90)
182160
(let ((repl-buffer (cider-current-repl))
183161
(eval-err '())
184162
(eval-out '()))
@@ -191,68 +169,67 @@ in the scope of the current buffer."
191169
(out err)
192170
(when err (push err eval-err))
193171
(when out (push out eval-out)))) )
194-
(nrepl-tests-sleep-until 10 eval-out)
172+
(cider-itu-poll-until eval-out 10)
195173
(expect eval-err :to-equal '())
196174
(expect eval-out :to-equal '(":clojure? true"))
197175
(cider-quit repl-buffer)
198-
(nrepl-tests-sleep-until 15 (not (eq (process-status nrepl-proc) 'run)))
176+
(cider-itu-poll-until (not (eq (process-status nrepl-proc) 'run)) 15)
199177
(expect (member (process-status nrepl-proc) '(exit signal)))
200178
(sleep-for 0.5)))
201179
(when-let ((nrepl-error-buffer (get-buffer "*nrepl-error*")))
202180
(with-current-buffer nrepl-error-buffer
203181
(message ":*nrepl-error* %S"
204-
(substring-no-properties (buffer-string))))))))))
182+
(substring-no-properties (buffer-string)))))))))))
205183

206184
(it "to shadow"
207185
;; shadow asks user whether they want to open a browser, force to no
208186
(spy-on 'y-or-n-p)
209187

210-
(with-temp-dir temp-dir
211-
(let* ((project-dir temp-dir)
212-
(shadow-cljs-edn (expand-file-name "shadow-cljs.edn" project-dir))
213-
(package-json (expand-file-name "package.json" project-dir)))
214-
(write-region "{}" nil shadow-cljs-edn)
215-
(write-region "{\"dependencies\":{\"shadow-cljs\": \"^2.20.13\"}}" nil package-json)
216-
(let ((default-directory project-dir))
217-
(message ":npm-install...")
218-
(shell-command "npm install")
219-
(message ":npm-install :done"))
220-
(let ((cider-preferred-build-tool 'shadow-cljs)
221-
;; request for a node repl, so that shadow forks one.
222-
(cider-shadow-default-options ":node-repl"))
223-
(with-temp-buffer
224-
(setq-local default-directory project-dir)
225-
(unwind-protect
226-
(let* ((client-connected?* (nrepl-client-connected?-ref-make!))
227-
(nrepl-proc (cider-jack-in-cljs '(:cljs-repl-type shadow)))
228-
(nrepl-buf (process-buffer nrepl-proc)))
229-
(nrepl-tests-sleep-until 5 client-connected?*)
230-
(expect client-connected?*)
231-
(nrepl-tests-sleep-until 120 (cider-repls 'cljs nil))
232-
(expect (cider-repls 'cljs nil) :not :to-be nil)
233-
(let ((repl-buffer (cider-current-repl))
234-
(eval-err '())
235-
(eval-out '()))
236-
(expect repl-buffer :not :to-be nil)
237-
(sleep-for 2)
238-
(cider-interactive-eval
239-
"(print :cljs? (some? *clojurescript-version*))"
240-
(lambda (return)
241-
(nrepl-dbind-response
242-
return
243-
(out err)
244-
(when err (push err eval-err))
245-
(when out (push out eval-out)))) )
246-
(nrepl-tests-sleep-until 10 eval-out)
247-
(expect eval-err :to-equal '())
248-
(expect eval-out :to-equal '(":cljs? true\n"))
249-
(cider-quit repl-buffer)
250-
(nrepl-tests-sleep-until 15 (not (eq (process-status nrepl-proc) 'run)))
251-
(expect (member (process-status nrepl-proc) '(exit signal)))))
252-
(when-let ((nrepl-error-buffer (get-buffer "*nrepl-error*")))
253-
(with-current-buffer nrepl-error-buffer
254-
(message ":*nrepl-error* %S"
255-
(substring-no-properties (buffer-string))))))))))))
188+
(with-cider-test-sandbox
189+
(with-temp-dir temp-dir
190+
(let* ((project-dir temp-dir)
191+
(shadow-cljs-edn (expand-file-name "shadow-cljs.edn" project-dir))
192+
(package-json (expand-file-name "package.json" project-dir)))
193+
(write-region "{}" nil shadow-cljs-edn)
194+
(write-region "{\"dependencies\":{\"shadow-cljs\": \"^2.20.13\"}}" nil package-json)
195+
(let ((default-directory project-dir))
196+
(message ":npm-install...")
197+
(shell-command "npm install")
198+
(message ":npm-install :done"))
199+
(let ((cider-preferred-build-tool 'shadow-cljs)
200+
;; request for a node repl, so that shadow forks one.
201+
(cider-shadow-default-options ":node-repl"))
202+
(with-temp-buffer
203+
(setq-local default-directory project-dir)
204+
(unwind-protect
205+
(let* ((client-is-connected* (cider-itu-nrepl-client-connected-ref-make!))
206+
(nrepl-proc (cider-jack-in-cljs '(:cljs-repl-type shadow)))
207+
(nrepl-buf (process-buffer nrepl-proc)))
208+
(cider-itu-poll-until (eq (gv-deref client-is-connected*) 'connected) 120)
209+
(cider-itu-poll-until (cider-repls 'cljs nil) 120)
210+
(let ((repl-buffer (cider-current-repl))
211+
(eval-err '())
212+
(eval-out '()))
213+
(expect repl-buffer :not :to-be nil)
214+
(sleep-for 2)
215+
(cider-interactive-eval
216+
"(print :cljs? (some? *clojurescript-version*))"
217+
(lambda (return)
218+
(nrepl-dbind-response
219+
return
220+
(out err)
221+
(when err (push err eval-err))
222+
(when out (push out eval-out)))) )
223+
(cider-itu-poll-until eval-out 10)
224+
(expect eval-err :to-equal '())
225+
(expect eval-out :to-equal '(":cljs? true\n"))
226+
(cider-quit repl-buffer)
227+
(cider-itu-poll-until (not (eq (process-status nrepl-proc) 'run)) 15)
228+
(expect (member (process-status nrepl-proc) '(exit signal)))))
229+
(when-let ((nrepl-error-buffer (get-buffer "*nrepl-error*")))
230+
(with-current-buffer nrepl-error-buffer
231+
(message ":*nrepl-error* %S"
232+
(substring-no-properties (buffer-string)))))))))))))
256233

257234
(provide 'integration-tests)
258235

0 commit comments

Comments
 (0)