Compare commits

...
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.

5 Commits
dev ... tests

Author SHA1 Message Date
contrapunctus 68c5a356c5 tests(key-values): change test tangle location; track test file 2021-06-06 17:09:09 +05:30
contrapunctus a532c0b1c4 doc(TODO): add task for removing duplication in tests 2021-06-06 16:41:48 +05:30
contrapunctus d7198f9932 doc: checkdoc fixes 2021-06-06 06:45:48 +05:30
contrapunctus 687b5f169c tests: restore user state if running interactively 2021-06-05 18:02:02 +05:30
contrapunctus be1a158db4 tests: move from Buttercup to ERT 2021-06-05 17:10:36 +05:30
11 changed files with 580 additions and 352 deletions

View File

@ -290,6 +290,7 @@ Some options and ideas -
* Actually, are transformers really necessary? It could be done with a function inserted by =chronometrist-goal= into =chronometrist-mode-hook=. The function itself would become a little more complex, but it would remove the two transformer lists + the =call-transformers= dependency from the code. =chronometrist-mode-hook= is required either way, to set up =chronometrist-goal=.
+ Turns out, they are. We set =tabulated-list-entries= to a function. To modify the value, we must hook into that function in some way. =tabulated-list-format= could be modified in a regular hook, but it feels more consistent to make it a transformer too 🤔
8. [ ] Ugly code - =chronometrist-print-non-tabular=; =insert-text-button= can be replaced with =make-text-button=
9. [ ] Duplication in tests for saving and restoring user state when run interactively
** Documentation [0%]
1. [ ] Move usage and customization sections to manual.org

View File

