gnu: Add wego.
[jackhill/guix/guix.git] / gnu / tests / base.scm
CommitLineData
e9f693d0 1;;; GNU Guix --- Functional package management for GNU
91ba90c1 2;;; Copyright © 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
c24b1547 3;;; Copyright © 2018 Clément Lassieur <clement@lassieur.org>
e9f693d0
LC
4;;;
5;;; This file is part of GNU Guix.
6;;;
7;;; GNU Guix is free software; you can redistribute it and/or modify it
8;;; under the terms of the GNU General Public License as published by
9;;; the Free Software Foundation; either version 3 of the License, or (at
10;;; your option) any later version.
11;;;
12;;; GNU Guix is distributed in the hope that it will be useful, but
13;;; WITHOUT ANY WARRANTY; without even the implied warranty of
14;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;;; GNU General Public License for more details.
16;;;
17;;; You should have received a copy of the GNU General Public License
18;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
19
20(define-module (gnu tests base)
21 #:use-module (gnu tests)
22 #:use-module (gnu system)
e9f693d0 23 #:use-module (gnu system shadow)
d2fa61bc 24 #:use-module (gnu system nss)
e9f693d0
LC
25 #:use-module (gnu system vm)
26 #:use-module (gnu services)
d2fa61bc
LC
27 #:use-module (gnu services base)
28 #:use-module (gnu services dbus)
29 #:use-module (gnu services avahi)
c311089b 30 #:use-module (gnu services mcron)
e9f693d0 31 #:use-module (gnu services shepherd)
d2fa61bc 32 #:use-module (gnu services networking)
76c321d8
LC
33 #:use-module (gnu packages base)
34 #:use-module (gnu packages bash)
fe933833
LC
35 #:use-module (gnu packages imagemagick)
36 #:use-module (gnu packages ocr)
e2f9832f 37 #:use-module (gnu packages package-management)
7f090203
LC
38 #:use-module (gnu packages linux)
39 #:use-module (gnu packages tmux)
e9f693d0
LC
40 #:use-module (guix gexp)
41 #:use-module (guix store)
76c321d8 42 #:use-module (guix monads)
e9f693d0
LC
43 #:use-module (guix packages)
44 #:use-module (srfi srfi-1)
4ab6a2f2 45 #:use-module (ice-9 match)
e3de272a 46 #:export (run-basic-test
c311089b 47 %test-basic-os
7f090203 48 %test-halt
76c321d8 49 %test-cleanup
d2fa61bc
LC
50 %test-mcron
51 %test-nss-mdns))
e9f693d0
LC
52
53(define %simple-os
892d9089 54 (simple-operating-system))
e9f693d0
LC
55
56\f
f7f292d3 57(define* (run-basic-test os command #:optional (name "basic")
6e71514a
MO
58 #:key
59 initialization
60 root-password
61 desktop?)
e3de272a
LC
62 "Return a derivation called NAME that tests basic features of the OS started
63using COMMAND, a gexp that evaluates to a list of strings. Compare some
f7f292d3
LC
64properties of running system to what's declared in OS, an <operating-system>.
65
66When INITIALIZATION is true, it must be a one-argument procedure that is
67passed a gexp denoting the marionette, and it must return gexp that is
68inserted before the first test. This is used to introduce an extra
91ba90c1
LC
69initialization step, such as entering a LUKS passphrase.
70
71When ROOT-PASSWORD is true, enter it as the root password when logging in.
72Otherwise assume that there is no password for root."
387e1754 73 (define special-files
efe7d19a 74 (service-value
387e1754
LC
75 (fold-services (operating-system-services os)
76 #:target-type special-files-service-type)))
77
4ab6a2f2
LC
78 (define guix&co
79 (match (package-transitive-propagated-inputs guix)
80 (((labels packages) ...)
81 (cons guix packages))))
82
e3de272a 83 (define test
caa78166
LC
84 (with-imported-modules '((gnu build marionette)
85 (guix build syscalls))
4ee96a79
LC
86 #~(begin
87 (use-modules (gnu build marionette)
caa78166 88 (guix build syscalls)
4ee96a79 89 (srfi srfi-1)
8c801194 90 (srfi srfi-19)
4ee96a79
LC
91 (srfi srfi-26)
92 (srfi srfi-64)
93 (ice-9 match))
94
95 (define marionette
96 (make-marionette #$command))
97
89b05442 98 (test-runner-current (system-test-runner #$output))
4ee96a79
LC
99 (test-begin "basic")
100
f7f292d3
LC
101 #$(and initialization
102 (initialization #~marionette))
103
4ee96a79
LC
104 (test-assert "uname"
105 (match (marionette-eval '(uname) marionette)
106 (#("Linux" host-name version _ architecture)
107 (and (string=? host-name
108 #$(operating-system-host-name os))
109 (string-prefix? #$(package-version
110 (operating-system-kernel os))
111 version)
112 (string-prefix? architecture %host-type)))))
113
3c78f5b5
LC
114 ;; Shepherd reads the config file *before* binding its control
115 ;; socket, so /var/run/shepherd/socket might not exist yet when the
116 ;; 'marionette' service is started.
117 (test-assert "shepherd socket ready"
118 (marionette-eval
119 `(begin
120 (use-modules (gnu services herd))
121 (let loop ((i 10))
122 (cond ((file-exists? (%shepherd-socket-file))
123 #t)
124 ((> i 0)
125 (sleep 1)
126 (loop (- i 1)))
127 (else
128 #f))))
129 marionette))
130
6ea6e147
LC
131 (test-eq "stdin is /dev/null"
132 'eof
133 ;; Make sure services can no longer read from stdin once the
134 ;; system has booted.
135 (marionette-eval
136 `(begin
137 (use-modules (gnu services herd))
138 (start 'user-processes)
139 ((@@ (gnu services herd) eval-there)
140 '(let ((result (read (current-input-port))))
141 (if (eof-object? result)
142 'eof
143 result))))
144 marionette))
145
4ee96a79
LC
146 (test-assert "shell and user commands"
147 ;; Is everything in $PATH?
148 (zero? (marionette-eval '(system "
e3de272a
LC
149. /etc/profile
150set -e -x
151guix --version
152ls --version
153grep --version
154info --version")
4ee96a79
LC
155 marionette)))
156
387e1754
LC
157 (test-equal "special files"
158 '#$special-files
159 (marionette-eval
160 '(begin
161 (use-modules (ice-9 match))
162
163 (map (match-lambda
164 ((file target)
165 (list file (readlink file))))
166 '#$special-files))
167 marionette))
168
4ee96a79
LC
169 (test-assert "accounts"
170 (let ((users (marionette-eval '(begin
171 (use-modules (ice-9 match))
172 (let loop ((result '()))
173 (match (getpw)
174 (#f (reverse result))
175 (x (loop (cons x result))))))
176 marionette)))
b2979344
LC
177 (lset= equal?
178 (map (lambda (user)
179 (list (passwd:name user)
180 (passwd:dir user)))
181 users)
4ee96a79 182 (list
b2979344
LC
183 #$@(map (lambda (account)
184 `(list ,(user-account-name account)
185 ,(user-account-home-directory account)))
4ee96a79
LC
186 (operating-system-user-accounts os))))))
187
188 (test-assert "shepherd services"
183605c8
LC
189 (let ((services (marionette-eval
190 '(begin
191 (use-modules (gnu services herd))
192
193 (map (compose car live-service-provision)
194 (current-services)))
195 marionette)))
4ee96a79
LC
196 (lset= eq?
197 (pk 'services services)
198 '(root #$@(operating-system-shepherd-service-names os)))))
199
8c801194
LC
200 (test-equal "libc honors /etc/localtime"
201 -7200 ;CEST = GMT+2
202 ;; Assume OS is configured to have a CEST timezone.
203 (let* ((sept-2021 (time-second
204 (date->time-utc
205 (make-date 0 0 00 12 01 09 2021 7200)))))
206 (marionette-eval
207 `(tm:gmtoff (localtime ,sept-2021))
208 marionette)))
209
d7113bb6
LC
210 (test-equal "/var/log/messages is not world-readable"
211 #o640 ;<https://bugs.gnu.org/40405>
212 (begin
213 (wait-for-file "/var/log/messages" marionette
214 #:read 'get-u8)
215 (marionette-eval '(stat:perms (lstat "/var/log/messages"))
216 marionette)))
217
ae763b5b
LC
218 (test-assert "homes"
219 (let ((homes
220 '#$(map user-account-home-directory
221 (filter user-account-create-home-directory?
222 (operating-system-user-accounts os)))))
223 (marionette-eval
224 `(begin
225 (use-modules (gnu services herd) (srfi srfi-1))
226
227 ;; Home directories are supposed to exist once 'user-homes'
228 ;; has been started.
229 (start-service 'user-homes)
230
231 (every (lambda (home)
232 (and (file-exists? home)
233 (file-is-directory? home)))
234 ',homes))
235 marionette)))
236
237 (test-assert "skeletons in home directories"
cf98d342 238 (let ((users+homes
ae763b5b
LC
239 '#$(filter-map (lambda (account)
240 (and (user-account-create-home-directory?
241 account)
242 (not (user-account-system? account))
cf98d342
LC
243 (list (user-account-name account)
244 (user-account-home-directory
245 account))))
ae763b5b
LC
246 (operating-system-user-accounts os))))
247 (marionette-eval
248 `(begin
f9d55c49
LC
249 (use-modules (guix build utils) (srfi srfi-1)
250 (ice-9 ftw) (ice-9 match))
cf98d342
LC
251
252 (every (match-lambda
253 ((user home)
254 ;; Make sure HOME has all the skeletons...
255 (and (null? (lset-difference string=?
256 (scandir "/etc/skel/")
257 (scandir home)))
258
259 ;; ... and that everything is user-owned.
260 (let* ((pw (getpwnam user))
261 (uid (passwd:uid pw))
262 (gid (passwd:gid pw))
263 (st (lstat home)))
264 (define (user-owned? file)
265 (= uid (stat:uid (lstat file))))
266
267 (and (= uid (stat:uid st))
268 (eq? 'directory (stat:type st))
269 (every user-owned?
270 (find-files home
271 #:directories? #t)))))))
272 ',users+homes))
ae763b5b
LC
273 marionette)))
274
41db5a75
LC
275 (test-equal "permissions on /root"
276 #o700
277 (let ((root-home #$(any (lambda (account)
278 (and (zero? (user-account-uid account))
279 (user-account-home-directory
280 account)))
281 (operating-system-user-accounts os))))
282 (stat:perms (marionette-eval `(stat ,root-home) marionette))))
283
d429878d
LC
284 (test-equal "ownership and permissions of /var/empty"
285 '(0 0 #o555)
286 (let ((st (marionette-eval `(stat "/var/empty") marionette)))
287 (list (stat:uid st) (stat:gid st)
288 (stat:perms st))))
289
41f76ae0
LC
290 (test-equal "no extra home directories"
291 '()
292
293 ;; Make sure the home directories that are not supposed to be
294 ;; created are indeed not created.
295 (let ((nonexistent
296 '#$(filter-map (lambda (user)
297 (and (not
298 (user-account-create-home-directory?
299 user))
300 (user-account-home-directory user)))
301 (operating-system-user-accounts os))))
302 (marionette-eval
303 `(begin
304 (use-modules (srfi srfi-1))
305
306 ;; Note: Do not flag "/var/empty".
307 (filter file-exists?
308 ',(remove (cut string-prefix? "/var/" <>)
309 nonexistent)))
310 marionette)))
311
4ee96a79
LC
312 (test-equal "login on tty1"
313 "root\n"
314 (begin
6e71514a
MO
315 ;; XXX: On desktop, GDM3 will switch to TTY7. If this happens
316 ;; after we switched to TTY1, we won't be able to login. Make
317 ;; sure to wait long enough before switching to TTY1.
318 (when #$desktop?
319 (sleep 30))
320
4ee96a79
LC
321 (marionette-control "sendkey ctrl-alt-f1" marionette)
322 ;; Wait for the 'term-tty1' service to be running (using
323 ;; 'start-service' is the simplest and most reliable way to do
324 ;; that.)
325 (marionette-eval
326 '(begin
327 (use-modules (gnu services herd))
328 (start-service 'term-tty1))
329 marionette)
330
331 ;; Now we can type.
91ba90c1
LC
332 (let ((password #$root-password))
333 (if password
334 (begin
335 (marionette-type "root\n" marionette)
336 (wait-for-screen-text marionette
337 (lambda (text)
338 (string-contains text "Password"))
339 #:ocrad
340 #$(file-append ocrad "/bin/ocrad"))
341 (marionette-type (string-append password "\n\n")
342 marionette))
343 (marionette-type "root\n\n" marionette)))
344 (marionette-type "id -un > logged-in\n" marionette)
4ee96a79
LC
345
346 ;; It can take a while before the shell commands are executed.
4ee96a79 347 (marionette-eval '(use-modules (rnrs io ports)) marionette)
13877c34
LC
348 (wait-for-file "/root/logged-in" marionette
349 #:read 'get-string-all)))
4ee96a79 350
e6b1a224
LC
351 (test-equal "getlogin on tty1"
352 "\"root\""
353 (begin
354 ;; Assume we logged in in the previous test and type.
8b310793
LC
355 (marionette-type "guile -c '(write (getlogin))' > /root/login-id.tmp\n"
356 marionette)
357 (marionette-type "mv /root/login-id{.tmp,}\n"
e6b1a224
LC
358 marionette)
359
360 ;; It can take a while before the shell commands are executed.
361 (marionette-eval '(use-modules (rnrs io ports)) marionette)
362 (wait-for-file "/root/login-id" marionette
363 #:read 'get-string-all)))
364
caa78166
LC
365 ;; There should be one utmpx entry for the user logged in on tty1.
366 (test-equal "utmpx entry"
367 '(("root" "tty1" #f))
368 (marionette-eval
369 '(begin
370 (use-modules (guix build syscalls)
371 (srfi srfi-1))
372
373 (filter-map (lambda (entry)
374 (and (equal? (login-type USER_PROCESS)
375 (utmpx-login-type entry))
376 (list (utmpx-user entry) (utmpx-line entry)
377 (utmpx-host entry))))
378 (utmpx-entries)))
379 marionette))
380
2986995b
LC
381 ;; Likewise for /var/log/wtmp (used by 'last').
382 (test-assert "wtmp entry"
383 (match (marionette-eval
384 '(begin
385 (use-modules (guix build syscalls)
386 (srfi srfi-1))
387
388 (define (entry->list entry)
389 (list (utmpx-user entry) (utmpx-line entry)
390 (utmpx-host entry) (utmpx-login-type entry)))
391
392 (call-with-input-file "/var/log/wtmp"
393 (lambda (port)
394 (let loop ((result '()))
395 (if (eof-object? (peek-char port))
396 (map entry->list (reverse result))
397 (loop (cons (read-utmpx port) result)))))))
398 marionette)
399 (((users lines hosts types) ..1)
400 (every (lambda (type)
401 (eqv? type (login-type LOGIN_PROCESS)))
402 types))))
403
4ee96a79
LC
404 (test-assert "host name resolution"
405 (match (marionette-eval
406 '(begin
407 ;; Wait for nscd or our requests go through it.
408 (use-modules (gnu services herd))
409 (start-service 'nscd)
410
411 (list (getaddrinfo "localhost")
412 (getaddrinfo #$(operating-system-host-name os))))
413 marionette)
414 ((((? vector?) ..1) ((? vector?) ..1))
415 #t)
416 (x
417 (pk 'failure x #f))))
418
d3f75179
LC
419 (test-equal "nscd invalidate action"
420 '(#t) ;one value, #t
421 (marionette-eval '(with-shepherd-action 'nscd ('invalidate "hosts")
422 result
423 result)
424 marionette))
425
33572a36
LC
426 ;; FIXME: The 'invalidate' action can't reliably obtain the exit
427 ;; code of 'nscd' so skip this test.
428 (test-skip 1)
d3f75179
LC
429 (test-equal "nscd invalidate action, wrong table"
430 '(#f) ;one value, #f
431 (marionette-eval '(with-shepherd-action 'nscd ('invalidate "xyz")
432 result
433 result)
434 marionette))
435
4ee96a79
LC
436 (test-equal "host not found"
437 #f
e3de272a 438 (marionette-eval
4ee96a79
LC
439 '(false-if-exception (getaddrinfo "does-not-exist"))
440 marionette))
441
ab3a6450
LC
442 (test-equal "locale"
443 "en_US.utf8"
cc73339b
LC
444 (marionette-eval '(let ((before (setlocale LC_ALL "en_US.utf8")))
445 (setlocale LC_ALL before))
ab3a6450
LC
446 marionette))
447
d5094c81
LC
448 (test-eq "/run/current-system is a GC root"
449 'success!
40d28609
LC
450 (marionette-eval '(begin
451 ;; Make sure the (guix …) modules are found.
4ab6a2f2
LC
452 (eval-when (expand load eval)
453 (set! %load-path
454 (append (map (lambda (package)
455 (string-append package
456 "/share/guile/site/"
457 (effective-version)))
458 '#$guix&co)
459 %load-path)))
40d28609
LC
460
461 (use-modules (srfi srfi-34) (guix store))
462
463 (let ((system (readlink "/run/current-system")))
ba926e35 464 (guard (c ((store-protocol-error? c)
d5094c81
LC
465 (and (file-exists? system)
466 'success!)))
40d28609
LC
467 (with-store store
468 (delete-paths store (list system))
469 #f))))
470 marionette))
471
334bda9a
LC
472 ;; This symlink is currently unused, but better have it point to the
473 ;; right place. See
474 ;; <https://lists.gnu.org/archive/html/guix-devel/2016-08/msg01641.html>.
475 (test-equal "/var/guix/gcroots/profiles is a valid symlink"
476 "/var/guix/profiles"
477 (marionette-eval '(readlink "/var/guix/gcroots/profiles")
478 marionette))
479
3302e03b
LC
480 (test-equal "guix-daemon set-http-proxy action"
481 '(#t) ;one value, #t
482 (marionette-eval '(with-shepherd-action 'guix-daemon
483 ('set-http-proxy "http://localhost:8118")
484 result
485 result)
486 marionette))
487
488 (test-equal "guix-daemon set-http-proxy action, clear"
489 '(#t) ;one value, #t
490 (marionette-eval '(with-shepherd-action 'guix-daemon
491 ('set-http-proxy)
492 result
493 result)
494 marionette))
334bda9a 495
4ee96a79
LC
496 (test-assert "screendump"
497 (begin
d27e871e
MO
498 (let ((capture
499 (string-append #$output "/tty1.ppm")))
500 (marionette-control
501 (string-append "screendump " capture) marionette)
502 (file-exists? capture))))
4ee96a79 503
fe933833
LC
504 (test-assert "screen text"
505 (let ((text (marionette-screen-text marionette
506 #:ocrad
507 #$(file-append ocrad
508 "/bin/ocrad"))))
509 ;; Check whether the welcome message and shell prompt are
510 ;; displayed. Note: OCR confuses "y" and "V" for instance, so
511 ;; we cannot reliably match the whole text.
512 (and (string-contains text "This is the GNU")
513 (string-contains text
514 (string-append
515 "root@"
516 #$(operating-system-host-name os))))))
517
1fb75128 518 (test-end))))
4ee96a79
LC
519
520 (gexp->derivation name test))
e3de272a 521
e9f693d0 522(define %test-basic-os
98b65b5f
LC
523 (system-test
524 (name "basic")
525 (description
125af57e 526 "Instrument %SIMPLE-OS, run it in a VM, and run a series of basic
98b65b5f
LC
527functionality tests.")
528 (value
8b113790
LC
529 (let* ((os (marionette-operating-system
530 %simple-os
531 #:imported-modules '((gnu services herd)
532 (guix combinators))))
533 (vm (virtual-machine os)))
98b65b5f
LC
534 ;; XXX: Add call to 'virtualized-operating-system' to get the exact same
535 ;; set of services as the OS produced by
536 ;; 'system-qemu-image/shared-store-script'.
537 (run-basic-test (virtualized-operating-system os '())
8b113790 538 #~(list #$vm))))))
c311089b
LC
539
540\f
7f090203
LC
541;;;
542;;; Halt.
543;;;
544
545(define (run-halt-test vm)
546 ;; As reported in <http://bugs.gnu.org/26931>, running tmux would previously
547 ;; lead the 'stop' method of 'user-processes' to an infinite loop, with the
548 ;; tmux server process as a zombie that remains in the list of processes.
549 ;; This test reproduces this scenario.
550 (define test
551 (with-imported-modules '((gnu build marionette))
552 #~(begin
553 (use-modules (gnu build marionette))
554
555 (define marionette
556 (make-marionette '(#$vm)))
557
558 (define ocrad
559 #$(file-append ocrad "/bin/ocrad"))
560
561 ;; Wait for tty1 and log in.
562 (marionette-eval '(begin
563 (use-modules (gnu services herd))
564 (start-service 'term-tty1))
565 marionette)
566 (marionette-type "root\n" marionette)
7f090203
LC
567
568 ;; Start tmux and wait for it to be ready.
569 (marionette-type "tmux new-session 'echo 1 > /ready; bash'\n"
570 marionette)
571 (wait-for-file "/ready" marionette)
572
573 ;; Make sure to stop the test after a while.
574 (sigaction SIGALRM (lambda _
575 (format (current-error-port)
576 "FAIL: Time is up, but VM still running.\n")
577 (primitive-exit 1)))
578 (alarm 10)
579
580 ;; Get debugging info.
581 (marionette-eval '(current-output-port
582 (open-file "/dev/console" "w0"))
583 marionette)
584 (marionette-eval '(system* #$(file-append procps "/bin/ps")
585 "-eo" "pid,ppid,stat,comm")
586 marionette)
587
588 ;; See if 'halt' actually works.
589 (marionette-eval '(system* "/run/current-system/profile/sbin/halt")
590 marionette)
591
592 ;; If we reach this line, that means the VM was properly stopped in
593 ;; a timely fashion.
594 (alarm 0)
595 (call-with-output-file #$output
596 (lambda (port)
597 (display "success!" port))))))
598
599 (gexp->derivation "halt" test))
600
601(define %test-halt
602 (system-test
603 (name "halt")
604 (description
605 "Use the 'halt' command and make sure it succeeds and does not get stuck
606in a loop. See <http://bugs.gnu.org/26931>.")
607 (value
608 (let ((os (marionette-operating-system
609 (operating-system
610 (inherit %simple-os)
611 (packages (cons tmux %base-packages)))
612 #:imported-modules '((gnu services herd)
613 (guix combinators)))))
614 (run-halt-test (virtual-machine os))))))
615
616\f
76c321d8
LC
617;;;
618;;; Cleanup of /tmp, /var/run, etc.
619;;;
620
621(define %cleanup-os
622 (simple-operating-system
623 (simple-service 'dirty-things
624 boot-service-type
378daa8c
LC
625 (let ((script (plain-file
626 "create-utf8-file.sh"
627 (string-append
628 "echo $0: dirtying /tmp...\n"
629 "set -e; set -x\n"
630 "touch /witness\n"
631 "exec touch /tmp/λαμβδα"))))
632 (with-imported-modules '((guix build utils))
633 #~(begin
634 (setenv "PATH"
635 #$(file-append coreutils "/bin"))
636 (invoke #$(file-append bash "/bin/sh")
637 #$script)))))))
76c321d8
LC
638
639(define (run-cleanup-test name)
640 (define os
641 (marionette-operating-system %cleanup-os
642 #:imported-modules '((gnu services herd)
643 (guix combinators))))
644 (define test
645 (with-imported-modules '((gnu build marionette))
646 #~(begin
647 (use-modules (gnu build marionette)
648 (srfi srfi-64)
649 (ice-9 match))
650
651 (define marionette
652 (make-marionette (list #$(virtual-machine os))))
653
89b05442 654 (test-runner-current (system-test-runner #$output))
76c321d8
LC
655 (test-begin "cleanup")
656
657 (test-assert "dirty service worked"
658 (marionette-eval '(file-exists? "/witness") marionette))
659
660 (test-equal "/tmp cleaned up"
661 '("." "..")
662 (marionette-eval '(begin
663 (use-modules (ice-9 ftw))
664 (scandir "/tmp"))
665 marionette))
666
1fb75128 667 (test-end))))
76c321d8
LC
668
669 (gexp->derivation "cleanup" test))
670
671(define %test-cleanup
672 ;; See <https://bugs.gnu.org/26353>.
673 (system-test
674 (name "cleanup")
675 (description "Make sure the 'cleanup' service can remove files with
676non-ASCII names from /tmp.")
677 (value (run-cleanup-test name))))
678
679\f
c311089b
LC
680;;;
681;;; Mcron.
682;;;
683
684(define %mcron-os
685 ;; System with an mcron service, with one mcron job for "root" and one mcron
cfbf6de1 686 ;; job for an unprivileged user.
67a51b67 687 (let ((job1 #~(job '(next-second '(0 5 10 15 20 25 30 35 40 45 50 55))
c311089b 688 (lambda ()
67a51b67
LC
689 (unless (file-exists? "witness")
690 (call-with-output-file "witness"
691 (lambda (port)
692 (display (list (getuid) (getgid)) port)))))))
c311089b
LC
693 (job2 #~(job next-second-from
694 (lambda ()
695 (call-with-output-file "witness"
696 (lambda (port)
697 (display (list (getuid) (getgid)) port))))
698 #:user "alice"))
699 (job3 #~(job next-second-from ;to test $PATH
700 "touch witness-touch")))
892d9089 701 (simple-operating-system
84a2de36
LC
702 (service mcron-service-type
703 (mcron-configuration (jobs (list job1 job2 job3)))))))
c311089b
LC
704
705(define (run-mcron-test name)
8b113790
LC
706 (define os
707 (marionette-operating-system
708 %mcron-os
709 #:imported-modules '((gnu services herd)
710 (guix combinators))))
711
712 (define test
713 (with-imported-modules '((gnu build marionette))
714 #~(begin
715 (use-modules (gnu build marionette)
716 (srfi srfi-64)
717 (ice-9 match))
718
719 (define marionette
720 (make-marionette (list #$(virtual-machine os))))
721
89b05442 722 (test-runner-current (system-test-runner #$output))
8b113790
LC
723 (test-begin "mcron")
724
c24b1547 725 (test-assert "service running"
8b113790
LC
726 (marionette-eval
727 '(begin
728 (use-modules (gnu services herd))
c24b1547 729 (start-service 'mcron))
8b113790
LC
730 marionette))
731
732 ;; Make sure root's mcron job runs, has its cwd set to "/root", and
733 ;; runs with the right UID/GID.
734 (test-equal "root's job"
735 '(0 0)
736 (wait-for-file "/root/witness" marionette))
737
738 ;; Likewise for Alice's job. We cannot know what its GID is since
739 ;; it's chosen by 'groupadd', but it's strictly positive.
740 (test-assert "alice's job"
741 (match (wait-for-file "/home/alice/witness" marionette)
742 ((1000 gid)
743 (>= gid 100))))
744
745 ;; Last, the job that uses a command; allows us to test whether
077f1e63 746 ;; $PATH is sane.
8b113790 747 (test-equal "root's job with command"
077f1e63
LC
748 ""
749 (wait-for-file "/root/witness-touch" marionette
750 #:read '(@ (ice-9 rdelim) read-string)))
8b113790 751
147c5aa5
LC
752 ;; Make sure the 'schedule' action is accepted.
753 (test-equal "schedule action"
754 '(#t) ;one value, #t
755 (marionette-eval '(with-shepherd-action 'mcron ('schedule) result
756 result)
757 marionette))
758
1fb75128 759 (test-end))))
8b113790
LC
760
761 (gexp->derivation name test))
c311089b
LC
762
763(define %test-mcron
764 (system-test
765 (name "mcron")
766 (description "Make sure the mcron service works as advertised.")
767 (value (run-mcron-test name))))
d2fa61bc
LC
768
769\f
770;;;
771;;; Avahi and NSS-mDNS.
772;;;
773
774(define %avahi-os
775 (operating-system
776 (inherit %simple-os)
777 (name-service-switch %mdns-host-lookup-nss)
2e04ab71
LC
778 (services (cons* (service avahi-service-type
779 (avahi-configuration (debug? #t)))
d2fa61bc 780 (dbus-service)
39d7fdce 781 (service dhcp-client-service-type) ;needed for multicast
d2fa61bc
LC
782
783 ;; Enable heavyweight debugging output.
784 (modify-services (operating-system-user-services
785 %simple-os)
786 (nscd-service-type config
787 => (nscd-configuration
788 (inherit config)
789 (debug-level 3)
790 (log-file "/dev/console")))
791 (syslog-service-type config
792 =>
ec2e2f6c
DC
793 (syslog-configuration
794 (inherit config)
795 (config-file
796 (plain-file
797 "syslog.conf"
798 "*.* /dev/console\n")))))))))
d2fa61bc
LC
799
800(define (run-nss-mdns-test)
801 ;; Test resolution of '.local' names via libc. Start the marionette service
802 ;; *after* nscd. Failing to do that, libc will try to connect to nscd,
803 ;; fail, then never try again (see '__nss_not_use_nscd_hosts' in libc),
804 ;; leading to '.local' resolution failures.
8b113790
LC
805 (define os
806 (marionette-operating-system
807 %avahi-os
808 #:requirements '(nscd)
809 #:imported-modules '((gnu services herd)
810 (guix combinators))))
4ee96a79 811
8b113790
LC
812 (define mdns-host-name
813 (string-append (operating-system-host-name os)
814 ".local"))
4ee96a79 815
8b113790
LC
816 (define test
817 (with-imported-modules '((gnu build marionette))
818 #~(begin
819 (use-modules (gnu build marionette)
820 (srfi srfi-1)
821 (srfi srfi-64)
822 (ice-9 match))
823
824 (define marionette
825 (make-marionette (list #$(virtual-machine os))))
826
827 (mkdir #$output)
828 (chdir #$output)
829
1fb75128 830 (test-runner-current (system-test-runner))
8b113790
LC
831 (test-begin "avahi")
832
c24b1547 833 (test-assert "nscd PID file is created"
8b113790
LC
834 (marionette-eval
835 '(begin
836 (use-modules (gnu services herd))
c24b1547
CL
837 (start-service 'nscd))
838 marionette))
839
840 (test-assert "nscd is listening on its socket"
841 (marionette-eval
842 ;; XXX: Work around a race condition in nscd: nscd creates its
843 ;; PID file before it is listening on its socket.
844 '(let ((sock (socket PF_UNIX SOCK_STREAM 0)))
845 (let try ()
846 (catch 'system-error
847 (lambda ()
848 (connect sock AF_UNIX "/var/run/nscd/socket")
849 (close-port sock)
850 (format #t "nscd is ready~%")
851 #t)
852 (lambda args
853 (format #t "waiting for nscd...~%")
854 (usleep 500000)
855 (try)))))
856 marionette))
857
858 (test-assert "avahi is running"
859 (marionette-eval
860 '(begin
861 (use-modules (gnu services herd))
862 (start-service 'avahi-daemon))
863 marionette))
8b113790 864
c24b1547
CL
865 (test-assert "network is up"
866 (marionette-eval
867 '(begin
868 (use-modules (gnu services herd))
869 (start-service 'networking))
8b113790
LC
870 marionette))
871
872 (test-equal "avahi-resolve-host-name"
873 0
874 (marionette-eval
875 '(system*
876 "/run/current-system/profile/bin/avahi-resolve-host-name"
877 "-v" #$mdns-host-name)
878 marionette))
879
880 (test-equal "avahi-browse"
881 0
882 (marionette-eval
572c59a7 883 '(system* "/run/current-system/profile/bin/avahi-browse" "-avt")
8b113790
LC
884 marionette))
885
886 (test-assert "getaddrinfo .local"
887 ;; Wait for the 'avahi-daemon' service and perform a resolution.
888 (match (marionette-eval
889 '(getaddrinfo #$mdns-host-name)
890 marionette)
891 (((? vector? addrinfos) ..1)
892 (pk 'getaddrinfo addrinfos)
893 (and (any (lambda (ai)
894 (= AF_INET (addrinfo:fam ai)))
895 addrinfos)
896 (any (lambda (ai)
897 (= AF_INET6 (addrinfo:fam ai)))
898 addrinfos)))))
899
900 (test-assert "gethostbyname .local"
901 (match (pk 'gethostbyname
902 (marionette-eval '(gethostbyname #$mdns-host-name)
903 marionette))
904 ((? vector? result)
905 (and (string=? (hostent:name result) #$mdns-host-name)
906 (= (hostent:addrtype result) AF_INET)))))
907
908
1fb75128 909 (test-end))))
8b113790
LC
910
911 (gexp->derivation "nss-mdns" test))
d2fa61bc
LC
912
913(define %test-nss-mdns
914 (system-test
915 (name "nss-mdns")
916 (description
917 "Test Avahi's multicast-DNS implementation, and in particular, test its
918glibc name service switch (NSS) module.")
919 (value (run-nss-mdns-test))))