aboutsummaryrefslogtreecommitdiff
path: root/tests/test
diff options
context:
space:
mode:
authorHugo Hörnquist <hugo@lysator.liu.se>2022-03-07 15:31:00 +0100
committerHugo Hörnquist <hugo@lysator.liu.se>2022-03-07 20:29:14 +0100
commitf7716ac1a87649cad96242f2d5bf0a987d7f430c (patch)
treeb4b84951ef468fd644c42e9e0fee535d879f0387 /tests/test
parentCleanup in (hnh util path). (diff)
downloadcalp-f7716ac1a87649cad96242f2d5bf0a987d7f430c.tar.gz
calp-f7716ac1a87649cad96242f2d5bf0a987d7f430c.tar.xz
Add new tests.
Diffstat (limited to 'tests/test')
-rw-r--r--tests/test/annoying-events.scm75
-rw-r--r--tests/test/base64.scm24
-rw-r--r--tests/test/cpp.scm39
-rw-r--r--tests/test/datetime-compare.scm145
-rw-r--r--tests/test/datetime-util.scm182
-rw-r--r--tests/test/datetime.scm395
-rw-r--r--tests/test/let-env.scm42
-rw-r--r--tests/test/let.scm45
-rw-r--r--tests/test/param.scm57
-rw-r--r--tests/test/recurrence-advanced.scm1347
-rw-r--r--tests/test/recurrence-simple.scm296
-rw-r--r--tests/test/rrule-serialization.scm75
-rw-r--r--tests/test/server.scm24
-rw-r--r--tests/test/srfi-41-util.scm44
-rw-r--r--tests/test/termios.scm48
-rw-r--r--tests/test/tz.scm87
-rw-r--r--tests/test/util.scm145
-rw-r--r--tests/test/vcomponent-control.scm36
-rw-r--r--tests/test/vcomponent-datetime.scm49
-rw-r--r--tests/test/vcomponent-formats-common-types.scm137
-rw-r--r--tests/test/vcomponent.scm23
-rw-r--r--tests/test/web-server.scm116
-rw-r--r--tests/test/xcal.scm58
-rw-r--r--tests/test/xml-namespace.scm36
24 files changed, 3525 insertions, 0 deletions
diff --git a/tests/test/annoying-events.scm b/tests/test/annoying-events.scm
new file mode 100644
index 00000000..4570a5a6
--- /dev/null
+++ b/tests/test/annoying-events.scm
@@ -0,0 +1,75 @@
+(define-module (test annoying-events)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((srfi srfi-41 util)
+ :select (filter-sorted-stream))
+ :use-module ((srfi srfi-41)
+ :select (stream
+ stream->list
+ stream-filter
+ stream-take-while))
+ :use-module ((vcomponent base)
+ :select (extract prop make-vcomponent))
+ :use-module ((vcomponent datetime) :select (event-overlaps?))
+ :use-module ((datetime) :select (date date+ date<))
+ :use-module ((hnh util) :select (set!)))
+
+(define* (event key: summary dtstart dtend)
+ (define ev (make-vcomponent 'VEVENT))
+ (set! (prop ev 'SUMMARY)
+ summary
+ (prop ev 'DTSTART)
+ dtstart
+ (prop ev 'DTEND)
+ dtend)
+ ev)
+
+(define start
+ #2021-11-01)
+
+(define end (date+ start (date day: 8)))
+
+(define ev-set
+ (stream
+ (event ; should be part of the result
+ summary: "A"
+ dtstart: #2021-10-01
+ dtend: #2021-12-01)
+ (event ; should NOT be part of the result
+ summary: "B"
+ dtstart: #2021-10-10
+ dtend: #2021-10-11)
+ (event ; should also be part of the result
+ summary: "C"
+ dtstart: #2021-11-02
+ dtend: #2021-11-03)))
+
+;; (if (and (date< (prop ev 'DTSTART) start-date)
+;; (date<= (prop ev 'DTEND) end-date))
+;; ;; event will be picked, but next event might have
+;; (and (date< start-date (prop ev 'DTSTART))
+;; (date< end-date (prop ev 'DTEND)))
+;; ;; meaning that it wont be added, stopping filter-sorted-stream
+;; )
+
+;; The naïve way to get all events in an interval. Misses C due to B being "in the way"
+
+(test-equal "incorrect handling of non-contigious"
+ '("A" #; "C")
+ (map (extract 'SUMMARY)
+ (stream->list
+ (filter-sorted-stream
+ (lambda (ev) (event-overlaps? ev start (date+ start (date day: 8))))
+ ev-set))))
+
+(test-equal "correct handling of non-contigious"
+ '("A" "C")
+ (map (extract 'SUMMARY)
+ (stream->list
+ (stream-filter
+ (lambda (ev) (event-overlaps? ev start end))
+ (stream-take-while
+ (lambda (ev) (date< (prop ev 'DTSTART) end))
+ ev-set)))))
+
+
diff --git a/tests/test/base64.scm b/tests/test/base64.scm
new file mode 100644
index 00000000..3463432e
--- /dev/null
+++ b/tests/test/base64.scm
@@ -0,0 +1,24 @@
+;;; Commentary:
+;; Test that Base64 encoding and decoding works
+;; Examples from RFC4648
+;;; Code:
+
+(define-module (test base64)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((base64) :select (base64encode base64decode)))
+
+(test-equal "" (base64encode ""))
+(test-equal "Zg==" (base64encode "f"))
+(test-equal "Zm8=" (base64encode "fo"))
+(test-equal "Zm9v" (base64encode "foo"))
+(test-equal "Zm9vYg==" (base64encode "foob"))
+(test-equal "Zm9vYmE=" (base64encode "fooba"))
+(test-equal "Zm9vYmFy" (base64encode "foobar"))
+(test-equal "" (base64decode ""))
+(test-equal "f" (base64decode "Zg=="))
+(test-equal "fo" (base64decode "Zm8="))
+(test-equal "foo" (base64decode "Zm9v"))
+(test-equal "foob" (base64decode "Zm9vYg=="))
+(test-equal "fooba" (base64decode "Zm9vYmE="))
+(test-equal "foobar" (base64decode "Zm9vYmFy"))
diff --git a/tests/test/cpp.scm b/tests/test/cpp.scm
new file mode 100644
index 00000000..9c720fde
--- /dev/null
+++ b/tests/test/cpp.scm
@@ -0,0 +1,39 @@
+;;; Commentary:
+;; Tests my parser for a subset of the C programming language.
+;;; Code:
+
+(define-module (test cpp)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((c lex) :select (lex))
+ :use-module ((c parse) :select (parse-lexeme-tree)))
+
+(define run (compose parse-lexeme-tree lex))
+
+(test-equal
+ '(+ (post-increment (dereference C)) 3)
+ (run "(*C)++ + 3"))
+
+(test-equal
+ '(+ (post-increment (dereference C)) 3)
+ (run "*C++ + 3"))
+
+(test-equal
+ '(post-increment (dereference C))
+ (run "*C++"))
+
+(test-equal
+ '(+ (post-increment C) (post-increment C))
+ (run "C++ + C++"))
+
+(test-equal
+ '(+ (pre-increment C) (pre-increment C))
+ (run "++C + ++C"))
+
+(test-equal '(+ 2 (* 2 2)) (run "2 + 2 * 2"))
+
+(test-equal '(+ (* 2 2) 2) (run "2 * 2 + 2"))
+
+(test-equal '(+ 2 2 2) (run "2+2+2"))
+
+
diff --git a/tests/test/datetime-compare.scm b/tests/test/datetime-compare.scm
new file mode 100644
index 00000000..0d07c52f
--- /dev/null
+++ b/tests/test/datetime-compare.scm
@@ -0,0 +1,145 @@
+;;; Commentary:
+;; Tests that all ordering predicates for dates,
+;; times, and datetimes hold.
+;;; Code:
+
+(define-module (test datetime-compare)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((datetime)
+ :select (date datetime
+ time
+ date<
+ date<=
+ date>
+ date>=
+ date/-time<
+ time<)))
+
+(test-assert "date< empty" (date<))
+
+(test-assert
+ "date< single"
+ (date< #2020-01-10))
+
+(test-assert
+ "date< double"
+ (date< #2020-01-10
+ #2020-01-11))
+
+(test-assert
+ "date< tripple"
+ (date< #2020-01-10
+ #2020-01-11
+ #2020-01-12))
+
+(test-assert
+ "date< tripple negate"
+ (not (date< #2020-01-10
+ #2020-01-12
+ #2020-01-11)))
+
+(test-assert "date<= empty" (date<=))
+
+(test-assert
+ "date<= single"
+ (date<= #2020-01-10))
+
+(test-assert
+ "date<= double"
+ (date<=
+ #2020-01-10
+ #2020-01-11))
+
+(test-assert
+ "date<="
+ (not (date<=
+ #2020-01-01
+ #2018-05-15
+ #2020-01-31)))
+
+(test-assert
+ "date<= equal"
+ (date<=
+ #2018-05-15
+ #2018-05-15))
+
+(test-assert
+ "date<"
+ (not (date< #2020-01-01
+ #2018-05-15
+ #2020-01-31)))
+
+(test-assert
+ "date>"
+ (not (date> #2020-01-31
+ #2018-05-15
+ #2020-01-01)))
+
+(test-assert
+ "date>="
+ (not (date>=
+ #2020-01-31
+ #2018-05-15
+ #2020-01-01)))
+
+(test-assert
+ "time< simple"
+ (time< #05:00:00
+ #10:00:00))
+
+(test-assert
+ "time<"
+ (time< (time)
+ #10:00:00))
+
+(test-assert
+ "date/-time<"
+ (date/-time<
+ #2020-01-01
+ #2020-01-02))
+
+(test-assert
+ "not date/-time<"
+ (not (date/-time<
+ #2020-01-01
+ #2020-01-01)))
+
+(test-assert
+ "date/-time< only other dt"
+ (date/-time<
+ #2020-01-01
+ #2020-01-02T10:00:00))
+
+(test-assert
+ "date/-time< other dt, same date"
+ (date/-time<
+ #2020-01-01
+ #2020-01-01T10:00:00))
+
+;; In UTC+2 (CEST) the below datetime overflows into midnight the following
+;; day. Earlier versions of this program only looked at the time component
+(test-assert
+ "date/-time< TZ overflow"
+ (date/-time<
+ #2020-04-05
+ (datetime
+ date:
+ #2020-04-05
+ time:
+ #22:00:00
+ tz:
+ "UTC")))
+
+(test-assert
+ "date/-time< time-only"
+ (date/-time<
+ #00:00:00
+ #10:00:00))
+
+(test-assert
+ (not (date/-time<
+ #2018-11-30T08:10:00
+ #2014-04-13T16:00:00)))
+
+
diff --git a/tests/test/datetime-util.scm b/tests/test/datetime-util.scm
new file mode 100644
index 00000000..ca8a9241
--- /dev/null
+++ b/tests/test/datetime-util.scm
@@ -0,0 +1,182 @@
+;;; Commentary:
+;; Tests timespan overlaps and month-streams.
+;; Separate from tests/datetime.scm since
+;; (datetime util) originally was its own module.
+;;; Code:
+
+(define-module (test datetime-util)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((datetime)
+ :select (date time
+ datetime
+ month-stream
+ in-date-range?
+ timespan-overlaps?))
+ :use-module ((srfi srfi-41)
+ :select (stream->list stream-take)))
+
+(test-assert
+ "jan->dec"
+ (stream->list
+ (stream-take
+ 11
+ (month-stream
+ #2020-01-01))))
+
+(test-assert
+ "dec->jan"
+ (stream->list
+ (stream-take
+ 2
+ (month-stream
+ #2020-12-01))))
+
+(test-assert
+ "dec->feb"
+ (stream->list
+ (stream-take
+ 3
+ (month-stream
+ #2020-12-01))))
+
+(test-assert
+ "20 months"
+ (stream->list
+ (stream-take
+ 20
+ (month-stream
+ #2020-01-01))))
+
+(test-equal
+ "Correct months"
+ (list #2020-02-01
+ #2020-03-01
+ #2020-04-01
+ #2020-05-01
+ #2020-06-01
+ #2020-07-01
+ #2020-08-01
+ #2020-09-01
+ #2020-10-01
+ #2020-11-01
+ #2020-12-01
+ #2021-01-01)
+ (stream->list
+ (stream-take
+ 12
+ (month-stream
+ #2020-02-01))))
+
+(test-assert
+ "in-date-range?"
+ (not ((in-date-range?
+ #2020-01-01
+ #2020-02-29)
+ #2018-02-02)))
+
+(test-assert
+ "A"
+ (timespan-overlaps?
+ #2020-01-01
+ #2020-01-10
+ #2020-01-05
+ #2020-01-15))
+
+(test-assert
+ "A, shared start"
+ (timespan-overlaps?
+ #2020-01-01
+ #2020-01-10
+ #2020-01-01
+ #2020-01-15))
+
+(test-assert
+ "A, tangential"
+ (not (timespan-overlaps?
+ #2020-01-01T00:00:00
+ #2020-01-10T00:00:00
+ #2020-01-10T00:00:00
+ #2020-01-30T00:00:00)))
+
+(test-assert
+ "s1 instant"
+ (timespan-overlaps?
+ #2020-01-15T10:00:00
+ #2020-01-15T10:00:00
+ #2020-01-10T00:00:00
+ #2020-01-30T00:00:00))
+
+(test-assert
+ "s2 instant"
+ (timespan-overlaps?
+ #2020-01-10T00:00:00
+ #2020-01-30T00:00:00
+ #2020-01-15T10:00:00
+ #2020-01-15T10:00:00))
+
+(test-assert
+ "s1 instant, shared start with s2"
+ (timespan-overlaps?
+ #2020-01-15T10:00:00
+ #2020-01-15T10:00:00
+ #2020-01-15T10:00:00
+ #2020-01-30T00:00:00))
+
+(test-assert
+ "s1 instant, shared end with s2"
+ (not (timespan-overlaps?
+ #2020-01-15T10:00:00
+ #2020-01-15T10:00:00
+ #2020-01-10T00:00:00
+ #2020-01-15T10:00:00)))
+
+(test-assert
+ "s2 instant, shared start with s1"
+ (timespan-overlaps?
+ #2020-01-15T10:00:00
+ #2020-01-30T00:00:00
+ #2020-01-15T10:00:00
+ #2020-01-15T10:00:00))
+
+(test-assert
+ "s2 instant, shared end with s1"
+ (not (timespan-overlaps?
+ #2020-01-10T00:00:00
+ #2020-01-15T10:00:00
+ #2020-01-15T10:00:00
+ #2020-01-15T10:00:00)))
+
+(test-assert
+ "both instant"
+ (not (timespan-overlaps?
+ #2020-01-15T10:00:00
+ #2020-01-15T10:00:00
+ #2020-01-15T10:00:00
+ #2020-01-15T10:00:00)))
+
+(test-assert
+ "tangential whole day"
+ (not (timespan-overlaps?
+ #2020-01-01
+ #2020-01-02
+ #2020-01-02
+ #2020-01-03)))
+
+(test-assert
+ "B"
+ (timespan-overlaps?
+ #2020-01-05
+ #2020-01-15
+ #2020-01-01
+ #2020-01-10))
+
+(test-assert
+ "E"
+ (timespan-overlaps?
+ #2020-01-01
+ #2020-01-10
+ #2020-01-01
+ #2020-01-10))
+
+
diff --git a/tests/test/datetime.scm b/tests/test/datetime.scm
new file mode 100644
index 00000000..1051e203
--- /dev/null
+++ b/tests/test/datetime.scm
@@ -0,0 +1,395 @@
+;;; Commentary:
+;; Tests date, time, and datetime creation,
+;; (output) formatting, and arithmetic.
+;;; Code:
+
+(define-module (test datetime)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((datetime)
+ :select (date+ date-
+ time+
+ time-
+ year
+ month
+ day
+ date
+ time
+ datetime
+ datetime+
+ datetime<=?
+ datetime-difference
+ datetime-
+ leap-year?
+ string->date
+ string->time
+ string->datetime
+ parse-month
+ days-in-interval))
+ :use-module ((ice-9 format) :select (format))
+ :use-module ((hnh util) :select (let*))
+ :use-module ((ice-9 i18n) :select (make-locale))
+ :use-module ((guile) :select (LC_TIME)))
+
+(test-equal
+ "empty time"
+ (time)
+ #00:00:00)
+
+(test-assert
+ "Synatx date"
+ #2020-01-01)
+
+(test-assert
+ "Test year type"
+ (integer? (year (date year: 2020))))
+
+(test-assert
+ "Test mmnth type"
+ (integer? (month (date month: 1))))
+
+(test-assert
+ "Test day type"
+ (integer? (day (date day: 1))))
+
+(test-equal
+ "Manual print (any)"
+ "2020-10-10"
+ (let ((d #2020-10-10))
+ (format #f "~a-~a-~a" (year d) (month d) (day d))))
+
+(test-equal
+ "Manual print (number)"
+ "2020-10-10"
+ (let ((d #2020-10-10))
+ (format #f "~d-~d-~d" (year d) (month d) (day d))))
+
+(test-equal
+ "Date print"
+ "#2020-01-01"
+ (format
+ #f
+ "~a"
+ #2020-01-01))
+
+(test-equal
+ "Syntax date="
+ (date year: 2020 month: 1 day: 1)
+ #2020-01-01)
+
+(test-equal
+ "Syntax time="
+ (time hour: 13 minute: 37 second: 0)
+ #13:37:00)
+
+(test-equal
+ "Syntax Datetime="
+ (datetime year: 2020 month: 1 day: 1
+ hour: 13 minute: 37 second: 0)
+ #2020-01-01T13:37:00)
+
+(test-equal
+ #2020-02-28
+ (date- #2020-03-05
+ (date day: 6)))
+
+(test-equal
+ #2020-02-29
+ (date- #2020-03-05
+ (date day: 5)))
+
+(test-equal
+ #2020-03-01
+ (date- #2020-03-05
+ (date day: 4)))
+
+(test-equal
+ "date+ day"
+ #2020-10-10
+ (date+ #2020-10-01
+ (date day: 9)))
+
+(test-equal
+ "date+ month"
+ #2020-10-10
+ (date+ #2020-01-10
+ (date month: 9)))
+
+(test-equal
+ "date+ day/month"
+ #2020-10-10
+ (date+ #2020-01-01
+ (date day: 9 month: 9)))
+
+(test-assert
+ "date+ first literal"
+ (date+ #2020-01-01
+ (date day: 0)))
+
+(test-assert
+ "date+ second literal"
+ (date+ #0001-01-01
+ #0001-00-00))
+
+(test-assert
+ "date+ both literal"
+ (date+ #2020-01-01
+ #0000-00-00))
+
+(test-equal
+ "date+ year overflow"
+ #2019-01-01
+ (date+ #2018-12-31
+ (date day: 1)))
+
+(test-equal
+ "date- year overflow"
+ #2018-12-31
+ (date- #2019-01-01
+ (date day: 1)))
+
+(test-equal
+ "date- large"
+ #0001-01-01
+ (date- #2020-01-01
+ #2019-00-00))
+
+(test-equal
+ "date- equal"
+ (date year: -1 month: 11 day: 31)
+ (date- #2020-01-01
+ #2020-01-01))
+
+(test-equal
+ #2020-01-01T10:00:00
+ (datetime
+ date:
+ #2020-01-01
+ time:
+ #10:00:00))
+
+(test-equal
+ #2020-01-01T10:00:00
+ (datetime+
+ (datetime
+ date:
+ #2020-01-01)
+ (datetime
+ time:
+ #10:00:00)))
+
+(test-equal
+ #2020-10-09T14:00:00
+ (datetime-
+ #2020-10-10T00:00:00
+ (datetime
+ time:
+ #10:00:00)))
+
+(test-equal
+ #2020-09-24T14:00:00
+ (datetime-
+ #2020-10-10T00:00:00
+ #0000-00-15T10:00:00))
+
+(test-equal
+ #2020-03-10
+ (date+ #2020-03-01
+ (date day: 4)
+ (date day: 5)))
+
+(let* ((diff overflow
+ (time- #10:20:30
+ #10:20:30)))
+ (test-equal
+ "time- self"
+ #00:00:00
+ diff)
+ (test-equal "time- self overflow" 0 overflow))
+
+(let* ((diff overflow
+ (time- #10:00:00
+ #10:00:01)))
+ (test-equal
+ "time- overflow 1s"
+ #23:59:59
+ diff)
+ (test-equal
+ "time- overflow 1s overflow"
+ 1
+ overflow))
+
+(let* ((diff overflow
+ (time- #10:00:00
+ (time hour: (+ 48 4)))))
+ (test-equal
+ "time- overflow multiple"
+ #06:00:00
+ diff)
+ (test-equal
+ "time- overflow multiple overflow"
+ 2
+ overflow))
+
+(test-equal
+ "datetime-difference self"
+ #0000-00-00T00:00:00
+ (datetime-difference
+ (datetime
+ date:
+ #2020-01-01)
+ (datetime
+ date:
+ #2020-01-01)))
+
+;; NOTE
+;; at the time of writing this returns #2020-02-00
+;; The general question is, how is the last in a month handled?
+(test-equal
+ #2020-01-31
+ (date+ #2019-12-31
+ (date month: 1)))
+
+(test-assert (leap-year? 2020))
+
+(test-equal
+ "Add to Leap day"
+ #2020-02-29
+ (date+ #2020-02-28
+ (date day: 1)))
+
+(test-equal
+ "Parse ISO"
+ #2021-12-30T13:53:33
+ (string->datetime
+ "2021-12-30T13:53:33"
+ "~Y-~m-~dT~H:~M:~S"))
+
+(test-equal
+ "Parse ical date-time"
+ #2021-12-30T13:53:33
+ (string->datetime
+ "20211230T135333"
+ "~Y~m~dT~H~M~S"))
+
+(test-equal
+ "Parse single hour (padded)"
+ (time hour: 5)
+ (string->time "05" "~H"))
+
+(test-equal
+ "Parse single hour (non-padded)"
+ (time hour: 5)
+ (string->time "5" "~H"))
+
+(test-equal
+ "Parse month (swedish)"
+ (date month: 5)
+ (string->date
+ "Maj"
+ "~b"
+ (make-locale LC_TIME "sv_SE.UTF-8")))
+
+(test-equal
+ "Parse month (english)"
+ (date month: 5)
+ (string->date
+ "May"
+ "~b"
+ (make-locale LC_TIME "en_US.UTF-8")))
+
+(test-equal
+ "AM/PM AM"
+ (time hour: 10)
+ (string->time "10 AM" "~H ~p"))
+
+(test-equal
+ "AM/PM PM"
+ (time hour: 22)
+ (string->time "10 PM" "~H ~p"))
+
+(test-equal
+ "AM/PM AM 12"
+ (time hour: 0)
+ (string->time "12 AM" "~H ~p"))
+
+(test-equal
+ "AM/PM PM 12"
+ (time hour: 12)
+ (string->time "12 PM" "~H ~p"))
+
+(test-equal
+ "AM/PM PM (prefix)"
+ (time hour: 22)
+ (string->time "PM 10" "~p ~H"))
+
+(test-equal
+ "Parse complicated 1"
+ #2021-12-30T10:56:00
+ (string->datetime
+ "Dec. 30, 2021, 10:56"
+ "~b. ~d, ~Y, ~H:~M"
+ (make-locale LC_TIME "en_US.UTF-8")))
+
+(test-equal
+ "Parse complicated 2"
+ #2021-12-30T10:56:00
+ (string->datetime
+ "Dec. 30, 2021, 10:56 a.m."
+ "~b. ~d, ~Y, ~H:~M"
+ (make-locale LC_TIME "en_US.UTF-8")))
+
+(test-equal
+ "Parse complicated 3"
+ #2021-12-30T22:56:00
+ (string->datetime
+ "Dec. 30, 2021, 10:56 p.m."
+ "~b. ~d, ~Y, ~H:~M ~p"
+ (make-locale LC_TIME "en_US.UTF-8")))
+
+(test-equal
+ "Parse date single digit day"
+ (date day: 6)
+ (string->date "6" "~d"))
+
+(test-equal
+ "Parse date single digit day, trailing comma"
+ (date day: 6)
+ (string->date "6," "~d,"))
+
+(test-equal
+ "Parse date single digit day, trailing comma + space"
+ (date day: 6)
+ (string->date "6, " "~d, "))
+
+(define en_US
+ (make-locale LC_TIME "en_US.UTF-8"))
+
+(define sv_SE
+ (make-locale LC_TIME "sv_SE.UTF-8"))
+
+(test-equal 1 (parse-month "jan" en_US))
+
+(test-equal 1 (parse-month "jan" sv_SE))
+
+(test-equal 12 (parse-month "dec" en_US))
+
+(test-equal -1 (parse-month "inv" en_US))
+
+(test-equal 5 (parse-month "mAJ" sv_SE))
+
+(test-equal
+ "Days in regular year"
+ 365
+ (days-in-interval
+ #2021-01-01
+ #2021-12-31))
+
+(test-equal
+ "Days in leap year"
+ 366
+ (days-in-interval
+ #2020-01-01
+ #2020-12-31))
+
+
diff --git a/tests/test/let-env.scm b/tests/test/let-env.scm
new file mode 100644
index 00000000..1d6d7507
--- /dev/null
+++ b/tests/test/let-env.scm
@@ -0,0 +1,42 @@
+(define-module (test let-env)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((guile) :select (setenv getenv))
+ :use-module ((hnh util) :select (let-env)))
+
+(setenv "CALP_TEST_ENV" "1")
+
+(test-equal
+ "Ensure we have set value beforehand"
+ "1"
+ (getenv "CALP_TEST_ENV"))
+
+(let-env
+ ((CALP_TEST_ENV "2"))
+ (test-equal
+ "Test our local override"
+ "2"
+ (getenv "CALP_TEST_ENV")))
+
+(test-equal
+ "Test that we have returned"
+ "1"
+ (getenv "CALP_TEST_ENV"))
+
+(catch 'test-error
+ (lambda ()
+ (let-env
+ ((CALP_TEST_ENV "2"))
+ (test-equal
+ "Test our local override again"
+ "2"
+ (getenv "CALP_TEST_ENV"))
+ (throw 'test-error)))
+ list)
+
+(test-equal
+ "Test restoration after non-local exit"
+ "1"
+ (getenv "CALP_TEST_ENV"))
+
+
diff --git a/tests/test/let.scm b/tests/test/let.scm
new file mode 100644
index 00000000..5312409e
--- /dev/null
+++ b/tests/test/let.scm
@@ -0,0 +1,45 @@
+;;; Commentary:
+;; Tests my custom let*.
+;;; Code:
+
+(define-module (test let)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((hnh util) :select (let*)))
+
+(test-assert (let* ((a #t)) a))
+
+(test-assert (let* (((a . b) (cons #t #f))) a))
+
+(test-assert (let* (((a . b) (cons* #f #t))) b))
+
+(test-assert
+ (let* ((a b c (values #f #t #f))) b))
+
+(test-assert
+ (let* (((a b c) (list #f #t #f))) b))
+
+(test-assert (let* (((a) '(#t))) a))
+
+(test-equal '(2) (let* (((a . b) '(1 2))) b))
+
+(test-equal
+ '(3 4)
+ (let* (((a b . c) '(1 2 3 4))) c))
+
+(test-equal 10 (let* (x) (set! x 10) x))
+
+(test-equal
+ 30
+ (let* (x y) (set! x 10) (set! y 20) (+ x y)))
+
+(test-assert (let* (x) (not x)))
+
+(test-equal
+ 6
+ (let* ((x 1) y z)
+ (set! y 2)
+ (set! z 3)
+ (+ x y z)))
+
+
diff --git a/tests/test/param.scm b/tests/test/param.scm
new file mode 100644
index 00000000..8b8a010d
--- /dev/null
+++ b/tests/test/param.scm
@@ -0,0 +1,57 @@
+;;; Commentary:
+;; Checks that parameters (1) are correctly parsed and stored.
+;; (1): 'A', and 'B' in the line "KEY;A=1;B=2:Some text"
+;;; Code:
+
+(define-module (test param)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((vcomponent base)
+ :select (param prop* parameters prop))
+ :use-module ((vcomponent formats ical parse)
+ :select (parse-calendar))
+ :use-module ((vcomponent) :select (make-vcomponent))
+ :use-module ((hnh util) :select (sort* set!))
+ :use-module ((ice-9 ports) :select (call-with-input-string))
+ :use-module ((vcomponent formats xcal output)
+ :select (vcomponent->sxcal))
+ )
+
+(define v
+ (call-with-input-string
+ "BEGIN:DUMMY
+X-KEY;A=1;B=2:Some text
+END:DUMMY"
+ parse-calendar))
+
+(test-equal '("1") (param (prop* v 'X-KEY) 'A))
+
+(test-equal '("2") (param (prop* v 'X-KEY) 'B))
+
+(test-equal #f (param (prop* v 'X-KEY) 'C))
+
+(test-equal
+ '(A B)
+ (sort* (map car (parameters (prop* v 'X-KEY)))
+ string<?
+ symbol->string))
+
+
+;; TODO possibly move this.
+;; Checks that a warning is properly raised for
+;; unkonwn keys (without an X-prefix)
+(test-error
+ 'warning
+ (call-with-input-string
+ "BEGIN:DUMMY
+KEY:Some Text
+END:DUMMY"))
+
+;; Similar thing happens for sxcal, but during serialization instead
+(let ((component (make-vcomponent 'DUMMY)))
+ (set! (prop component 'KEY) "Anything")
+ (test-error
+ 'warning
+ (vcomponent->sxcal component)))
+
+
diff --git a/tests/test/recurrence-advanced.scm b/tests/test/recurrence-advanced.scm
new file mode 100644
index 00000000..9ea1e075
--- /dev/null
+++ b/tests/test/recurrence-advanced.scm
@@ -0,0 +1,1347 @@
+;;; Commentary:
+;; Tests of recurrence rule generation with focus on correct instances
+;; being generated. For tests of basic recurrence functionallity, see
+;; recurrence-simple.scm.
+;;
+;; This file also tests format-recurrence-rule, which checks that human
+;; readable representations of the RRULES work.
+;;
+;; Also contains the tests for EXDATE.
+;;
+;; Most examples copied from RFC5545, some home written.
+;;; Code:
+
+(define-module (test recurrence-advanced)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((vcomponent recurrence parse)
+ :select (parse-recurrence-rule))
+ :use-module ((vcomponent recurrence generate)
+ :select (generate-recurrence-set))
+ :use-module ((vcomponent recurrence display)
+ :select (format-recurrence-rule))
+ :use-module ((vcomponent recurrence internal)
+ :select (count until))
+ :use-module ((vcomponent base)
+ :select (make-vcomponent prop prop* extract))
+ :use-module ((datetime)
+ :select (parse-ics-datetime
+ datetime
+ time
+ date
+ datetime->string))
+ :use-module ((hnh util) :select (-> set!))
+ :use-module ((srfi srfi-41) :select (stream->list))
+ :use-module ((srfi srfi-88) :select (keyword->string)))
+
+(test-expect-fail "RSET: The third instance into the month of one of Tuesday, Wednesday, or Thursday, for the next 3 months")
+
+(test-expect-fail "STR: The third instance into the month of one of Tuesday, Wednesday, or Thursday, for the next 3 months")
+
+(test-expect-fail "RSET: The second-to-last weekday of the month")
+
+(test-expect-fail "STR: The second-to-last weekday of the month")
+
+;; TODO this test is really slow, figure out why (takes approx. 25s to run)
+(test-skip "RSET: Every day in January, for 3 years (alt 2)")
+
+(define (run-test comp)
+ (test-equal
+ (string-append "RSET: " (prop comp 'SUMMARY))
+ (prop comp 'X-SET)
+ (let ((r (generate-recurrence-set comp)))
+ (map (extract 'DTSTART)
+ (if (or (until (prop comp 'RRULE))
+ (count (prop comp 'RRULE)))
+ (stream->list r)
+ (stream->list 20 r)))))
+ (test-equal
+ (string-append "STR: " (prop comp 'SUMMARY))
+ (prop comp 'X-SUMMARY)
+ (format-recurrence-rule (prop comp 'RRULE))))
+
+(define (vevent . rest)
+ (define v (make-vcomponent 'VEVENT))
+ (let loop ((rem rest))
+ (unless
+ (null? rem)
+ (let ((symb (-> (car rem)
+ keyword->string
+ string-upcase
+ string->symbol)))
+ (set! (prop v symb)
+ (case symb
+ ((DTSTART EXDATE)
+ (parse-ics-datetime (cadr rem)))
+ ((RRULE) (parse-recurrence-rule (cadr rem)))
+ (else (cadr rem))))
+ (when (eq? symb 'EXDATE)
+ (set! (prop* v symb) = list)))
+ (loop (cddr rem))))
+ v)
+
+(map run-test
+ (list (vevent
+ summary:
+ "Daily for 10 occurrences"
+ dtstart:
+ "19970902T090000"
+ rrule:
+ "FREQ=DAILY;COUNT=10"
+ x-summary:
+ "dagligen, totalt 10 gånger"
+ x-set:
+ (list #1997-09-02T09:00:00
+ #1997-09-03T09:00:00
+ #1997-09-04T09:00:00
+ #1997-09-05T09:00:00
+ #1997-09-06T09:00:00
+ #1997-09-07T09:00:00
+ #1997-09-08T09:00:00
+ #1997-09-09T09:00:00
+ #1997-09-10T09:00:00
+ #1997-09-11T09:00:00))
+ (vevent
+ summary:
+ "Daily until December 24, 1997"
+ dtstart:
+ "19970902T090000"
+ rrule:
+ "FREQ=DAILY;UNTIL=19971224T000000Z"
+ x-summary:
+ "dagligen, till och med den 24 december, 1997 kl. 0:00"
+ x-set:
+ (list #1997-09-02T09:00:00
+ #1997-09-03T09:00:00
+ #1997-09-04T09:00:00
+ #1997-09-05T09:00:00
+ #1997-09-06T09:00:00
+ #1997-09-07T09:00:00
+ #1997-09-08T09:00:00
+ #1997-09-09T09:00:00
+ #1997-09-10T09:00:00
+ #1997-09-11T09:00:00
+ #1997-09-12T09:00:00
+ #1997-09-13T09:00:00
+ #1997-09-14T09:00:00
+ #1997-09-15T09:00:00
+ #1997-09-16T09:00:00
+ #1997-09-17T09:00:00
+ #1997-09-18T09:00:00
+ #1997-09-19T09:00:00
+ #1997-09-20T09:00:00
+ #1997-09-21T09:00:00
+ #1997-09-22T09:00:00
+ #1997-09-23T09:00:00
+ #1997-09-24T09:00:00
+ #1997-09-25T09:00:00
+ #1997-09-26T09:00:00
+ #1997-09-27T09:00:00
+ #1997-09-28T09:00:00
+ #1997-09-29T09:00:00
+ #1997-09-30T09:00:00
+ #1997-10-01T09:00:00
+ #1997-10-02T09:00:00
+ #1997-10-03T09:00:00
+ #1997-10-04T09:00:00
+ #1997-10-05T09:00:00
+ #1997-10-06T09:00:00
+ #1997-10-07T09:00:00
+ #1997-10-08T09:00:00
+ #1997-10-09T09:00:00
+ #1997-10-10T09:00:00
+ #1997-10-11T09:00:00
+ #1997-10-12T09:00:00
+ #1997-10-13T09:00:00
+ #1997-10-14T09:00:00
+ #1997-10-15T09:00:00
+ #1997-10-16T09:00:00
+ #1997-10-17T09:00:00
+ #1997-10-18T09:00:00
+ #1997-10-19T09:00:00
+ #1997-10-20T09:00:00
+ #1997-10-21T09:00:00
+ #1997-10-22T09:00:00
+ #1997-10-23T09:00:00
+ #1997-10-24T09:00:00
+ #1997-10-25T09:00:00
+ #1997-10-26T09:00:00
+ #1997-10-27T09:00:00
+ #1997-10-28T09:00:00
+ #1997-10-29T09:00:00
+ #1997-10-30T09:00:00
+ #1997-10-31T09:00:00
+ #1997-11-01T09:00:00
+ #1997-11-02T09:00:00
+ #1997-11-03T09:00:00
+ #1997-11-04T09:00:00
+ #1997-11-05T09:00:00
+ #1997-11-06T09:00:00
+ #1997-11-07T09:00:00
+ #1997-11-08T09:00:00
+ #1997-11-09T09:00:00
+ #1997-11-10T09:00:00
+ #1997-11-11T09:00:00
+ #1997-11-12T09:00:00
+ #1997-11-13T09:00:00
+ #1997-11-14T09:00:00
+ #1997-11-15T09:00:00
+ #1997-11-16T09:00:00
+ #1997-11-17T09:00:00
+ #1997-11-18T09:00:00
+ #1997-11-19T09:00:00
+ #1997-11-20T09:00:00
+ #1997-11-21T09:00:00
+ #1997-11-22T09:00:00
+ #1997-11-23T09:00:00
+ #1997-11-24T09:00:00
+ #1997-11-25T09:00:00
+ #1997-11-26T09:00:00
+ #1997-11-27T09:00:00
+ #1997-11-28T09:00:00
+ #1997-11-29T09:00:00
+ #1997-11-30T09:00:00
+ #1997-12-01T09:00:00
+ #1997-12-02T09:00:00
+ #1997-12-03T09:00:00
+ #1997-12-04T09:00:00
+ #1997-12-05T09:00:00
+ #1997-12-06T09:00:00
+ #1997-12-07T09:00:00
+ #1997-12-08T09:00:00
+ #1997-12-09T09:00:00
+ #1997-12-10T09:00:00
+ #1997-12-11T09:00:00
+ #1997-12-12T09:00:00
+ #1997-12-13T09:00:00
+ #1997-12-14T09:00:00
+ #1997-12-15T09:00:00
+ #1997-12-16T09:00:00
+ #1997-12-17T09:00:00
+ #1997-12-18T09:00:00
+ #1997-12-19T09:00:00
+ #1997-12-20T09:00:00
+ #1997-12-21T09:00:00
+ #1997-12-22T09:00:00
+ #1997-12-23T09:00:00))
+ (vevent
+ summary:
+ "Every other day - forever"
+ dtstart:
+ "19970902T090000"
+ rrule:
+ "FREQ=DAILY;INTERVAL=2"
+ x-summary:
+ "varannan dag"
+ x-set:
+ (list #1997-09-02T09:00:00
+ #1997-09-04T09:00:00
+ #1997-09-06T09:00:00
+ #1997-09-08T09:00:00
+ #1997-09-10T09:00:00
+ #1997-09-12T09:00:00
+ #1997-09-14T09:00:00
+ #1997-09-16T09:00:00
+ #1997-09-18T09:00:00
+ #1997-09-20T09:00:00
+ #1997-09-22T09:00:00
+ #1997-09-24T09:00:00
+ #1997-09-26T09:00:00
+ #1997-09-28T09:00:00
+ #1997-09-30T09:00:00
+ #1997-10-02T09:00:00
+ #1997-10-04T09:00:00
+ #1997-10-06T09:00:00
+ #1997-10-08T09:00:00
+ #1997-10-10T09:00:00))
+ (vevent
+ summary:
+ "Every 10 days, 5 occurrences"
+ dtstart:
+ "19970902T090000"
+ rrule:
+ "FREQ=DAILY;INTERVAL=10;COUNT=5"
+ x-summary:
+ "var tionde dag, totalt 5 gånger"
+ x-set:
+ (list #1997-09-02T09:00:00
+ #1997-09-12T09:00:00
+ #1997-09-22T09:00:00
+ #1997-10-02T09:00:00
+ #1997-10-12T09:00:00))
+ (vevent
+ summary:
+ "Every day in January, for 3 years (alt 1)"
+ dtstart:
+ "19980101T090000"
+ rrule:
+ "FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA"
+ x-summary:
+ "varje lördag, fredag, torsdag, onsdag, tisdag, måndag & söndag i januari, årligen, till och med den 31 januari, 2000 kl. 14:00"
+ x-set:
+ (list #1998-01-01T09:00:00
+ #1998-01-02T09:00:00
+ #1998-01-03T09:00:00
+ #1998-01-04T09:00:00
+ #1998-01-05T09:00:00
+ #1998-01-06T09:00:00
+ #1998-01-07T09:00:00
+ #1998-01-08T09:00:00
+ #1998-01-09T09:00:00
+ #1998-01-10T09:00:00
+ #1998-01-11T09:00:00
+ #1998-01-12T09:00:00
+ #1998-01-13T09:00:00
+ #1998-01-14T09:00:00
+ #1998-01-15T09:00:00
+ #1998-01-16T09:00:00
+ #1998-01-17T09:00:00
+ #1998-01-18T09:00:00
+ #1998-01-19T09:00:00
+ #1998-01-20T09:00:00
+ #1998-01-21T09:00:00
+ #1998-01-22T09:00:00
+ #1998-01-23T09:00:00
+ #1998-01-24T09:00:00
+ #1998-01-25T09:00:00
+ #1998-01-26T09:00:00
+ #1998-01-27T09:00:00
+ #1998-01-28T09:00:00
+ #1998-01-29T09:00:00
+ #1998-01-30T09:00:00
+ #1998-01-31T09:00:00
+ #1999-01-01T09:00:00
+ #1999-01-02T09:00:00
+ #1999-01-03T09:00:00
+ #1999-01-04T09:00:00
+ #1999-01-05T09:00:00
+ #1999-01-06T09:00:00
+ #1999-01-07T09:00:00
+ #1999-01-08T09:00:00
+ #1999-01-09T09:00:00
+ #1999-01-10T09:00:00
+ #1999-01-11T09:00:00
+ #1999-01-12T09:00:00
+ #1999-01-13T09:00:00
+ #1999-01-14T09:00:00
+ #1999-01-15T09:00:00
+ #1999-01-16T09:00:00
+ #1999-01-17T09:00:00
+ #1999-01-18T09:00:00
+ #1999-01-19T09:00:00
+ #1999-01-20T09:00:00
+ #1999-01-21T09:00:00
+ #1999-01-22T09:00:00
+ #1999-01-23T09:00:00
+ #1999-01-24T09:00:00
+ #1999-01-25T09:00:00
+ #1999-01-26T09:00:00
+ #1999-01-27T09:00:00
+ #1999-01-28T09:00:00
+ #1999-01-29T09:00:00
+ #1999-01-30T09:00:00
+ #1999-01-31T09:00:00
+ #2000-01-01T09:00:00
+ #2000-01-02T09:00:00
+ #2000-01-03T09:00:00
+ #2000-01-04T09:00:00
+ #2000-01-05T09:00:00
+ #2000-01-06T09:00:00
+ #2000-01-07T09:00:00
+ #2000-01-08T09:00:00
+ #2000-01-09T09:00:00
+ #2000-01-10T09:00:00
+ #2000-01-11T09:00:00
+ #2000-01-12T09:00:00
+ #2000-01-13T09:00:00
+ #2000-01-14T09:00:00
+ #2000-01-15T09:00:00
+ #2000-01-16T09:00:00
+ #2000-01-17T09:00:00
+ #2000-01-18T09:00:00
+ #2000-01-19T09:00:00
+ #2000-01-20T09:00:00
+ #2000-01-21T09:00:00
+ #2000-01-22T09:00:00
+ #2000-01-23T09:00:00
+ #2000-01-24T09:00:00
+ #2000-01-25T09:00:00
+ #2000-01-26T09:00:00
+ #2000-01-27T09:00:00
+ #2000-01-28T09:00:00
+ #2000-01-29T09:00:00
+ #2000-01-30T09:00:00
+ #2000-01-31T09:00:00))
+ (vevent
+ summary:
+ "Every day in January, for 3 years (alt 2)"
+ dtstart:
+ "19980101T090000"
+ rrule:
+ "FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1"
+ x-summary:
+ "dagligen, till och med den 31 januari, 2000 kl. 14:00"
+ x-set:
+ (list #1998-01-01T09:00:00
+ #1998-01-02T09:00:00
+ #1998-01-03T09:00:00
+ #1998-01-04T09:00:00
+ #1998-01-05T09:00:00
+ #1998-01-06T09:00:00
+ #1998-01-07T09:00:00
+ #1998-01-08T09:00:00
+ #1998-01-09T09:00:00
+ #1998-01-10T09:00:00
+ #1998-01-11T09:00:00
+ #1998-01-12T09:00:00
+ #1998-01-13T09:00:00
+ #1998-01-14T09:00:00
+ #1998-01-15T09:00:00
+ #1998-01-16T09:00:00
+ #1998-01-17T09:00:00
+ #1998-01-18T09:00:00
+ #1998-01-19T09:00:00
+ #1998-01-20T09:00:00
+ #1998-01-21T09:00:00
+ #1998-01-22T09:00:00
+ #1998-01-23T09:00:00
+ #1998-01-24T09:00:00
+ #1998-01-25T09:00:00
+ #1998-01-26T09:00:00
+ #1998-01-27T09:00:00
+ #1998-01-28T09:00:00
+ #1998-01-29T09:00:00
+ #1998-01-30T09:00:00
+ #1998-01-31T09:00:00
+ #1999-01-01T09:00:00
+ #1999-01-02T09:00:00
+ #1999-01-03T09:00:00
+ #1999-01-04T09:00:00
+ #1999-01-05T09:00:00
+ #1999-01-06T09:00:00
+ #1999-01-07T09:00:00
+ #1999-01-08T09:00:00
+ #1999-01-09T09:00:00
+ #1999-01-10T09:00:00
+ #1999-01-11T09:00:00
+ #1999-01-12T09:00:00
+ #1999-01-13T09:00:00
+ #1999-01-14T09:00:00
+ #1999-01-15T09:00:00
+ #1999-01-16T09:00:00
+ #1999-01-17T09:00:00
+ #1999-01-18T09:00:00
+ #1999-01-19T09:00:00
+ #1999-01-20T09:00:00
+ #1999-01-21T09:00:00
+ #1999-01-22T09:00:00
+ #1999-01-23T09:00:00
+ #1999-01-24T09:00:00
+ #1999-01-25T09:00:00
+ #1999-01-26T09:00:00
+ #1999-01-27T09:00:00
+ #1999-01-28T09:00:00
+ #1999-01-29T09:00:00
+ #1999-01-30T09:00:00
+ #1999-01-31T09:00:00
+ #2000-01-01T09:00:00
+ #2000-01-02T09:00:00
+ #2000-01-03T09:00:00
+ #2000-01-04T09:00:00
+ #2000-01-05T09:00:00
+ #2000-01-06T09:00:00
+ #2000-01-07T09:00:00
+ #2000-01-08T09:00:00
+ #2000-01-09T09:00:00
+ #2000-01-10T09:00:00
+ #2000-01-11T09:00:00
+ #2000-01-12T09:00:00
+ #2000-01-13T09:00:00
+ #2000-01-14T09:00:00
+ #2000-01-15T09:00:00
+ #2000-01-16T09:00:00
+ #2000-01-17T09:00:00
+ #2000-01-18T09:00:00
+ #2000-01-19T09:00:00
+ #2000-01-20T09:00:00
+ #2000-01-21T09:00:00
+ #2000-01-22T09:00:00
+ #2000-01-23T09:00:00
+ #2000-01-24T09:00:00
+ #2000-01-25T09:00:00
+ #2000-01-26T09:00:00
+ #2000-01-27T09:00:00
+ #2000-01-28T09:00:00
+ #2000-01-29T09:00:00
+ #2000-01-30T09:00:00
+ #2000-01-31T09:00:00))
+ (vevent
+ summary:
+ "Weekly for 10 occurrences"
+ dtstart:
+ "19970902T090000"
+ rrule:
+ "FREQ=WEEKLY;COUNT=10"
+ x-summary:
+ "varje vecka, totalt 10 gånger"
+ x-set:
+ (list #1997-09-02T09:00:00
+ #1997-09-09T09:00:00
+ #1997-09-16T09:00:00
+ #1997-09-23T09:00:00
+ #1997-09-30T09:00:00
+ #1997-10-07T09:00:00
+ #1997-10-14T09:00:00
+ #1997-10-21T09:00:00
+ #1997-10-28T09:00:00
+ #1997-11-04T09:00:00))
+ (vevent
+ summary:
+ "Weekly until December 24, 1997"
+ dtstart:
+ "19970902T090000"
+ rrule:
+ "FREQ=WEEKLY;UNTIL=19971224T000000Z"
+ x-summary:
+ "varje vecka, till och med den 24 december, 1997 kl. 0:00"
+ x-set:
+ (list #1997-09-02T09:00:00
+ #1997-09-09T09:00:00
+ #1997-09-16T09:00:00
+ #1997-09-23T09:00:00
+ #1997-09-30T09:00:00
+ #1997-10-07T09:00:00
+ #1997-10-14T09:00:00
+ #1997-10-21T09:00:00
+ #1997-10-28T09:00:00
+ #1997-11-04T09:00:00
+ #1997-11-11T09:00:00
+ #1997-11-18T09:00:00
+ #1997-11-25T09:00:00
+ #1997-12-02T09:00:00
+ #1997-12-09T09:00:00
+ #1997-12-16T09:00:00
+ #1997-12-23T09:00:00))
+ (vevent
+ summary:
+ "Every other week - forever"
+ dtstart:
+ "19970902T090000"
+ rrule:
+ "FREQ=WEEKLY;INTERVAL=2;WKST=SU"
+ x-summary:
+ "varannan vecka"
+ x-set:
+ (list #1997-09-02T09:00:00
+ #1997-09-16T09:00:00
+ #1997-09-30T09:00:00
+ #1997-10-14T09:00:00
+ #1997-10-28T09:00:00
+ #1997-11-11T09:00:00
+ #1997-11-25T09:00:00
+ #1997-12-09T09:00:00
+ #1997-12-23T09:00:00
+ #1998-01-06T09:00:00
+ #1998-01-20T09:00:00
+ #1998-02-03T09:00:00
+ #1998-02-17T09:00:00
+ #1998-03-03T09:00:00
+ #1998-03-17T09:00:00
+ #1998-03-31T09:00:00
+ #1998-04-14T09:00:00
+ #1998-04-28T09:00:00
+ #1998-05-12T09:00:00
+ #1998-05-26T09:00:00))
+ (vevent
+ summary:
+ "Weekly on Tuesday and Thursday for five weeks (alt 1)"
+ dtstart:
+ "19970902T090000"
+ rrule:
+ "FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH"
+ x-summary:
+ "varje tisdag & torsdag, till och med den 07 oktober, 1997 kl. 0:00"
+ x-set:
+ (list #1997-09-02T09:00:00
+ #1997-09-04T09:00:00
+ #1997-09-09T09:00:00
+ #1997-09-11T09:00:00
+ #1997-09-16T09:00:00
+ #1997-09-18T09:00:00
+ #1997-09-23T09:00:00
+ #1997-09-25T09:00:00
+ #1997-09-30T09:00:00
+ #1997-10-02T09:00:00))
+ (vevent
+ summary:
+ "Weekly on Tuesday and Thursday for five weeks (alt 2)"
+ dtstart:
+ "19970902T090000"
+ rrule:
+ "FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH"
+ x-summary:
+ "varje tisdag & torsdag, totalt 10 gånger"
+ x-set:
+ (list #1997-09-02T09:00:00
+ #1997-09-04T09:00:00
+ #1997-09-09T09:00:00
+ #1997-09-11T09:00:00
+ #1997-09-16T09:00:00
+ #1997-09-18T09:00:00
+ #1997-09-23T09:00:00
+ #1997-09-25T09:00:00
+ #1997-09-30T09:00:00
+ #1997-10-02T09:00:00))
+ (vevent
+ summary:
+ "Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997:"
+ dtstart:
+ "19970901T090000"
+ rrule:
+ "FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR"
+ x-summary:
+ "varannan måndag, onsdag & fredag, till och med den 24 december, 1997 kl. 0:00"
+ x-set:
+ (list #1997-09-01T09:00:00
+ #1997-09-03T09:00:00
+ #1997-09-05T09:00:00
+ #1997-09-15T09:00:00
+ #1997-09-17T09:00:00
+ #1997-09-19T09:00:00
+ #1997-09-29T09:00:00
+ #1997-10-01T09:00:00
+ #1997-10-03T09:00:00
+ #1997-10-13T09:00:00
+ #1997-10-15T09:00:00
+ #1997-10-17T09:00:00
+ #1997-10-27T09:00:00
+ #1997-10-29T09:00:00
+ #1997-10-31T09:00:00
+ #1997-11-10T09:00:00
+ #1997-11-12T09:00:00
+ #1997-11-14T09:00:00
+ #1997-11-24T09:00:00
+ #1997-11-26T09:00:00
+ #1997-11-28T09:00:00
+ #1997-12-08T09:00:00
+ #1997-12-10T09:00:00
+ #1997-12-12T09:00:00
+ #1997-12-22T09:00:00))
+ (vevent
+ summary:
+ "Every other week on Tuesday and Thursday, for 8 occurrences"
+ dtstart:
+ "19970902T090000"
+ rrule:
+ "FREQ=WEEKLY;INTERVAL=2;COUNT=8;WKST=SU;BYDAY=TU,TH"
+ x-summary:
+ "varannan tisdag & torsdag, totalt 8 gånger"
+ x-set:
+ (list #1997-09-02T09:00:00
+ #1997-09-04T09:00:00
+ #1997-09-16T09:00:00
+ #1997-09-18T09:00:00
+ #1997-09-30T09:00:00
+ #1997-10-02T09:00:00
+ #1997-10-14T09:00:00
+ #1997-10-16T09:00:00))
+ (vevent
+ summary:
+ "Monthly on the first Friday for 10 occurrences"
+ dtstart:
+ "19970905T090000"
+ rrule:
+ "FREQ=MONTHLY;COUNT=10;BYDAY=1FR"
+ x-summary:
+ "första fredagen varje månad, totalt 10 gånger"
+ x-set:
+ (list #1997-09-05T09:00:00
+ #1997-10-03T09:00:00
+ #1997-11-07T09:00:00
+ #1997-12-05T09:00:00
+ #1998-01-02T09:00:00
+ #1998-02-06T09:00:00
+ #1998-03-06T09:00:00
+ #1998-04-03T09:00:00
+ #1998-05-01T09:00:00
+ #1998-06-05T09:00:00))
+ (vevent
+ summary:
+ "Monthly on the first Friday until December 24, 1997"
+ dtstart:
+ "19970905T090000"
+ rrule:
+ "FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR"
+ x-summary:
+ "första fredagen varje månad, till och med den 24 december, 1997 kl. 0:00"
+ x-set:
+ (list #1997-09-05T09:00:00
+ #1997-10-03T09:00:00
+ #1997-11-07T09:00:00
+ #1997-12-05T09:00:00))
+ (vevent
+ summary:
+ "Every other month on the first and last Sunday of the month for 10 occurrences"
+ dtstart:
+ "19970907T090000"
+ rrule:
+ "FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU"
+ x-summary:
+ "första söndagen samt sista söndagen varannan månad, totalt 10 gånger"
+ x-set:
+ (list #1997-09-07T09:00:00
+ #1997-09-28T09:00:00
+ #1997-11-02T09:00:00
+ #1997-11-30T09:00:00
+ #1998-01-04T09:00:00
+ #1998-01-25T09:00:00
+ #1998-03-01T09:00:00
+ #1998-03-29T09:00:00
+ #1998-05-03T09:00:00
+ #1998-05-31T09:00:00))
+ (vevent
+ summary:
+ "Monthly on the second-to-last Monday of the month for 6 months"
+ dtstart:
+ "19970922T090000"
+ rrule:
+ "FREQ=MONTHLY;COUNT=6;BYDAY=-2MO"
+ x-summary:
+ "näst sista måndagen varje månad, totalt 6 gånger"
+ x-set:
+ (list #1997-09-22T09:00:00
+ #1997-10-20T09:00:00
+ #1997-11-17T09:00:00
+ #1997-12-22T09:00:00
+ #1998-01-19T09:00:00
+ #1998-02-16T09:00:00))
+ (vevent
+ summary:
+ "Monthly on the third-to-the-last day of the month, forever"
+ dtstart:
+ "19970928T090000"
+ rrule:
+ "FREQ=MONTHLY;BYMONTHDAY=-3"
+ x-summary:
+ "den tredje sista varje månad"
+ x-set:
+ (list #1997-09-28T09:00:00
+ #1997-10-29T09:00:00
+ #1997-11-28T09:00:00
+ #1997-12-29T09:00:00
+ #1998-01-29T09:00:00
+ #1998-02-26T09:00:00
+ #1998-03-29T09:00:00
+ #1998-04-28T09:00:00
+ #1998-05-29T09:00:00
+ #1998-06-28T09:00:00
+ #1998-07-29T09:00:00
+ #1998-08-29T09:00:00
+ #1998-09-28T09:00:00
+ #1998-10-29T09:00:00
+ #1998-11-28T09:00:00
+ #1998-12-29T09:00:00
+ #1999-01-29T09:00:00
+ #1999-02-26T09:00:00
+ #1999-03-29T09:00:00
+ #1999-04-28T09:00:00))
+ (vevent
+ summary:
+ "Monthly on the 2nd and 15th of the month for 10 occurrences"
+ dtstart:
+ "19970902T090000"
+ rrule:
+ "FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15"
+ x-summary:
+ "den andre & femtonde varje månad, totalt 10 gånger"
+ x-set:
+ (list #1997-09-02T09:00:00
+ #1997-09-15T09:00:00
+ #1997-10-02T09:00:00
+ #1997-10-15T09:00:00
+ #1997-11-02T09:00:00
+ #1997-11-15T09:00:00
+ #1997-12-02T09:00:00
+ #1997-12-15T09:00:00
+ #1998-01-02T09:00:00
+ #1998-01-15T09:00:00))
+ (vevent
+ summary:
+ "Monthly on the first and last day of the month for 10 occurrences"
+ dtstart:
+ "19970930T090000"
+ rrule:
+ "FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1"
+ x-summary:
+ "den förste & sista varje månad, totalt 10 gånger"
+ x-set:
+ (list #1997-09-30T09:00:00
+ #1997-10-01T09:00:00
+ #1997-10-31T09:00:00
+ #1997-11-01T09:00:00
+ #1997-11-30T09:00:00
+ #1997-12-01T09:00:00
+ #1997-12-31T09:00:00
+ #1998-01-01T09:00:00
+ #1998-01-31T09:00:00
+ #1998-03-01T09:00:00))
+ (vevent
+ summary:
+ "Every 18 months on the 10th thru 15th of the month for 10 occurrences"
+ dtstart:
+ "19970910T090000"
+ rrule:
+ "FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15"
+ x-summary:
+ "den tionde, elfte, tolfte, trettonde, fjortonde & femtonde var artonde månad, totalt 10 gånger"
+ x-set:
+ (list #1997-09-10T09:00:00
+ #1997-09-11T09:00:00
+ #1997-09-12T09:00:00
+ #1997-09-13T09:00:00
+ #1997-09-14T09:00:00
+ #1997-09-15T09:00:00
+ #1999-03-10T09:00:00
+ #1999-03-11T09:00:00
+ #1999-03-12T09:00:00
+ #1999-03-13T09:00:00))
+ (vevent
+ summary:
+ "Every Tuesday, every other month"
+ dtstart:
+ "19970902T090000"
+ rrule:
+ "FREQ=MONTHLY;INTERVAL=2;BYDAY=TU"
+ x-summary:
+ "varje tisdag varannan månad"
+ x-set:
+ (list #1997-09-02T09:00:00
+ #1997-09-09T09:00:00
+ #1997-09-16T09:00:00
+ #1997-09-23T09:00:00
+ #1997-09-30T09:00:00
+ #1997-11-04T09:00:00
+ #1997-11-11T09:00:00
+ #1997-11-18T09:00:00
+ #1997-11-25T09:00:00
+ #1998-01-06T09:00:00
+ #1998-01-13T09:00:00
+ #1998-01-20T09:00:00
+ #1998-01-27T09:00:00
+ #1998-03-03T09:00:00
+ #1998-03-10T09:00:00
+ #1998-03-17T09:00:00
+ #1998-03-24T09:00:00
+ #1998-03-31T09:00:00
+ #1998-05-05T09:00:00
+ #1998-05-12T09:00:00))
+ (vevent
+ summary:
+ "Yearly in June and July for 10 occurrences:\n: Since none of the BYDAY, BYMONTHDAY, or BYYEARDAY\nonents are specified, the day is gotten from \"DTSTART\""
+ dtstart:
+ "19970610T090000"
+ rrule:
+ "FREQ=YEARLY;COUNT=10;BYMONTH=6,7"
+ x-summary:
+ "juni & juli, årligen, totalt 10 gånger"
+ x-set:
+ (list #1997-06-10T09:00:00
+ #1997-07-10T09:00:00
+ #1998-06-10T09:00:00
+ #1998-07-10T09:00:00
+ #1999-06-10T09:00:00
+ #1999-07-10T09:00:00
+ #2000-06-10T09:00:00
+ #2000-07-10T09:00:00
+ #2001-06-10T09:00:00
+ #2001-07-10T09:00:00))
+ (vevent
+ summary:
+ "Every other year on January, February, and March for 10 occurrences"
+ dtstart:
+ "19970310T090000"
+ rrule:
+ "FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3"
+ x-summary:
+ "januari, februari & mars vartannat år, totalt 10 gånger"
+ x-set:
+ (list #1997-03-10T09:00:00
+ #1999-01-10T09:00:00
+ #1999-02-10T09:00:00
+ #1999-03-10T09:00:00
+ #2001-01-10T09:00:00
+ #2001-02-10T09:00:00
+ #2001-03-10T09:00:00
+ #2003-01-10T09:00:00
+ #2003-02-10T09:00:00
+ #2003-03-10T09:00:00))
+ (vevent
+ summary:
+ "Every third year on the 1st, 100th, and 200th day for 10 occurrences"
+ dtstart:
+ "19970101T090000"
+ rrule:
+ "FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200"
+ x-summary:
+ "dag 1, 100 & 200 vart tredje år, totalt 10 gånger"
+ x-set:
+ (list #1997-01-01T09:00:00
+ #1997-04-10T09:00:00
+ #1997-07-19T09:00:00
+ #2000-01-01T09:00:00
+ #2000-04-09T09:00:00
+ #2000-07-18T09:00:00
+ #2003-01-01T09:00:00
+ #2003-04-10T09:00:00
+ #2003-07-19T09:00:00
+ #2006-01-01T09:00:00))
+ (vevent
+ summary:
+ "Every 20th Monday of the year, forever"
+ dtstart:
+ "19970519T090000"
+ rrule:
+ "FREQ=YEARLY;BYDAY=20MO"
+ x-summary:
+ "tjugonde måndagen, årligen"
+ x-set:
+ (list #1997-05-19T09:00:00
+ #1998-05-18T09:00:00
+ #1999-05-17T09:00:00
+ #2000-05-15T09:00:00
+ #2001-05-14T09:00:00
+ #2002-05-20T09:00:00
+ #2003-05-19T09:00:00
+ #2004-05-17T09:00:00
+ #2005-05-16T09:00:00
+ #2006-05-15T09:00:00
+ #2007-05-14T09:00:00
+ #2008-05-19T09:00:00
+ #2009-05-18T09:00:00
+ #2010-05-17T09:00:00
+ #2011-05-16T09:00:00
+ #2012-05-14T09:00:00
+ #2013-05-20T09:00:00
+ #2014-05-19T09:00:00
+ #2015-05-18T09:00:00
+ #2016-05-16T09:00:00))
+ (vevent
+ summary:
+ "Monday of week number 20 (where the default start of the week is Monday), forever"
+ dtstart:
+ "19970512T090000"
+ rrule:
+ "FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO"
+ x-summary:
+ "varje måndag v.20, årligen"
+ x-set:
+ (list #1997-05-12T09:00:00
+ #1998-05-11T09:00:00
+ #1999-05-17T09:00:00
+ #2000-05-15T09:00:00
+ #2001-05-14T09:00:00
+ #2002-05-13T09:00:00
+ #2003-05-12T09:00:00
+ #2004-05-10T09:00:00
+ #2005-05-16T09:00:00
+ #2006-05-15T09:00:00
+ #2007-05-14T09:00:00
+ #2008-05-12T09:00:00
+ #2009-05-11T09:00:00
+ #2010-05-17T09:00:00
+ #2011-05-16T09:00:00
+ #2012-05-14T09:00:00
+ #2013-05-13T09:00:00
+ #2014-05-12T09:00:00
+ #2015-05-11T09:00:00
+ #2016-05-16T09:00:00))
+ (vevent
+ summary:
+ "Every Thursday in March, forever"
+ dtstart:
+ "19970313T090000"
+ rrule:
+ "FREQ=YEARLY;BYMONTH=3;BYDAY=TH"
+ x-summary:
+ "varje torsdag i mars, årligen"
+ x-set:
+ (list #1997-03-13T09:00:00
+ #1997-03-20T09:00:00
+ #1997-03-27T09:00:00
+ #1998-03-05T09:00:00
+ #1998-03-12T09:00:00
+ #1998-03-19T09:00:00
+ #1998-03-26T09:00:00
+ #1999-03-04T09:00:00
+ #1999-03-11T09:00:00
+ #1999-03-18T09:00:00
+ #1999-03-25T09:00:00
+ #2000-03-02T09:00:00
+ #2000-03-09T09:00:00
+ #2000-03-16T09:00:00
+ #2000-03-23T09:00:00
+ #2000-03-30T09:00:00
+ #2001-03-01T09:00:00
+ #2001-03-08T09:00:00
+ #2001-03-15T09:00:00
+ #2001-03-22T09:00:00))
+ (vevent
+ summary:
+ "Every Thursday, but only during June, July, and August, forever"
+ dtstart:
+ "19970605T090000"
+ rrule:
+ "FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8"
+ x-summary:
+ "varje torsdag i juni, juli & augusti, årligen"
+ x-set:
+ (list #1997-06-05T09:00:00
+ #1997-06-12T09:00:00
+ #1997-06-19T09:00:00
+ #1997-06-26T09:00:00
+ #1997-07-03T09:00:00
+ #1997-07-10T09:00:00
+ #1997-07-17T09:00:00
+ #1997-07-24T09:00:00
+ #1997-07-31T09:00:00
+ #1997-08-07T09:00:00
+ #1997-08-14T09:00:00
+ #1997-08-21T09:00:00
+ #1997-08-28T09:00:00
+ #1998-06-04T09:00:00
+ #1998-06-11T09:00:00
+ #1998-06-18T09:00:00
+ #1998-06-25T09:00:00
+ #1998-07-02T09:00:00
+ #1998-07-09T09:00:00
+ #1998-07-16T09:00:00))
+ (vevent
+ summary:
+ "Every Friday the 13th, forever"
+ dtstart:
+ "19970902T090000"
+ exdate:
+ "19970902T090000"
+ rrule:
+ "FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13"
+ x-summary:
+ "varje fredag den trettonde varje månad"
+ x-set:
+ (list #1998-02-13T09:00:00
+ #1998-03-13T09:00:00
+ #1998-11-13T09:00:00
+ #1999-08-13T09:00:00
+ #2000-10-13T09:00:00
+ #2001-04-13T09:00:00
+ #2001-07-13T09:00:00
+ #2002-09-13T09:00:00
+ #2002-12-13T09:00:00
+ #2003-06-13T09:00:00
+ #2004-02-13T09:00:00
+ #2004-08-13T09:00:00
+ #2005-05-13T09:00:00
+ #2006-01-13T09:00:00
+ #2006-10-13T09:00:00
+ #2007-04-13T09:00:00
+ #2007-07-13T09:00:00
+ #2008-06-13T09:00:00
+ #2009-02-13T09:00:00
+ #2009-03-13T09:00:00))
+ (vevent
+ summary:
+ "The first Saturday that follows the first Sunday of the month, forever"
+ dtstart:
+ "19970913T090000"
+ rrule:
+ "FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13"
+ x-summary:
+ "varje lördag den sjunde, åttonde, nionde, tionde, elfte, tolfte & trettonde varje månad"
+ x-set:
+ (list #1997-09-13T09:00:00
+ #1997-10-11T09:00:00
+ #1997-11-08T09:00:00
+ #1997-12-13T09:00:00
+ #1998-01-10T09:00:00
+ #1998-02-07T09:00:00
+ #1998-03-07T09:00:00
+ #1998-04-11T09:00:00
+ #1998-05-09T09:00:00
+ #1998-06-13T09:00:00
+ #1998-07-11T09:00:00
+ #1998-08-08T09:00:00
+ #1998-09-12T09:00:00
+ #1998-10-10T09:00:00
+ #1998-11-07T09:00:00
+ #1998-12-12T09:00:00
+ #1999-01-09T09:00:00
+ #1999-02-13T09:00:00
+ #1999-03-13T09:00:00
+ #1999-04-10T09:00:00))
+ (vevent
+ summary:
+ "Every 4 years, the first Tuesday after a Monday in November,\nver (U.S. Presidential Election day)"
+ dtstart:
+ "19961105T090000"
+ rrule:
+ "FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8"
+ x-summary:
+ "varje tisdag den andre, tredje, fjärde, femte, sjätte, sjunde eller åttonde i november vart fjärde år"
+ x-set:
+ (list #1996-11-05T09:00:00
+ #2000-11-07T09:00:00
+ #2004-11-02T09:00:00
+ #2008-11-04T09:00:00
+ #2012-11-06T09:00:00
+ #2016-11-08T09:00:00
+ #2020-11-03T09:00:00
+ #2024-11-05T09:00:00
+ #2028-11-07T09:00:00
+ #2032-11-02T09:00:00
+ #2036-11-04T09:00:00
+ #2040-11-06T09:00:00
+ #2044-11-08T09:00:00
+ #2048-11-03T09:00:00
+ #2052-11-05T09:00:00
+ #2056-11-07T09:00:00
+ #2060-11-02T09:00:00
+ #2064-11-04T09:00:00
+ #2068-11-06T09:00:00
+ #2072-11-08T09:00:00))
+ (vevent
+ summary:
+ "The third instance into the month of one of Tuesday, Wednesday, or Thursday, for the next 3 months"
+ dtstart:
+ "19970904T090000"
+ rrule:
+ "FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3"
+ x-summary:
+ "NOT YET IMPLEMENTED"
+ x-set:
+ (list #1997-09-04T09:00:00
+ #1997-10-07T09:00:00
+ #1997-11-06T09:00:00))
+ (vevent
+ summary:
+ "The second-to-last weekday of the month"
+ dtstart:
+ "19970929T090000"
+ rrule:
+ "FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2"
+ x-summary:
+ "NOT YET IMPLEMENTED"
+ x-set:
+ (list #1997-09-29T09:00:00
+ #1997-10-30T09:00:00
+ #1997-11-27T09:00:00
+ #1997-12-30T09:00:00
+ #1998-01-29T09:00:00))
+ (vevent
+ summary:
+ "Every 3 hours from 9:00 AM to 5:00 PM on a specific day"
+ dtstart:
+ "19970902T090000"
+ rrule:
+ "FREQ=HOURLY;INTERVAL=3;UNTIL=19970902T170000Z"
+ x-summary:
+ "var tredje timme, till och med den 02 september, 1997 kl. 17:00"
+ x-set:
+ (list #1997-09-02T09:00:00
+ #1997-09-02T12:00:00
+ #1997-09-02T15:00:00))
+ (vevent
+ summary:
+ "Every 15 minutes for 6 occurrences"
+ dtstart:
+ "19970902T090000"
+ rrule:
+ "FREQ=MINUTELY;INTERVAL=15;COUNT=6"
+ x-summary:
+ "varje kvart, totalt 6 gånger"
+ x-set:
+ (list #1997-09-02T09:00:00
+ #1997-09-02T09:15:00
+ #1997-09-02T09:30:00
+ #1997-09-02T09:45:00
+ #1997-09-02T10:00:00
+ #1997-09-02T10:15:00))
+ (vevent
+ summary:
+ "Every hour and a half for 4 occurrences"
+ dtstart:
+ "19970902T090000"
+ rrule:
+ "FREQ=MINUTELY;INTERVAL=90;COUNT=4"
+ x-summary:
+ "var sjätte kvart, totalt 4 gånger"
+ x-set:
+ (list #1997-09-02T09:00:00
+ #1997-09-02T10:30:00
+ #1997-09-02T12:00:00
+ #1997-09-02T13:30:00))
+ (vevent
+ summary:
+ "Every 20 minutes from 9:00 AM to 4:40 PM every day (alt 1)"
+ dtstart:
+ "19970902T090000"
+ rrule:
+ "FREQ=DAILY;BYHOUR=9,10,11,12,13,14,15,16;BYMINUTE=0,20,40"
+ x-summary:
+ "dagligen kl. 09:00, 09:20, 09:40, 10:00, 10:20, 10:40, 11:00, 11:20, 11:40, 12:00, 12:20, 12:40, 13:00, 13:20, 13:40, 14:00, 14:20, 14:40, 15:00, 15:20, 15:40, 16:00, 16:20 & 16:40"
+ x-set:
+ (list #1997-09-02T09:00:00
+ #1997-09-02T09:20:00
+ #1997-09-02T09:40:00
+ #1997-09-02T10:00:00
+ #1997-09-02T10:20:00
+ #1997-09-02T10:40:00
+ #1997-09-02T11:00:00
+ #1997-09-02T11:20:00
+ #1997-09-02T11:40:00
+ #1997-09-02T12:00:00
+ #1997-09-02T12:20:00
+ #1997-09-02T12:40:00
+ #1997-09-02T13:00:00
+ #1997-09-02T13:20:00
+ #1997-09-02T13:40:00
+ #1997-09-02T14:00:00
+ #1997-09-02T14:20:00
+ #1997-09-02T14:40:00
+ #1997-09-02T15:00:00
+ #1997-09-02T15:20:00))
+ (vevent
+ summary:
+ "Every 20 minutes from 9:00 AM to 4:40 PM every day (alt 2)"
+ dtstart:
+ "19970902T090000"
+ rrule:
+ "FREQ=MINUTELY;INTERVAL=20;BYHOUR=9,10,11,12,13,14,15,16"
+ x-summary:
+ "var tjugonde minut kl. 9, 10, 11, 12, 13, 14, 15 & 16"
+ x-set:
+ (list #1997-09-02T09:00:00
+ #1997-09-02T09:20:00
+ #1997-09-02T09:40:00
+ #1997-09-02T10:00:00
+ #1997-09-02T10:20:00
+ #1997-09-02T10:40:00
+ #1997-09-02T11:00:00
+ #1997-09-02T11:20:00
+ #1997-09-02T11:40:00
+ #1997-09-02T12:00:00
+ #1997-09-02T12:20:00
+ #1997-09-02T12:40:00
+ #1997-09-02T13:00:00
+ #1997-09-02T13:20:00
+ #1997-09-02T13:40:00
+ #1997-09-02T14:00:00
+ #1997-09-02T14:20:00
+ #1997-09-02T14:40:00
+ #1997-09-02T15:00:00
+ #1997-09-02T15:20:00))
+ (vevent
+ summary:
+ "An example where the days generated makes a difference because of WKST"
+ dtstart:
+ "19970805T090000"
+ rrule:
+ "FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO"
+ x-summary:
+ "varannan tisdag & söndag, totalt 4 gånger"
+ x-set:
+ (list #1997-08-05T09:00:00
+ #1997-08-10T09:00:00
+ #1997-08-19T09:00:00
+ #1997-08-24T09:00:00))
+ (vevent
+ summary:
+ "changing only WKST from MO to SU, yields different results.."
+ dtstart:
+ "19970805T090000"
+ rrule:
+ "FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU"
+ x-summary:
+ "varannan tisdag & söndag, totalt 4 gånger"
+ x-set:
+ (list #1997-08-05T09:00:00
+ #1997-08-17T09:00:00
+ #1997-08-19T09:00:00
+ #1997-08-31T09:00:00))
+ (vevent
+ summary:
+ "An example where an invalid date (i.e., February 30) is ignored"
+ dtstart:
+ "20070115T090000"
+ rrule:
+ "FREQ=MONTHLY;BYMONTHDAY=15,30;COUNT=5"
+ x-summary:
+ "den femtonde & tretionde varje månad, totalt 5 gånger"
+ x-set:
+ (list #2007-01-15T09:00:00
+ #2007-01-30T09:00:00
+ #2007-02-15T09:00:00
+ #2007-03-15T09:00:00
+ #2007-03-30T09:00:00))
+ (vevent
+ summary:
+ "Every Friday & Wednesday the 13th, forever"
+ dtstart:
+ "19970902T090000"
+ exdate:
+ "19970902T090000"
+ rrule:
+ "FREQ=MONTHLY;BYDAY=FR,WE;BYMONTHDAY=13"
+ x-summary:
+ "varje onsdag & fredag den trettonde varje månad"
+ x-set:
+ (list #1998-02-13T09:00:00
+ #1998-03-13T09:00:00
+ #1998-05-13T09:00:00
+ #1998-11-13T09:00:00
+ #1999-01-13T09:00:00
+ #1999-08-13T09:00:00
+ #1999-10-13T09:00:00
+ #2000-09-13T09:00:00
+ #2000-10-13T09:00:00
+ #2000-12-13T09:00:00
+ #2001-04-13T09:00:00
+ #2001-06-13T09:00:00
+ #2001-07-13T09:00:00
+ #2002-02-13T09:00:00
+ #2002-03-13T09:00:00
+ #2002-09-13T09:00:00
+ #2002-11-13T09:00:00
+ #2002-12-13T09:00:00
+ #2003-06-13T09:00:00
+ #2003-08-13T09:00:00))
+ (vevent
+ summary:
+ "Monday & Wednesday of week number 20 (where the default start of the week is Monday), forever"
+ dtstart:
+ "19970512T090000"
+ rrule:
+ "FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO,WE"
+ x-summary:
+ "varje onsdag & måndag v.20, årligen"
+ x-set:
+ (list #1997-05-12T09:00:00
+ #1997-05-14T09:00:00
+ #1998-05-11T09:00:00
+ #1998-05-13T09:00:00
+ #1999-05-17T09:00:00
+ #1999-05-19T09:00:00
+ #2000-05-15T09:00:00
+ #2000-05-17T09:00:00
+ #2001-05-14T09:00:00
+ #2001-05-16T09:00:00
+ #2002-05-13T09:00:00
+ #2002-05-15T09:00:00
+ #2003-05-12T09:00:00
+ #2003-05-14T09:00:00
+ #2004-05-10T09:00:00
+ #2004-05-12T09:00:00
+ #2005-05-16T09:00:00
+ #2005-05-18T09:00:00
+ #2006-05-15T09:00:00
+ #2006-05-17T09:00:00))))
+
+
diff --git a/tests/test/recurrence-simple.scm b/tests/test/recurrence-simple.scm
new file mode 100644
index 00000000..0f421b05
--- /dev/null
+++ b/tests/test/recurrence-simple.scm
@@ -0,0 +1,296 @@
+;;; Commentary:
+;; Simples tests of recurrence system, ensuring that all parsers and
+;; basic generators work. Some more fully-featured tests are here, but
+;; most are instead in recurrence-advanced.scm.
+;;; Code:
+
+(define-module (test recurrence-simple)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((srfi srfi-41)
+ :select (stream-take stream-map stream->list stream-car))
+ :use-module ((datetime) :select (day-stream mon))
+ :use-module ((vcomponent base) :select (extract prop))
+ :use-module ((hnh util exceptions)
+ :select (warnings-are-errors warning-handler))
+ :use-module ((vcomponent formats ical parse)
+ :select (parse-calendar))
+ :use-module ((vcomponent formats xcal parse)
+ :select (sxcal->vcomponent))
+ :use-module ((vcomponent recurrence)
+ :select (parse-recurrence-rule
+ make-recur-rule
+ generate-recurrence-set)))
+
+;;; Test that basic parsing or recurrence rules work.
+
+(test-equal (make-recur-rule freq: 'HOURLY wkst: mon interval: 1)
+ (parse-recurrence-rule "FREQ=HOURLY"))
+
+(test-equal (make-recur-rule freq: 'HOURLY count: 3 interval: 1 wkst: mon)
+ (parse-recurrence-rule "FREQ=HOURLY;COUNT=3"))
+
+;;; Test that recurrence rule parsing fails where appropriate
+
+(parameterize ((warnings-are-errors #t)
+ (warning-handler identity))
+ (test-error "Invalid FREQ"
+ 'warning
+ (parse-recurrence-rule "FREQ=ERR;COUNT=3"))
+ (test-error "Negative COUNT"
+ 'warning
+ (parse-recurrence-rule "FREQ=HOURLY;COUNT=-1"))
+ (test-error "Invalid COUNT"
+ 'wrong-type-argument
+ (parse-recurrence-rule "FREQ=HOURLY;COUNT=err")))
+
+;;; Test that basic recurrence works
+;;; also see the neighbour test file recurrence.scm for more tests.
+
+(define ev
+ (call-with-input-string
+ "BEGIN:VEVENT
+DTSTART;VALUE=DATE:20190302
+RRULE:FREQ=DAILY
+END:VEVENT"
+ parse-calendar))
+
+(test-assert "Generate at all"
+ (stream-car (generate-recurrence-set ev)))
+
+(test-assert "Generate some"
+ (stream->list
+ (stream-take 5 (generate-recurrence-set ev))))
+
+(test-equal "Generate First"
+ (stream->list
+ 5
+ (stream-map
+ (extract 'DTSTART)
+ (generate-recurrence-set ev)))
+ (stream->list 5 (day-stream (prop ev 'DTSTART))))
+
+;; We run the exact same thing a secound time, since I had an error with
+;; that during development.
+
+(test-equal "Generate Again"
+ (stream->list
+ (stream-take
+ 5
+ (stream-map
+ (extract 'DTSTART)
+ (generate-recurrence-set ev))))
+ (stream->list
+ (stream-take 5 (day-stream (prop ev 'DTSTART)))))
+
+(define ev
+ (call-with-input-string
+ "BEGIN:VEVENT
+DTSTART:20190302T100000
+RRULE:FREQ=DAILY
+END:VEVENT"
+ parse-calendar))
+
+(test-assert "daily 10:00"
+ (stream-car (generate-recurrence-set ev)))
+
+(define ev
+ (call-with-input-string
+ "BEGIN:VEVENT
+DTSTART:20190302T100000
+DTEND:20190302T120000
+RRULE:FREQ=DAILY
+END:VEVENT"
+ parse-calendar))
+
+(test-assert "daily 10-12"
+ (stream-car (generate-recurrence-set ev)))
+
+(define ev
+ (call-with-input-string
+ "BEGIN:VEVENT
+DTSTART:20190302T100000
+DTEND:20190302T120000
+RRULE:FREQ=WEEKLY
+END:VEVENT"
+ parse-calendar))
+
+(test-assert "weekly 10-12"
+ (stream-car (generate-recurrence-set ev)))
+
+(define ev
+ (call-with-input-string
+ "BEGIN:VEVENT
+DTSTART;TZID=Europe/Stockholm:20190302T100000
+DTEND;TZID=Europe/Stockholm:20190302T120000
+RRULE:FREQ=WEEKLY
+END:VEVENT"
+ parse-calendar))
+
+(test-assert "weekly TZ 10-12"
+ (stream-car (generate-recurrence-set ev)))
+
+(define ev
+ (call-with-input-string
+ "BEGIN:VEVENT
+DTSTART;TZID=Europe/Stockholm:20190302T100000
+DTEND;TZID=Europe/Stockholm:20190302T120000
+RRULE:FREQ=WEEKLY
+SEQUENCE:1
+END:VEVENT"
+ parse-calendar))
+
+(test-assert "weekly TZ SEQUENCE 10-12"
+ (stream-car (generate-recurrence-set ev)))
+
+(define ev
+ (call-with-input-string
+ "BEGIN:VEVENT
+DTSTART;TZID=Europe/Stockholm:20190302T100000
+RRULE:FREQ=WEEKLY
+DTEND;TZID=Europe/Stockholm:20190302T120000
+SEQUENCE:1
+LOCATION:Here
+END:VEVENT"
+ parse-calendar))
+
+(test-assert "weekly TZ SEQUENCE LOCATION 10-12"
+ (stream-car (generate-recurrence-set ev)))
+
+(define ev
+ (call-with-input-string
+ "BEGIN:VEVENT
+DTSTART:20180117T170000
+RRULE:FREQ=WEEKLY
+LOCATION:~
+END:VEVENT"
+ parse-calendar))
+
+(test-assert "Just location"
+ (stream-car (generate-recurrence-set ev)))
+
+(define ev
+ (call-with-input-string
+ "BEGIN:VEVENT
+DTSTART;TZID=Europe/Stockholm:20180117T170000
+DTEND;TZID=Europe/Stockholm:20180117T200000
+RRULE:FREQ=WEEKLY
+END:VEVENT"
+ parse-calendar))
+
+(test-assert "Same times"
+ (stream-car (generate-recurrence-set ev)))
+
+(define ev
+ (call-with-input-string
+ "BEGIN:VEVENT
+DTSTART;TZID=Europe/Stockholm:20180117T170000
+RRULE:FREQ=WEEKLY
+DTEND;TZID=Europe/Stockholm:20180117T200000
+SEQUENCE:1
+LOCATION:~
+END:VEVENT"
+ parse-calendar))
+
+;; errer in dtend ?
+
+(test-assert "Full test"
+ (stream-car (generate-recurrence-set ev)))
+
+;;; Tests that exceptions (in the recurrence-id meaning)
+;;; in recurrence sets are handled correctly.
+;;; TODO Is however far from done.
+
+(define uid (symbol->string (gensym "areallyuniqueid")))
+
+;; TODO standardize vcomponents for tests as xcal, for example:
+`(vcalendar
+ (children
+ (vevent
+ (properties
+ (summary
+ (text "Changing type on Recurrence-id."))
+ (uid (text ,uid))
+ (dtstart (date "20090127"))))
+ (vevent
+ (properties
+ (summary
+ (text "Changing type on Recurrence-id."))
+ (uid (text ,uid))
+ (dtstart
+ (params (TZID "Europe/Stockholm"))
+ (date-time "20100127T120000"))
+ (recurrence-id (date "20100127"))
+ (summary
+ "This instance only has a time component")))))
+
+(define ev
+ (call-with-input-string
+ (format
+ #f
+ "BEGIN:VCALENDAR
+BEGIN:VEVENT
+SUMMARY:Changing type on Recurrence-id.
+UID:~a
+DTSTART;VALUE=DATE:20090127
+END:VEVENT
+BEGIN:VEVENT
+UID:~a
+SUMMARY:Changing type on Recurrence-id.
+DTSTART;TZID=Europe/Stockholm:20100127T120000
+RECURRENCE-ID;VALUE=DATE:20100127
+SUMMARY:This instance only has a time component
+END:VEVENT
+END:VCALENDAR"
+ uid
+ uid)
+ parse-calendar))
+
+(test-assert "Changing type on Recurrence id."
+ (stream->list 10 (generate-recurrence-set ev)))
+
+;;; Earlier I failed to actually parse the recurrence parts, in short, 1 ≠ "1".
+
+(test-assert "Test that xcal recur rules are parseable"
+ ((@@ (vcomponent formats xcal parse) handle-value)
+ 'recur
+ 'props-are-unused-for-recur
+ '((freq "WEEKLY") (interval "1") (wkst "MO"))))
+
+(define ev
+ (sxcal->vcomponent
+ '(vevent
+ (properties
+ (summary (text "reptest"))
+ (dtend (date-time "2021-01-13T02:00:00"))
+ (dtstart (date-time "2021-01-13T01:00:00"))
+ (uid (text "RNW198S6QANQPV1C4FDNFH6ER1VZX6KXEYNB"))
+ (rrule (recur (freq "WEEKLY")
+ (interval "1")
+ (wkst "MO")))
+ (dtstamp (date-time "2021-01-13T01:42:20Z"))
+ (sequence (integer "0")))
+ (components))))
+
+(test-assert
+ "Check that recurrence rule commint from xcal also works"
+ (generate-recurrence-set ev))
+
+
+;;; TODO test here, for byday parsing, and multiple byday instances in one recur element
+;;; TODO which should also test serializing and deserializing to xcal.
+;;; For example, the following rules specify every workday
+
+;; BEGIN:VCALENDAR
+;; PRODID:-//hugo//calp 0.6.1//EN
+;; VERSION:2.0
+;; CALSCALE:GREGORIAN
+;; BEGIN:VEVENT
+;; SUMMARY:Lunch
+;; DTSTART:20211129T133000
+;; DTEND:20211129T150000
+;; LAST-MODIFIED:20211204T220944Z
+;; UID:3d82c73c-6cdb-4799-beba-5f1d20d55347
+;; RRULE:FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR
+;; END:VEVENT
+;; END:VCALENDAR
diff --git a/tests/test/rrule-serialization.scm b/tests/test/rrule-serialization.scm
new file mode 100644
index 00000000..e616c5a2
--- /dev/null
+++ b/tests/test/rrule-serialization.scm
@@ -0,0 +1,75 @@
+(define-module (test rrule-serialization)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((vcomponent recurrence internal)
+ :select (recur-rule->rrule-string
+ recur-rule->rrule-sxml
+ byday))
+ :use-module ((vcomponent recurrence parse)
+ :select (parse-recurrence-rule))
+ :use-module ((ice-9 peg) :select (keyword-flatten)))
+
+(test-equal
+ "Parse of week day"
+ '(#f . 3)
+ ((@@ (vcomponent recurrence parse) parse-day-spec)
+ "WE"))
+
+(test-equal
+ "Parse of week day with positive offset"
+ '(1 . 3)
+ ((@@ (vcomponent recurrence parse) parse-day-spec)
+ "1WE"))
+
+(test-equal
+ "Parse of week day with positive offset (and plus)"
+ '(2 . 3)
+ ((@@ (vcomponent recurrence parse) parse-day-spec)
+ "+2WE"))
+
+(test-equal
+ "Parse of week day with negative offset"
+ '(-3 . 3)
+ ((@@ (vcomponent recurrence parse) parse-day-spec)
+ "-3WE"))
+
+
+;; numeric prefixes in the BYDAY list is only valid when
+;; FREQ={MONTHLY,YEARLY}, but that should be handled in a
+;; later stage since we are just testing the parser here.
+;; (p. 41)
+
+
+(define field->string
+ (@@ (vcomponent recurrence internal)
+ field->string))
+
+(let ((rule (parse-recurrence-rule "BYDAY=MO,TU,WE")))
+ (test-equal
+ "Direct return of parsed value"
+ "MO,TU,WE"
+ (field->string 'byday (byday rule)))
+ (test-equal
+ "Direct return, but as SXML"
+ '((byday "MO") (byday "TU") (byday "WE"))
+ (filter
+ (lambda (pair) (eq? 'byday (car pair)))
+ (keyword-flatten
+ '(interval byday wkst)
+ (recur-rule->rrule-sxml rule)))))
+
+(let ((rule (parse-recurrence-rule "BYDAY=+1MO,1TU,-2FR")))
+ (test-equal
+ "Direct return of parsed value"
+ "1MO,1TU,-2FR"
+ (field->string 'byday (byday rule)))
+ (test-equal
+ "Direct return, but as SXML"
+ '((byday "1MO") (byday "1TU") (byday "-2FR"))
+ (filter
+ (lambda (pair) (eq? 'byday (car pair)))
+ (keyword-flatten
+ '(interval byday wkst)
+ (recur-rule->rrule-sxml rule)))))
+
+
diff --git a/tests/test/server.scm b/tests/test/server.scm
new file mode 100644
index 00000000..1b5d4775
--- /dev/null
+++ b/tests/test/server.scm
@@ -0,0 +1,24 @@
+;;; Commentary:
+;; Tests parse-endpoint-string, used for defining server routes.
+;;; Code:
+
+(define-module (test server)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((web http make-routes)
+ :select (parse-endpoint-string))
+ :use-module ((hnh util) :select (let*)))
+
+(test-assert "Check that parsing doesn't crash"
+ (parse-endpoint-string "/static/:dir/:file"))
+
+;; Checks that parsing produces correct results
+(let* ((path args (parse-endpoint-string "/static/:dir/:file")))
+ (test-equal "/static/([^/.]+)/([^/.]+)" path)
+ (test-equal '(dir file) args))
+
+;; Checks that parsing with custom regex works
+;; along with literal periods.
+(let* ((path args (parse-endpoint-string "/static/:filename{.*}.:ext")))
+ (test-equal "/static/(.*)\\.([^/.]+)" path)
+ (test-equal '(filename ext) args))
diff --git a/tests/test/srfi-41-util.scm b/tests/test/srfi-41-util.scm
new file mode 100644
index 00000000..176fb38e
--- /dev/null
+++ b/tests/test/srfi-41-util.scm
@@ -0,0 +1,44 @@
+;;; Commentary:
+;; Tests (srfi srfi-41 util).
+;; Currently only tests stream-paginate.
+;;; Code:
+
+(define-module (test srfi-41-util)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((srfi srfi-41 util) :select (stream-paginate))
+ :use-module ((srfi srfi-41)
+ :select (stream->list
+ stream-ref
+ stream-from
+ stream-filter
+ stream-car
+ stream))
+ :use-module ((ice-9 sandbox) :select (call-with-time-limit)))
+
+(test-equal "Finite stream"
+ '((0 1 2) (3 4 5) (6 7 8) (9))
+ (let ((strm (stream-paginate (stream 0 1 2 3 4 5 6 7 8 9) 3)))
+ (map stream->list (stream->list strm))))
+
+(test-equal "slice of infinite"
+ '(1000 1001 1002 1003 1004 1005 1006 1007 1008 1009)
+ (let ((strm (stream-paginate (stream-from 0))))
+ (stream->list (stream-ref strm 100))))
+
+(define unique-symbol (gensym))
+
+(test-equal "time out on infinite 'empty' stream"
+ unique-symbol
+ ;; defined outside time limit since creation should always
+ ;; succeed. Only reference is expected to fail.
+ (let ((strm (stream-paginate
+ ;; easy way to get stream which never finds
+ ;; any elements.
+ (stream-filter negative? (stream-from 0)))))
+ (call-with-time-limit
+ 0.1
+ (lambda () (stream-car strm))
+ (lambda _ unique-symbol))))
+
+
diff --git a/tests/test/termios.scm b/tests/test/termios.scm
new file mode 100644
index 00000000..7f607cc4
--- /dev/null
+++ b/tests/test/termios.scm
@@ -0,0 +1,48 @@
+;;; Commentary:
+;; Tests that my termios function works, at least somewhat.
+;; Note that this actually modifies the terminal it's run on, and might fail
+;; if the terminal doesn't support the wanted modes. See termios(3).
+;; It might also leave the terminal in a broken state if exited prematurely.
+;;; Code:
+
+(define-module (test termios)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((hnh util) :select (set!))
+ :use-module ((vulgar termios)
+ :select (make-termios
+ copy-termios
+ lflag
+ tcgetattr!
+ tcsetattr!
+ ECHO
+ ICANON))
+ :use-module ((srfi srfi-60)
+ :select ((bitwise-ior . ||)
+ (bitwise-not . ~)
+ (bitwise-and . &))))
+
+(define tty (open-input-file "/dev/tty"))
+
+(define-syntax-rule (&= lvalue val)
+ (set! lvalue = ((lambda (v) (& v val)))))
+
+(define t (make-termios))
+
+(test-equal 0 (tcgetattr! t tty))
+
+(define ifl (lflag t))
+
+(define copy (copy-termios t))
+
+#!curly-infix {(lflag t) &= (~ (|| ECHO ICANON))}
+
+(test-equal 0 (tcsetattr! t tty))
+
+(test-equal
+ (& ifl (~ (|| ECHO ICANON)))
+ (lflag t))
+
+(test-equal 0 (tcsetattr! copy tty))
+
+
diff --git a/tests/test/tz.scm b/tests/test/tz.scm
new file mode 100644
index 00000000..245258d0
--- /dev/null
+++ b/tests/test/tz.scm
@@ -0,0 +1,87 @@
+;;; Commentary:
+;; Tests that datetime->unix-time correctly converts between Olssen
+;; timezone definitions (e.g. Europe/Stockholm), into correct times
+;; and offsets (in unix time).
+;; Also indirectly tests the Zone Info Compiler (datetime zic), since
+;; the zoneinfo comes from there.
+;;; Code:
+
+(define-module (test tz)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((datetime)
+ :select (parse-ics-datetime
+ datetime
+ date
+ time
+ datetime->unix-time
+ unix-time->datetime
+ get-datetime))
+ :use-module ((hnh util) :select (let-env)))
+
+;; London alternates between +0000 and +0100
+(let-env
+ ((TZ "Europe/London"))
+ (test-equal
+ "London winter"
+ #2020-01-12T13:30:00
+ (get-datetime
+ (parse-ics-datetime "20200112T133000Z")))
+ (test-equal
+ "London summer"
+ #2020-06-12T14:30:00
+ (get-datetime
+ (parse-ics-datetime "20200612T133000Z"))))
+
+;; Stockholm alternates between +0100 and +0200
+(let-env
+ ((TZ "Europe/Stockholm"))
+ (test-equal
+ "Stockholm winter"
+ #2020-01-12T14:30:00
+ (get-datetime
+ (parse-ics-datetime "20200112T133000Z")))
+ (test-equal
+ "Stockholm summer"
+ #2020-06-12T15:30:00
+ (get-datetime
+ (parse-ics-datetime "20200612T133000Z"))))
+
+(test-equal
+ -10800
+ (datetime->unix-time
+ (parse-ics-datetime
+ "19700101T000000"
+ "Europe/Tallinn")))
+
+(test-equal
+ -3600
+ (datetime->unix-time
+ (parse-ics-datetime
+ "19700101T000000"
+ "Europe/Stockholm")))
+
+(test-equal
+ 0
+ (datetime->unix-time
+ (parse-ics-datetime "19700101T000000Z")))
+
+;; yes, really
+(test-equal
+ -3600
+ (datetime->unix-time
+ (parse-ics-datetime
+ "19700101T000000"
+ "Europe/London")))
+
+(test-equal
+ (datetime
+ date:
+ #1970-01-01
+ time:
+ #00:00:00
+ tz:
+ "UTC")
+ (unix-time->datetime 0))
+
+
diff --git a/tests/test/util.scm b/tests/test/util.scm
new file mode 100644
index 00000000..47edb225
--- /dev/null
+++ b/tests/test/util.scm
@@ -0,0 +1,145 @@
+;;; Commentary:
+;; Checks some prodecuders from (hnh util)
+;;; Code:
+
+(define-module (test util)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((hnh util)
+ :select (filter-sorted
+ set/r!
+ find-min
+ find-max
+ find-extreme
+ span-upto
+ iterate
+ ->string
+ ->quoted-string
+ begin1))
+ :use-module ((hnh util path)
+ :select (path-append path-split)))
+
+(test-equal
+ "Filter sorted"
+ '(3 4 5)
+ (filter-sorted (lambda (x) (<= 3 x 5)) (iota 10)))
+
+(test-equal
+ "set/r! = single"
+ #f
+ (let ((x #t)) (set/r! x = not)))
+
+(test-error
+ 'syntax-error
+ (test-read-eval-string "(set/r! x err not)"))
+
+(call-with-values
+ (lambda () (find-min (iota 10)))
+ (lambda (extreme rest)
+ (test-equal "Found correct minimum" 0 extreme)
+ (test-equal
+ "Removed \"something\" from the set"
+ 9
+ (length rest))))
+
+(call-with-values
+ (lambda ()
+ (find-max
+ '("Hello" "Test" "Something long")
+ string-length))
+ (lambda (extreme rest)
+ (test-equal
+ "Found the longest string"
+ "Something long"
+ extreme)
+ (test-equal "Removed the string" 2 (length rest))
+ (test-assert
+ "Other members left 1"
+ (member "Hello" rest))
+ (test-assert
+ "Other members left 2"
+ (member "Test" rest))))
+
+(test-error 'misc-error (find-extreme '()))
+
+(call-with-values
+ (lambda ()
+ (span-upto
+ 2
+ char-numeric?
+ (string->list "123456")))
+ (lambda (head tail)
+ (test-equal '(#\1 #\2) head)
+ (test-equal '(#\3 #\4 #\5 #\6) tail)))
+
+(call-with-values
+ (lambda ()
+ (span-upto
+ 2
+ char-numeric?
+ (string->list "H123456")))
+ (lambda (head tail)
+ (test-equal '() head)
+ (test-equal '(#\H #\1 #\2 #\3 #\4 #\5 #\6) tail)))
+
+(let ((value #f))
+ (test-equal
+ "begin1 return value"
+ "Hello"
+ (begin1 "Hello" (set! value "World")))
+ (test-equal "begin1 side effects" "World" value))
+
+(test-equal 0 (iterate 1- zero? 10))
+
+(test-equal "5" (->string 5))
+
+(test-equal "5" (->string "5"))
+
+(test-equal "5" (->quoted-string 5))
+
+(test-equal "\"5\"" (->quoted-string "5"))
+
+(test-equal
+ "no slashes"
+ "home/user"
+ (path-append "home" "user"))
+
+(test-equal
+ "no slashes, absolute"
+ "/home/user"
+ (path-append "" "home" "user"))
+
+(test-equal
+ "slashes in one component, absolute"
+ "/home/user"
+ (path-append "" "/home/" "user"))
+
+(test-equal
+ "slashes in one component, absolute due to first"
+ "/home/user"
+ (path-append "/home/" "user"))
+
+(test-equal
+ "Slashes in both"
+ "home/user"
+ (path-append "home/" "/user"))
+
+(test-equal "root" "/" (path-append ""))
+
+(test-equal
+ '("usr" "lib" "test")
+ (path-split "usr/lib/test"))
+
+(test-equal
+ '("usr" "lib" "test")
+ (path-split "usr/lib/test/"))
+
+(test-equal
+ '("" "usr" "lib" "test")
+ (path-split "/usr/lib/test"))
+
+(test-equal
+ '("" "usr" "lib" "test")
+ (path-split "//usr////lib/test"))
+
+
diff --git a/tests/test/vcomponent-control.scm b/tests/test/vcomponent-control.scm
new file mode 100644
index 00000000..f408c8b4
--- /dev/null
+++ b/tests/test/vcomponent-control.scm
@@ -0,0 +1,36 @@
+;;; Commentary:
+;; Tests that with-replaced-properties work.
+;;; Code:
+
+(define-module (test vcomponent-control)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((vcomponent util control)
+ :select (with-replaced-properties))
+ :use-module ((vcomponent formats ical parse)
+ :select (parse-calendar))
+ :use-module ((vcomponent base) :select (prop)))
+
+(define ev
+ (call-with-input-string
+ "BEGIN:DUMMY\nX-KEY:value\nEND:DUMMY"
+ parse-calendar))
+
+;; Test that temoraries are set and restored
+(test-equal "value" (prop ev 'X-KEY))
+
+(with-replaced-properties
+ (ev (X-KEY "other"))
+ (test-equal "other" (prop ev 'X-KEY)))
+
+(test-equal "value" (prop ev 'X-KEY))
+
+;; Test that they are restored on non-local exit
+(catch #t
+ (lambda ()
+ (with-replaced-properties
+ (ev (X-KEY "other"))
+ (throw 'any)))
+ (lambda _ (test-equal "value" (prop ev 'X-KEY))))
+
+
diff --git a/tests/test/vcomponent-datetime.scm b/tests/test/vcomponent-datetime.scm
new file mode 100644
index 00000000..073a70ae
--- /dev/null
+++ b/tests/test/vcomponent-datetime.scm
@@ -0,0 +1,49 @@
+;;; Commentary:
+;; Tests that event-clamping (checking how long part of an event
+;; overlaps another time span) works.
+;;; Code:
+
+(define-module (test vcomponent-datetime)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((datetime) :select (date time datetime))
+ :use-module ((vcomponent datetime) :select (event-length/clamped))
+ :use-module ((vcomponent formats ical parse) :select (parse-calendar)))
+
+(define ev
+ (call-with-input-string
+ "BEGIN:VEVENT
+DTSTART:20200329T170000
+DTEND:20200401T100000
+END:VEVENT"
+ parse-calendar))
+
+
+;; |-----------------| test interval
+;; |----------| event interval
+
+(test-equal
+ "Correct clamping"
+ (datetime time: (time hour: 7)) ; 2020-03-29T17:00 - 2020-03-30T00:00
+ (event-length/clamped
+ #2020-03-23 ; a time way before the start of the event
+ #2020-03-29 ; a time slightly after the end of the event
+ ev))
+
+(define utc-ev
+ (call-with-input-string
+ "BEGIN:VEVENT
+DTSTART:20200329T150000Z
+DTEND:20200401T080000Z
+END:VEVENT"
+ parse-calendar))
+
+(test-equal
+ "Correct clamping UTC"
+ (datetime time: (time hour: 7))
+ (event-length/clamped
+ #2020-03-23
+ #2020-03-29
+ ev))
+
+
diff --git a/tests/test/vcomponent-formats-common-types.scm b/tests/test/vcomponent-formats-common-types.scm
new file mode 100644
index 00000000..c8bfd323
--- /dev/null
+++ b/tests/test/vcomponent-formats-common-types.scm
@@ -0,0 +1,137 @@
+(define-module (test vcomponent-formats-common-types)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((vcomponent formats common types)
+ :select (get-parser))
+ :use-module ((datetime) :select (date time datetime)))
+
+
+
+(define parse-binary (get-parser 'BINARY))
+;; TODO
+
+
+
+(define parse-boolean (get-parser 'BOOLEAN))
+
+(test-equal #t (parse-boolean #f "TRUE"))
+(test-equal #f (parse-boolean #f "FALSE"))
+
+(test-error 'warning (parse-boolean #f "ANYTHING ELSE"))
+
+
+
+(define parse-cal-address
+ (get-parser 'CAL-ADDRESS))
+
+(test-equal "Test uri is passthrough"
+ 74 (parse-cal-address #f 74))
+
+
+
+(define parse-date (get-parser 'DATE))
+
+(test-equal
+ #2021-12-02
+ (parse-date #f "20211202"))
+;; TODO negative test here
+
+(define parse-datetime (get-parser 'DATE-TIME))
+
+(test-equal
+ #2021-12-02T10:20:30
+ (parse-datetime
+ (make-hash-table)
+ "20211202T102030"))
+
+;; TODO tests with timezones here
+;; TODO test -X-HNH-ORIGINAL here
+
+;; TODO negative test here
+
+
+
+(define parse-duration (get-parser 'DURATION))
+
+;; assume someone else tests this one
+;; (test-eq (@ (vcomponent duration) parse-duration)
+;; parse-duration)
+
+
+
+(define parse-float (get-parser 'FLOAT))
+
+(test-equal 1.0 (parse-float #f "1.0"))
+(test-equal 1 (parse-float #f "1"))
+(test-equal 1/2 (parse-float #f "1/2"))
+
+;; TODO negative test here?
+
+
+
+(define parse-integer (get-parser 'INTEGER))
+
+(test-equal
+ "parse integer"
+ 123456
+ (parse-integer #f "123456"))
+
+(test-equal
+ "parse bigint"
+ 123451234512345123456666123456
+ (parse-integer
+ #f
+ "123451234512345123456666123456"))
+
+;; TODO is this expected behaivour?
+(test-error 'warning (parse-integer #f "failure"))
+
+(test-error
+ "Non-integers aren't integers"
+ 'warning
+ (parse-integer #f "1.1"))
+
+(test-equal
+ "But exact floats are"
+ 1.0
+ (parse-integer #f "1.0"))
+
+
+
+(define parse-period (get-parser 'PERIOD))
+
+;; TODO
+
+
+
+(define parse-recur (get-parser 'RECUR))
+
+;; (test-eq (@ (vcomponent recurrence parse) parse-recurrence-rule))
+
+
+
+(define parse-text (get-parser 'TEXT))
+
+;; TODO
+
+
+
+(define parse-time (get-parser 'TIME))
+
+(test-equal
+ #10:20:30
+ (parse-time #f "102030"))
+;; TODO negative test here
+
+
+
+(define parse-uri (get-parser 'URI))
+
+(test-equal "Test uri is passthrough" 74 (parse-uri #f 74))
+
+
+
+(define parse-utc-offset
+ (get-parser 'UTC-OFFSET))
+
+;; TODO
diff --git a/tests/test/vcomponent.scm b/tests/test/vcomponent.scm
new file mode 100644
index 00000000..d3ee37dc
--- /dev/null
+++ b/tests/test/vcomponent.scm
@@ -0,0 +1,23 @@
+;;; Commentary:
+;; Test that vcomponent parsing works at all.
+;;; Code:
+
+(define-module (test vcomponent)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((vcomponent base) :select (prop))
+ :use-module ((vcomponent formats ical parse)
+ :select (parse-calendar)))
+
+(define ev
+ (call-with-input-string
+ "BEGIN:DUMMY\nX-KEY:value\nEND:DUMMY"
+ parse-calendar))
+
+(test-assert (eq? #f (prop ev 'MISSING)))
+
+(test-assert (prop ev 'X-KEY))
+
+(test-equal "value" (prop ev 'X-KEY))
+
+
diff --git a/tests/test/web-server.scm b/tests/test/web-server.scm
new file mode 100644
index 00000000..e5a796b6
--- /dev/null
+++ b/tests/test/web-server.scm
@@ -0,0 +1,116 @@
+;;; Commentary:
+;; Checks that HTTP server can start correctly, and that at least some
+;; endpoints return correct information.
+;;
+;; NOTE This test, when ran in as `tests/run-tests --only web-server.scm'
+;; segfaults on Guile 2.2.7, but not on Guile 3.0.8. This doesn't happen
+;; when it's run as one of all tests.
+;;; Code:
+
+(define-module (test web-server)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((calp server routes) :select (make-make-routes))
+ :use-module ((web server) :select (run-server))
+ :use-module ((ice-9 threads)
+ :select (call-with-new-thread cancel-thread))
+ :use-module ((web client) :select (http-get))
+ :use-module ((hnh util) :select (let*))
+ :use-module ((web response) :select (response-code response-location))
+ :use-module ((web uri) :select (build-uri uri-path))
+ :use-module ((guile)
+ :select (socket
+ inet-pton
+ bind
+ make-socket-address
+ setsockopt
+ AF_INET
+ PF_INET
+ SOL_SOCKET
+ SO_REUSEADDR
+ SOCK_STREAM
+ current-error-port))
+ :use-module ((ice-9 format) :select (format))
+ :use-module ((web response) :select (build-response)))
+
+(define host "127.8.9.5")
+
+(define sock (socket PF_INET SOCK_STREAM 0))
+
+(setsockopt sock SOL_SOCKET SO_REUSEADDR 1)
+
+(define-values
+ (port sock)
+ (let ((addr (inet-pton AF_INET host)))
+ (let loop ((port 8090))
+ (catch 'system-error
+ (lambda ()
+ (bind sock
+ (make-socket-address AF_INET addr port))
+ (values port sock))
+ (lambda (err proc fmt args data)
+ (if (and (not (null? data))
+ ;; errno address already in use
+ (= 98 (car data)))
+ (loop (1+ port))
+ ;; rethrow
+ (throw err fmt args data)))))))
+
+(define server-thread
+ (call-with-new-thread
+ (lambda ()
+ (catch #t
+ (lambda ()
+ (run-server
+ (make-make-routes)
+ 'http
+ `(socket: ,sock)))
+ (lambda args
+ (format #f "~s~%" args)
+ (test-assert "Server Crashed" #f)))
+ ;; This test should always fail, but should never be run
+ (test-assert "Server returned unexpectedly" #f))))
+
+(let* ((response
+ _
+ (catch 'system-error
+ (lambda ()
+ (http-get
+ (build-uri 'http host: host port: port)))
+ (lambda (err proc fmt args data)
+ (format
+ (current-error-port)
+ "~a (in ~a) ~?~%"
+ err
+ proc
+ fmt
+ args)
+ (values (build-response code: 500) #f)))))
+ (test-eqv
+ "Basic connect"
+ 200
+ (response-code response)))
+
+(let* ((response
+ body
+ (http-get
+ (build-uri
+ 'http
+ host:
+ host
+ port:
+ port
+ path:
+ "/today"
+ query:
+ "view=week&date=2020-01-04"))))
+ (test-eqv
+ "Redirect"
+ 302
+ (response-code response))
+ (test-equal
+ "Fully specified redirect position"
+ "/week/2020-01-04.html"
+ (uri-path (response-location response))))
+
+(cancel-thread server-thread)
diff --git a/tests/test/xcal.scm b/tests/test/xcal.scm
new file mode 100644
index 00000000..48d43c59
--- /dev/null
+++ b/tests/test/xcal.scm
@@ -0,0 +1,58 @@
+;;; Commentary:
+;; Basic tests of xcal convertion.
+;; Currently only checks that events survive a round trip.
+;;; Code:
+
+(define-module (test xcal)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((vcomponent formats xcal parse)
+ :select (sxcal->vcomponent))
+ :use-module ((vcomponent formats xcal output)
+ :select (vcomponent->sxcal))
+ :use-module ((vcomponent formats ical parse)
+ :select (parse-calendar))
+ :use-module ((hnh util) :select (->))
+ :use-module ((vcomponent base)
+ :select (parameters prop* children)))
+
+;;; Some different types, same parameters
+
+(define ev
+ (call-with-input-string
+ "BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//calparse-test
+BEGIN:VEVENT
+SUMMARY:Test event
+DTSTART;TZID=Europe/Stockholm:20200625T133000
+DTEND:20200625T143000Z
+DTSTAMP:20200609T131418Z
+UID:1
+SEQUENCE:0
+CREATED:20200609T081725Z
+DESCRIPTION:Short description
+LAST-MODIFIED:20200609T081725Z
+STATUS;X-TEST-PARAM=10:CONFIRMED
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR"
+ parse-calendar))
+
+(define twice-converted
+ (-> ev vcomponent->sxcal sxcal->vcomponent))
+
+;;; NOTE both these tests may fail since neither properties nor parameters are ordered sorted.
+
+(test-equal
+ "c->x & c->x->c->x"
+ (vcomponent->sxcal ev)
+ (vcomponent->sxcal twice-converted))
+
+(test-equal
+ "xcal parameters"
+ '((X-TEST-PARAM "10"))
+ (parameters
+ (prop* (car (children twice-converted)) 'STATUS)))
+
+
diff --git a/tests/test/xml-namespace.scm b/tests/test/xml-namespace.scm
new file mode 100644
index 00000000..09402ceb
--- /dev/null
+++ b/tests/test/xml-namespace.scm
@@ -0,0 +1,36 @@
+(define-module (test xml-namespace)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((sxml namespace) :select (move-to-namespace)))
+
+(test-equal
+ "Move unnamespaced to namespace"
+ '(NEW:test)
+ (move-to-namespace '(test) '((#f . NEW))))
+
+(test-equal
+ "Swap namespaces"
+ '(b:a (a:b))
+ (move-to-namespace
+ '(a:a (b:b))
+ '((a . b) (b . a))))
+
+(test-equal
+ "Remove all namespaces"
+ '(a (b))
+ (move-to-namespace '(a:a (b:b)) #f))
+
+(test-equal
+ "Move everything to one namespace"
+ '(c:a (c:b))
+ (move-to-namespace '(a:a (b:b)) 'c))
+
+(test-equal
+ "Partial namespace change"
+ '(c:a (b:b))
+ (move-to-namespace '(a:a (b:b)) '((a . c))))
+
+(test-equal
+ "Remove specific namespace"
+ '(a:a (b))
+ (move-to-namespace '(a:a (b:b)) '((b . #f))))