tests.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2016-2020, 2022-2023 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
  4. ;;; Copyright © 2017 Tobias Geerinckx-Rice <me@tobias.gr>
  5. ;;; Copyright © 2021 Maxime Devos <maximedevos@telenet.be>
  6. ;;;
  7. ;;; This file is part of GNU Guix.
  8. ;;;
  9. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  10. ;;; under the terms of the GNU General Public License as published by
  11. ;;; the Free Software Foundation; either version 3 of the License, or (at
  12. ;;; your option) any later version.
  13. ;;;
  14. ;;; GNU Guix is distributed in the hope that it will be useful, but
  15. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ;;; GNU General Public License for more details.
  18. ;;;
  19. ;;; You should have received a copy of the GNU General Public License
  20. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  21. (define-module (gnu tests)
  22. #:use-module (guix gexp)
  23. #:use-module (guix diagnostics)
  24. #:use-module (guix records)
  25. #:use-module ((guix ui) #:select (warn-about-load-error))
  26. #:use-module (gnu bootloader)
  27. #:use-module (gnu bootloader grub)
  28. #:use-module (gnu system)
  29. #:use-module (gnu system file-systems)
  30. #:use-module (gnu system shadow)
  31. #:use-module (gnu services)
  32. #:use-module (gnu services base)
  33. #:use-module (gnu services shepherd)
  34. #:use-module (guix discovery)
  35. #:use-module (srfi srfi-1)
  36. #:use-module (srfi srfi-9 gnu)
  37. #:use-module (ice-9 match)
  38. #:export (marionette-configuration
  39. marionette-configuration?
  40. marionette-configuration-device
  41. marionette-configuration-imported-modules
  42. marionette-configuration-requirements
  43. marionette-service-type
  44. marionette-operating-system
  45. define-os-with-source
  46. %simple-os
  47. simple-operating-system
  48. system-test
  49. system-test?
  50. system-test-name
  51. system-test-value
  52. system-test-description
  53. system-test-location
  54. fold-system-tests
  55. all-system-tests))
  56. ;;; Commentary:
  57. ;;;
  58. ;;; This module provides the infrastructure to run operating system tests.
  59. ;;; The most important part of that is tools to instrument the OS under test,
  60. ;;; essentially allowing it to run in a virtual machine controlled by the host
  61. ;;; system--hence the name "marionette".
  62. ;;;
  63. ;;; Code:
  64. (define-record-type* <marionette-configuration>
  65. marionette-configuration make-marionette-configuration
  66. marionette-configuration?
  67. (device marionette-configuration-device ;string
  68. (default "/dev/virtio-ports/org.gnu.guix.port.0"))
  69. (imported-modules marionette-configuration-imported-modules
  70. (default '()))
  71. (extensions marionette-configuration-extensions
  72. (default '())) ; list of packages
  73. (requirements marionette-configuration-requirements ;list of symbols
  74. (default '())))
  75. ;; Hack: avoid indenting code beyond column 80 in marionette-shepherd-service.
  76. (define-syntax-rule (with-imported-modules-and-extensions imported-modules
  77. extensions
  78. gexp)
  79. (with-imported-modules imported-modules
  80. (with-extensions extensions
  81. gexp)))
  82. (define (marionette-program device imported-modules extensions)
  83. "Return the program that runs the marionette REPL on DEVICE. Ensure
  84. IMPORTED-MODULES and EXTENSIONS are accessible from the REPL."
  85. (define code
  86. (with-imported-modules-and-extensions
  87. `((guix build utils)
  88. (guix build syscalls)
  89. ,@imported-modules)
  90. extensions
  91. #~(begin
  92. (use-modules (ice-9 match)
  93. (ice-9 binary-ports))
  94. (define (self-quoting? x)
  95. (letrec-syntax ((one-of (syntax-rules ()
  96. ((_) #f)
  97. ((_ pred rest ...)
  98. (or (pred x)
  99. (one-of rest ...))))))
  100. (one-of symbol? string? keyword? pair? null? array?
  101. number? boolean? char?)))
  102. (let ((repl (open-file #$device "r+0"))
  103. (console (open-file "/dev/console" "r+0")))
  104. ;; Redirect output to the console.
  105. (close-fdes 1)
  106. (close-fdes 2)
  107. (dup2 (fileno console) 1)
  108. (dup2 (fileno console) 2)
  109. (close-port console)
  110. (display 'ready repl)
  111. (let loop ()
  112. (newline repl)
  113. (match (read repl)
  114. ((? eof-object?)
  115. (primitive-exit 0))
  116. (expr
  117. (catch #t
  118. (lambda ()
  119. (let ((result (primitive-eval expr)))
  120. (write (if (self-quoting? result)
  121. result
  122. (object->string result))
  123. repl)))
  124. (lambda (key . args)
  125. (print-exception (current-error-port)
  126. (stack-ref (make-stack #t) 1)
  127. key args)
  128. (write #f repl)))))
  129. (loop))))))
  130. (program-file "marionette-repl.scm" code))
  131. (define (marionette-shepherd-service config)
  132. "Return the Shepherd service for the marionette REPL"
  133. (match config
  134. (($ <marionette-configuration> device imported-modules extensions
  135. requirement)
  136. (list (shepherd-service
  137. (provision '(marionette))
  138. ;; Always depend on UDEV so that DEVICE is available.
  139. (requirement `(udev ,@requirement))
  140. (modules '((ice-9 match)
  141. (srfi srfi-9 gnu)))
  142. (start #~(make-forkexec-constructor
  143. (list #$(marionette-program device
  144. imported-modules
  145. extensions))))
  146. (stop #~(make-kill-destructor)))))))
  147. (define marionette-service-type
  148. ;; This is the type of the "marionette" service, allowing a guest system to
  149. ;; be manipulated from the host. This marionette REPL is essentially a
  150. ;; universal backdoor.
  151. (service-type (name 'marionette-repl)
  152. (extensions
  153. (list (service-extension shepherd-root-service-type
  154. marionette-shepherd-service)))
  155. (description "The @dfn{marionette} service allows a guest
  156. system (virtual machine) to be manipulated by the host. It is used for system
  157. tests.")))
  158. (define* (marionette-operating-system os
  159. #:key
  160. (imported-modules '())
  161. (extensions '())
  162. (requirements '()))
  163. "Return a marionetteed variant of OS such that OS can be used as a
  164. marionette in a virtual machine--i.e., controlled from the host system. The
  165. marionette service in the guest is started after the Shepherd services listed
  166. in REQUIREMENTS. The packages in the list EXTENSIONS are made available from
  167. the backdoor REPL."
  168. (operating-system
  169. (inherit os)
  170. ;; Make sure the guest dies on error.
  171. (kernel-arguments (cons "panic=1"
  172. (operating-system-user-kernel-arguments os)))
  173. ;; Make sure the guest doesn't hang in the REPL on error.
  174. (initrd (lambda (fs . rest)
  175. (apply (operating-system-initrd os) fs
  176. #:on-error 'backtrace
  177. rest)))
  178. (services (cons (service marionette-service-type
  179. (marionette-configuration
  180. (requirements requirements)
  181. (extensions extensions)
  182. (imported-modules imported-modules)))
  183. (operating-system-user-services os)))))
  184. (define-syntax define-os-with-source
  185. (syntax-rules (use-modules operating-system)
  186. "Define two variables: OS containing the given operating system, and
  187. SOURCE containing the source to define OS as an sexp.
  188. This is convenient when we need both the <operating-system> object so we can
  189. instantiate it, and the source to create it so we can store in in a file in
  190. the system under test."
  191. ((_ (os source)
  192. (use-modules modules ...)
  193. (operating-system fields ...))
  194. (begin
  195. (define os
  196. (operating-system fields ...))
  197. (define source
  198. '(begin
  199. (use-modules modules ...)
  200. (operating-system fields ...)))))))
  201. ;;;
  202. ;;; Simple operating systems.
  203. ;;;
  204. (define %simple-os
  205. (operating-system
  206. (host-name "komputilo")
  207. (timezone "Europe/Berlin")
  208. (locale "en_US.UTF-8")
  209. (bootloader (bootloader-configuration
  210. (bootloader grub-bootloader)
  211. (targets '("/dev/sdX"))))
  212. (file-systems (cons (file-system
  213. (device (file-system-label "my-root"))
  214. (mount-point "/")
  215. (type "ext4"))
  216. %base-file-systems))
  217. (firmware '())
  218. (users (cons (user-account
  219. (name "alice")
  220. (comment "Bob's sister")
  221. (group "users")
  222. (supplementary-groups '("wheel" "audio" "video")))
  223. %base-user-accounts))))
  224. (define-syntax-rule (simple-operating-system user-services ...)
  225. "Return an operating system that includes USER-SERVICES in addition to
  226. %BASE-SERVICES."
  227. (operating-system (inherit %simple-os)
  228. (services (cons* user-services ... %base-services))))
  229. ;;;
  230. ;;; Tests.
  231. ;;;
  232. (define-record-type* <system-test> system-test make-system-test
  233. system-test?
  234. (name system-test-name) ;string
  235. (value system-test-value) ;%STORE-MONAD value
  236. (description system-test-description) ;string
  237. (location system-test-location (innate) ;<location>
  238. (default (and=> (current-source-location)
  239. source-properties->location))))
  240. (define (write-system-test test port)
  241. (match test
  242. (($ <system-test> name _ _ ($ <location> file line))
  243. (format port "#<system-test ~a ~a:~a ~a>"
  244. name file line
  245. (number->string (object-address test) 16)))
  246. (($ <system-test> name)
  247. (format port "#<system-test ~a ~a>" name
  248. (number->string (object-address test) 16)))))
  249. (set-record-type-printer! <system-test> write-system-test)
  250. (define-gexp-compiler (compile-system-test (test <system-test>)
  251. system target)
  252. "Compile TEST to a derivation."
  253. ;; XXX: SYSTEM and TARGET are ignored.
  254. (system-test-value test))
  255. (define (test-modules)
  256. "Return the list of modules that define system tests."
  257. (scheme-modules (dirname (search-path %load-path "guix.scm"))
  258. "gnu/tests"
  259. #:warn warn-about-load-error))
  260. (define (fold-system-tests proc seed)
  261. "Invoke PROC on each system test, passing it the test and the previous
  262. result."
  263. (fold-module-public-variables (lambda (obj result)
  264. (if (system-test? obj)
  265. (cons obj result)
  266. result))
  267. '()
  268. (test-modules)))
  269. (define (all-system-tests)
  270. "Return the list of system tests."
  271. (reverse (fold-system-tests cons '())))
  272. ;; Local Variables:
  273. ;; eval: (put 'with-imported-modules-and-extensions 'scheme-indent-function 2)
  274. ;; End:
  275. ;;; tests.scm ends here