base.scm 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920
  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. (test-equal "getlogin on tty1"
  313. "\"root\""
  314. (begin
  315. ;; Assume we logged in in the previous test and type.
  316. (marionette-type "guile -c '(write (getlogin))' > /root/login-id.tmp\n"
  317. marionette)
  318. (marionette-type "mv /root/login-id{.tmp,}\n"
  319. marionette)
  320. ;; It can take a while before the shell commands are executed.
  321. (marionette-eval '(use-modules (rnrs io ports)) marionette)
  322. (wait-for-file "/root/login-id" marionette
  323. #:read 'get-string-all)))
  324. ;; There should be one utmpx entry for the user logged in on tty1.
  325. (test-equal "utmpx entry"
  326. '(("root" "tty1" #f))
  327. (marionette-eval
  328. '(begin
  329. (use-modules (guix build syscalls)
  330. (srfi srfi-1))
  331. (filter-map (lambda (entry)
  332. (and (equal? (login-type USER_PROCESS)
  333. (utmpx-login-type entry))
  334. (list (utmpx-user entry) (utmpx-line entry)
  335. (utmpx-host entry))))
  336. (utmpx-entries)))
  337. marionette))
  338. ;; Likewise for /var/log/wtmp (used by 'last').
  339. (test-assert "wtmp entry"
  340. (match (marionette-eval
  341. '(begin
  342. (use-modules (guix build syscalls)
  343. (srfi srfi-1))
  344. (define (entry->list entry)
  345. (list (utmpx-user entry) (utmpx-line entry)
  346. (utmpx-host entry) (utmpx-login-type entry)))
  347. (call-with-input-file "/var/log/wtmp"
  348. (lambda (port)
  349. (let loop ((result '()))
  350. (if (eof-object? (peek-char port))
  351. (map entry->list (reverse result))
  352. (loop (cons (read-utmpx port) result)))))))
  353. marionette)
  354. (((users lines hosts types) ..1)
  355. (every (lambda (type)
  356. (eqv? type (login-type LOGIN_PROCESS)))
  357. types))))
  358. (test-assert "host name resolution"
  359. (match (marionette-eval
  360. '(begin
  361. ;; Wait for nscd or our requests go through it.
  362. (use-modules (gnu services herd))
  363. (start-service 'nscd)
  364. (list (getaddrinfo "localhost")
  365. (getaddrinfo #$(operating-system-host-name os))))
  366. marionette)
  367. ((((? vector?) ..1) ((? vector?) ..1))
  368. #t)
  369. (x
  370. (pk 'failure x #f))))
  371. (test-equal "nscd invalidate action"
  372. '(#t) ;one value, #t
  373. (marionette-eval '(with-shepherd-action 'nscd ('invalidate "hosts")
  374. result
  375. result)
  376. marionette))
  377. ;; FIXME: The 'invalidate' action can't reliably obtain the exit
  378. ;; code of 'nscd' so skip this test.
  379. (test-skip 1)
  380. (test-equal "nscd invalidate action, wrong table"
  381. '(#f) ;one value, #f
  382. (marionette-eval '(with-shepherd-action 'nscd ('invalidate "xyz")
  383. result
  384. result)
  385. marionette))
  386. (test-equal "host not found"
  387. #f
  388. (marionette-eval
  389. '(false-if-exception (getaddrinfo "does-not-exist"))
  390. marionette))
  391. (test-equal "locale"
  392. "en_US.utf8"
  393. (marionette-eval '(let ((before (setlocale LC_ALL "en_US.utf8")))
  394. (setlocale LC_ALL before))
  395. marionette))
  396. (test-eq "/run/current-system is a GC root"
  397. 'success!
  398. (marionette-eval '(begin
  399. ;; Make sure the (guix …) modules are found.
  400. (eval-when (expand load eval)
  401. (set! %load-path
  402. (append (map (lambda (package)
  403. (string-append package
  404. "/share/guile/site/"
  405. (effective-version)))
  406. '#$guix&co)
  407. %load-path)))
  408. (use-modules (srfi srfi-34) (guix store))
  409. (let ((system (readlink "/run/current-system")))
  410. (guard (c ((store-protocol-error? c)
  411. (and (file-exists? system)
  412. 'success!)))
  413. (with-store store
  414. (delete-paths store (list system))
  415. #f))))
  416. marionette))
  417. ;; This symlink is currently unused, but better have it point to the
  418. ;; right place. See
  419. ;; <https://lists.gnu.org/archive/html/guix-devel/2016-08/msg01641.html>.
  420. (test-equal "/var/guix/gcroots/profiles is a valid symlink"
  421. "/var/guix/profiles"
  422. (marionette-eval '(readlink "/var/guix/gcroots/profiles")
  423. marionette))
  424. (test-equal "guix-daemon set-http-proxy action"
  425. '(#t) ;one value, #t
  426. (marionette-eval '(with-shepherd-action 'guix-daemon
  427. ('set-http-proxy "http://localhost:8118")
  428. result
  429. result)
  430. marionette))
  431. (test-equal "guix-daemon set-http-proxy action, clear"
  432. '(#t) ;one value, #t
  433. (marionette-eval '(with-shepherd-action 'guix-daemon
  434. ('set-http-proxy)
  435. result
  436. result)
  437. marionette))
  438. (test-assert "screendump"
  439. (begin
  440. (let ((capture
  441. (string-append #$output "/tty1.ppm")))
  442. (marionette-control
  443. (string-append "screendump " capture) marionette)
  444. (file-exists? capture))))
  445. (test-assert "screen text"
  446. (let ((text (marionette-screen-text marionette
  447. #:ocrad
  448. #$(file-append ocrad
  449. "/bin/ocrad"))))
  450. ;; Check whether the welcome message and shell prompt are
  451. ;; displayed. Note: OCR confuses "y" and "V" for instance, so
  452. ;; we cannot reliably match the whole text.
  453. (and (string-contains text "This is the GNU")
  454. (string-contains text
  455. (string-append
  456. "root@"
  457. #$(operating-system-host-name os))))))
  458. (test-end))))
  459. (gexp->derivation name test))
  460. (define %test-basic-os
  461. (system-test
  462. (name "basic")
  463. (description
  464. "Instrument %SIMPLE-OS, run it in a VM, and run a series of basic
  465. functionality tests.")
  466. (value
  467. (let* ((os (marionette-operating-system
  468. %simple-os
  469. #:imported-modules '((gnu services herd)
  470. (guix combinators))))
  471. (vm (virtual-machine os)))
  472. ;; XXX: Add call to 'virtualized-operating-system' to get the exact same
  473. ;; set of services as the OS produced by
  474. ;; 'system-qemu-image/shared-store-script'.
  475. (run-basic-test (virtualized-operating-system os '())
  476. #~(list #$vm))))))
  477. ;;;
  478. ;;; Halt.
  479. ;;;
  480. (define (run-halt-test vm)
  481. ;; As reported in <http://bugs.gnu.org/26931>, running tmux would previously
  482. ;; lead the 'stop' method of 'user-processes' to an infinite loop, with the
  483. ;; tmux server process as a zombie that remains in the list of processes.
  484. ;; This test reproduces this scenario.
  485. (define test
  486. (with-imported-modules '((gnu build marionette))
  487. #~(begin
  488. (use-modules (gnu build marionette))
  489. (define marionette
  490. (make-marionette '(#$vm)))
  491. (define ocrad
  492. #$(file-append ocrad "/bin/ocrad"))
  493. ;; Wait for tty1 and log in.
  494. (marionette-eval '(begin
  495. (use-modules (gnu services herd))
  496. (start-service 'term-tty1))
  497. marionette)
  498. (marionette-type "root\n" marionette)
  499. ;; Start tmux and wait for it to be ready.
  500. (marionette-type "tmux new-session 'echo 1 > /ready; bash'\n"
  501. marionette)
  502. (wait-for-file "/ready" marionette)
  503. ;; Make sure to stop the test after a while.
  504. (sigaction SIGALRM (lambda _
  505. (format (current-error-port)
  506. "FAIL: Time is up, but VM still running.\n")
  507. (primitive-exit 1)))
  508. (alarm 10)
  509. ;; Get debugging info.
  510. (marionette-eval '(current-output-port
  511. (open-file "/dev/console" "w0"))
  512. marionette)
  513. (marionette-eval '(system* #$(file-append procps "/bin/ps")
  514. "-eo" "pid,ppid,stat,comm")
  515. marionette)
  516. ;; See if 'halt' actually works.
  517. (marionette-eval '(system* "/run/current-system/profile/sbin/halt")
  518. marionette)
  519. ;; If we reach this line, that means the VM was properly stopped in
  520. ;; a timely fashion.
  521. (alarm 0)
  522. (call-with-output-file #$output
  523. (lambda (port)
  524. (display "success!" port))))))
  525. (gexp->derivation "halt" test))
  526. (define %test-halt
  527. (system-test
  528. (name "halt")
  529. (description
  530. "Use the 'halt' command and make sure it succeeds and does not get stuck
  531. in a loop. See <http://bugs.gnu.org/26931>.")
  532. (value
  533. (let ((os (marionette-operating-system
  534. (operating-system
  535. (inherit %simple-os)
  536. (packages (cons tmux %base-packages)))
  537. #:imported-modules '((gnu services herd)
  538. (guix combinators)))))
  539. (run-halt-test (virtual-machine os))))))
  540. ;;;
  541. ;;; Cleanup of /tmp, /var/run, etc.
  542. ;;;
  543. (define %cleanup-os
  544. (simple-operating-system
  545. (simple-service 'dirty-things
  546. boot-service-type
  547. (let ((script (plain-file
  548. "create-utf8-file.sh"
  549. (string-append
  550. "echo $0: dirtying /tmp...\n"
  551. "set -e; set -x\n"
  552. "touch /witness\n"
  553. "exec touch /tmp/λαμβδα"))))
  554. (with-imported-modules '((guix build utils))
  555. #~(begin
  556. (setenv "PATH"
  557. #$(file-append coreutils "/bin"))
  558. (invoke #$(file-append bash "/bin/sh")
  559. #$script)))))))
  560. (define (run-cleanup-test name)
  561. (define os
  562. (marionette-operating-system %cleanup-os
  563. #:imported-modules '((gnu services herd)
  564. (guix combinators))))
  565. (define test
  566. (with-imported-modules '((gnu build marionette))
  567. #~(begin
  568. (use-modules (gnu build marionette)
  569. (srfi srfi-64)
  570. (ice-9 match))
  571. (define marionette
  572. (make-marionette (list #$(virtual-machine os))))
  573. (test-runner-current (system-test-runner #$output))
  574. (test-begin "cleanup")
  575. (test-assert "dirty service worked"
  576. (marionette-eval '(file-exists? "/witness") marionette))
  577. (test-equal "/tmp cleaned up"
  578. '("." "..")
  579. (marionette-eval '(begin
  580. (use-modules (ice-9 ftw))
  581. (scandir "/tmp"))
  582. marionette))
  583. (test-end))))
  584. (gexp->derivation "cleanup" test))
  585. (define %test-cleanup
  586. ;; See <https://bugs.gnu.org/26353>.
  587. (system-test
  588. (name "cleanup")
  589. (description "Make sure the 'cleanup' service can remove files with
  590. non-ASCII names from /tmp.")
  591. (value (run-cleanup-test name))))
  592. ;;;
  593. ;;; Mcron.
  594. ;;;
  595. (define %mcron-os
  596. ;; System with an mcron service, with one mcron job for "root" and one mcron
  597. ;; job for an unprivileged user.
  598. (let ((job1 #~(job '(next-second '(0 5 10 15 20 25 30 35 40 45 50 55))
  599. (lambda ()
  600. (unless (file-exists? "witness")
  601. (call-with-output-file "witness"
  602. (lambda (port)
  603. (display (list (getuid) (getgid)) port)))))))
  604. (job2 #~(job next-second-from
  605. (lambda ()
  606. (call-with-output-file "witness"
  607. (lambda (port)
  608. (display (list (getuid) (getgid)) port))))
  609. #:user "alice"))
  610. (job3 #~(job next-second-from ;to test $PATH
  611. "touch witness-touch")))
  612. (simple-operating-system
  613. (service mcron-service-type
  614. (mcron-configuration (jobs (list job1 job2 job3)))))))
  615. (define (run-mcron-test name)
  616. (define os
  617. (marionette-operating-system
  618. %mcron-os
  619. #:imported-modules '((gnu services herd)
  620. (guix combinators))))
  621. (define test
  622. (with-imported-modules '((gnu build marionette))
  623. #~(begin
  624. (use-modules (gnu build marionette)
  625. (srfi srfi-64)
  626. (ice-9 match))
  627. (define marionette
  628. (make-marionette (list #$(virtual-machine os))))
  629. (test-runner-current (system-test-runner #$output))
  630. (test-begin "mcron")
  631. (test-assert "service running"
  632. (marionette-eval
  633. '(begin
  634. (use-modules (gnu services herd))
  635. (start-service 'mcron))
  636. marionette))
  637. ;; Make sure root's mcron job runs, has its cwd set to "/root", and
  638. ;; runs with the right UID/GID.
  639. (test-equal "root's job"
  640. '(0 0)
  641. (wait-for-file "/root/witness" marionette))
  642. ;; Likewise for Alice's job. We cannot know what its GID is since
  643. ;; it's chosen by 'groupadd', but it's strictly positive.
  644. (test-assert "alice's job"
  645. (match (wait-for-file "/home/alice/witness" marionette)
  646. ((1000 gid)
  647. (>= gid 100))))
  648. ;; Last, the job that uses a command; allows us to test whether
  649. ;; $PATH is sane.
  650. (test-equal "root's job with command"
  651. ""
  652. (wait-for-file "/root/witness-touch" marionette
  653. #:read '(@ (ice-9 rdelim) read-string)))
  654. ;; Make sure the 'schedule' action is accepted.
  655. (test-equal "schedule action"
  656. '(#t) ;one value, #t
  657. (marionette-eval '(with-shepherd-action 'mcron ('schedule) result
  658. result)
  659. marionette))
  660. (test-end))))
  661. (gexp->derivation name test))
  662. (define %test-mcron
  663. (system-test
  664. (name "mcron")
  665. (description "Make sure the mcron service works as advertised.")
  666. (value (run-mcron-test name))))
  667. ;;;
  668. ;;; Avahi and NSS-mDNS.
  669. ;;;
  670. (define %avahi-os
  671. (operating-system
  672. (inherit %simple-os)
  673. (name-service-switch %mdns-host-lookup-nss)
  674. (services (cons* (service avahi-service-type
  675. (avahi-configuration (debug? #t)))
  676. (dbus-service)
  677. (service dhcp-client-service-type) ;needed for multicast
  678. ;; Enable heavyweight debugging output.
  679. (modify-services (operating-system-user-services
  680. %simple-os)
  681. (nscd-service-type config
  682. => (nscd-configuration
  683. (inherit config)
  684. (debug-level 3)
  685. (log-file "/dev/console")))
  686. (syslog-service-type config
  687. =>
  688. (syslog-configuration
  689. (inherit config)
  690. (config-file
  691. (plain-file
  692. "syslog.conf"
  693. "*.* /dev/console\n")))))))))
  694. (define (run-nss-mdns-test)
  695. ;; Test resolution of '.local' names via libc. Start the marionette service
  696. ;; *after* nscd. Failing to do that, libc will try to connect to nscd,
  697. ;; fail, then never try again (see '__nss_not_use_nscd_hosts' in libc),
  698. ;; leading to '.local' resolution failures.
  699. (define os
  700. (marionette-operating-system
  701. %avahi-os
  702. #:requirements '(nscd)
  703. #:imported-modules '((gnu services herd)
  704. (guix combinators))))
  705. (define mdns-host-name
  706. (string-append (operating-system-host-name os)
  707. ".local"))
  708. (define test
  709. (with-imported-modules '((gnu build marionette))
  710. #~(begin
  711. (use-modules (gnu build marionette)
  712. (srfi srfi-1)
  713. (srfi srfi-64)
  714. (ice-9 match))
  715. (define marionette
  716. (make-marionette (list #$(virtual-machine os))))
  717. (mkdir #$output)
  718. (chdir #$output)
  719. (test-runner-current (system-test-runner))
  720. (test-begin "avahi")
  721. (test-assert "nscd PID file is created"
  722. (marionette-eval
  723. '(begin
  724. (use-modules (gnu services herd))
  725. (start-service 'nscd))
  726. marionette))
  727. (test-assert "nscd is listening on its socket"
  728. (marionette-eval
  729. ;; XXX: Work around a race condition in nscd: nscd creates its
  730. ;; PID file before it is listening on its socket.
  731. '(let ((sock (socket PF_UNIX SOCK_STREAM 0)))
  732. (let try ()
  733. (catch 'system-error
  734. (lambda ()
  735. (connect sock AF_UNIX "/var/run/nscd/socket")
  736. (close-port sock)
  737. (format #t "nscd is ready~%")
  738. #t)
  739. (lambda args
  740. (format #t "waiting for nscd...~%")
  741. (usleep 500000)
  742. (try)))))
  743. marionette))
  744. (test-assert "avahi is running"
  745. (marionette-eval
  746. '(begin
  747. (use-modules (gnu services herd))
  748. (start-service 'avahi-daemon))
  749. marionette))
  750. (test-assert "network is up"
  751. (marionette-eval
  752. '(begin
  753. (use-modules (gnu services herd))
  754. (start-service 'networking))
  755. marionette))
  756. (test-equal "avahi-resolve-host-name"
  757. 0
  758. (marionette-eval
  759. '(system*
  760. "/run/current-system/profile/bin/avahi-resolve-host-name"
  761. "-v" #$mdns-host-name)
  762. marionette))
  763. (test-equal "avahi-browse"
  764. 0
  765. (marionette-eval
  766. '(system* "/run/current-system/profile/bin/avahi-browse" "-avt")
  767. marionette))
  768. (test-assert "getaddrinfo .local"
  769. ;; Wait for the 'avahi-daemon' service and perform a resolution.
  770. (match (marionette-eval
  771. '(getaddrinfo #$mdns-host-name)
  772. marionette)
  773. (((? vector? addrinfos) ..1)
  774. (pk 'getaddrinfo addrinfos)
  775. (and (any (lambda (ai)
  776. (= AF_INET (addrinfo:fam ai)))
  777. addrinfos)
  778. (any (lambda (ai)
  779. (= AF_INET6 (addrinfo:fam ai)))
  780. addrinfos)))))
  781. (test-assert "gethostbyname .local"
  782. (match (pk 'gethostbyname
  783. (marionette-eval '(gethostbyname #$mdns-host-name)
  784. marionette))
  785. ((? vector? result)
  786. (and (string=? (hostent:name result) #$mdns-host-name)
  787. (= (hostent:addrtype result) AF_INET)))))
  788. (test-end))))
  789. (gexp->derivation "nss-mdns" test))
  790. (define %test-nss-mdns
  791. (system-test
  792. (name "nss-mdns")
  793. (description
  794. "Test Avahi's multicast-DNS implementation, and in particular, test its
  795. glibc name service switch (NSS) module.")
  796. (value (run-nss-mdns-test))))