123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 |
- ;;;; base.scm -- tests for (mcron base) module
- ;;; Copyright © 2018 Mathieu Lirzin <mthl@gnu.org>
- ;;; Copyright © 2021 Maxim Cournoyer <maxim.cournoyer@gmail.com>
- ;;;
- ;;; This file is part of GNU Mcron.
- ;;;
- ;;; GNU Mcron is free software: you can redistribute it and/or modify
- ;;; it under the terms of the GNU General Public License as published by
- ;;; the Free Software Foundation, either version 3 of the License, or
- ;;; (at your option) any later version.
- ;;;
- ;;; GNU Mcron is distributed in the hope that it will be useful,
- ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ;;; GNU General Public License for more details.
- ;;;
- ;;; You should have received a copy of the GNU General Public License
- ;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
- (use-modules ((rnrs base) #:select (assert))
- (srfi srfi-64)
- (srfi srfi-111)
- (mcron base))
- (test-begin "base")
- (setlocale LC_ALL "C")
- (setenv "TZ" "UTC0")
- ;;; Import private procedures.
- (define make-schedule (@@ (mcron base) make-schedule))
- (define schedule-current (@@ (mcron base) schedule-current))
- (define schedule-user (@@ (mcron base) schedule-user))
- (define schedule-system (@@ (mcron base) schedule-system))
- (define make-job (@@ (mcron base) make-job))
- (define find-next-jobs (@@ (mcron base) find-next-jobs))
- (define %user0 #("user0" "x" 0 0 "user0" "/var/empty" "/bin/sh"))
- (define %user1 #("user1" "x" 1 1 "user1" "/var/empty" "/bin/sh"))
- (define* (make-dummy-job #:optional (displayable "dummy")
- #:key
- (user (getpw))
- (time-proc 1+)
- (action (lambda () "dummy action"))
- (environment '())
- (next-time 0))
- (make-job user time-proc action environment displayable next-time))
- ;;; Check 'use-system-job-list' and 'use-user-job-list' effect
- (let ((schdl (make-schedule '() '() 'user)))
- (use-system-job-list #:schedule schdl)
- (test-eq "use-system-job-list"
- 'system
- (schedule-current schdl))
- (use-user-job-list #:schedule schdl)
- (test-eq "use-user-job-list"
- 'user
- (schedule-current schdl)))
- ;;; Check that 'remove-user-jobs' with only one type of user job clears the
- ;;; schedule.
- (let* ((job (make-dummy-job #:user %user0))
- (schdl (make-schedule (list job) '() 'user)))
- (remove-user-jobs %user0 #:schedule schdl)
- (test-equal "remove-user-jobs: only one"
- '()
- (schedule-user schdl)))
- ;;; Check that 'remove-user-jobs' with only two types of user jobs keep the
- ;;; other user jobs in the schedule.
- (let* ((job0 (make-dummy-job #:user %user0))
- (job1 (make-dummy-job #:user %user1))
- (schdl (make-schedule (list job0 job1) '() 'user)))
- (remove-user-jobs %user0 #:schedule schdl)
- (test-equal "remove-user-jobs: keep one"
- (list job1)
- (schedule-user schdl)))
- ;;; Check that 'clear-system-jobs' removes all system jobs and has no effect
- ;;; on the user jobs.
- (let* ((job0 (make-dummy-job #:user %user0))
- (job1 (make-dummy-job #:user %user1))
- (schdl (make-schedule (list job0) (list job1) 'user)))
- (clear-system-jobs #:schedule schdl)
- (test-assert "clear-system-jobs: basic"
- (and (equal? (list job0) (schedule-user schdl))
- (equal? '() (schedule-system schdl)))))
- ;;; Check that 'add-job' adds a user job when the current schedule is 'user.
- (let ((schdl (make-schedule '() '() 'user)))
- (add-job 1+ (const #t) "job0" 0 "user" #:schedule schdl)
- (test-assert "add-job: user schedule"
- (and (= 1 (length (schedule-user schdl)))
- (= 0 (length (schedule-system schdl))))))
- ;;; Check that 'add-job' adds a system job when the current schedule is
- ;;; 'system.
- (let ((schdl (make-schedule '() '() 'system)))
- (add-job 1+ (const #t) "job0" 0 "user" #:schedule schdl)
- (test-assert "add-job: system schedule"
- (and (= 0 (length (schedule-user schdl)))
- (= 1 (length (schedule-system schdl))))))
- ;;; Check that 'find-next-jobs' find the soonest job.
- (let* ((job0 (make-dummy-job #:user %user0 #:next-time 5))
- (job1 (make-dummy-job #:user %user1 #:next-time 10))
- (schdl (make-schedule (list job0) (list job1) 'user)))
- (test-equal "find-next-jobs: basic"
- (list 5 job0)
- (find-next-jobs #:schedule schdl)))
- ;;; Check that 'find-next-jobs' can find multiple soonest jobs.
- (let* ((job0 (make-dummy-job #:user %user0 #:next-time 5))
- (job1 (make-dummy-job #:user %user1 #:next-time 5))
- (schdl (make-schedule (list job0) (list job1) 'user)))
- (test-equal "find-next-jobs: two jobs"
- (list 5 job0 job1)
- (find-next-jobs #:schedule schdl)))
- ;;; Check that 'find-next-jobs' returns #f when the schedule is empty.
- (let ((schdl (make-schedule '() '() 'user)))
- (test-equal "find-next-jobs: empty"
- (list #f)
- (find-next-jobs #:schedule schdl)))
- ;;; Check output of 'display-schedule' with a basic schedule.
- (test-assert "display-schedule: basic"
- (and (equal?
- "Thu Jan 1 00:00:05 1970 +0000\ndummy\n\n"
- (let* ((job0 (make-dummy-job #:user %user0 #:next-time 5))
- (job1 (make-dummy-job #:user %user1 #:next-time 10))
- (schdl (make-schedule (list job0) (list job1) 'user)))
- (with-output-to-string
- (λ () (display-schedule 1 #:schedule schdl)))))
- (equal?
- (string-append
- "Thu Jan 1 00:00:05 1970 +0000\ndummy\n\n"
- "Thu Jan 1 00:00:06 1970 +0000\ndummy\n\n")
- (let* ((job0 (make-dummy-job #:user %user0 #:next-time 5))
- (job1 (make-dummy-job #:user %user1 #:next-time 10))
- (schdl (make-schedule (list job0) (list job1) 'user)))
- (with-output-to-string
- (λ () (display-schedule 2 #:schedule schdl)))))))
- ;;; Check output of 'display-schedule' with an empty schedule.
- (let ((schdl (make-schedule '() '() 'user)))
- (test-equal "display-schedule: empty"
- ""
- (with-output-to-string
- (λ () (display-schedule 1 #:schedule schdl)))))
- ;;;
- ;;; Running jobs
- ;;;
- ;;; Import private global.
- (define number-children (@@ (mcron base) number-children))
- ;;; Import private procedures.
- (define update-number-children! (@@ (mcron base) update-number-children!))
- (define child-cleanup (@@ (mcron base) child-cleanup))
- (define run-job (@@ (mcron base) run-job))
- ;;; Check 'number-children' initial value.
- (test-equal "number-children: init"
- 0
- (unbox number-children))
- ;;; Check 'update-number-children!' incrementation.
- (test-equal "update-number-children!: 1+"
- 2
- (begin
- (update-number-children! 1+)
- (update-number-children! 1+)
- (unbox number-children)))
- ;;; Check 'update-number-children!' decrementation.
- (test-equal "update-number-children!: 1-"
- 1
- (begin
- (update-number-children! 1-)
- (unbox number-children)))
- ;;; Check 'update-number-children!' constant value.
- (test-equal "update-number-children!: set value"
- 0
- (begin
- (update-number-children! (const 0))
- (unbox number-children)))
- ;;; Check 'run-job' and 'child-cleanup'.
- ;;; XXX: Having to use the filesystem for a unit test is wrong.
- (let* ((filename (tmpnam))
- (action (lambda () (close-port (open-output-file filename))))
- (job (make-dummy-job #:user (getpw (getuid)) #:action action)))
- (dynamic-wind
- (const #t)
- (lambda ()
- (sigaction SIGCHLD (const #t))
- (let ((child-data (run-job job)))
- ;; Wait for the SIGCHLD signal sent when job exits.
- (pause)
- ;; Check 'run-job' result and if the number of children is up-to-date.
- (test-equal "run-job: basic"
- 1
- (and (access? filename F_OK)
- (unbox number-children)))
- (child-cleanup (list child-data)))
- ;; Check that 'child-cleanup' updates the number of children.
- (test-equal "child-cleanup: one" 0 (unbox number-children)))
- (lambda ()
- (and (access? filename F_OK) (delete-file filename))
- (sigaction SIGCHLD SIG_DFL))))
- (define (dummy-job/capture-output action)
- "Return the output of a dummy-job that ran ACTION."
- (with-output-to-string
- (lambda ()
- (dynamic-wind
- (const #t)
- (lambda ()
- (sigaction SIGCHLD (const #t))
- (let ((child-data
- (run-job
- (make-dummy-job
- #:user (getpw (getuid))
- #:action action))))
- (pause)
- (child-cleanup (list child-data))))
- (lambda ()
- #t
- (sigaction SIGCHLD SIG_DFL))))))
- (test-assert "run-job, output"
- (let ((output (dummy-job/capture-output
- (lambda ()
- (format #t "output line 1~%")
- (format #t "output line 2\nand 3~%")
- (system "echo poutine")
- (format (current-error-port)
- "some error~%")))))
- (assert (string-contains output "dummy: running"))
- (assert (string-contains output "dummy: output line 1"))
- (assert (string-contains output "dummy: and 3"))
- (assert (string-contains output "dummy: poutine"))
- (assert (string-contains output "dummy: some error"))
- (assert (string-contains output "dummy: completed in"))))
- (test-assert "validate-date-format, valid"
- (validate-date-format "~1"))
- (test-assert "validate-date-format, invalid"
- (catch 'mcron-error
- (lambda ()
- (validate-date-format "~¾")
- #f)
- (const #t)))
- (test-assert "validate-log-format, valid"
- (validate-log-format "the message only: ~3@*~a~%"))
- (test-assert "validate-log-format, invalid"
- (catch 'mcron-error
- (lambda ()
- ;; There aren't that many arguments!
- (validate-log-format "~20@*~a~%")
- #f)
- (const #t)))
- (test-assert "run-job, output with custom format"
- (let ((output (parameterize ((%log-format "the message only: ~3@*~a~%"))
- (dummy-job/capture-output
- (lambda ()
- (format #t "output line 1~%"))))))
- (string-contains output "the message only: output line 1\n")))
- (test-assert "run-job, failure"
- (let ((output (dummy-job/capture-output
- (lambda ()
- (error "that didn't go well")))))
- (assert (string-contains output "that didn't go well"))
- (assert (string-contains output "failed after"))))
- (test-assert "run-job, failure in shell action"
- (let ((output (dummy-job/capture-output
- (lambda ()
- (system "exit 1")))))
- (assert (string-contains output "unclean exit status"))
- (assert (string-contains output "failed after"))))
- (test-end)
|