desktop.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2017, 2021, 2023 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2021 muradm <mail@muradm.net>
  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 desktop)
  20. #:use-module (gnu tests)
  21. #:use-module (gnu packages shells)
  22. #:use-module (gnu services)
  23. #:use-module (gnu services base)
  24. #:use-module (gnu services dbus)
  25. #:use-module (gnu services desktop)
  26. #:use-module (gnu system)
  27. #:use-module (gnu system vm)
  28. #:use-module (guix gexp)
  29. #:use-module (srfi srfi-1)
  30. #:export (%test-elogind
  31. %test-minimal-desktop))
  32. ;;;
  33. ;;; Elogind.
  34. ;;;
  35. (define (run-elogind-test vm)
  36. (define test
  37. (with-imported-modules '((gnu build marionette)
  38. (guix build syscalls))
  39. #~(begin
  40. (use-modules (gnu build marionette)
  41. (guix build syscalls)
  42. (srfi srfi-64))
  43. (define marionette
  44. (make-marionette '(#$vm)))
  45. (test-runner-current (system-test-runner #$output))
  46. (test-begin "elogind")
  47. ;; Log in as root on tty1, and check what 'loginctl' returns.
  48. (test-equal "login on tty1"
  49. '(("c1" "0" "root" "seat0" "tty1") ;session
  50. ("seat0") ;seat
  51. ("0" "root")) ;user
  52. (begin
  53. ;; Wait for tty1.
  54. (marionette-eval
  55. '(begin
  56. (use-modules (gnu services herd))
  57. (start-service 'term-tty1)
  58. (start-service 'elogind))
  59. marionette)
  60. (marionette-control "sendkey ctrl-alt-f1" marionette)
  61. ;; Now we can type.
  62. (marionette-type "root\n" marionette)
  63. (marionette-type "loginctl list-users --no-legend > users\n"
  64. marionette)
  65. (marionette-type "loginctl list-seats --no-legend > seats\n"
  66. marionette)
  67. (marionette-type "loginctl list-sessions --no-legend > sessions\n"
  68. marionette)
  69. ;; Read the three files.
  70. (marionette-eval '(use-modules (rnrs io ports)) marionette)
  71. (let ((guest-file (lambda (file)
  72. (string-tokenize
  73. (wait-for-file file marionette
  74. #:read 'get-string-all)))))
  75. (list (guest-file "/root/sessions")
  76. (guest-file "/root/seats")
  77. (guest-file "/root/users")))))
  78. (test-assert "screendump"
  79. (begin
  80. (let ((capture (string-append #$output "/tty1.ppm")))
  81. (marionette-control
  82. (string-append "screendump " capture) marionette)
  83. (file-exists? capture))))
  84. (test-end))))
  85. (gexp->derivation "elogind" test))
  86. (define %test-elogind
  87. (system-test
  88. (name "elogind")
  89. (description
  90. "Test whether we can log in when elogind is enabled, and whether
  91. 'loginctl' reports accurate user, session, and seat information.")
  92. (value
  93. (let ((os (marionette-operating-system
  94. (simple-operating-system
  95. (service elogind-service-type)
  96. (service polkit-service-type)
  97. (service dbus-root-service-type))
  98. #:imported-modules '((gnu services herd)
  99. (guix combinators)))))
  100. (run-elogind-test (virtual-machine os))))))
  101. ;;;
  102. ;;; Seatd/greetd based minimal desktop
  103. ;;;
  104. (define %minimal-services
  105. (append
  106. (modify-services %base-services
  107. ;; greetd-service-type provides "greetd" PAM service
  108. (delete login-service-type)
  109. ;; and can be used in place of mingetty-service-type
  110. (delete mingetty-service-type))
  111. (list
  112. (service seatd-service-type)
  113. (service greetd-service-type
  114. (greetd-configuration
  115. (greeter-supplementary-groups '("input" "video"))
  116. (terminals
  117. (list
  118. ;; we can make any terminal active by default
  119. (greetd-terminal-configuration (terminal-vt "1") (terminal-switch #t))
  120. ;; we can make environment without XDG_RUNTIME_DIR set
  121. ;; even provide our own environment variables
  122. (greetd-terminal-configuration
  123. (terminal-vt "2")
  124. (default-session-command
  125. (greetd-agreety-session
  126. (extra-env '(("MY_VAR" . "1")))
  127. (xdg-env? #f))))
  128. ;; we can use different shell instead of default bash
  129. (greetd-terminal-configuration
  130. (terminal-vt "3")
  131. (default-session-command
  132. (greetd-agreety-session (command (file-append zsh "/bin/zsh")))))
  133. ;; we can use any other executable command as greeter
  134. (greetd-terminal-configuration
  135. (terminal-vt "4")
  136. (default-session-command (program-file "my-noop-greeter" #~(exit))))
  137. (greetd-terminal-configuration (terminal-vt "5"))
  138. (greetd-terminal-configuration (terminal-vt "6"))))))
  139. ;; mingetty-service-type can be used in parallel
  140. ;; if needed to do so, do not (delete login-service-type)
  141. ;; as illustrated above
  142. #| (service mingetty-service-type (mingetty-configuration (tty "tty8"))) |#)))
  143. (define-syntax-rule (minimal-operating-system user-services ...)
  144. "Return an operating system that includes USER-SERVICES in addition to
  145. minimal %BASE-SERVICES."
  146. (operating-system (inherit %simple-os)
  147. (services (cons* user-services ... %minimal-services))))
  148. (define (run-minimal-desktop-test os vm)
  149. (define test
  150. (with-imported-modules '((gnu build marionette)
  151. (guix build syscalls))
  152. #~(begin
  153. (use-modules (gnu build marionette)
  154. (guix build syscalls)
  155. (srfi srfi-1)
  156. (srfi srfi-64)
  157. (ice-9 pretty-print))
  158. (define marionette
  159. (make-marionette #$vm))
  160. (define (file-get-all-strings fname)
  161. (marionette-eval '(use-modules (rnrs io ports)) marionette)
  162. (wait-for-file fname marionette #:read 'get-string-all))
  163. (define (wait-for-unix-socket-m socket)
  164. (wait-for-unix-socket socket marionette))
  165. (mkdir #$output)
  166. (chdir #$output)
  167. (test-runner-current (system-test-runner #$output))
  168. (test-begin "minimal-desktop")
  169. (test-assert "seatd is ready"
  170. (wait-for-unix-socket-m "/run/seatd.sock"))
  171. (test-equal "login user on tty1"
  172. "alice\n"
  173. (begin
  174. ;; Wait for tty1.
  175. (marionette-eval
  176. '(begin
  177. (use-modules (gnu services herd))
  178. (start-service 'term-tty1))
  179. marionette)
  180. (marionette-control "sendkey ctrl-alt-f1" marionette)
  181. ;; login as root change alice password and exit
  182. ;; then login as alice
  183. (for-each
  184. (lambda (cmd) (marionette-type cmd marionette) (sleep 1))
  185. (list
  186. "root\n"
  187. "passwd alice\n"
  188. "alice\n"
  189. "alice\n"
  190. "exit\n"
  191. "alice\n"
  192. "alice\n"
  193. "id -un > logged-in\n"))
  194. (file-get-all-strings "/home/alice/logged-in")))
  195. (test-equal "validate user environment"
  196. '("SEATD_SOCK=/run/seatd.sock"
  197. "XDG_RUNTIME_DIR=/run/user/1000"
  198. "XDG_SEAT=seat0"
  199. "XDG_VTNR=1")
  200. (begin
  201. (marionette-type "env > env\n" marionette)
  202. (sleep 1)
  203. (define user-env (string-tokenize
  204. (file-get-all-strings "/home/alice/env")))
  205. (define (expected-var var)
  206. (any (lambda (s) (string-contains var s))
  207. '("SEATD_SOCK"
  208. "XDG_RUNTIME_DIR"
  209. "XDG_SEAT"
  210. "XDG_VTNR")))
  211. (sort (filter expected-var user-env) string<?)))
  212. (test-assert "validate SEATD_SOCK and GREETD_SOCK"
  213. (begin
  214. (marionette-type "env > env\n" marionette)
  215. (sleep 1)
  216. (define (sock-var? var)
  217. (any (lambda (s) (string-contains var s))
  218. '("SEATD_SOCK" "GREETD_SOCK")))
  219. (define (sock-var-sock var)
  220. (car (cdr (string-split var #\=))))
  221. (let*
  222. ((out (file-get-all-strings "/home/alice/env"))
  223. (out (string-tokenize out))
  224. (out (filter sock-var? out))
  225. (socks (map sock-var-sock out))
  226. (socks (map wait-for-unix-socket-m socks)))
  227. (and (= 2 (length socks)) (every identity socks)))))
  228. (test-equal "seatd.sock ownership"
  229. '("root" "seat")
  230. `(,(marionette-eval
  231. '(passwd:name (getpwuid (stat:uid (stat "/run/seatd.sock"))))
  232. marionette)
  233. ,(marionette-eval
  234. '(group:name (getgrgid (stat:gid (stat "/run/seatd.sock"))))
  235. marionette)))
  236. (test-assert "greetd is ready"
  237. (begin
  238. (marionette-type "ps -C greetd -o pid,args --no-headers > ps-greetd\n"
  239. marionette)
  240. (sleep 1)
  241. (define (greetd-daemon? cmd)
  242. (string-contains cmd "config"))
  243. (define (greetd-cmd-to-pid cmd)
  244. (car (string-split cmd #\space)))
  245. (define (greetd-pid-to-sock pid)
  246. (string-append "/run/greetd-" pid ".sock"))
  247. (let* ((out (file-get-all-strings "/home/alice/ps-greetd"))
  248. (out (string-split out #\newline))
  249. (out (map string-trim-both out))
  250. (out (filter greetd-daemon? out))
  251. (pids (map greetd-cmd-to-pid out))
  252. (socks (map greetd-pid-to-sock pids))
  253. (socks (map wait-for-unix-socket-m socks)))
  254. (every identity socks))))
  255. ;; a bit weak, but tests everything at once actually
  256. (test-equal "check /run/user/<uid> mounted and writable"
  257. "alice\n"
  258. (begin
  259. (marionette-type "echo alice > /run/user/1000/test\n" marionette)
  260. (file-get-all-strings "/run/user/1000/test")))
  261. (test-equal "check greeter user has correct groups"
  262. "greeter input video\n"
  263. (begin
  264. (marionette-type "id -Gn greeter > /run/user/1000/greeter-groups\n"
  265. marionette)
  266. (file-get-all-strings "/run/user/1000/greeter-groups")))
  267. (test-assert "screendump"
  268. (begin
  269. (marionette-control (string-append "screendump " #$output
  270. "/tty1.ppm")
  271. marionette)
  272. (file-exists? "tty1.ppm")))
  273. (test-end))))
  274. (gexp->derivation "minimal-desktop" test))
  275. (define %test-minimal-desktop
  276. (system-test
  277. (name "minimal-desktop")
  278. (description
  279. "Test whether we can log in when seatd and greetd is enabled")
  280. (value
  281. (let* ((os (marionette-operating-system
  282. (minimal-operating-system)
  283. #:imported-modules '((gnu services herd)
  284. (guix combinators))))
  285. (vm (virtual-machine os)))
  286. (run-minimal-desktop-test (virtualized-operating-system os '())
  287. #~(list #$vm))))))