base.scm 36 KB

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