Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unit tests #150

Merged
merged 4 commits into from
Jul 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
name: CI
on: [push, pull_request]
on:
push:
branches:
- main
pull_request: {}
jobs:
ci:
runs-on: ubuntu-latest
strategy:
matrix:
emacs_version: [25, 26, 27, master]
emacs_version: [25, 26, 27, 28]
steps:
- name: Checkout
uses: actions/checkout@v2
- name: CI
env:
VERSION: ${{ matrix.emacs_version }}
run: >-
make docker CMD="make -k compile checkdoc longlines"
make docker CMD="make -k compile checkdoc longlines test"
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ help: ## Show this message
column -t -s'|' >&2

.PHONY: lint
lint: compile checkdoc longlines ## Run all the linters
lint: compile checkdoc longlines test ## Run all the linters and tests

.PHONY: compile
compile: ## Byte-compile
Expand All @@ -44,6 +44,11 @@ checkdoc: ## Check docstring style
longlines: ## Check for long lines
@scripts/check-line-length.bash

.PHONY: test
test:
$(EMACS) -Q --batch -L . -l ert -l ./test/prescient-test.el \
--eval "(let ((ert-quiet t)) (ert-run-tests-batch-and-exit))"

.PHONY: clean
clean: ## Remove build artifacts
@echo "[clean]" *.elc
Expand Down
1 change: 1 addition & 0 deletions scripts/check-line-length.bash
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ find=(
-name .git -prune -o
-name "*.elc" -o
-name "*.png" -o
-name prescient-test.el -o
-type f -print
)

Expand Down
140 changes: 140 additions & 0 deletions test/prescient-test.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
;;; prescient-test.el --- ERT tests -*- lexical-binding: t -*-

