tests.scm 11 KB

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