gnu: qt: Update to 5.8.0.
[jackhill/guix/guix.git] / gnu / tests / base.scm
CommitLineData
e9f693d0 1;;; GNU Guix --- Functional package management for GNU
caa78166 2;;; Copyright © 2016, 2017 Ludovic Courtès <ludo@gnu.org>
e9f693d0
LC
3;;;
4;;; This file is part of GNU Guix.
5;;;
6;;; GNU Guix is free software; you can redistribute it and/or modify it
7;;; under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 3 of the License, or (at
9;;; your option) any later version.
10;;;
11;;; GNU Guix is distributed in the hope that it will be useful, but
12;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19(define-module (gnu tests base)
20 #:use-module (gnu tests)
21 #:use-module (gnu system)
e9f693d0 22 #:use-module (gnu system shadow)
d2fa61bc 23 #:use-module (gnu system nss)
e9f693d0
LC
24 #:use-module (gnu system vm)
25 #:use-module (gnu services)
d2fa61bc
LC
26 #:use-module (gnu services base)
27 #:use-module (gnu services dbus)
28 #:use-module (gnu services avahi)
c311089b 29 #:use-module (gnu services mcron)
e9f693d0 30 #:use-module (gnu services shepherd)
d2fa61bc 31 #:use-module (gnu services networking)
fe933833
LC
32 #:use-module (gnu packages imagemagick)
33 #:use-module (gnu packages ocr)
e2f9832f 34 #:use-module (gnu packages package-management)
e9f693d0
LC
35 #:use-module (guix gexp)
36 #:use-module (guix store)
37 #:use-module (guix monads)
38 #:use-module (guix packages)
39 #:use-module (srfi srfi-1)
e3de272a 40 #:export (run-basic-test
c311089b 41 %test-basic-os
d2fa61bc
LC
42 %test-mcron
43 %test-nss-mdns))
e9f693d0
LC
44
45(define %simple-os
892d9089 46 (simple-operating-system))
e9f693d0
LC
47
48\f
f7f292d3
LC
49(define* (run-basic-test os command #:optional (name "basic")
50 #:key initialization)
e3de272a
LC
51 "Return a derivation called NAME that tests basic features of the OS started
52using COMMAND, a gexp that evaluates to a list of strings. Compare some
f7f292d3
LC
53properties of running system to what's declared in OS, an <operating-system>.
54
55When INITIALIZATION is true, it must be a one-argument procedure that is
56passed a gexp denoting the marionette, and it must return gexp that is
57inserted before the first test. This is used to introduce an extra
58initialization step, such as entering a LUKS passphrase."
387e1754 59 (define special-files
efe7d19a 60 (service-value
387e1754
LC
61 (fold-services (operating-system-services os)
62 #:target-type special-files-service-type)))
63
e3de272a 64 (define test
caa78166
LC
65 (with-imported-modules '((gnu build marionette)
66 (guix build syscalls))
4ee96a79
LC
67 #~(begin
68 (use-modules (gnu build marionette)
caa78166 69 (guix build syscalls)
4ee96a79
LC
70 (srfi srfi-1)
71 (srfi srfi-26)
72 (srfi srfi-64)
73 (ice-9 match))
74
75 (define marionette
76 (make-marionette #$command))
77
78 (mkdir #$output)
79 (chdir #$output)
80
81 (test-begin "basic")
82
f7f292d3
LC
83 #$(and initialization
84 (initialization #~marionette))
85
4ee96a79
LC
86 (test-assert "uname"
87 (match (marionette-eval '(uname) marionette)
88 (#("Linux" host-name version _ architecture)
89 (and (string=? host-name
90 #$(operating-system-host-name os))
91 (string-prefix? #$(package-version
92 (operating-system-kernel os))
93 version)
94 (string-prefix? architecture %host-type)))))
95
96 (test-assert "shell and user commands"
97 ;; Is everything in $PATH?
98 (zero? (marionette-eval '(system "
e3de272a
LC
99. /etc/profile
100set -e -x
101guix --version
102ls --version
103grep --version
104info --version")
4ee96a79
LC
105 marionette)))
106
387e1754
LC
107 (test-equal "special files"
108 '#$special-files
109 (marionette-eval
110 '(begin
111 (use-modules (ice-9 match))
112
113 (map (match-lambda
114 ((file target)
115 (list file (readlink file))))
116 '#$special-files))
117 marionette))
118
4ee96a79
LC
119 (test-assert "accounts"
120 (let ((users (marionette-eval '(begin
121 (use-modules (ice-9 match))
122 (let loop ((result '()))
123 (match (getpw)
124 (#f (reverse result))
125 (x (loop (cons x result))))))
126 marionette)))
127 (lset= string=?
128 (map passwd:name users)
129 (list
130 #$@(map user-account-name
131 (operating-system-user-accounts os))))))
132
133 (test-assert "shepherd services"
183605c8
LC
134 (let ((services (marionette-eval
135 '(begin
136 (use-modules (gnu services herd))
137
138 (map (compose car live-service-provision)
139 (current-services)))
140 marionette)))
4ee96a79
LC
141 (lset= eq?
142 (pk 'services services)
143 '(root #$@(operating-system-shepherd-service-names os)))))
144
ae763b5b
LC
145 (test-assert "homes"
146 (let ((homes
147 '#$(map user-account-home-directory
148 (filter user-account-create-home-directory?
149 (operating-system-user-accounts os)))))
150 (marionette-eval
151 `(begin
152 (use-modules (gnu services herd) (srfi srfi-1))
153
154 ;; Home directories are supposed to exist once 'user-homes'
155 ;; has been started.
156 (start-service 'user-homes)
157
158 (every (lambda (home)
159 (and (file-exists? home)
160 (file-is-directory? home)))
161 ',homes))
162 marionette)))
163
164 (test-assert "skeletons in home directories"
cf98d342 165 (let ((users+homes
ae763b5b
LC
166 '#$(filter-map (lambda (account)
167 (and (user-account-create-home-directory?
168 account)
169 (not (user-account-system? account))
cf98d342
LC
170 (list (user-account-name account)
171 (user-account-home-directory
172 account))))
ae763b5b
LC
173 (operating-system-user-accounts os))))
174 (marionette-eval
175 `(begin
cf98d342
LC
176 (use-modules (srfi srfi-1) (ice-9 ftw)
177 (ice-9 match))
178
179 (every (match-lambda
180 ((user home)
181 ;; Make sure HOME has all the skeletons...
182 (and (null? (lset-difference string=?
183 (scandir "/etc/skel/")
184 (scandir home)))
185
186 ;; ... and that everything is user-owned.
187 (let* ((pw (getpwnam user))
188 (uid (passwd:uid pw))
189 (gid (passwd:gid pw))
190 (st (lstat home)))
191 (define (user-owned? file)
192 (= uid (stat:uid (lstat file))))
193
194 (and (= uid (stat:uid st))
195 (eq? 'directory (stat:type st))
196 (every user-owned?
197 (find-files home
198 #:directories? #t)))))))
199 ',users+homes))
ae763b5b
LC
200 marionette)))
201
4ee96a79
LC
202 (test-equal "login on tty1"
203 "root\n"
204 (begin
205 (marionette-control "sendkey ctrl-alt-f1" marionette)
206 ;; Wait for the 'term-tty1' service to be running (using
207 ;; 'start-service' is the simplest and most reliable way to do
208 ;; that.)
209 (marionette-eval
210 '(begin
211 (use-modules (gnu services herd))
212 (start-service 'term-tty1))
213 marionette)
214
215 ;; Now we can type.
216 (marionette-type "root\n\nid -un > logged-in\n" marionette)
217
218 ;; It can take a while before the shell commands are executed.
4ee96a79 219 (marionette-eval '(use-modules (rnrs io ports)) marionette)
056d0b40
LC
220 (marionette-eval
221 '(let loop ((i 0))
222 (catch 'system-error
223 (lambda ()
224 (call-with-input-file "/root/logged-in"
225 get-string-all))
226 (lambda args
227 (if (and (< i 15) (= ENOENT (system-error-errno args)))
228 (begin
229 (sleep 1)
230 (loop (+ i 1)))
231 (apply throw args)))))
232 marionette)))
4ee96a79 233
caa78166
LC
234 ;; There should be one utmpx entry for the user logged in on tty1.
235 (test-equal "utmpx entry"
236 '(("root" "tty1" #f))
237 (marionette-eval
238 '(begin
239 (use-modules (guix build syscalls)
240 (srfi srfi-1))
241
242 (filter-map (lambda (entry)
243 (and (equal? (login-type USER_PROCESS)
244 (utmpx-login-type entry))
245 (list (utmpx-user entry) (utmpx-line entry)
246 (utmpx-host entry))))
247 (utmpx-entries)))
248 marionette))
249
2986995b
LC
250 ;; Likewise for /var/log/wtmp (used by 'last').
251 (test-assert "wtmp entry"
252 (match (marionette-eval
253 '(begin
254 (use-modules (guix build syscalls)
255 (srfi srfi-1))
256
257 (define (entry->list entry)
258 (list (utmpx-user entry) (utmpx-line entry)
259 (utmpx-host entry) (utmpx-login-type entry)))
260
261 (call-with-input-file "/var/log/wtmp"
262 (lambda (port)
263 (let loop ((result '()))
264 (if (eof-object? (peek-char port))
265 (map entry->list (reverse result))
266 (loop (cons (read-utmpx port) result)))))))
267 marionette)
268 (((users lines hosts types) ..1)
269 (every (lambda (type)
270 (eqv? type (login-type LOGIN_PROCESS)))
271 types))))
272
4ee96a79
LC
273 (test-assert "host name resolution"
274 (match (marionette-eval
275 '(begin
276 ;; Wait for nscd or our requests go through it.
277 (use-modules (gnu services herd))
278 (start-service 'nscd)
279
280 (list (getaddrinfo "localhost")
281 (getaddrinfo #$(operating-system-host-name os))))
282 marionette)
283 ((((? vector?) ..1) ((? vector?) ..1))
284 #t)
285 (x
286 (pk 'failure x #f))))
287
288 (test-equal "host not found"
289 #f
e3de272a 290 (marionette-eval
4ee96a79
LC
291 '(false-if-exception (getaddrinfo "does-not-exist"))
292 marionette))
293
ab3a6450
LC
294 (test-equal "locale"
295 "en_US.utf8"
cc73339b
LC
296 (marionette-eval '(let ((before (setlocale LC_ALL "en_US.utf8")))
297 (setlocale LC_ALL before))
ab3a6450
LC
298 marionette))
299
d5094c81
LC
300 (test-eq "/run/current-system is a GC root"
301 'success!
40d28609
LC
302 (marionette-eval '(begin
303 ;; Make sure the (guix …) modules are found.
e2f9832f
LC
304 ;;
305 ;; XXX: Currently shepherd and marionette run
306 ;; on Guile 2.0 whereas Guix is on 2.2. Yet
307 ;; we should be able to load the 2.0 Scheme
308 ;; files since it's pure Scheme.
309 (add-to-load-path
310 #+(file-append guix "/share/guile/site/2.2"))
40d28609
LC
311
312 (use-modules (srfi srfi-34) (guix store))
313
314 (let ((system (readlink "/run/current-system")))
315 (guard (c ((nix-protocol-error? c)
d5094c81
LC
316 (and (file-exists? system)
317 'success!)))
40d28609
LC
318 (with-store store
319 (delete-paths store (list system))
320 #f))))
321 marionette))
322
334bda9a
LC
323 ;; This symlink is currently unused, but better have it point to the
324 ;; right place. See
325 ;; <https://lists.gnu.org/archive/html/guix-devel/2016-08/msg01641.html>.
326 (test-equal "/var/guix/gcroots/profiles is a valid symlink"
327 "/var/guix/profiles"
328 (marionette-eval '(readlink "/var/guix/gcroots/profiles")
329 marionette))
330
331
4ee96a79
LC
332 (test-assert "screendump"
333 (begin
334 (marionette-control (string-append "screendump " #$output
335 "/tty1.ppm")
336 marionette)
337 (file-exists? "tty1.ppm")))
338
fe933833
LC
339 (test-assert "screen text"
340 (let ((text (marionette-screen-text marionette
341 #:ocrad
342 #$(file-append ocrad
343 "/bin/ocrad"))))
344 ;; Check whether the welcome message and shell prompt are
345 ;; displayed. Note: OCR confuses "y" and "V" for instance, so
346 ;; we cannot reliably match the whole text.
347 (and (string-contains text "This is the GNU")
348 (string-contains text
349 (string-append
350 "root@"
351 #$(operating-system-host-name os))))))
352
4ee96a79
LC
353 (test-end)
354 (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
355
356 (gexp->derivation name test))
e3de272a 357
e9f693d0 358(define %test-basic-os
98b65b5f
LC
359 (system-test
360 (name "basic")
361 (description
125af57e 362 "Instrument %SIMPLE-OS, run it in a VM, and run a series of basic
98b65b5f
LC
363functionality tests.")
364 (value
365 (mlet* %store-monad ((os -> (marionette-operating-system
366 %simple-os
367 #:imported-modules '((gnu services herd)
368 (guix combinators))))
369 (run (system-qemu-image/shared-store-script
370 os #:graphic? #f)))
371 ;; XXX: Add call to 'virtualized-operating-system' to get the exact same
372 ;; set of services as the OS produced by
373 ;; 'system-qemu-image/shared-store-script'.
374 (run-basic-test (virtualized-operating-system os '())
375 #~(list #$run))))))
c311089b
LC
376
377\f
378;;;
379;;; Mcron.
380;;;
381
382(define %mcron-os
383 ;; System with an mcron service, with one mcron job for "root" and one mcron
384 ;; job for an unprivileged user (note: #:user is an 'mcron2' thing.)
385 (let ((job1 #~(job next-second-from
386 (lambda ()
387 (call-with-output-file "witness"
388 (lambda (port)
389 (display (list (getuid) (getgid)) port))))))
390 (job2 #~(job next-second-from
391 (lambda ()
392 (call-with-output-file "witness"
393 (lambda (port)
394 (display (list (getuid) (getgid)) port))))
395 #:user "alice"))
396 (job3 #~(job next-second-from ;to test $PATH
397 "touch witness-touch")))
892d9089
LC
398 (simple-operating-system
399 (mcron-service (list job1 job2 job3)))))
c311089b
LC
400
401(define (run-mcron-test name)
402 (mlet* %store-monad ((os -> (marionette-operating-system
403 %mcron-os
404 #:imported-modules '((gnu services herd)
405 (guix combinators))))
406 (command (system-qemu-image/shared-store-script
407 os #:graphic? #f)))
408 (define test
4ee96a79
LC
409 (with-imported-modules '((gnu build marionette))
410 #~(begin
411 (use-modules (gnu build marionette)
412 (srfi srfi-64)
413 (ice-9 match))
414
415 (define marionette
416 (make-marionette (list #$command)))
417
418 (define (wait-for-file file)
419 ;; Wait until FILE exists in the guest; 'read' its content and
420 ;; return it.
421 (marionette-eval
422 `(let loop ((i 10))
423 (cond ((file-exists? ,file)
424 (call-with-input-file ,file read))
425 ((> i 0)
426 (sleep 1)
427 (loop (- i 1)))
428 (else
429 (error "file didn't show up" ,file))))
430 marionette))
431
432 (mkdir #$output)
433 (chdir #$output)
434
435 (test-begin "mcron")
436
437 (test-eq "service running"
438 'running!
439 (marionette-eval
440 '(begin
441 (use-modules (gnu services herd))
442 (start-service 'mcron)
443 'running!)
444 marionette))
445
446 ;; Make sure root's mcron job runs, has its cwd set to "/root", and
447 ;; runs with the right UID/GID.
448 (test-equal "root's job"
449 '(0 0)
450 (wait-for-file "/root/witness"))
451
452 ;; Likewise for Alice's job. We cannot know what its GID is since
453 ;; it's chosen by 'groupadd', but it's strictly positive.
454 (test-assert "alice's job"
455 (match (wait-for-file "/home/alice/witness")
456 ((1000 gid)
457 (>= gid 100))))
458
459 ;; Last, the job that uses a command; allows us to test whether
460 ;; $PATH is sane. (Note that 'marionette-eval' stringifies objects
461 ;; that don't have a read syntax, hence the string.)
462 (test-equal "root's job with command"
463 "#<eof>"
464 (wait-for-file "/root/witness-touch"))
465
466 (test-end)
467 (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
468
469 (gexp->derivation name test)))
c311089b
LC
470
471(define %test-mcron
472 (system-test
473 (name "mcron")
474 (description "Make sure the mcron service works as advertised.")
475 (value (run-mcron-test name))))
d2fa61bc
LC
476
477\f
478;;;
479;;; Avahi and NSS-mDNS.
480;;;
481
482(define %avahi-os
483 (operating-system
484 (inherit %simple-os)
485 (name-service-switch %mdns-host-lookup-nss)
486 (services (cons* (avahi-service #:debug? #t)
487 (dbus-service)
488 (dhcp-client-service) ;needed for multicast
489
490 ;; Enable heavyweight debugging output.
491 (modify-services (operating-system-user-services
492 %simple-os)
493 (nscd-service-type config
494 => (nscd-configuration
495 (inherit config)
496 (debug-level 3)
497 (log-file "/dev/console")))
498 (syslog-service-type config
499 =>
ec2e2f6c
DC
500 (syslog-configuration
501 (inherit config)
502 (config-file
503 (plain-file
504 "syslog.conf"
505 "*.* /dev/console\n")))))))))
d2fa61bc
LC
506
507(define (run-nss-mdns-test)
508 ;; Test resolution of '.local' names via libc. Start the marionette service
509 ;; *after* nscd. Failing to do that, libc will try to connect to nscd,
510 ;; fail, then never try again (see '__nss_not_use_nscd_hosts' in libc),
511 ;; leading to '.local' resolution failures.
512 (mlet* %store-monad ((os -> (marionette-operating-system
513 %avahi-os
514 #:requirements '(nscd)
515 #:imported-modules '((gnu services herd)
516 (guix combinators))))
517 (run (system-qemu-image/shared-store-script
518 os #:graphic? #f)))
519 (define mdns-host-name
520 (string-append (operating-system-host-name os)
521 ".local"))
522
523 (define test
4ee96a79
LC
524 (with-imported-modules '((gnu build marionette))
525 #~(begin
526 (use-modules (gnu build marionette)
527 (srfi srfi-1)
528 (srfi srfi-64)
529 (ice-9 match))
530
531 (define marionette
532 (make-marionette (list #$run)))
533
534 (mkdir #$output)
535 (chdir #$output)
536
537 (test-begin "avahi")
538
539 (test-assert "wait for services"
540 (marionette-eval
541 '(begin
542 (use-modules (gnu services herd))
543
544 (start-service 'nscd)
545
546 ;; XXX: Work around a race condition in nscd: nscd creates its
547 ;; PID file before it is listening on its socket.
548 (let ((sock (socket PF_UNIX SOCK_STREAM 0)))
549 (let try ()
550 (catch 'system-error
551 (lambda ()
552 (connect sock AF_UNIX "/var/run/nscd/socket")
553 (close-port sock)
554 (format #t "nscd is ready~%"))
555 (lambda args
556 (format #t "waiting for nscd...~%")
557 (usleep 500000)
558 (try)))))
559
560 ;; Wait for the other useful things.
561 (start-service 'avahi-daemon)
562 (start-service 'networking)
563
564 #t)
565 marionette))
566
567 (test-equal "avahi-resolve-host-name"
568 0
569 (marionette-eval
570 '(system*
571 "/run/current-system/profile/bin/avahi-resolve-host-name"
572 "-v" #$mdns-host-name)
573 marionette))
574
575 (test-equal "avahi-browse"
576 0
577 (marionette-eval
578 '(system* "avahi-browse" "-avt")
579 marionette))
580
581 (test-assert "getaddrinfo .local"
582 ;; Wait for the 'avahi-daemon' service and perform a resolution.
583 (match (marionette-eval
584 '(getaddrinfo #$mdns-host-name)
585 marionette)
586 (((? vector? addrinfos) ..1)
587 (pk 'getaddrinfo addrinfos)
588 (and (any (lambda (ai)
589 (= AF_INET (addrinfo:fam ai)))
590 addrinfos)
591 (any (lambda (ai)
592 (= AF_INET6 (addrinfo:fam ai)))
593 addrinfos)))))
594
595 (test-assert "gethostbyname .local"
596 (match (pk 'gethostbyname
597 (marionette-eval '(gethostbyname #$mdns-host-name)
598 marionette))
599 ((? vector? result)
600 (and (string=? (hostent:name result) #$mdns-host-name)
601 (= (hostent:addrtype result) AF_INET)))))
602
603
604 (test-end)
605 (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
606
607 (gexp->derivation "nss-mdns" test)))
d2fa61bc
LC
608
609(define %test-nss-mdns
610 (system-test
611 (name "nss-mdns")
612 (description
613 "Test Avahi's multicast-DNS implementation, and in particular, test its
614glibc name service switch (NSS) module.")
615 (value (run-nss-mdns-test))))