tests.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2016-2020, 2022 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-shepherd-service config)
  83. "Return the Shepherd service for the marionette REPL"
  84. (match config
  85. (($ <marionette-configuration> device imported-modules extensions
  86. requirement)
  87. (list (shepherd-service
  88. (provision '(marionette))
  89. ;; Always depend on UDEV so that DEVICE is available.
  90. (requirement `(udev ,@requirement))
  91. (modules '((ice-9 match)
  92. (srfi srfi-9 gnu)))
  93. (start
  94. (with-imported-modules-and-extensions imported-modules extensions
  95. #~(lambda ()
  96. (define (self-quoting? x)
  97. (letrec-syntax ((one-of (syntax-rules ()
  98. ((_) #f)
  99. ((_ pred rest ...)
  100. (or (pred x)
  101. (one-of rest ...))))))
  102. (one-of symbol? string? keyword? pair? null? array?
  103. number? boolean? char?)))
  104. (match (primitive-fork)
  105. (0
  106. (dynamic-wind
  107. (const #t)
  108. (lambda ()
  109. (let ((repl (open-file #$device "r+0"))
  110. (console (open-file "/dev/console" "r+0")))
  111. ;; Redirect output to the console.
  112. (close-fdes 1)
  113. (close-fdes 2)
  114. (dup2 (fileno console) 1)
  115. (dup2 (fileno console) 2)
  116. (close-port console)
  117. (display 'ready repl)
  118. (let loop ()
  119. (newline repl)
  120. (match (read repl)
  121. ((? eof-object?)
  122. (primitive-exit 0))
  123. (expr
  124. (catch #t
  125. (lambda ()
  126. (let ((result (primitive-eval expr)))
  127. (write (if (self-quoting? result)
  128. result
  129. (object->string result))
  130. repl)))
  131. (lambda (key . args)
  132. (print-exception (current-error-port)
  133. (stack-ref (make-stack #t) 1)
  134. key args)
  135. (write #f repl)))))
  136. (loop))))
  137. (lambda ()
  138. (primitive-exit 1))))
  139. (pid
  140. pid)))))
  141. (stop #~(make-kill-destructor)))))))
  142. (define marionette-service-type
  143. ;; This is the type of the "marionette" service, allowing a guest system to
  144. ;; be manipulated from the host. This marionette REPL is essentially a
  145. ;; universal backdoor.
  146. (service-type (name 'marionette-repl)
  147. (extensions
  148. (list (service-extension shepherd-root-service-type
  149. marionette-shepherd-service)))
  150. (description "The @dfn{marionette} service allows a guest
  151. system (virtual machine) to be manipulated by the host. It is used for system
  152. tests.")))
  153. (define* (marionette-operating-system os
  154. #:key
  155. (imported-modules '())
  156. (extensions '())
  157. (requirements '()))
  158. "Return a marionetteed variant of OS such that OS can be used as a
  159. marionette in a virtual machine--i.e., controlled from the host system. The
  160. marionette service in the guest is started after the Shepherd services listed
  161. in REQUIREMENTS. The packages in the list EXTENSIONS are made available from
  162. the backdoor REPL."
  163. (operating-system
  164. (inherit os)
  165. ;; Make sure the guest dies on error.
  166. (kernel-arguments (cons "panic=1"
  167. (operating-system-user-kernel-arguments os)))
  168. ;; Make sure the guest doesn't hang in the REPL on error.
  169. (initrd (lambda (fs . rest)
  170. (apply (operating-system-initrd os) fs
  171. #:on-error 'backtrace
  172. rest)))
  173. (services (cons (service marionette-service-type
  174. (marionette-configuration
  175. (requirements requirements)
  176. (extensions extensions)
  177. (imported-modules imported-modules)))
  178. (operating-system-user-services os)))))
  179. (define-syntax define-os-with-source
  180. (syntax-rules (use-modules operating-system)
  181. "Define two variables: OS containing the given operating system, and
  182. SOURCE containing the source to define OS as an sexp.
  183. This is convenient when we need both the <operating-system> object so we can
  184. instantiate it, and the source to create it so we can store in in a file in
  185. the system under test."
  186. ((_ (os source)
  187. (use-modules modules ...)
  188. (operating-system fields ...))
  189. (begin
  190. (define os
  191. (operating-system fields ...))
  192. (define source
  193. '(begin
  194. (use-modules modules ...)
  195. (operating-system fields ...)))))))
  196. ;;;
  197. ;;; Simple operating systems.
  198. ;;;
  199. (define %simple-os
  200. (operating-system
  201. (host-name "komputilo")
  202. (timezone "Europe/Berlin")
  203. (locale "en_US.UTF-8")
  204. (bootloader (bootloader-configuration
  205. (bootloader grub-bootloader)
  206. (targets '("/dev/sdX"))))
  207. (file-systems (cons (file-system
  208. (device (file-system-label "my-root"))
  209. (mount-point "/")
  210. (type "ext4"))
  211. %base-file-systems))
  212. (firmware '())
  213. (users (cons (user-account
  214. (name "alice")
  215. (comment "Bob's sister")
  216. (group "users")
  217. (supplementary-groups '("wheel" "audio" "video")))
  218. %base-user-accounts))))
  219. (define-syntax-rule (simple-operating-system user-services ...)
  220. "Return an operating system that includes USER-SERVICES in addition to
  221. %BASE-SERVICES."
  222. (operating-system (inherit %simple-os)
  223. (services (cons* user-services ... %base-services))))
  224. ;;;
  225. ;;; Tests.
  226. ;;;
  227. (define-record-type* <system-test> system-test make-system-test
  228. system-test?
  229. (name system-test-name) ;string
  230. (value system-test-value) ;%STORE-MONAD value
  231. (description system-test-description) ;string
  232. (location system-test-location (innate) ;<location>
  233. (default (and=> (current-source-location)
  234. source-properties->location))))
  235. (define (write-system-test test port)
  236. (match test
  237. (($ <system-test> name _ _ ($ <location> file line))
  238. (format port "#<system-test ~a ~a:~a ~a>"
  239. name file line
  240. (number->string (object-address test) 16)))
  241. (($ <system-test> name)
  242. (format port "#<system-test ~a ~a>" name
  243. (number->string (object-address test) 16)))))
  244. (set-record-type-printer! <system-test> write-system-test)
  245. (define-gexp-compiler (compile-system-test (test <system-test>)
  246. system target)
  247. "Compile TEST to a derivation."
  248. ;; XXX: SYSTEM and TARGET are ignored.
  249. (system-test-value test))
  250. (define (test-modules)
  251. "Return the list of modules that define system tests."
  252. (scheme-modules (dirname (search-path %load-path "guix.scm"))
  253. "gnu/tests"
  254. #:warn warn-about-load-error))
  255. (define (fold-system-tests proc seed)
  256. "Invoke PROC on each system test, passing it the test and the previous
  257. result."
  258. (fold-module-public-variables (lambda (obj result)
  259. (if (system-test? obj)
  260. (cons obj result)
  261. result))
  262. '()
  263. (test-modules)))
  264. (define (all-system-tests)
  265. "Return the list of system tests."
  266. (reverse (fold-system-tests cons '())))
  267. ;; Local Variables:
  268. ;; eval: (put 'with-imported-modules-and-extensions 'scheme-indent-function 2)
  269. ;; End:
  270. ;;; tests.scm ends here