Skip to content

Commit a4b8ab1

Browse files
ikappakibbatsov
authored andcommitted
Introduce integration tests
1. Migrated macos regression tests to circle ci, but with a reduced set to only cover Emacs latest version. 2. Replaced existing macos only testing github action to run the integration tests across macos, ubuntu and windows latest on Emacs 28, 27 and 26. It was nearly impossible to do so on circleci across all platforms, thus a github action was chosen. 3. Updated the `Eldev` project file to support integration test via a new `--test-type` command line option (the template was taken from the `Eldev` tool's project file itself). 4. The cider server is given now a 0.5 sec opportunity to execute the `close` op before the server is killed. 5. The hacking section in documentation has been updated to mention integration tests. 6. The `nrepl--kill-process` has been updated to address the orphaned child process menace under MS-Windows, whereby the nREPL child processes were most likely to be left orphaned after the parent process was killed. The kill is now done by contracting the external `taskkill` windows program to do the job if available, and falls back to the previous logic otherwise. 7. The `nrepl-server-sentinel` has been simplified to only report an error if the nREPL process couldn't bootstrap itself, or send an exit message otherwise. The old code only closed client connections on `hangup` (i.e. HUP signal), but the new logic will close clients on any fatal signal. Not sure why the old code only did this on HUP, but it seems sensible to me that any open client connection should close when the server has exited. 8. Fixed an issue in `cider-tests.el` that a couple of buffer local shadow-cljs variables were set as global by mistake, thus affecting other tests (and in particular the integration tests). 9. Updated nrepl-client-tests.el to test the new nrepl plist property to indicate that the nREPL server has been successfully brought up, and also test that the new process-status `exit` returned on MS-Windows with `taskkill` is set. 10. Introduced integration tests covering jack-in for bb, clojure cli, lein and shadow.
1 parent fc7b929 commit a4b8ab1

File tree

11 files changed

+439
-51
lines changed

11 files changed

+439
-51
lines changed

.circleci/config.yml

+20
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ commands:
1515
name: Install unzip
1616
command: apt-get update && apt-get install unzip
1717

18+
macos-setup:
19+
steps:
20+
- checkout
21+
- run:
22+
name: Install Emacs latest
23+
command: |
24+
echo "HOMEBREW_NO_AUTO_UPDATE=1" >> $BASH_ENV
25+
brew install homebrew/cask/emacs
26+
- run:
27+
name: Install Eldev
28+
command: curl -fsSL https://raw.github.com/doublep/eldev/master/webinstall/circle-eldev > x.sh && source ./x.sh
29+
1830
setup-windows:
1931
steps:
2032
- checkout
@@ -74,6 +86,13 @@ jobs:
7486
- setup
7587
- test
7688

89+
test-macos-emacs-latest:
90+
macos:
91+
xcode: "14.0.0"
92+
steps:
93+
- macos-setup
94+
- test
95+
7796
test-windows-emacs-latest:
7897
executor: win/default
7998
steps:
@@ -100,4 +119,5 @@ workflows:
100119
- test-emacs-28
101120
- test-emacs-master
102121
- test-lint
122+
- test-macos-emacs-latest
103123
- test-windows-emacs-latest

.codespellrc

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[codespell]
2+
skip = .git,.eldev,logo

.github/workflows/test.yml

+47-5
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,68 @@ name: CI
33
on: [push, pull_request]
44

55
jobs:
6-
test:
6+
integration:
7+
# Run integration tests for all OSs and EMACS_VERSIONs.
78
runs-on: ${{matrix.os}}
89

910
strategy:
1011
matrix:
11-
os: [macos-latest]
12-
emacs_version: ['26.3', '27.2', '28.1']
12+
os: [macos-latest, ubuntu-latest, windows-latest]
13+
emacs_version: ['26.3', '27.2', '28.2']
1314

1415
steps:
1516
- name: Set up Emacs
17+
if: "!startsWith (matrix.os, 'windows')"
1618
uses: purcell/setup-emacs@master
1719
with:
1820
version: ${{matrix.emacs_version}}
1921

22+
- name: Set up Emacs on Windows
23+
if: startsWith (matrix.os, 'windows')
24+
uses: jcs090218/setup-emacs-windows@master
25+
with:
26+
version: ${{matrix.emacs_version}}
27+
2028
- name: Install Eldev
29+
if: "!startsWith (matrix.os, 'windows')"
2130
run: curl -fsSL https://raw.github.com/doublep/eldev/master/webinstall/github-eldev | sh
2231

32+
- name: Install Eldev on MS-Windows
33+
if: startsWith (matrix.os, 'windows')
34+
run: |
35+
# Remove expired DST Root CA X3 certificate. Workaround
36+
# for https://debbugs.gnu.org/cgi/bugreport.cgi?bug=51038
37+
# bug on Emacs 27.2.
38+
gci cert:\LocalMachine\Root\DAC9024F54D8F6DF94935FB1732638CA6AD77C13
39+
gci cert:\LocalMachine\Root\DAC9024F54D8F6DF94935FB1732638CA6AD77C13 | Remove-Item
40+
41+
curl.exe -fsSL https://raw.github.com/doublep/eldev/master/webinstall/github-eldev.bat | cmd /Q
42+
2343
- name: Check out the source code
2444
uses: actions/checkout@v2
2545

26-
- name: Test the project
46+
- name: Prepare java
47+
uses: actions/setup-java@v3
48+
with:
49+
distribution: 'temurin'
50+
# shadow requires java 11
51+
java-version: 11
52+
53+
- name: Install Clojure Tools
54+
# Use SHA until
55+
# https://github.com/DeLaGuardo/setup-clojure/issues/78 is
56+
# released
57+
uses: DeLaGuardo/setup-clojure@1376ded6747c79645e82c856f16375af5f5de307
58+
with:
59+
bb: '1.0.165'
60+
cli: '1.10.3.1013'
61+
lein: '2.9.10'
62+
63+
- uses: actions/setup-node@v3
64+
with:
65+
node-version: 16
66+
- run: npm install [email protected] -g
67+
68+
- name: Test integration
2769
run: |
28-
eldev -p -dtT test
70+
eldev -p -dtTC test --test-type integration

Eldev

+30-6
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,36 @@
1010

1111
(eldev-add-loading-roots 'test "test/utils")
1212

13-
;; Otherwise `cider-test.el' will be considered a test file.
14-
(setf eldev-test-fileset "./test/")
15-
;; This file is _supposed_ to be excluded from automated testing. Since Eldev
16-
;; uses everything inside `test/' subdirectory, tell it to leave this file alone
17-
;; explicitly.
18-
(setf eldev-standard-excludes `(:or ,eldev-standard-excludes "test/cider-tests--no-auto.el"))
13+
(defvar cider-test-type 'main)
14+
(setf eldev-standard-excludes `(:or ,eldev-standard-excludes
15+
;; Avoid including files in test "projects".
16+
(eldev-pcase-exhaustive cider-test-type
17+
(`main "./test/*/")
18+
(`integration '("./test/" "!./test/integration"))
19+
(`all '("./test/*/" "!./test/integration")))
20+
"test/integration/projects"
21+
;; This file is _supposed_ to be excluded
22+
;; from automated testing.
23+
"test/cider-tests--no-auto.el"))
24+
25+
(eldev-defoption cider-test-selection (type)
26+
"Select tests to run; type can be `main', `integration' or `all'"
27+
:options (-T --test-type)
28+
:for-command test
29+
:value TYPE
30+
:default-value cider-test-type
31+
(unless (memq (intern type) '(main integration all))
32+
(signal 'eldev-wrong-option-usage `("unknown test type `%s'" ,type)))
33+
(setf cider-test-type (intern type)))
34+
35+
(add-hook 'eldev-test-hook
36+
(lambda ()
37+
(eldev-verbose "Using cider tests of type `%s'" cider-test-type)))
38+
(add-hook 'eldev-executing-command-hook
39+
(lambda (command)
40+
(unless (eq command 'test)
41+
;; So that e.g. byte-compilation works on all tests.
42+
(setf cider-test-type 'all))))
1943

2044
;; CIDER cannot be compiled otherwise.
2145
(setf eldev-build-load-before-byte-compiling t)

cider-connection.el

+2
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ buffer."
180180
;; Sync request will hang if the server is dead.
181181
(process-live-p (get-buffer-process nrepl-server-buffer))))
182182
(nrepl-sync-request:close repl)
183+
;; give a chance to the REPL to respond to the closing of the connection
184+
(sleep-for 0.5)
183185
(delete-process proc)))
184186
(when-let* ((messages-buffer (and nrepl-log-messages
185187
(nrepl-messages-buffer repl))))

doc/modules/ROOT/pages/contributing/hacking.adoc

+13-2
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,20 @@ all the details.
8484

8585
If you prefer running all tests outside Emacs that's also an option.
8686

87-
Run all tests with:
87+
There are two test types, `main` (unit tests) and `integration`. By
88+
default only main tests are run.
8889

89-
$ eldev test
90+
Run all unit tests with:
91+
92+
$ eldev[.bat] test
93+
94+
Run integration tests with:
95+
96+
$ eldev[.bat] test --test-type integration
97+
98+
or all tests with
99+
100+
$ eldev[.bat] test --test-type all
90101

91102
NOTE: Tests may not run correctly inside Emacs' `shell-mode` buffers. Running
92103
them in a terminal is recommended.

nrepl-client.el

+64-28
Original file line numberDiff line numberDiff line change
@@ -627,12 +627,33 @@ If NO-ERROR is non-nil, show messages instead of throwing an error."
627627
;;; Client: Process Handling
628628

629629
(defun nrepl--kill-process (proc)
630-
"Kill PROC using the appropriate, os specific way.
631-
Implement a workaround to clean up an orphaned JVM process left around
632-
after exiting the REPL on some windows machines."
633-
(if (memq system-type '(cygwin windows-nt))
634-
(interrupt-process proc)
635-
(kill-process proc)))
630+
"Attempt to kill PROC tree.
631+
On MS-Windows, using the standard API is highly likely to leave the child
632+
processes still running in the background as orphans. As a workaround, an
633+
attempt is made to delegate the task to the `taskkill` program, which comes
634+
with windows since at least Windows XP, and fallback to the Emacs API if it
635+
can't be found.
636+
637+
It is expected that the `(process-status PROC)` return value after PROC is
638+
killed is `exit` when `taskkill` is used and `signal` otherwise."
639+
(cond
640+
((and (eq system-type 'windows-nt)
641+
(process-live-p proc)
642+
(executable-find "taskkill"))
643+
;; try to use `taskkill` if available
644+
(with-temp-buffer
645+
(call-process-shell-command (format "taskkill /PID %s /T /F" (process-id proc))
646+
nil (buffer-name) )
647+
;; useful for debugging.
648+
;;(message ":PROCESS-KILL-OUPUT %s" (buffer-string))
649+
))
650+
651+
((memq system-type '(cygwin windows-nt))
652+
;; fallback, this is considered to work better than `kill-process` on
653+
;; MS-Windows.
654+
(interrupt-process proc))
655+
656+
(t (kill-process proc))))
636657

637658
(defun nrepl-kill-server-buffer (server-buf)
638659
"Kill SERVER-BUF and its process."
@@ -1090,8 +1111,22 @@ match groups:
10901111
1 for the port, and
10911112
2 for the host (babashka only).")
10921113

1114+
(defun cider--process-plist-put (proc prop val)
1115+
"Change value in PROC's plist of PROP to VAL.
1116+
Value is changed using `plist-put`, of which see."
1117+
(thread-first
1118+
proc
1119+
(process-plist)
1120+
(plist-put prop val)
1121+
(thread-last (set-process-plist proc))))
1122+
10931123
(defun nrepl-server-filter (process output)
1094-
"Process nREPL server output from PROCESS contained in OUTPUT."
1124+
"Process nREPL server output from PROCESS contained in OUTPUT.
1125+
1126+
The PROCESS plist is updated as (non-exhaustive list):
1127+
1128+
:cider--nrepl-server-ready set to t when the server is successfully brought
1129+
up."
10951130
;; In Windows this can be false:
10961131
(let ((server-buffer (process-buffer process)))
10971132
(when (buffer-live-p server-buffer)
@@ -1117,6 +1152,7 @@ match groups:
11171152
(setq nrepl-endpoint (list :host host
11181153
:port port))
11191154
(message "[nREPL] server started on %s" port)
1155+
(cider--process-plist-put process :cider--nrepl-server-ready t)
11201156
(when nrepl-on-port-callback
11211157
(funcall nrepl-on-port-callback (process-buffer process)))))))))
11221158

@@ -1129,16 +1165,11 @@ match groups:
11291165

11301166
(declare-function cider--close-connection "cider-connection")
11311167
(defun nrepl-server-sentinel (process event)
1132-
"Handle nREPL server PROCESS EVENT."
1133-
(let* ((server-buffer (process-buffer process))
1134-
(clients (seq-filter (lambda (b)
1135-
(eq (buffer-local-value 'nrepl-server-buffer b)
1136-
server-buffer))
1137-
(buffer-list)))
1138-
(problem (if (and server-buffer (buffer-live-p server-buffer))
1139-
(with-current-buffer server-buffer
1140-
(buffer-substring (point-min) (point-max)))
1141-
"")))
1168+
"Handle nREPL server PROCESS EVENT.
1169+
On a fatal EVENT, attempt to close any open client connections, and signal
1170+
an `error' if the nREPL PROCESS exited because it couldn't start up."
1171+
;; only interested on fatal signals.
1172+
(when (not (process-live-p process))
11421173
(emacs-bug-46284/when-27.1-windows-nt
11431174
;; There is a bug in emacs 27.1 (since fixed) that sets all EVENT
11441175
;; descriptions for signals to "unknown signal". We correct this by
@@ -1151,17 +1182,22 @@ match groups:
11511182
(2 (setq event "Interrupt"))
11521183
;; SIGKILL==9 emacs nt/inc/ms-w32.h
11531184
(9 (setq event "Killed")))))
1154-
1155-
(when server-buffer
1156-
(kill-buffer server-buffer))
1157-
(cond
1158-
((string-match-p "^killed\\|^interrupt" event)
1159-
nil)
1160-
((string-match-p "^hangup" event)
1161-
(mapc #'cider--close-connection clients))
1162-
;; On Windows, a failed start sends the "finished" event. On Linux it sends
1163-
;; "exited abnormally with code 1".
1164-
(t (error "Could not start nREPL server: %s" problem)))))
1185+
(let* ((server-buffer (process-buffer process))
1186+
(clients (seq-filter (lambda (b)
1187+
(eq (buffer-local-value 'nrepl-server-buffer b)
1188+
server-buffer))
1189+
(buffer-list))))
1190+
;; close any known open client connections
1191+
(when server-buffer
1192+
(kill-buffer server-buffer))
1193+
(mapc #'cider--close-connection clients)
1194+
1195+
(if (process-get process :cider--nrepl-server-ready)
1196+
(message "nREPL server exited.")
1197+
(let ((problem (when (and server-buffer (buffer-live-p server-buffer))
1198+
(with-current-buffer server-buffer
1199+
(buffer-substring (point-min) (point-max))))))
1200+
(error "Could not start nREPL server: %s (%S)" problem (string-trim event)))))))
11651201

11661202

11671203
;;; Messages

test/cider-tests.el

+6-5
Original file line numberDiff line numberDiff line change
@@ -555,11 +555,12 @@
555555
"(do (require '[shadow.cljs.devtools.api :as shadow]) (shadow/browser-repl))")))
556556
(describe "can watch multiple builds"
557557
(it "watches 2 builds and selects user-defined builds"
558-
(setq-local cider-shadow-default-options "client-build")
559-
(setq-local cider-shadow-watched-builds '("client-build" "other-build"))
560-
(expect (cider-shadow-cljs-init-form)
561-
:to-equal
562-
"(do (require '[shadow.cljs.devtools.api :as shadow]) (shadow/watch :client-build) (shadow/watch :other-build) (shadow/nrepl-select :client-build))"))))
558+
(with-temp-buffer
559+
(setq-local cider-shadow-default-options "client-build")
560+
(setq-local cider-shadow-watched-builds '("client-build" "other-build"))
561+
(expect (cider-shadow-cljs-init-form)
562+
:to-equal
563+
"(do (require '[shadow.cljs.devtools.api :as shadow]) (shadow/watch :client-build) (shadow/watch :other-build) (shadow/nrepl-select :client-build))")))))
563564

564565
(describe "cider--resolve-project-command"
565566
(it "if command starts with ./ it resolves relative to clojure-project-dir"

0 commit comments

Comments
 (0)