(require 'prescient)

;; Stolen from straight.el, credit to @progfolio
;; https://github.com/radian-software/straight.el/blob/ff63b154bef1ef8d92c141bd189001bff74f6982/tests/straight-test.el#L10-L74

(eval-and-compile
(defmacro prescient-test--template (template &optional vars &rest bindings)
"Return a list of filled TEMPLATEs.
TEMPLATE is an implicitly backquoted form.
VARS should be a list of symbols denoting the destructuring pattern
for BINDINGS."
(declare (indent 1))
(if (or (null vars) (null bindings))
(list template)
(let ((unbound (mod (length bindings) (length vars))))
(unless (zerop unbound)
(error "Unven binding list: %S" (last bindings unbound)))
(let ((body nil)
(bindings
(eval
`(cl-loop for ,vars on ',bindings
by (lambda (l) (nthcdr ,(length vars) l))
collect
(apply #'append
(cl-mapcar #'list ',vars (list ,@vars)))))))
(dolist (env bindings (mapcar (lambda (it) (eval it t))
(nreverse body)))
(unless (mod (length env) 2) (error "Uneven binding list: %S" env))
(let (e)
(cl-loop for (var val) on env by #'cddr
do (push (list var `(quote ,val)) e))
(push `(let* ,(nreverse e) (backquote ,template)) body))))))))

(cl-defmacro prescient-deftest (object
(&key before-each after-each expected-result
doc tags &allow-other-keys)
&rest template)
"Return auto-tagged and documented `ert-deftest' for OBJECT with TEMPLATE."
(declare (indent defun))
(let ((counter 0)
(autotags
(delq nil
(list
object
(if (string-match-p "--" (symbol-name object))
'private 'public)
(if (macrop object) 'macro))))
(tests (when template
(macroexpand `(prescient-test--template ,@template)))))
(setq tags (append autotags tags))
`(progn
,@(mapcar
(lambda (test)
`(ert-deftest
,(intern (concat
(format "%s/test" object)
(when (> (length tests) 1)
(format "@%d" (cl-incf counter)))))
()
,(or doc (when (fboundp object) (documentation object)))
,@(when tags `(:tags ',tags))
,@(when expected-result `(:expected-result ,expected-result))
,@(when before-each (if (cl-every #'listp before-each)
before-each
(list before-each)))
,test
,@(when after-each (if (cl-every #'listp after-each)
after-each
(list after-each)))))
tests))))

(font-lock-add-keywords
nil
'(("(\\(\\<prescient-deftest\\)\\>\\s *\\(\\(?:\\sw\\|\\s_\\)+\\)?"
(1 font-lock-keyword-face nil t)
(2 font-lock-function-name-face nil t))))

;;; Hacks and utilities

(defmacro prescient-test--stateless (&rest body)
"Exec BODY ignoring existing recency data."
(declare (indent 0))
`(let ((prescient--history (make-hash-table :test 'equal))
(prescient--frequency (make-hash-table :test 'equal)))
,@body))

;;; Begin tests

(prescient-deftest prescient-split-query ()
(should (equal ,result (prescient-split-query ,query)))
(query result)
"foo" '("foo")
"foo bar" '("foo" "bar")
"foo bar" '("foo bar")
"foo bar" '("foo bar")
" foo bar " '("foo" "bar")
" foo bar " '(" foo" "bar ")
" foo bar " '(" foo bar ")
"foo bar baz" '("foo" "bar" "baz")
"foo bar baz" '("foo bar" "baz")
"foo bar baz" '("foo" "bar baz")
)

(prescient-deftest prescient-filter-regexps ()
(let ((prescient-filter-method ,methods)
;; Simplify the regexes in the test cases by disabling char
;; folding
(prescient-use-char-folding nil))
(should (equal ,result (prescient-filter-regexps ,query ,with-group))))
(methods with-group separated query result)
'(literal) nil nil "foo" '("foo")
'(literal) nil nil "foo bar" '("foo" "bar")
'(literal) nil nil "foo bar" '("foo bar")
'(literal) t nil "hello" '("hello")
'(literal) 'all nil "hello" '("\\(hello\\)")
'(literal) 'all nil "hello world" '("\\(hello\\)" "\\(world\\)")
'(literal) nil nil "**[amaze]**" '("\\*\\*\\[amaze]\\*\\*")
'(initialism) nil nil "hai" '("\\bh\\w*\\W*\\ba\\w*\\W*\\bi\\w*")
'(initialism) t nil "hai" '("\\b\\(h\\)\\w*\\W*\\b\\(a\\)\\w*\\W*\\b\\(i\\)\\w*")
'(literal initialism) nil nil "hai" '("hai\\|\\bh\\w*\\W*\\ba\\w*\\W*\\bi\\w*")
'(literal initialism) t nil "hai" '("hai\\|\\b\\(h\\)\\w*\\W*\\b\\(a\\)\\w*\\W*\\b\\(i\\)\\w*")
'(literal initialism) 'all nil "hai" '("\\(hai\\)\\|\\b\\(h\\)\\w*\\W*\\b\\(a\\)\\w*\\W*\\b\\(i\\)\\w*")
;; '(literal initialism) nil t "hai" '("hai" "\\bh\\w*\\W*\\ba\\w*\\W*\\bi\\w*")
'(literal regexp) nil nil "f.*d b[o]*t" '("f\\.\\*d\\|f.*d" "b\\[o]\\*t\\|b[o]*t")
;; '(literal regexp) nil t "f.*d b[o]*t" '("f\\.\\*d" "f.*d" "b\\[o]\\*t" "b[o]*t")
)

(prescient-deftest prescient-sort-full-matches-first ()
(let ((prescient-filter-method ,methods)
(prescient-sort-full-matches-first ,full-match-first))
(prescient-test--stateless
(should (equal ,result (prescient-completion-sort
(prescient-filter ,query ,candidates))))))
(candidates methods full-match-first query result)
'("rebar" "restart-emacs" "barracuda") '(literal initialism) nil "bar" '("rebar" "barracuda")
'("rebar" "restart-emacs" "barracuda") '(literal initialism) nil "re" '("rebar" "restart-emacs")
;; '("rebar" "restart-emacs" "barracuda") '(literal initialism) t "re" '("restart-emacs" "rebar")
)