@ -210,7 +210,7 @@ HISTORY-TABLE must be a hash table. (see `chronometrist-tags-history')"
(chronometrist-history-prep task history-table))
#+END_SRC
**** tests
#+BEGIN_SRC emacs-lisp :tangle chronometrist-key-values-tests.el :load test
#+BEGIN_SRC emacs-lisp :tangle ../tests/chronometrist-key-values-tests.el :load test
(ert-deftest chronometrist-tags-history ()
(progn
(clrhash chronometrist-tags-history)
@ -371,7 +371,7 @@ HISTORY-TABLE must be a hash table (see `chronometrist-key-history')."
(chronometrist-history-prep task history-table))
#+END_SRC
**** tests
#+BEGIN_SRC emacs-lisp :tangle chronometrist-key-values-tests.el :load test
#+BEGIN_SRC emacs-lisp :tangle ../tests/chronometrist-key-values-tests.el :load test
(ert-deftest chronometrist-key-history ()
(progn
(clrhash chronometrist-key-history)
@ -419,7 +419,7 @@ HISTORY-TABLE must be a hash table. (see `chronometrist-value-history')"
history-table))
#+END_SRC
**** tests
#+BEGIN_SRC emacs-lisp :tangle chronometrist-key-values-tests.el :load test
#+BEGIN_SRC emacs-lisp :tangle ../tests/chronometrist-key-values-tests.el :load test
(ert-deftest chronometrist-value-history ()
(progn
(clrhash chronometrist-value-history)

View File

@ -183,7 +183,7 @@ TS must be a ts struct (see `ts.el')."
(chronometrist-plist-remove plist :name :tags :start :stop))
(defun chronometrist-plist-p (list)
"Return non-nil if LIST is a property list, i.e. (:KEYWORD VALUE ...)"
"Return non-nil if LIST is a property list, i.e. (:KEYWORD VALUE ...)."
(while (consp list)
(setq list (if (and (keywordp (car list))
(consp (cdr list)))
@ -238,7 +238,9 @@ that point is after the first opening parenthesis."
(cl-defun chronometrist-plist-pp-buffer (&optional inside-sublist-p)
"Recursively indent the alist, plist, or a list of plists after point.
The list must be on a single line, as emitted by `prin1'."
The list must be on a single line, as emitted by `prin1'.
INSIDE-SUBLIST-P, if non-nil, means point is inside a sublist
value."
(if (not (looking-at-p (rx (or ")" line-end))))
(let ((sexp (save-excursion (read (current-buffer)))))
(cond
@ -268,7 +270,9 @@ The list must be on a single line, as emitted by `prin1'."
(forward-char)))))
(defun chronometrist-plist-pp-buffer-plist (&optional inside-sublist-p)
"Indent a single plist after point."
"Indent a single plist after point.
INSIDE-SUBLIST-P, if non-nil, means point is inside a sublist
value."
(down-list)
(let ((left-indent (1- (chronometrist-plist-pp-column)))
(right-indent (chronometrist-plist-pp-longest-keyword-length))
@ -373,7 +377,7 @@ STREAM (which is the value of `current-buffer')."
(defmacro chronometrist-loop-file (_for expr _in file &rest loop-clauses)
"`cl-loop' LOOP-CLAUSES over s-expressions in FILE, in reverse.
VAR is bound to each s-expression."
EXPR is bound to each s-expression."
(declare (indent defun)
(debug nil)
;; FIXME

View File

@ -396,6 +396,10 @@ But I discovered that if I do that, =package-lint= says - =error: Couldn't parse
(defvar chronometrist-mode-map)
(require 'subr-x))
#+END_SRC
#+BEGIN_SRC emacs-lisp :tangle ../tests/chronometrist-tests.el :load test
(require 'chronometrist)
#+END_SRC
** Common
*** custom group :custom:group:
#+BEGIN_SRC emacs-lisp
@ -419,7 +423,7 @@ file.")
"Return the name of the currently clocked-in task, or nil if not clocked in."
(chronometrist-sexp-current-task))
#+END_SRC
*** format-time :function:
*** format-duration :function:
#+BEGIN_SRC emacs-lisp
(cl-defun chronometrist-format-duration (seconds &optional (blank (make-string 3 ?\s)))
"Format SECONDS as a string suitable for display in Chronometrist buffers.
@ -439,6 +443,27 @@ supplied, 3 spaces are used."
(format "%02d" s))))
(concat h m s)))))
#+END_SRC
**** tests
#+BEGIN_SRC emacs-lisp :tangle ../tests/chronometrist-tests.el :load test
(ert-deftest chronometrist-format-duration ()
(should
(equal (chronometrist-format-duration 0) " -"))
(should
(equal (chronometrist-format-duration 1) " 1"))
(should
(equal (chronometrist-format-duration 10) " 10"))
(should
(equal (chronometrist-format-duration 70) " 1:10"))
(should
(equal (chronometrist-format-duration (+ (* 10 60) 10))
" 10:10"))
(should
(equal (chronometrist-format-duration (+ (* 1 60 60) (* 10 60) 10))
" 1:10:10"))
(should
(equal (chronometrist-format-duration (+ (* 10 60 60) (* 10 60) 10))
"10:10:10")))
#+END_SRC
*** file-empty-p :reader:
#+BEGIN_SRC emacs-lisp
(defun chronometrist-common-file-empty-p (file)
@ -524,6 +549,30 @@ TS must be a ts struct (see `ts.el')."
do (ts-decf (ts-day ts))
finally return ts))
#+END_SRC
**** tests
#+BEGIN_SRC emacs-lisp :tangle ../tests/chronometrist-tests.el :load test
(ert-deftest chronometrist-previous-week-start ()
(let ((chronometrist-report-week-start-day "Sunday")
(ts (chronometrist-iso-date-to-ts "2018-09-02")))
(should (ts= (chronometrist-previous-week-start
(chronometrist-iso-date-to-ts "2018-09-01"))
(chronometrist-iso-date-to-ts "2018-08-26")))
(should (ts= ts (chronometrist-previous-week-start
(chronometrist-iso-date-to-ts "2018-09-02"))))
(should (ts= ts (chronometrist-previous-week-start
(chronometrist-iso-date-to-ts "2018-09-03"))))
(should (ts= ts (chronometrist-previous-week-start
(chronometrist-iso-date-to-ts "2018-09-04"))))
(should (ts= ts (chronometrist-previous-week-start
(chronometrist-iso-date-to-ts "2018-09-05"))))
(should (ts= ts (chronometrist-previous-week-start
(chronometrist-iso-date-to-ts "2018-09-06"))))
(should (ts= ts (chronometrist-previous-week-start
(chronometrist-iso-date-to-ts "2018-09-07"))))
(should (ts= ts (chronometrist-previous-week-start
(chronometrist-iso-date-to-ts "2018-09-08"))))))
#+END_SRC
*** plist-remove :function:
#+BEGIN_SRC emacs-lisp
(defun chronometrist-plist-remove (plist &rest keys)
@ -537,12 +586,14 @@ TS must be a ts struct (see `ts.el')."
plist))
#+END_SRC
**** tests
#+BEGIN_SRC emacs-lisp :tangle chronometrist-tests.el :load test
#+BEGIN_SRC emacs-lisp :tangle ../tests/chronometrist-tests.el :load test
(ert-deftest chronometrist-plist-remove ()
(should
(equal (chronometrist-plist-remove '(:a 1 :b 2 :c 3 :d 4) :a)
'(:b 2 :c 3 :d 4)))
(should
(equal (chronometrist-plist-remove '(:a 1 :b 2 :c 3 :d 4) :b)
'(:a 1 :c 3 :d 4)))
(should
@ -577,7 +628,7 @@ TS must be a ts struct (see `ts.el')."
*** plist-p :function:
#+BEGIN_SRC emacs-lisp
(defun chronometrist-plist-p (list)
"Return non-nil if LIST is a property list, i.e. (:KEYWORD VALUE ...)"
"Return non-nil if LIST is a property list, i.e. (:KEYWORD VALUE ...)."
(while (consp list)
(setq list (if (and (keywordp (car list))
(consp (cdr list)))
@ -586,7 +637,7 @@ TS must be a ts struct (see `ts.el')."
(null list))
#+END_SRC
**** tests
#+BEGIN_SRC emacs-lisp :tangle chronometrist-tests.el :load test
#+BEGIN_SRC emacs-lisp :tangle ../tests/chronometrist-tests.el :load test
(ert-deftest plist-p ()
(should (eq t (chronometrist-plist-p '(:a 1 :b 2))))
(should (eq nil (chronometrist-plist-p '(0 :a 1 :b 2))))
@ -669,7 +720,9 @@ It might help to make =inside-sublist-p= an integer representing depth, instead
#+BEGIN_SRC emacs-lisp
(cl-defun chronometrist-plist-pp-buffer (&optional inside-sublist-p)
"Recursively indent the alist, plist, or a list of plists after point.
The list must be on a single line, as emitted by `prin1'."
The list must be on a single line, as emitted by `prin1'.
INSIDE-SUBLIST-P, if non-nil, means point is inside a sublist
value."
(if (not (looking-at-p (rx (or ")" line-end))))
(let ((sexp (save-excursion (read (current-buffer)))))
(cond
@ -699,7 +752,7 @@ The list must be on a single line, as emitted by `prin1'."
(forward-char)))))
#+END_SRC
**** tests
#+BEGIN_SRC emacs-lisp :tangle chronometrist-tests.el :load test
#+BEGIN_SRC emacs-lisp :tangle ../tests/chronometrist-tests.el :load test
(ert-deftest plist-pp-buffer ()
(should
(equal
@ -797,7 +850,9 @@ The list must be on a single line, as emitted by `prin1'."
*** buffer-plist :writer:
#+BEGIN_SRC emacs-lisp
(defun chronometrist-plist-pp-buffer-plist (&optional inside-sublist-p)
"Indent a single plist after point."
"Indent a single plist after point.
INSIDE-SUBLIST-P, if non-nil, means point is inside a sublist
value."
(down-list)
(let ((left-indent (1- (chronometrist-plist-pp-column)))
(right-indent (chronometrist-plist-pp-longest-keyword-length))
@ -894,7 +949,7 @@ The reasons I like this format are -
*** tests
:PROPERTIES:
:header-args: :tangle chronometrist-tests.el
:header-args: :tangle ../tests/chronometrist-tests.el
:END:
#+BEGIN_SRC emacs-lisp :load test
@ -902,7 +957,7 @@ The reasons I like this format are -
(make-temp-file
"chronometrist-test-" nil ".sexp"
(with-output-to-string
(mapcar
(mapc
(lambda (plist)
;; to use this, we'd have to move `chronometrist-plist-pp' before this
;; definition, and I'm perfectly content with where it is
@ -970,6 +1025,7 @@ Boilerplate for updating state between file operations in tests.
(list :last (chronometrist-file-hash :before-last nil)
:rest (chronometrist-file-hash nil :before-last t)))))
#+END_SRC
*** chronometrist-file :custom:variable:
#+BEGIN_SRC emacs-lisp
(defcustom chronometrist-file
@ -1025,7 +1081,7 @@ STREAM (which is the value of `current-buffer')."
#+BEGIN_SRC emacs-lisp
(defmacro chronometrist-loop-file (_for expr _in file &rest loop-clauses)
"`cl-loop' LOOP-CLAUSES over s-expressions in FILE, in reverse.
VAR is bound to each s-expression."
EXPR is bound to each s-expression."
(declare (indent defun)
(debug nil)
;; FIXME
@ -1169,7 +1225,7 @@ This is meant to be run in `chronometrist-file' when using the s-expression back
(sort it #'string-lessp)))
#+END_SRC
**** tests
#+BEGIN_SRC emacs-lisp :tangle chronometrist-tests.el :load test
#+BEGIN_SRC emacs-lisp :tangle ../tests/chronometrist-tests.el :load test
(ert-deftest task-list ()
(let ((task-list (chronometrist-task-list)))
(should (listp task-list))
@ -1233,7 +1289,7 @@ in `chronometrist-file' describing the region for which HASH was calculated."
(list start end)))))
#+END_SRC
**** tests
#+BEGIN_SRC emacs-lisp :tangle chronometrist-tests.el :load test
#+BEGIN_SRC emacs-lisp :tangle ../tests/chronometrist-tests.el :load test
(ert-deftest file-hash ()
(-let* ((chronometrist-file chronometrist-test-file)
((last-start last-end)
@ -1312,7 +1368,7 @@ Return
:modify))))
#+END_SRC
**** tests
#+BEGIN_SRC emacs-lisp :tangle chronometrist-tests.el :load test
#+BEGIN_SRC emacs-lisp :tangle ../tests/chronometrist-tests.el :load test
(ert-deftest chronometrist-file-change-type ()
(let* ((chronometrist-file chronometrist-test-file)
(test-contents (with-current-buffer (find-file-noselect chronometrist-file)
@ -1363,6 +1419,7 @@ Return
(current-buffer))
(save-buffer))
(chronometrist-tests--change-type-and-update chronometrist--file-state)))))
;; restore test file contents
(with-current-buffer (find-file-noselect chronometrist-file)
(delete-region (point-min) (point-max))
(insert test-contents)
@ -1529,7 +1586,7 @@ Return value is a ts struct (see `ts.el')."
(chronometrist-iso-timestamp-to-ts timestamp))))
#+END_SRC
**** tests
#+BEGIN_SRC emacs-lisp :tangle chronometrist-tests.el :load test
#+BEGIN_SRC emacs-lisp :tangle ../tests/chronometrist-tests.el :load test
(ert-deftest chronometrist-apply-time ()
(should
(equal (ts-format "%FT%T%z" (chronometrist-apply-time "01:02:03" "2021-02-17T01:20:18+0530"))
@ -1563,7 +1620,7 @@ Return a list of two events if EVENT was split, else nil."
#+END_SRC
**** tests
#+BEGIN_SRC emacs-lisp :tangle chronometrist-tests.el :load test
#+BEGIN_SRC emacs-lisp :tangle ../tests/chronometrist-tests.el :load test
(ert-deftest chronometrist-events-maybe-split ()
(should
(null (chronometrist-events-maybe-split
@ -1682,6 +1739,47 @@ The return value is seconds, as an integer."
;; no events for this task on TS, i.e. no time spent
0)))
#+END_SRC
**** tests
#+BEGIN_SRC emacs-lisp :tangle ../tests/chronometrist-tests.el :load test
(ert-deftest chronometrist-task-time-one-day ()
(let ((chronometrist-file-old chronometrist-file)
(chronometrist-file chronometrist-test-file)
(chronometrist--file-state-old chronometrist--file-state)
(chronometrist-events-old chronometrist-events)
(ts-1 (chronometrist-iso-date-to-ts "2018-01-01"))
(ts-2 (chronometrist-iso-date-to-ts "2018-01-02"))
(ts-3 (chronometrist-iso-date-to-ts "2018-01-03"))
(1-hour 3600))
(chronometrist-events-populate)
(unwind-protect
(progn
(should
(equal (chronometrist-task-time-one-day "Programming" ts-1)
1-hour))
(should
(equal (chronometrist-task-time-one-day "Swimming" ts-1)
1-hour))
(should
(equal (chronometrist-task-time-one-day "Cooking" ts-1)
1-hour))
(should
(equal (chronometrist-task-time-one-day "Guitar" ts-1)
1-hour))
(should
(equal (chronometrist-task-time-one-day "Cycling" ts-1)
1-hour))
(should
(equal (chronometrist-task-time-one-day "Programming" ts-2)
1-hour))
(should
(equal (chronometrist-task-time-one-day "Programming" ts-3)
1-hour)))
(setq chronometrist-file chronometrist-file-old
chronometrist--file-state chronometrist--file-state-old)
;; Not sure why simply assigning `chronometrist-events-old' did not restore
;; the value - it seems to work in `chronometrist-file-change-type'
(chronometrist-events-populate))))
#+END_SRC
*** active-time-one-day :reader:
#+BEGIN_SRC emacs-lisp
(defvar chronometrist-task-list)
@ -1829,7 +1927,7 @@ Return a list in the form
:stop ,stop-time)))))
#+END_SRC
**** tests
#+BEGIN_SRC emacs-lisp :tangle chronometrist-tests.el :load test
#+BEGIN_SRC emacs-lisp :tangle ../tests/chronometrist-tests.el :load test
(ert-deftest chronometrist-midnight-spanning-p ()
(should
(null
@ -3156,7 +3254,7 @@ ARG must be either tags (a list of symbols) or a plist."
#+END_SRC
***** tests
#+BEGIN_SRC emacs-lisp :tangle chronometrist-tests.el :load test
#+BEGIN_SRC emacs-lisp :tangle ../tests/chronometrist-tests.el :load test
(ert-deftest chronometrist-details-row-helper ()
(let ((tags '(a b c))
(plist '(:a 1 :b 2 :c 3)))

View File

@ -1,57 +0,0 @@
(: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")

View File

@ -1,42 +0,0 @@
;;; -*- lexical-binding: t; -*-
(require 'buttercup)
(require 'chronometrist)
(describe
"chronometrist-format-time"
(it "works with lists"
(expect (chronometrist-format-time 0)
:to-equal " -")
(expect (chronometrist-format-time 1)
:to-equal " 1")
(expect (chronometrist-format-time 10)
:to-equal " 10")
(expect (chronometrist-format-time 70)
:to-equal " 1:10")
(expect (chronometrist-format-time (+ (* 10 60) ;; 10 minutes
10)) ;; 10 seconds
:to-equal " 10:10")
(expect (chronometrist-format-time (+ (* 1 60 60) ;; 1 hour
(* 10 60) ;; 10 minutes
10)) ;; 10 seconds
:to-equal " 1:10:10")
(expect (chronometrist-format-time (+ (* 10 60 60) ;; 10 hours
(* 10 60) ;; 10 minutes
10)) ;; 10 seconds
:to-equal "10:10:10")))
(describe
"chronometrist-previous-week-start"
:var ((chronometrist-report-week-start-day "Sunday")
(ts (chronometrist-iso-date->ts "2018-09-02")))
(it "should work with Sundays"
(should (ts= (chronometrist-previous-week-start
(chronometrist-iso-date->ts "2018-09-01"))
(chronometrist-iso-date->ts "2018-08-26")))
(should (ts= ts (chronometrist-previous-week-start (chronometrist-iso-date->ts "2018-09-02"))))
(should (ts= ts (chronometrist-previous-week-start (chronometrist-iso-date->ts "2018-09-03"))))
(should (ts= ts (chronometrist-previous-week-start (chronometrist-iso-date->ts "2018-09-04"))))
(should (ts= ts (chronometrist-previous-week-start (chronometrist-iso-date->ts "2018-09-05"))))
(should (ts= ts (chronometrist-previous-week-start (chronometrist-iso-date->ts "2018-09-06"))))
(should (ts= ts (chronometrist-previous-week-start (chronometrist-iso-date->ts "2018-09-07"))))
(should (ts= ts (chronometrist-previous-week-start (chronometrist-iso-date->ts "2018-09-08"))))))

View File

@ -1,87 +0,0 @@
(require 'buttercup)
(require 'chronometrist-key-values)
(describe
"chronometrist-plist-remove"
(it "works with one key"
(expect (chronometrist-plist-remove '(:a 1 :b 2 :c 3 :d 4)
:a)
:to-equal '(:b 2 :c 3 :d 4))
(expect (chronometrist-plist-remove '(:a 1 :b 2 :c 3 :d 4)
:b)
:to-equal '(:a 1 :c 3 :d 4))
(expect (chronometrist-plist-remove '(:a 1 :b 2 :c 3 :d 4)
:c)
:to-equal '(:a 1 :b 2 :d 4))
(expect (chronometrist-plist-remove '(:a 1 :b 2 :c 3 :d 4)
:d)
:to-equal '(:a 1 :b 2 :c 3)))
(it "works with multiple keys"
(expect (chronometrist-plist-remove '(:a 1 :b 2 :c 3 :d 4)
:a :b)
:to-equal '(:c 3 :d 4))
(expect (chronometrist-plist-remove '(:a 1 :b 2 :c 3 :d 4)
:a :d)
:to-equal '(:b 2 :c 3))
(expect (chronometrist-plist-remove '(:a 1 :b 2 :c 3 :d 4)
:c :d)
:to-equal '(:a 1 :b 2))
(expect (chronometrist-plist-remove '(:a 1 :b 2 :c 3 :d 4)
:a :b :c :d)
:to-equal nil))
(it "works with keys in any order"
(expect (chronometrist-plist-remove '(:a 1 :b 2 :c 3 :d 4)
:d :a)
:to-equal '(:b 2 :c 3))))
(describe
"chronometrist-tags-history"
(before-all
(setq chronometrist-file "tests/test.sexp")
(chronometrist-events-populate)
(chronometrist-tags-history-populate chronometrist-events chronometrist-tags-history))
(it "should have 3 hash keys"
(expect (hash-table-count chronometrist-tags-history)
:to-be 3)
(expect (cl-loop for task being the hash-keys of chronometrist-tags-history
always (stringp task))
:to-be t))
(it "should have lists as hash values"
(expect (gethash "Guitar" chronometrist-tags-history)
:to-equal '((classical solo)
(classical warm-up)))
(expect (gethash "Programming" chronometrist-tags-history)
:to-equal '((reading) (bug-hunting)))))
(describe
"chronometrist-key-history"
(before-all
(setq chronometrist-file "tests/test.sexp")
(chronometrist-events-populate)
(setq chronometrist-task-list (chronometrist-tasks-from-table))
(chronometrist-key-history-populate chronometrist-events chronometrist-key-history))
(it "should have 6 hash keys"
(expect (hash-table-count chronometrist-key-history)
:to-be 6))
(it "should store multiple values"
(expect (length (gethash "Programming" chronometrist-key-history))
:to-be 3)
(expect (length (gethash "Arrangement/new edition" chronometrist-key-history))
:to-be 2)))
(describe
"chronometrist-value-history"
(before-all
(setq chronometrist-file "tests/test.sexp")
(chronometrist-events-populate)
(chronometrist-value-history-populate chronometrist-events chronometrist-value-history))
(it "should have 5 hash keys"
(expect (hash-table-count chronometrist-value-history)
:to-be 5)
(expect (cl-loop for task being the hash-keys of chronometrist-value-history
always (stringp task))
:to-be t)))
;; Local Variables:
;; nameless-current-name: "chronometrist"
;; End:

View File

@ -0,0 +1,35 @@
(ert-deftest chronometrist-tags-history ()
(progn
(clrhash chronometrist-tags-history)
(cl-loop for task in '("Guitar" "Programming") do
(chronometrist-tags-history-populate task chronometrist-tags-history "test.sexp")))
(should
(= (hash-table-count chronometrist-tags-history) 2))
(should
(cl-loop for task being the hash-keys of chronometrist-tags-history
always (stringp task)))
(should
(equal (gethash "Guitar" chronometrist-tags-history)
'((classical solo)
(classical warm-up))))
(should
(equal (gethash "Programming" chronometrist-tags-history)
'((reading) (bug-hunting)))))
(ert-deftest chronometrist-key-history ()
(progn
(clrhash chronometrist-key-history)
(cl-loop for task in '("Programming" "Arrangement/new edition") do
(chronometrist-key-history-populate task chronometrist-key-history "test.sexp")))
(should (= (hash-table-count chronometrist-key-history) 2))
(should (= (length (gethash "Programming" chronometrist-key-history)) 3))
(should (= (length (gethash "Arrangement/new edition" chronometrist-key-history)) 2)))
(ert-deftest chronometrist-value-history ()
(progn
(clrhash chronometrist-value-history)
(chronometrist-value-history-populate chronometrist-value-history "test.sexp"))
(should (= (hash-table-count chronometrist-value-history) 5))
(should
(cl-loop for task being the hash-keys of chronometrist-value-history
always (stringp task))))

View File

@ -1,105 +0,0 @@
;; -*- lexical-binding: t; -*-
(require 'buttercup)
(require 'chronometrist)
(describe "chronometrist-plist-pp-buffer"
:var ((buffer (find-file-noselect "tests/plist-pp-test.sexp")))
(it "indents plists nicely"
(expect
(chronometrist-plist-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"))
:to-equal
(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\")\n"))
(expect
(chronometrist-plist-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"))
:to-equal
(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\")"))
(expect
(chronometrist-plist-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"))
:to-equal
(concat
"(: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\")"))
(expect
(chronometrist-plist-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"))))
:to-equal
(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\")))"))))
;; Local Variables:
;; nameless-current-name: "chronometrist"
;; End:

View File

@ -1,37 +0,0 @@
;; -*- lexical-binding: t; -*-
(require 'buttercup)
(require 'ts)
(require 'chronometrist)
(describe "chronometrist-task-time-one-day"
:var ((ts-1 (chronometrist-iso-date->ts "2018-01-01"))
(ts-2 (chronometrist-iso-date->ts "2018-01-02"))
(ts-3 (chronometrist-iso-date->ts "2018-01-03"))
(1-hour 3600))
(before-all
(setq chronometrist-file-old chronometrist-file
chronometrist-file "tests/test.sexp")
(chronometrist-events-populate))
(after-all
(setq chronometrist-file chronometrist-file-old))
(it "returns the time spent in one day, in seconds"
(expect (chronometrist-task-time-one-day "Programming" ts-1)
:to-equal 1-hour)
(expect (chronometrist-task-time-one-day "Swimming" ts-1)
:to-equal 1-hour)
(expect (chronometrist-task-time-one-day "Cooking" ts-1)
:to-equal 1-hour)
(expect (chronometrist-task-time-one-day "Guitar" ts-1)
:to-equal 1-hour)
(expect (chronometrist-task-time-one-day "Cycling" ts-1)
:to-equal 1-hour))
(it "works with midnight-crossing events"
(expect (chronometrist-task-time-one-day "Programming" ts-2)
:to-equal 1-hour)
(expect (chronometrist-task-time-one-day "Programming" ts-3)
:to-equal 1-hour)))
;; Local Variables:
;; nameless-current-name: "chronometrist"
;; End:

View File

@ -0,0 +1,418 @@
(require 'chronometrist)
(ert-deftest chronometrist-format-duration ()
(should
(equal (chronometrist-format-duration 0) " -"))
(should
(equal (chronometrist-format-duration 1) " 1"))
(should
(equal (chronometrist-format-duration 10) " 10"))
(should
(equal (chronometrist-format-duration 70) " 1:10"))
(should
(equal (chronometrist-format-duration (+ (* 10 60) 10))
" 10:10"))
(should
(equal (chronometrist-format-duration (+ (* 1 60 60) (* 10 60) 10))
" 1:10:10"))
(should
(equal (chronometrist-format-duration (+ (* 10 60 60) (* 10 60) 10))
"10:10:10")))
(ert-deftest chronometrist-previous-week-start ()
(let ((chronometrist-report-week-start-day "Sunday")
(ts (chronometrist-iso-date-to-ts "2018-09-02")))
(should (ts= (chronometrist-previous-week-start
(chronometrist-iso-date-to-ts "2018-09-01"))
(chronometrist-iso-date-to-ts "2018-08-26")))
(should (ts= ts (chronometrist-previous-week-start
(chronometrist-iso-date-to-ts "2018-09-02"))))
(should (ts= ts (chronometrist-previous-week-start
(chronometrist-iso-date-to-ts "2018-09-03"))))
(should (ts= ts (chronometrist-previous-week-start
(chronometrist-iso-date-to-ts "2018-09-04"))))
(should (ts= ts (chronometrist-previous-week-start
(chronometrist-iso-date-to-ts "2018-09-05"))))
(should (ts= ts (chronometrist-previous-week-start
(chronometrist-iso-date-to-ts "2018-09-06"))))
(should (ts= ts (chronometrist-previous-week-start
(chronometrist-iso-date-to-ts "2018-09-07"))))
(should (ts= ts (chronometrist-previous-week-start
(chronometrist-iso-date-to-ts "2018-09-08"))))))
(ert-deftest chronometrist-plist-remove ()
(should
(equal (chronometrist-plist-remove '(:a 1 :b 2 :c 3 :d 4) :a)
'(:b 2 :c 3 :d 4)))
(should
(equal (chronometrist-plist-remove '(:a 1 :b 2 :c 3 :d 4) :b)
'(:a 1 :c 3 :d 4)))
(should
(equal (chronometrist-plist-remove '(:a 1 :b 2 :c 3 :d 4) :c)
'(:a 1 :b 2 :d 4)))
(should
(equal (chronometrist-plist-remove '(:a 1 :b 2 :c 3 :d 4) :d)
'(:a 1 :b 2 :c 3)))
(should
(equal (chronometrist-plist-remove '(:a 1 :b 2 :c 3 :d 4) :a :b)
'(:c 3 :d 4)))
(should
(equal (chronometrist-plist-remove '(:a 1 :b 2 :c 3 :d 4) :a :d)
'(:b 2 :c 3)))
(should
(equal (chronometrist-plist-remove '(:a 1 :b 2 :c 3 :d 4) :c :d)
'(:a 1 :b 2)))
(should (equal
(chronometrist-plist-remove '(:a 1 :b 2 :c 3 :d 4) :a :b :c :d)
nil))
(should
(equal (chronometrist-plist-remove '(:a 1 :b 2 :c 3 :d 4) :d :a)
'(:b 2 :c 3))))
(ert-deftest 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)))))
(ert-deftest plist-pp-buffer ()
(should
(equal
(chronometrist-plist-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-plist-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-plist-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-plist-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\")))"))))
(defvar chronometrist-test-file
(make-temp-file
"chronometrist-test-" nil ".sexp"
(with-output-to-string
(mapc
(lambda (plist)
;; to use this, we'd have to move `chronometrist-plist-pp' before this
;; definition, and I'm perfectly content with where it is
;; right now
(chronometrist-plist-pp plist) (princ "\n\n")
;; (print plist) (princ "\n")
)
'((: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"))))))
(defmacro chronometrist-tests--change-type-and-update (state)
`(prog1 (chronometrist-file-change-type ,state)
(setq ,state
(list :last (chronometrist-file-hash :before-last nil)
:rest (chronometrist-file-hash nil :before-last t)))))
(ert-deftest task-list ()
(let ((task-list (chronometrist-task-list)))
(should (listp task-list))
(should (seq-every-p #'stringp task-list))))
(ert-deftest file-hash ()
(-let* ((chronometrist-file chronometrist-test-file)
((last-start last-end)
(chronometrist-file-hash :before-last nil))
((rest-start rest-end rest-hash)
(chronometrist-file-hash nil :before-last t)))
(message "chronometrist - file-hash test - file path is %s"
chronometrist-test-file)
(should (= 1 rest-start))
(should (= 1254 rest-end))
(should (= 1256 last-start))
(should (= 1426 last-end))))
(ert-deftest chronometrist-file-change-type ()
(let* ((chronometrist-file chronometrist-test-file)
(test-contents (with-current-buffer (find-file-noselect chronometrist-file)
(buffer-substring (point-min) (point-max))))
(chronometrist--file-state-old chronometrist--file-state)
(chronometrist--file-state (list :last (chronometrist-file-hash :before-last nil)
:rest (chronometrist-file-hash nil :before-last t)))
(chronometrist-events-old chronometrist-events))
(chronometrist-events-populate)
(unwind-protect
(progn
(should
(eq nil (chronometrist-file-change-type chronometrist--file-state)))
(should
(eq :append
(progn
(chronometrist-sexp-new
'(:name "Append Test"
:start "2021-02-01T13:06:46+0530"
:stop "2021-02-01T13:06:49+0530"))
(chronometrist-tests--change-type-and-update chronometrist--file-state))))
(should
(eq :modify
(progn
(chronometrist-sexp-replace-last
'(:name "Modify Test"
:tags (some tags)
:start "2021-02-01T13:06:46+0530"
:stop "2021-02-01T13:06:49+0530"))
(chronometrist-tests--change-type-and-update chronometrist--file-state))))
(should
(eq :remove
(progn
(chronometrist-sexp-in-file chronometrist-file
(goto-char (point-max))
(backward-list 1)
(chronometrist-sexp-delete-list 1)
(save-buffer))
(chronometrist-tests--change-type-and-update chronometrist--file-state))))
(should
(eq t
(progn
(chronometrist-sexp-in-file chronometrist-file
(goto-char (point-min))
(chronometrist-plist-pp '(:name "Other Change Test"
:start "2021-02-02T17:39:40+0530"
:stop "2021-02-02T17:39:44+0530")
(current-buffer))
(save-buffer))
(chronometrist-tests--change-type-and-update chronometrist--file-state)))))
;; restore test file contents
(with-current-buffer (find-file-noselect chronometrist-file)
(delete-region (point-min) (point-max))
(insert test-contents)
(save-buffer))
(setq chronometrist--file-state chronometrist--file-state-old
chronometrist-events chronometrist-events-old))))
(ert-deftest chronometrist-apply-time ()
(should
(equal (ts-format "%FT%T%z" (chronometrist-apply-time "01:02:03" "2021-02-17T01:20:18+0530"))
"2021-02-17T01:02:03+0530")))
(ert-deftest chronometrist-events-maybe-split ()
(should
(null (chronometrist-events-maybe-split
'(:name "Task"
:start "2021-02-17T01:33:12+0530"
:stop "2021-02-17T01:56:08+0530"))))
(should
(equal (chronometrist-events-maybe-split
'(:name "Guitar"
:tags (classical warm-up)
:start "2021-02-12T23:45:21+0530"
:stop "2021-02-13T00:03:46+0530"))
'((:name "Guitar"
:tags (classical warm-up)
:start "2021-02-12T23:45:21+0530"
:stop "2021-02-13T00:00:00+0530")
(:name "Guitar"
:tags (classical warm-up)
:start "2021-02-13T00:00:00+0530"
:stop "2021-02-13T00:03:46+0530")))))
(ert-deftest chronometrist-task-time-one-day ()
(let ((chronometrist-file-old chronometrist-file)
(chronometrist-file chronometrist-test-file)
(chronometrist--file-state-old chronometrist--file-state)
(chronometrist-events-old chronometrist-events)
(ts-1 (chronometrist-iso-date-to-ts "2018-01-01"))
(ts-2 (chronometrist-iso-date-to-ts "2018-01-02"))
(ts-3 (chronometrist-iso-date-to-ts "2018-01-03"))
(1-hour 3600))
(chronometrist-events-populate)
(unwind-protect
(progn
(should
(equal (chronometrist-task-time-one-day "Programming" ts-1)
1-hour))
(should
(equal (chronometrist-task-time-one-day "Swimming" ts-1)
1-hour))
(should
(equal (chronometrist-task-time-one-day "Cooking" ts-1)
1-hour))
(should
(equal (chronometrist-task-time-one-day "Guitar" ts-1)
1-hour))
(should
(equal (chronometrist-task-time-one-day "Cycling" ts-1)
1-hour))
(should
(equal (chronometrist-task-time-one-day "Programming" ts-2)
1-hour))
(should
(equal (chronometrist-task-time-one-day "Programming" ts-3)
1-hour)))
(setq chronometrist-file chronometrist-file-old
chronometrist--file-state chronometrist--file-state-old)
;; Not sure why simply assigning `chronometrist-events-old' did not restore
;; the value - it seems to work in `chronometrist-file-change-type'
(chronometrist-events-populate))))
(ert-deftest chronometrist-midnight-spanning-p ()
(should
(null
(chronometrist-midnight-spanning-p "2021-02-17T01:33:12+0530"
"2021-02-17T01:56:08+0530"
"00:00:00")))
(should
(equal
(chronometrist-midnight-spanning-p "2021-02-19T23:45:36+0530"
"2021-02-20T00:18:40+0530"
"00:00:00")
'((:start "2021-02-19T23:45:36+0530"
:stop "2021-02-20T00:00:00+0530")
(:start "2021-02-20T00:00:00+0530"
:stop "2021-02-20T00:18:40+0530"))))
(should
(equal
(chronometrist-midnight-spanning-p "2021-02-19T23:45:36+0530"
"2021-02-20T03:18:40+0530"
"01:20:30")
'((:start "2021-02-19T23:45:36+0530"
:stop "2021-02-20T01:20:30+0530")
(:start "2021-02-20T01:20:30+0530"
:stop "2021-02-20T03:18:40+0530")))))
(ert-deftest chronometrist-details-row-helper ()
(let ((tags '(a b c))
(plist '(:a 1 :b 2 :c 3)))
(let ((chronometrist-details-display-tags nil)
(chronometrist-details-display-key-values nil))
(should (equal (chronometrist-details-rows-helper tags) ""))
(should (equal (chronometrist-details-rows-helper plist) "")))
(let ((chronometrist-details-display-tags "%s")
(chronometrist-details-display-key-values "%s"))
(should (equal (chronometrist-details-rows-helper nil) ""))
(should (equal (chronometrist-details-rows-helper nil) ""))
(should (equal (chronometrist-details-rows-helper tags)
"a b c"))
(should (equal (chronometrist-details-rows-helper plist)
"1 2 3")))))