This repository has been archived on 2022-05-13. You can view files and clone it, but cannot push or open issues or pull requests.
chronometrist/tests/chronometrist-tests.org

489 lines
19 KiB
Org Mode
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#+PROPERTY: header-args :tangle yes :load yes :comments link
#+BEGIN_SRC emacs-lisp :load no :tangle no
(setq nameless-current-name "chronometrist")
#+END_SRC
* Setup
** test-file-path-stem
#+BEGIN_SRC emacs-lisp
(defvar chronometrist-test-file-path-stem
(format "%stest" (file-name-directory (or load-file-name default-directory))))
#+END_SRC
** test-backends
=chronometrist-test-backend= is just here till I finish writing the tests for a single backend. Will tackle how to apply the same tests to different backends afterwards - possibly with a [[#chronometrist-ert-deftest][wrapper macro]] around =ert-deftest=.
#+BEGIN_SRC emacs-lisp
(defvar chronometrist-test-backend
(make-instance 'chronometrist-plist-group-backend :path chronometrist-test-file-path-stem))
(defun chronometrist-make-test-backends ()
(cl-loop for backend in '(chronometrist-plist-backend chronometrist-plist-group-backend)
collect (make-instance backend :path chronometrist-test-file-path-stem)))
(defvar chronometrist-test-backends (chronometrist-make-test-backends))
#+END_SRC
** test-first-record
#+BEGIN_SRC emacs-lisp
(defvar chronometrist-test-first-record
'(:name "Programming"
:start "2018-01-01T00:00:00+0530"
:stop "2018-01-01T01:00:00+0530"))
#+END_SRC
** test-latest-record
#+BEGIN_SRC emacs-lisp
(defvar chronometrist-test-latest-record
'(:name "Programming"
:tags (reading)
:book "Smalltalk-80: The Language and Its Implementation"
:start "2020-05-10T16:33:17+0530"
:stop "2020-05-10T17:10:48+0530"))
#+END_SRC
** test-records
#+BEGIN_SRC emacs-lisp
(defvar chronometrist-test-records
'((:name "Programming"
:start "2018-01-01T00:00:00+0530"
:stop "2018-01-01T01:00:00+0530")
(:name "Swimming"
:start "2018-01-01T02:00:00+0530"
:stop "2018-01-01T03:00:00+0530")
(:name "Cooking"
:start "2018-01-01T04:00:00+0530"
:stop "2018-01-01T05:00:00+0530")
(:name "Guitar"
:start "2018-01-01T06:00:00+0530"
:stop "2018-01-01T07:00:00+0530")
(:name "Cycling"
:start "2018-01-01T08:00:00+0530"
:stop "2018-01-01T09:00:00+0530")
(:name "Programming"
:start "2018-01-02T23:00:00+0530"
:stop "2018-01-03T01:00:00+0530")
(:name "Cooking"
:start "2018-01-03T23:00:00+0530"
:stop "2018-01-04T01:00:00+0530")
(:name "Programming"
:tags (bug-hunting)
:project "Chronometrist"
:component "goals"
:start "2020-05-09T20:03:25+0530"
:stop "2020-05-09T20:05:55+0530")
(:name "Arrangement/new edition"
:tags (new edition)
:song "Songs of Travel"
:composer "Vaughan Williams, Ralph"
:start "2020-05-10T00:04:14+0530"
:stop "2020-05-10T00:25:48+0530")
(:name "Guitar"
:tags (classical warm-up)
:start "2020-05-10T15:41:14+0530"
:stop "2020-05-10T15:55:42+0530")
(:name "Guitar"
:tags (classical solo)
:start "2020-05-10T16:00:00+0530"
:stop "2020-05-10T16:30:00+0530")
(:name "Programming"
:tags (reading)
:book "Smalltalk-80: The Language and Its Implementation"
:start "2020-05-10T16:33:17+0530"
:stop "2020-05-10T17:10:48+0530")))
#+END_SRC
** cleanup
#+BEGIN_SRC emacs-lisp
(cl-defgeneric chronometrist-backend-test-cleanup (backend)
"Delete any files created by BACKEND during testing.")
(cl-defmethod chronometrist-backend-test-cleanup ((backend chronometrist-elisp-sexp-backend))
(with-slots (file hash-table) backend
(when file
(when (file-exists-p file)
(delete-file file))
(with-current-buffer (get-file-buffer file)
;; (erase-buffer)
;; Unsuccessful attempt to inhibit "Delete excess backup versions of <file>?" prompts
(defvar delete-old-versions)
(let ((delete-old-versions t))
(set-buffer-modified-p nil)
(kill-buffer))))
(setf hash-table (clrhash hash-table))))
(cl-defmethod chronometrist-backend-test-cleanup :after ((backend t))
(setf chronometrist-test-backends (chronometrist-make-test-backends)))
#+END_SRC
** ert-deftest :macro:
:PROPERTIES:
:CUSTOM_ID: chronometrist-ert-deftest
:END:
#+BEGIN_SRC emacs-lisp
(defmacro chronometrist-ert-deftest (name backend-var &rest test-forms)
"Generate test groups containing TEST-FORMS for each backend.
BACKEND-VAR is bound to each backend in
`chronometrist-test-backends'. TEST-FORMS are passed to
`ert-deftest'."
(declare (indent defun) (debug t))
(cl-loop for backend in chronometrist-test-backends collect
(let* ((backend-name (string-remove-suffix
"-backend"
(string-remove-prefix "chronometrist"
(symbol-name
(eieio-object-class-name backend)))))
(test-name (concat "chronometrist-" (symbol-name name) backend-name)))
`(ert-deftest ,(intern test-name) ()
(let ((,backend-var ,backend))
(unwind-protect
(progn ,@test-forms)
;; cleanup - remove test backend file
(chronometrist-backend-test-cleanup ,backend)))))
into test-groups
finally return (cons 'progn test-groups)))
#+END_SRC
* Tests
** common
*** current-task
:PROPERTIES:
:CUSTOM_ID: tests-common-current-task
:END:
#+BEGIN_SRC emacs-lisp
(chronometrist-ert-deftest current-task b
;; (message "current-task test - hash-table-count %s" (hash-table-count (chronometrist-backend-hash-table b)))
(chronometrist-create-file b)
(should (not (chronometrist-current-task b)))
(chronometrist-insert b (list :name "Test" :start (chronometrist-format-time-iso8601)))
(should (equal "Test" (chronometrist-current-task b)))
(chronometrist-remove-last b)
(should (not (chronometrist-current-task b))))
#+END_SRC
*** plist-p
:PROPERTIES:
:CUSTOM_ID: tests-common-plist-p
:END:
#+BEGIN_SRC emacs-lisp
(ert-deftest chronometrist-plist-p ()
(should (eq t (chronometrist-plist-p '(:a 1 :b 2))))
(should (eq nil (chronometrist-plist-p '(0 :a 1 :b 2))))
(should (eq nil (chronometrist-plist-p '(:a 1 :b 2 3))))
(should (not (chronometrist-plist-p nil))))
#+END_SRC
*** plists-split-p
:PROPERTIES:
:CUSTOM_ID: tests-common-plists-split-p
:END:
#+BEGIN_SRC emacs-lisp
(ert-deftest chronometrist-plists-split-p ()
(should
(chronometrist-plists-split-p
'(:name "Cooking"
:recipe "whole wheat penne rigate in arrabbiata sauce"
:start "2021-11-30T23:01:10+0530"
:stop "2021-12-01T00:00:00+0530")
'(:name "Cooking"
:recipe "whole wheat penne rigate in arrabbiata sauce"
:start "2021-12-01T00:00:00+0530"
:stop "2021-12-01T00:06:22+0530")))
;; without :stop
(should
(chronometrist-plists-split-p
'(:name "Cooking"
:recipe "whole wheat penne rigate in arrabbiata sauce"
:start "2021-11-30T23:01:10+0530"
:stop "2021-12-01T00:00:00+0530")
'(:name "Cooking"
:recipe "whole wheat penne rigate in arrabbiata sauce"
:start "2021-12-01T00:00:00+0530")))
;; difference in time
(should
(not (chronometrist-plists-split-p
'(:name "Cooking"
:recipe "whole wheat penne rigate in arrabbiata sauce"
:start "2021-11-30T23:01:10+0530"
:stop "2021-12-01T00:00:00+0530")
'(:name "Cooking"
:recipe "whole wheat penne rigate in arrabbiata sauce"
:start "2021-12-01T00:00:01+0530"
:stop "2021-12-01T00:06:22+0530"))))
;; difference in key-values
(should
(not (chronometrist-plists-split-p
'(:name "Cooking"
:recipe "whole wheat penne rigate in arrabbiata sauce"
:start "2021-11-30T23:01:10+0530"
:stop "2021-12-01T00:00:00+0530")
'(:name "Cooking"
:start "2021-12-01T00:00:00+0530"
:stop "2021-12-01T00:06:22+0530")))))
#+END_SRC
** data structures
*** list-tasks
#+BEGIN_SRC emacs-lisp
(chronometrist-ert-deftest list-tasks b
;; (message "list-tasks test - hash-table-count %s" (hash-table-count (chronometrist-backend-hash-table b)))
(chronometrist-create-file b)
(let ((task-list (chronometrist-list-tasks b)))
(should (listp task-list))
(should (seq-every-p #'stringp task-list))))
#+END_SRC
** time functions
*** format-duration-long :pure:
#+BEGIN_SRC emacs-lisp
(ert-deftest chronometrist-format-duration-long ()
(should (equal (chronometrist-format-duration-long 5) ""))
(should (equal (chronometrist-format-duration-long 65) "1 minute"))
(should (equal (chronometrist-format-duration-long 125) "2 minutes"))
(should (equal (chronometrist-format-duration-long 3605) "1 hour"))
(should (equal (chronometrist-format-duration-long 3660) "1 hour, 1 minute"))
(should (equal (chronometrist-format-duration-long 3725) "1 hour, 2 minutes"))
(should (equal (chronometrist-format-duration-long 7200) "2 hours"))
(should (equal (chronometrist-format-duration-long 7260) "2 hours, 1 minute"))
(should (equal (chronometrist-format-duration-long 7320) "2 hours, 2 minutes")))
#+END_SRC
** plist pretty-printing
[[file:../elisp/chronometrist.org::#program-pretty-printer][source]]
*** plist-group-p
#+BEGIN_SRC emacs-lisp
(ert-deftest chronometrist-plist-group-p ()
(should (eq t (chronometrist-plist-group-p '(symbol (:a 1 :b 2)))))
(should (eq t (chronometrist-plist-group-p '("string" (:a 1 :b 2)))))
(should (not (chronometrist-plist-group-p nil)))
(should (not (chronometrist-plist-group-p '("string")))))
#+END_SRC
*** plist-pp-to-string
#+BEGIN_SRC emacs-lisp
(ert-deftest chronometrist-pp-to-string ()
(should
(equal
(chronometrist-pp-to-string
'(:name "Task"
:tags (foo bar)
:comment ((70 . "baz")
"zot"
(16 . "frob")
(20 20 "quux"))
:start "2020-06-25T19:27:57+0530"
:stop "2020-06-25T19:43:30+0530"))
(concat
"(:name \"Task\"\n"
" :tags (foo bar)\n"
" :comment ((70 . \"baz\")\n"
" \"zot\"\n"
" (16 . \"frob\")\n"
" (20 20 \"quux\"))\n"
" :start \"2020-06-25T19:27:57+0530\"\n"
" :stop \"2020-06-25T19:43:30+0530\")")))
(should
(equal
(chronometrist-pp-to-string
'(:name "Singing"
:tags (classical solo)
:piece ((:composer "Gioachino Rossini"
:name "Il barbiere di Siviglia"
:aria ("All'idea di quel metallo" "Dunque io son"))
(:composer "Ralph Vaughan Williams"
:name "Songs of Travel"
:movement ((4 . "Youth and Love")
(5 . "In Dreams")
(7 . "Wither Must I Wander?")))
(:composer "Ralph Vaughan Williams"
:name "Merciless Beauty"
:movement 1)
(:composer "Franz Schubert"
:name "Winterreise"
:movement ((1 . "Gute Nacht")
(2 . "Die Wetterfahne")
(4 . "Erstarrung"))))
:start "2020-11-01T12:01:20+0530"
:stop "2020-11-01T13:08:32+0530"))
(concat
"(:name \"Singing\"\n"
" :tags (classical solo)\n"
" :piece ((:composer \"Gioachino Rossini\"\n"
" :name \"Il barbiere di Siviglia\"\n"
" :aria (\"All'idea di quel metallo\" \"Dunque io son\"))\n"
" (:composer \"Ralph Vaughan Williams\"\n"
" :name \"Songs of Travel\"\n"
" :movement ((4 . \"Youth and Love\")\n"
" (5 . \"In Dreams\")\n"
" (7 . \"Wither Must I Wander?\")))\n"
" (:composer \"Ralph Vaughan Williams\"\n"
" :name \"Merciless Beauty\"\n"
" :movement 1)\n"
" (:composer \"Franz Schubert\"\n"
" :name \"Winterreise\"\n"
" :movement ((1 . \"Gute Nacht\")\n"
" (2 . \"Die Wetterfahne\")\n"
" (4 . \"Erstarrung\"))))\n"
" :start \"2020-11-01T12:01:20+0530\"\n"
" :stop \"2020-11-01T13:08:32+0530\")")))
(should (equal
(chronometrist-pp-to-string
'(:name "Cooking"
:tags (lunch)
:recipe (:name "moong-masoor ki dal"
:url "https://www.mirchitales.com/moong-masoor-dal-red-and-yellow-lentil-curry/")
:start "2020-09-23T15:22:39+0530"
:stop "2020-09-23T16:29:49+0530"))
(concat
"(:name \"Cooking\"\n"
" :tags (lunch)\n"
" :recipe (:name \"moong-masoor ki dal\"\n"
" :url \"https://www.mirchitales.com/moong-masoor-dal-red-and-yellow-lentil-curry/\")\n"
" :start \"2020-09-23T15:22:39+0530\"\n"
" :stop \"2020-09-23T16:29:49+0530\")")))
(should (equal
(chronometrist-pp-to-string
'(:name "Exercise"
:tags (warm-up)
:start "2018-11-21T15:35:04+0530"
:stop "2018-11-21T15:38:41+0530"
:comment ("stretching" (25 10 "push-ups"))))
(concat
"(:name \"Exercise\"\n"
" :tags (warm-up)\n"
" :start \"2018-11-21T15:35:04+0530\"\n"
" :stop \"2018-11-21T15:38:41+0530\"\n"
" :comment (\"stretching\" (25 10 \"push-ups\")))")))
(should (equal
(chronometrist-pp-to-string
'(:name "Guitar"
:tags (classical)
:warm-up ((right-hand-patterns "pima" "piam" "pmia" "pmai" "pami" "paim"))
:start "2021-09-28T17:49:18+0530"
:stop "2021-09-28T17:53:49+0530"))
(concat
"(:name \"Guitar\"\n"
" :tags (classical)\n"
" :warm-up ((right-hand-patterns \"pima\" \"piam\" \"pmia\" \"pmai\" \"pami\" \"paim\"))\n"
" :start \"2021-09-28T17:49:18+0530\"\n"
" :stop \"2021-09-28T17:53:49+0530\")")))
(should (equal
(chronometrist-pp-to-string
'(:name "Cooking"
:tags (lunch)
:recipe ("urad dhuli"
(:name "brown rice"
:brand "Dawat quick-cooking"
:quantity "40% of steel measuring glass"
:water "2× dry rice"))
:start "2021-11-07T14:40:45+0530"
:stop "2021-11-07T15:28:13+0530"))
(concat
"(:name \"Cooking\"\n"
" :tags (lunch)\n"
" :recipe (\"urad dhuli\"\n"
" (:name \"brown rice\"\n"
" :brand \"Dawat quick-cooking\"\n"
" :quantity \"40% of steel measuring glass\"\n"
" :water \"2× dry rice\"))\n"
" :start \"2021-11-07T14:40:45+0530\"\n"
" :stop \"2021-11-07T15:28:13+0530\")"))))
#+END_SRC
** backend
Situations
1. no file
2. empty file
3. non-empty file with no records
4. single record
* active
* inactive
* active, day-crossing
* inactive, day-crossing
5. multiple records
6. +[plist-group] latest plist is split+ (covered in #4)
Tests to be added -
1. to-hash-table
2. to-file
The order of these tests is important - the last test for each case is one which moves into the next case.
*** create-file
:PROPERTIES:
:CUSTOM_ID: tests-backend-create-file
:END:
#+BEGIN_SRC emacs-lisp
(chronometrist-ert-deftest create-file b
;; (message "create-file test - hash-table-count %s" (hash-table-count (chronometrist-backend-hash-table b)))
;; * file does not exist *
(should (chronometrist-create-file b))
;; * file exists but has no records *
(should (not (chronometrist-create-file b))))
#+END_SRC
*** latest-date-records
#+BEGIN_SRC emacs-lisp
(chronometrist-ert-deftest latest-date-records b
;; (message "latest-date-records test - hash-table-count %s" (hash-table-count (chronometrist-backend-hash-table b)))
(let ((plist-1 (cl-first chronometrist-test-records))
(plist-2 (cl-second chronometrist-test-records))
(today-ts (chronometrist-date-ts)))
;; * file does not exist *
(should-error (chronometrist-latest-date-records b))
(should (chronometrist-create-file b))
;; (message "latest-date-records: %S" (chronometrist-latest-date-records b))
;; * file exists but has no records *
(should (not (chronometrist-latest-date-records b)))
(should (chronometrist-insert b plist-1))
;; * backend has a single active record *
;; * backend has a single inactive record *
;; * backend has a single active day-crossing record *
;; * backend has a single inactive day-crossing record *
;; * backend has two records and one is active *
;; * backend has two records and both are inactive *
;; * backend has two day-crossing records and one is active *
;; * backend has two day-crossing records and both are inactive *
))
#+END_SRC
*** insert
#+BEGIN_SRC emacs-lisp
(chronometrist-ert-deftest insert b
;; (message "insert test - hash-table-count %s" (hash-table-count (chronometrist-backend-hash-table b)))
(let* ((plist1 (list :name "Test" :start (chronometrist-format-time-iso8601)))
(plist2 (append plist1 (list :stop (chronometrist-format-time-iso8601)))))
;; * file does not exist *
(should-error (chronometrist-insert b plist1))
(should (chronometrist-create-file b))
;; * file exists but has no records *
(should (chronometrist-insert b plist1))
(should (equal (progn (chronometrist-reset-backend b)
(chronometrist-latest-date-records b))
(list (chronometrist-date-iso) plist1)))
;; * backend has a single active record *
(should (chronometrist-replace-last b plist2))
(should (equal (progn (chronometrist-reset-backend b)
(chronometrist-latest-date-records b))
(list (chronometrist-date-iso) plist2)))
;; * backend has a single inactive record *
;; * backend has a single active day-crossing record *
;; * backend has a single inactive day-crossing record *
;; * backend has two records and one is active *
;; * backend has two records and both are inactive *
;; * backend has two day-crossing records and one is active *
;; * backend has two day-crossing records and both are inactive *
))
#+END_SRC
* Local Variables
# Local Variables:
# delete-old-versions: t
# End: