tests.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2016, 2017, 2018, 2019, 2020 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. (define* (marionette-operating-system os
  151. #:key
  152. (imported-modules '())
  153. (extensions '())
  154. (requirements '()))
  155. "Return a marionetteed variant of OS such that OS can be used as a
  156. marionette in a virtual machine--i.e., controlled from the host system. The
  157. marionette service in the guest is started after the Shepherd services listed
  158. in REQUIREMENTS. The packages in the list EXTENSIONS are made available from
  159. the backdoor REPL."
  160. (operating-system
  161. (inherit os)
  162. ;; Make sure the guest dies on error.
  163. (kernel-arguments (cons "panic=1"
  164. (operating-system-user-kernel-arguments os)))
  165. ;; Make sure the guest doesn't hang in the REPL on error.
  166. (initrd (lambda (fs . rest)
  167. (apply (operating-system-initrd os) fs
  168. #:on-error 'backtrace
  169. rest)))
  170. (services (cons (service marionette-service-type
  171. (marionette-configuration
  172. (requirements requirements)
  173. (extensions extensions)
  174. (imported-modules imported-modules)))
  175. (operating-system-user-services os)))))
  176. (define-syntax define-os-with-source
  177. (syntax-rules (use-modules operating-system)
  178. "Define two variables: OS containing the given operating system, and
  179. SOURCE containing the source to define OS as an sexp.
  180. This is convenient when we need both the <operating-system> object so we can
  181. instantiate it, and the source to create it so we can store in in a file in
  182. the system under test."
  183. ((_ (os source)
  184. (use-modules modules ...)
  185. (operating-system fields ...))
  186. (begin
  187. (define os
  188. (operating-system fields ...))
  189. (define source
  190. '(begin
  191. (use-modules modules ...)
  192. (operating-system fields ...)))))))
  193. ;;;
  194. ;;; Simple operating systems.
  195. ;;;
  196. (define %simple-os
  197. (operating-system
  198. (host-name "komputilo")
  199. (timezone "Europe/Berlin")
  200. (locale "en_US.UTF-8")
  201. (bootloader (bootloader-configuration
  202. (bootloader grub-bootloader)
  203. (targets '("/dev/sdX"))))
  204. (file-systems (cons (file-system
  205. (device (file-system-label "my-root"))
  206. (mount-point "/")
  207. (type "ext4"))
  208. %base-file-systems))
  209. (firmware '())
  210. (users (cons (user-account
  211. (name "alice")
  212. (comment "Bob's sister")
  213. (group "users")
  214. (supplementary-groups '("wheel" "audio" "video")))
  215. %base-user-accounts))))
  216. (define-syntax-rule (simple-operating-system user-services ...)
  217. "Return an operating system that includes USER-SERVICES in addition to
  218. %BASE-SERVICES."
  219. (operating-system (inherit %simple-os)
  220. (services (cons* user-services ... %base-services))))
  221. ;;;
  222. ;;; Tests.
  223. ;;;
  224. (define-record-type* <system-test> system-test make-system-test
  225. system-test?
  226. (name system-test-name) ;string
  227. (value system-test-value) ;%STORE-MONAD value
  228. (description system-test-description) ;string
  229. (location system-test-location (innate) ;<location>
  230. (default (and=> (current-source-location)
  231. source-properties->location))))
  232. (define (write-system-test test port)
  233. (match test
  234. (($ <system-test> name _ _ ($ <location> file line))
  235. (format port "#<system-test ~a ~a:~a ~a>"
  236. name file line
  237. (number->string (object-address test) 16)))
  238. (($ <system-test> name)
  239. (format port "#<system-test ~a ~a>" name
  240. (number->string (object-address test) 16)))))
  241. (set-record-type-printer! <system-test> write-system-test)
  242. (define-gexp-compiler (compile-system-test (test <system-test>)
  243. system target)
  244. "Compile TEST to a derivation."
  245. ;; XXX: SYSTEM and TARGET are ignored.
  246. (system-test-value test))
  247. (define (test-modules)
  248. "Return the list of modules that define system tests."
  249. (scheme-modules (dirname (search-path %load-path "guix.scm"))
  250. "gnu/tests"
  251. #:warn warn-about-load-error))
  252. (define (fold-system-tests proc seed)
  253. "Invoke PROC on each system test, passing it the test and the previous
  254. result."
  255. (fold-module-public-variables (lambda (obj result)
  256. (if (system-test? obj)
  257. (cons obj result)
  258. result))
  259. '()
  260. (test-modules)))
  261. (define (all-system-tests)
  262. "Return the list of system tests."
  263. (reverse (fold-system-tests cons '())))
  264. ;; Local Variables:
  265. ;; eval: (put 'with-imported-modules-and-extensions 'scheme-indent-function 2)
  266. ;; End:
  267. ;;; tests.scm ends here