2016-05-04 17:31:08 -04:00
|
|
|
|
;;; GNU Guix --- Functional package management for GNU
|
2024-01-23 08:27:30 -05:00
|
|
|
|
;;; Copyright © 2016-2022, 2024 Ludovic Courtès <ludo@gnu.org>
|
2018-07-31 01:50:16 -04:00
|
|
|
|
;;; Copyright © 2018 Chris Marusich <cmmarusich@gmail.com>
|
2023-06-30 09:58:12 -04:00
|
|
|
|
;;; Copyright © 2022, 2023 Maxim Cournoyer <maxim.cournoyer@gmail.com>
|
|
|
|
|
;;; Copyright © 2023 Bruno Victal <mirai@makinata.eu>
|
2016-05-04 17:31:08 -04:00
|
|
|
|
;;;
|
|
|
|
|
;;; This file is part of GNU Guix.
|
|
|
|
|
;;;
|
|
|
|
|
;;; GNU Guix is free software; you can redistribute it and/or modify it
|
|
|
|
|
;;; under the terms of the GNU General Public License as published by
|
|
|
|
|
;;; the Free Software Foundation; either version 3 of the License, or (at
|
|
|
|
|
;;; your option) any later version.
|
|
|
|
|
;;;
|
|
|
|
|
;;; GNU Guix is distributed in the hope that it will be useful, but
|
|
|
|
|
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
;;; GNU General Public License for more details.
|
|
|
|
|
;;;
|
|
|
|
|
;;; You should have received a copy of the GNU General Public License
|
|
|
|
|
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
(define-module (gnu build marionette)
|
|
|
|
|
#:use-module (srfi srfi-9)
|
|
|
|
|
#:use-module (srfi srfi-26)
|
tests: Adjust to SRFI-64 as found in Guile 3.0.7.
In Guile 3.0.7, 'test-runner-current' is set to #f upon 'test-end'.
Consequently, the previous strategy, where we'd call
'test-runner-current' after 'test-end', no longer works. Instead, set
the test runner in each test right before 'test-begin'.
* gnu/build/marionette.scm (system-test-runner): New procedure.
* gnu/tests/audio.scm (run-mpd-test): Replace (exit (= ...)) idiom
by (test-runner-current (system-test-runner)).
* gnu/tests/base.scm (run-basic-test)
(run-cleanup-test, run-mcron-test, run-nss-mdns-test): Likewise.
* gnu/tests/ci.scm (run-laminar-test): Likewise.
* gnu/tests/cups.scm (run-cups-test): Likewise.
* gnu/tests/databases.scm (run-memcached-test)
(run-postgresql-test, run-mysql-test): Likewise.
* gnu/tests/desktop.scm (run-elogind-test): Likewise.
* gnu/tests/dict.scm (run-dicod-test): Likewise.
* gnu/tests/docker.scm (run-docker-test): Likewise.
(run-docker-system-test): Likewise.
* gnu/tests/file-sharing.scm (run-transmission-daemon-test): Likewise.
* gnu/tests/ganeti.scm (run-ganeti-test): Likewise.
* gnu/tests/guix.scm (run-guix-build-coordinator-test): Likewise.
(run-guix-data-service-test): Likewise.
* gnu/tests/ldap.scm (run-ldap-test): Likewise.
* gnu/tests/linux-modules.scm (run-loadable-kernel-modules-test-base): Likewise.
* gnu/tests/mail.scm (run-opensmtpd-test)
(run-exim-test, run-dovecot-test, run-getmail-test): Likewise.
* gnu/tests/messaging.scm (run-xmpp-test)
(run-bitlbee-test, run-quassel-test): Likewise.
* gnu/tests/monitoring.scm (run-prometheus-node-exporter-server-test)
(run-zabbix-server-test): Likewise.
* gnu/tests/networking.scm (run-inetd-test, run-openvswitch-test)
(run-dhcpd-test, run-tor-test, run-iptables-test, run-ipfs-test): Likewise.
* gnu/tests/nfs.scm (run-nfs-test)
(run-nfs-server-test, run-nfs-root-fs-test): Likewise.
* gnu/tests/package-management.scm (run-nix-test): Likewise.
* gnu/tests/reconfigure.scm (run-switch-to-system-test)
(run-upgrade-services-test, run-install-bootloader-test): Likewise.
* gnu/tests/rsync.scm (run-rsync-test): Likewise.
* gnu/tests/security-token.scm (run-pcscd-test): Likewise.
* gnu/tests/singularity.scm (run-singularity-test): Likewise.
* gnu/tests/ssh.scm (run-ssh-test): Likewise.
* gnu/tests/telephony.scm (run-jami-test): Likewise.
* gnu/tests/version-control.scm (run-cgit-test): Likewise.
(run-git-http-test, run-gitolite-test, run-gitile-test): Likewise.
* gnu/tests/virtualization.scm (run-libvirt-test, run-childhurd-test): Likewise.
* gnu/tests/web.scm (run-webserver-test, run-php-fpm-test)
(run-hpcguix-web-server-test, run-tailon-test, run-patchwork-test): Likewise.
2021-09-25 12:36:04 -04:00
|
|
|
|
#:use-module (srfi srfi-64)
|
2022-09-19 22:06:54 -04:00
|
|
|
|
#:use-module (srfi srfi-71)
|
2016-05-04 17:31:08 -04:00
|
|
|
|
#:use-module (rnrs io ports)
|
|
|
|
|
#:use-module (ice-9 match)
|
2016-11-23 07:56:42 -05:00
|
|
|
|
#:use-module (ice-9 popen)
|
2021-12-16 07:32:11 -05:00
|
|
|
|
#:use-module (ice-9 regex)
|
2016-05-04 17:31:08 -04:00
|
|
|
|
#:export (marionette?
|
2022-11-17 04:26:01 -05:00
|
|
|
|
marionette-pid
|
2016-05-04 17:31:08 -04:00
|
|
|
|
make-marionette
|
|
|
|
|
marionette-eval
|
2017-06-12 17:21:24 -04:00
|
|
|
|
wait-for-file
|
2018-06-01 04:01:05 -04:00
|
|
|
|
wait-for-tcp-port
|
2018-07-31 01:50:16 -04:00
|
|
|
|
wait-for-unix-socket
|
2016-05-04 17:31:08 -04:00
|
|
|
|
marionette-control
|
2016-11-23 14:59:13 -05:00
|
|
|
|
wait-for-screen-text
|
2023-06-30 09:58:12 -04:00
|
|
|
|
%default-ocrad-arguments
|
2016-05-04 17:31:08 -04:00
|
|
|
|
%qwerty-us-keystrokes
|
tests: Adjust to SRFI-64 as found in Guile 3.0.7.
In Guile 3.0.7, 'test-runner-current' is set to #f upon 'test-end'.
Consequently, the previous strategy, where we'd call
'test-runner-current' after 'test-end', no longer works. Instead, set
the test runner in each test right before 'test-begin'.
* gnu/build/marionette.scm (system-test-runner): New procedure.
* gnu/tests/audio.scm (run-mpd-test): Replace (exit (= ...)) idiom
by (test-runner-current (system-test-runner)).
* gnu/tests/base.scm (run-basic-test)
(run-cleanup-test, run-mcron-test, run-nss-mdns-test): Likewise.
* gnu/tests/ci.scm (run-laminar-test): Likewise.
* gnu/tests/cups.scm (run-cups-test): Likewise.
* gnu/tests/databases.scm (run-memcached-test)
(run-postgresql-test, run-mysql-test): Likewise.
* gnu/tests/desktop.scm (run-elogind-test): Likewise.
* gnu/tests/dict.scm (run-dicod-test): Likewise.
* gnu/tests/docker.scm (run-docker-test): Likewise.
(run-docker-system-test): Likewise.
* gnu/tests/file-sharing.scm (run-transmission-daemon-test): Likewise.
* gnu/tests/ganeti.scm (run-ganeti-test): Likewise.
* gnu/tests/guix.scm (run-guix-build-coordinator-test): Likewise.
(run-guix-data-service-test): Likewise.
* gnu/tests/ldap.scm (run-ldap-test): Likewise.
* gnu/tests/linux-modules.scm (run-loadable-kernel-modules-test-base): Likewise.
* gnu/tests/mail.scm (run-opensmtpd-test)
(run-exim-test, run-dovecot-test, run-getmail-test): Likewise.
* gnu/tests/messaging.scm (run-xmpp-test)
(run-bitlbee-test, run-quassel-test): Likewise.
* gnu/tests/monitoring.scm (run-prometheus-node-exporter-server-test)
(run-zabbix-server-test): Likewise.
* gnu/tests/networking.scm (run-inetd-test, run-openvswitch-test)
(run-dhcpd-test, run-tor-test, run-iptables-test, run-ipfs-test): Likewise.
* gnu/tests/nfs.scm (run-nfs-test)
(run-nfs-server-test, run-nfs-root-fs-test): Likewise.
* gnu/tests/package-management.scm (run-nix-test): Likewise.
* gnu/tests/reconfigure.scm (run-switch-to-system-test)
(run-upgrade-services-test, run-install-bootloader-test): Likewise.
* gnu/tests/rsync.scm (run-rsync-test): Likewise.
* gnu/tests/security-token.scm (run-pcscd-test): Likewise.
* gnu/tests/singularity.scm (run-singularity-test): Likewise.
* gnu/tests/ssh.scm (run-ssh-test): Likewise.
* gnu/tests/telephony.scm (run-jami-test): Likewise.
* gnu/tests/version-control.scm (run-cgit-test): Likewise.
(run-git-http-test, run-gitolite-test, run-gitile-test): Likewise.
* gnu/tests/virtualization.scm (run-libvirt-test, run-childhurd-test): Likewise.
* gnu/tests/web.scm (run-webserver-test, run-php-fpm-test)
(run-hpcguix-web-server-test, run-tailon-test, run-patchwork-test): Likewise.
2021-09-25 12:36:04 -04:00
|
|
|
|
marionette-type
|
|
|
|
|
|
2021-12-16 07:32:11 -05:00
|
|
|
|
system-test-runner
|
|
|
|
|
qemu-command))
|
2016-05-04 17:31:08 -04:00
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
;;;
|
|
|
|
|
;;; Instrumentation tools for QEMU virtual machines (VMs). A "marionette" is
|
|
|
|
|
;;; essentially a VM (a QEMU instance) with its monitor connected to a
|
|
|
|
|
;;; Unix-domain socket, and with a REPL inside the guest listening on a
|
|
|
|
|
;;; virtual console, which is itself connected to the host via a Unix-domain
|
|
|
|
|
;;; socket--these are the marionette's strings, connecting it to the almighty
|
|
|
|
|
;;; puppeteer.
|
|
|
|
|
;;;
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
|
|
|
|
(define-record-type <marionette>
|
|
|
|
|
(marionette command pid monitor repl)
|
|
|
|
|
marionette?
|
|
|
|
|
(command marionette-command) ;list of strings
|
|
|
|
|
(pid marionette-pid) ;integer
|
|
|
|
|
(monitor marionette-monitor) ;port
|
2016-11-23 05:04:28 -05:00
|
|
|
|
(repl %marionette-repl)) ;promise of a port
|
|
|
|
|
|
|
|
|
|
(define-syntax-rule (marionette-repl marionette)
|
|
|
|
|
(force (%marionette-repl marionette)))
|
2016-05-04 17:31:08 -04:00
|
|
|
|
|
|
|
|
|
(define* (wait-for-monitor-prompt port #:key (quiet? #t))
|
|
|
|
|
"Read from PORT until we have seen all of QEMU's monitor prompt. When
|
|
|
|
|
QUIET? is false, the monitor's output is written to the current output port."
|
|
|
|
|
(define full-prompt
|
|
|
|
|
(string->list "(qemu) "))
|
|
|
|
|
|
|
|
|
|
(let loop ((prompt full-prompt)
|
|
|
|
|
(matches '())
|
|
|
|
|
(prefix '()))
|
|
|
|
|
(match prompt
|
|
|
|
|
(()
|
|
|
|
|
;; It's useful to set QUIET? so we don't display the echo of our own
|
|
|
|
|
;; commands.
|
|
|
|
|
(unless quiet?
|
|
|
|
|
(for-each (lambda (line)
|
|
|
|
|
(format #t "qemu monitor: ~a~%" line))
|
|
|
|
|
(string-tokenize (list->string (reverse prefix))
|
|
|
|
|
(char-set-complement (char-set #\newline))))))
|
|
|
|
|
((chr rest ...)
|
|
|
|
|
(let ((read (read-char port)))
|
|
|
|
|
(cond ((eqv? read chr)
|
|
|
|
|
(loop rest (cons read matches) prefix))
|
|
|
|
|
((eof-object? read)
|
|
|
|
|
(error "EOF while waiting for QEMU monitor prompt"
|
|
|
|
|
(list->string (reverse prefix))))
|
|
|
|
|
(else
|
|
|
|
|
(loop full-prompt
|
|
|
|
|
'()
|
|
|
|
|
(cons read (append matches prefix))))))))))
|
|
|
|
|
|
|
|
|
|
(define* (make-marionette command
|
|
|
|
|
#:key (socket-directory "/tmp") (timeout 20))
|
|
|
|
|
"Return a QEMU marionette--i.e., a virtual machine with open connections to the
|
|
|
|
|
QEMU monitor and to the guest's backdoor REPL."
|
|
|
|
|
(define (file->sockaddr file)
|
|
|
|
|
(make-socket-address AF_UNIX
|
|
|
|
|
(string-append socket-directory "/" file)))
|
|
|
|
|
|
|
|
|
|
(define extra-options
|
|
|
|
|
(list "-nographic"
|
|
|
|
|
"-monitor" (string-append "unix:" socket-directory "/monitor")
|
|
|
|
|
"-chardev" (string-append "socket,id=repl,path=" socket-directory
|
|
|
|
|
"/repl")
|
2022-07-28 11:03:26 -04:00
|
|
|
|
"-chardev" (string-append "socket,id=qga,server=on,wait=off,path="
|
|
|
|
|
socket-directory "/qemu-ga")
|
2018-02-19 15:58:18 -05:00
|
|
|
|
|
|
|
|
|
;; See
|
|
|
|
|
;; <http://www.linux-kvm.org/page/VMchannel_Requirements#Invocation>.
|
2016-05-04 17:31:08 -04:00
|
|
|
|
"-device" "virtio-serial"
|
2022-07-28 11:03:26 -04:00
|
|
|
|
"-device" "virtserialport,chardev=repl,name=org.gnu.guix.port.0"
|
|
|
|
|
"-device" "virtserialport,chardev=qga,name=org.qemu.guest_agent.0"))
|
2016-05-04 17:31:08 -04:00
|
|
|
|
|
2016-11-22 16:57:41 -05:00
|
|
|
|
(define (accept* port)
|
|
|
|
|
(match (select (list port) '() (list port) timeout)
|
|
|
|
|
(((port) () ())
|
|
|
|
|
(accept port))
|
|
|
|
|
(_
|
|
|
|
|
(error "timeout in 'accept'" port))))
|
|
|
|
|
|
2016-05-04 17:31:08 -04:00
|
|
|
|
(let ((monitor (socket AF_UNIX SOCK_STREAM 0))
|
|
|
|
|
(repl (socket AF_UNIX SOCK_STREAM 0)))
|
|
|
|
|
(bind monitor (file->sockaddr "monitor"))
|
|
|
|
|
(listen monitor 1)
|
|
|
|
|
(bind repl (file->sockaddr "repl"))
|
|
|
|
|
(listen repl 1)
|
|
|
|
|
|
|
|
|
|
(match (primitive-fork)
|
|
|
|
|
(0
|
|
|
|
|
(catch #t
|
|
|
|
|
(lambda ()
|
|
|
|
|
(close monitor)
|
|
|
|
|
(close repl)
|
|
|
|
|
(match command
|
|
|
|
|
((program . args)
|
|
|
|
|
(apply execl program program
|
|
|
|
|
(append args extra-options)))))
|
|
|
|
|
(lambda (key . args)
|
|
|
|
|
(print-exception (current-error-port)
|
|
|
|
|
(stack-ref (make-stack #t) 1)
|
|
|
|
|
key args)
|
|
|
|
|
(primitive-exit 1))))
|
|
|
|
|
(pid
|
|
|
|
|
(format #t "QEMU runs as PID ~a~%" pid)
|
|
|
|
|
|
2016-11-22 16:57:41 -05:00
|
|
|
|
(match (accept* monitor)
|
2016-05-04 17:31:08 -04:00
|
|
|
|
((monitor-conn . _)
|
|
|
|
|
(display "connected to QEMU's monitor\n")
|
|
|
|
|
(close-port monitor)
|
|
|
|
|
(wait-for-monitor-prompt monitor-conn)
|
|
|
|
|
(display "read QEMU monitor prompt\n")
|
2016-11-23 05:04:28 -05:00
|
|
|
|
|
|
|
|
|
(marionette (append command extra-options) pid
|
|
|
|
|
monitor-conn
|
|
|
|
|
|
|
|
|
|
;; The following 'accept' call connects immediately, but
|
|
|
|
|
;; we don't know whether the guest has connected until
|
|
|
|
|
;; we actually receive the 'ready' message.
|
|
|
|
|
(match (accept* repl)
|
|
|
|
|
((repl-conn . addr)
|
|
|
|
|
(display "connected to guest REPL\n")
|
|
|
|
|
(close-port repl)
|
|
|
|
|
;; Delay reception of the 'ready' message so that the
|
|
|
|
|
;; caller can already send monitor commands.
|
|
|
|
|
(delay
|
|
|
|
|
(match (read repl-conn)
|
|
|
|
|
('ready
|
|
|
|
|
(display "marionette is ready\n")
|
|
|
|
|
repl-conn))))))))))))
|
2016-05-04 17:31:08 -04:00
|
|
|
|
|
|
|
|
|
(define (marionette-eval exp marionette)
|
|
|
|
|
"Evaluate EXP in MARIONETTE's backdoor REPL. Return the result."
|
|
|
|
|
(match marionette
|
2016-11-23 05:04:28 -05:00
|
|
|
|
(($ <marionette> command pid monitor (= force repl))
|
2016-05-04 17:31:08 -04:00
|
|
|
|
(write exp repl)
|
|
|
|
|
(newline repl)
|
2022-09-15 18:49:48 -04:00
|
|
|
|
(with-exception-handler
|
|
|
|
|
(lambda (exn)
|
|
|
|
|
(simple-format
|
|
|
|
|
(current-error-port)
|
|
|
|
|
"error reading marionette response: ~A
|
|
|
|
|
remaining response: ~A\n"
|
|
|
|
|
exn
|
|
|
|
|
(get-line repl))
|
|
|
|
|
(raise-exception exn))
|
|
|
|
|
(lambda ()
|
|
|
|
|
(read repl))
|
|
|
|
|
#:unwind? #t))))
|
2016-05-04 17:31:08 -04:00
|
|
|
|
|
2017-09-07 17:31:21 -04:00
|
|
|
|
(define* (wait-for-file file marionette
|
|
|
|
|
#:key (timeout 10) (read 'read))
|
|
|
|
|
"Wait until FILE exists in MARIONETTE; READ its content and return it. If
|
2017-06-12 17:21:24 -04:00
|
|
|
|
FILE has not shown up after TIMEOUT seconds, raise an error."
|
2017-08-27 14:58:33 -04:00
|
|
|
|
(match (marionette-eval
|
|
|
|
|
`(let loop ((i ,timeout))
|
|
|
|
|
(cond ((file-exists? ,file)
|
2022-09-16 03:20:25 -04:00
|
|
|
|
(cons 'success
|
|
|
|
|
(let ((content
|
|
|
|
|
(call-with-input-file ,file ,read)))
|
|
|
|
|
(if (eof-object? content)
|
|
|
|
|
;; #<eof> can't be read, so convert to the
|
|
|
|
|
;; empty string
|
|
|
|
|
""
|
|
|
|
|
content))))
|
2017-08-27 14:58:33 -04:00
|
|
|
|
((> i 0)
|
|
|
|
|
(sleep 1)
|
|
|
|
|
(loop (- i 1)))
|
|
|
|
|
(else
|
|
|
|
|
'failure)))
|
|
|
|
|
marionette)
|
|
|
|
|
(('success . result)
|
|
|
|
|
result)
|
|
|
|
|
('failure
|
|
|
|
|
(error "file didn't show up" file))))
|
2017-06-12 17:21:24 -04:00
|
|
|
|
|
2018-06-01 04:01:05 -04:00
|
|
|
|
(define* (wait-for-tcp-port port marionette
|
2022-05-22 12:23:27 -04:00
|
|
|
|
#:key
|
|
|
|
|
(timeout 20)
|
2024-01-23 08:27:30 -05:00
|
|
|
|
(peek? #f)
|
2022-05-22 12:23:27 -04:00
|
|
|
|
(address `(make-socket-address AF_INET
|
|
|
|
|
INADDR_LOOPBACK
|
|
|
|
|
,port)))
|
2018-06-01 04:01:05 -04:00
|
|
|
|
"Wait for up to TIMEOUT seconds for PORT to accept connections in
|
2022-05-22 12:23:27 -04:00
|
|
|
|
MARIONETTE. ADDRESS must be an expression that returns a socket address,
|
2024-01-23 08:27:30 -05:00
|
|
|
|
typically a call to 'make-socket-address'. When PEEK? is true, attempt to
|
|
|
|
|
read a byte from the socket upon connection; retry if that gives the
|
|
|
|
|
end-of-file object.
|
|
|
|
|
|
|
|
|
|
Raise an error on failure."
|
2018-06-01 04:01:05 -04:00
|
|
|
|
;; Note: The 'connect' loop has to run within the guest because, when we
|
|
|
|
|
;; forward ports to the host, connecting to the host never raises
|
|
|
|
|
;; ECONNREFUSED.
|
|
|
|
|
(match (marionette-eval
|
2024-01-23 08:27:30 -05:00
|
|
|
|
`(let* ((address ,address))
|
|
|
|
|
(define (open-socket)
|
|
|
|
|
(socket (sockaddr:fam address) SOCK_STREAM 0))
|
|
|
|
|
|
|
|
|
|
(let loop ((sock (open-socket))
|
|
|
|
|
(i 0))
|
2022-05-22 12:23:27 -04:00
|
|
|
|
(catch 'system-error
|
|
|
|
|
(lambda ()
|
|
|
|
|
(connect sock address)
|
2024-01-23 08:27:30 -05:00
|
|
|
|
(when ,peek?
|
|
|
|
|
(let ((byte ((@ (ice-9 binary-ports) lookahead-u8)
|
|
|
|
|
sock)))
|
|
|
|
|
(when (eof-object? byte)
|
|
|
|
|
(close-port sock)
|
|
|
|
|
(throw 'system-error
|
|
|
|
|
"wait-for-tcp-port" "~A"
|
|
|
|
|
(list (strerror ECONNRESET))
|
|
|
|
|
(list ECONNRESET)))))
|
2022-05-22 12:23:27 -04:00
|
|
|
|
(close-port sock)
|
|
|
|
|
'success)
|
|
|
|
|
(lambda args
|
|
|
|
|
(if (< i ,timeout)
|
|
|
|
|
(begin
|
|
|
|
|
(sleep 1)
|
2024-01-23 08:27:30 -05:00
|
|
|
|
(loop (if (port-closed? sock)
|
|
|
|
|
(open-socket)
|
|
|
|
|
sock)
|
|
|
|
|
(+ 1 i)))
|
2022-05-22 12:23:27 -04:00
|
|
|
|
(list 'failure address))))))
|
2018-06-01 04:01:05 -04:00
|
|
|
|
marionette)
|
|
|
|
|
('success #t)
|
2022-05-22 12:23:27 -04:00
|
|
|
|
(('failure address)
|
|
|
|
|
(error "nobody's listening on port"
|
|
|
|
|
(list (inet-ntop (sockaddr:fam address) (sockaddr:addr address))
|
|
|
|
|
(sockaddr:port address))))))
|
2018-06-01 04:01:05 -04:00
|
|
|
|
|
2018-07-31 01:50:16 -04:00
|
|
|
|
(define* (wait-for-unix-socket file-name marionette
|
|
|
|
|
#:key (timeout 20))
|
|
|
|
|
"Wait for up to TIMEOUT seconds for FILE-NAME, a Unix domain socket, to
|
|
|
|
|
accept connections in MARIONETTE. Raise an error on failure."
|
|
|
|
|
(match (marionette-eval
|
|
|
|
|
`(begin
|
|
|
|
|
(let ((sock (socket PF_UNIX SOCK_STREAM 0)))
|
|
|
|
|
(let loop ((i 0))
|
|
|
|
|
(catch 'system-error
|
|
|
|
|
(lambda ()
|
|
|
|
|
(connect sock AF_UNIX ,file-name)
|
2020-02-18 12:21:55 -05:00
|
|
|
|
(close-port sock)
|
2018-07-31 01:50:16 -04:00
|
|
|
|
'success)
|
|
|
|
|
(lambda args
|
|
|
|
|
(if (< i ,timeout)
|
|
|
|
|
(begin
|
|
|
|
|
(sleep 1)
|
|
|
|
|
(loop (+ 1 i)))
|
|
|
|
|
'failure))))))
|
|
|
|
|
marionette)
|
|
|
|
|
('success #t)
|
|
|
|
|
('failure
|
|
|
|
|
(error "nobody's listening on unix domain socket" file-name))))
|
|
|
|
|
|
2016-05-04 17:31:08 -04:00
|
|
|
|
(define (marionette-control command marionette)
|
|
|
|
|
"Run COMMAND in the QEMU monitor of MARIONETTE. COMMAND is a string such as
|
2022-08-11 11:38:55 -04:00
|
|
|
|
\"sendkey ctrl-alt-f1\" or \"screendump foo.ppm\" (info \"(QEMU) QEMU
|
|
|
|
|
Monitor\")."
|
2016-05-04 17:31:08 -04:00
|
|
|
|
(match marionette
|
|
|
|
|
(($ <marionette> _ _ monitor)
|
|
|
|
|
(display command monitor)
|
|
|
|
|
(newline monitor)
|
2018-07-31 01:47:43 -04:00
|
|
|
|
;; The "quit" command terminates QEMU immediately, with no output.
|
|
|
|
|
(unless (string=? command "quit") (wait-for-monitor-prompt monitor)))))
|
2016-05-04 17:31:08 -04:00
|
|
|
|
|
2023-06-30 09:58:12 -04:00
|
|
|
|
(define %default-ocrad-arguments
|
|
|
|
|
'("--invert" "--scale=10"))
|
|
|
|
|
|
|
|
|
|
(define* (invoke-ocrad-ocr image #:key (ocrad "ocrad")
|
|
|
|
|
(ocr-arguments %default-ocrad-arguments))
|
2022-08-12 11:23:29 -04:00
|
|
|
|
"Invoke the OCRAD command on image, and return the recognized text."
|
2023-06-30 09:58:12 -04:00
|
|
|
|
(let* ((command (string-join `(,ocrad ,@ocr-arguments ,image)))
|
|
|
|
|
(pipe (open-input-pipe command))
|
2022-08-12 11:23:29 -04:00
|
|
|
|
(text (get-string-all pipe)))
|
|
|
|
|
(unless (zero? (close-pipe pipe))
|
|
|
|
|
(error "'ocrad' failed" ocrad))
|
|
|
|
|
text))
|
2016-11-23 07:56:42 -05:00
|
|
|
|
|
2023-06-30 09:58:12 -04:00
|
|
|
|
(define* (invoke-tesseract-ocr image #:key (tesseract "tesseract")
|
|
|
|
|
(ocr-arguments '()))
|
2022-08-12 11:23:29 -04:00
|
|
|
|
"Invoke the TESSERACT command on IMAGE, and return the recognized text."
|
|
|
|
|
(let* ((output-basename (tmpnam))
|
2023-06-30 09:58:12 -04:00
|
|
|
|
(output-basename* (string-append output-basename ".txt"))
|
|
|
|
|
(arguments (cons* image output-basename ocr-arguments)))
|
2016-11-23 07:56:42 -05:00
|
|
|
|
(dynamic-wind
|
|
|
|
|
(const #t)
|
|
|
|
|
(lambda ()
|
2022-08-12 11:23:29 -04:00
|
|
|
|
(let ((exit-val (status:exit-val
|
2023-06-30 09:58:12 -04:00
|
|
|
|
(apply system* tesseract arguments))))
|
2022-08-12 11:23:29 -04:00
|
|
|
|
(unless (zero? exit-val)
|
|
|
|
|
(error "'tesseract' failed" tesseract))
|
|
|
|
|
(call-with-input-file output-basename* get-string-all)))
|
2016-11-23 07:56:42 -05:00
|
|
|
|
(lambda ()
|
2022-08-12 11:23:29 -04:00
|
|
|
|
(false-if-exception (delete-file output-basename))
|
|
|
|
|
(false-if-exception (delete-file output-basename*))))))
|
|
|
|
|
|
2023-06-30 09:58:12 -04:00
|
|
|
|
(define* (marionette-screen-text marionette #:key (ocr "ocrad")
|
|
|
|
|
ocr-arguments)
|
2022-08-12 11:23:29 -04:00
|
|
|
|
"Take a screenshot of MARIONETTE, perform optical character
|
2022-09-19 22:06:54 -04:00
|
|
|
|
recognition (OCR), and return the text read from the screen as a string, along
|
|
|
|
|
the screen dump image used. Do this by invoking OCR, which should be the file
|
|
|
|
|
name of GNU Ocrad's@command{ocrad} or Tesseract OCR's @command{tesseract}
|
|
|
|
|
command. The screen dump image returned as the second value should be deleted
|
|
|
|
|
if it is not needed."
|
2022-08-12 11:23:29 -04:00
|
|
|
|
(define image (string-append (tmpnam) ".ppm"))
|
|
|
|
|
;; Use the QEMU Monitor to save an image of the screen to the host.
|
|
|
|
|
(marionette-control (string-append "screendump " image) marionette)
|
|
|
|
|
;; Process it via the OCR.
|
|
|
|
|
(cond
|
|
|
|
|
((string-contains ocr "ocrad")
|
2023-06-30 09:58:12 -04:00
|
|
|
|
(values (invoke-ocrad-ocr image
|
|
|
|
|
#:ocrad ocr
|
|
|
|
|
#:ocr-arguments
|
|
|
|
|
(or ocr-arguments %default-ocrad-arguments))
|
|
|
|
|
image))
|
2022-08-12 11:23:29 -04:00
|
|
|
|
((string-contains ocr "tesseract")
|
2023-06-30 09:58:12 -04:00
|
|
|
|
(values (invoke-tesseract-ocr image
|
|
|
|
|
#:tesseract ocr
|
|
|
|
|
#:ocr-arguments (or ocr-arguments '()))
|
|
|
|
|
image))
|
2022-08-12 11:23:29 -04:00
|
|
|
|
(else (error "unsupported ocr command"))))
|
2016-11-23 07:56:42 -05:00
|
|
|
|
|
2016-11-23 14:59:13 -05:00
|
|
|
|
(define* (wait-for-screen-text marionette predicate
|
2022-08-12 11:23:29 -04:00
|
|
|
|
#:key
|
|
|
|
|
(ocr "ocrad")
|
2023-06-30 09:58:12 -04:00
|
|
|
|
ocr-arguments
|
2022-09-23 00:23:47 -04:00
|
|
|
|
(timeout 30)
|
|
|
|
|
pre-action
|
|
|
|
|
post-action)
|
2016-11-23 14:59:13 -05:00
|
|
|
|
"Wait for TIMEOUT seconds or until the screen text on MARIONETTE matches
|
2022-09-19 22:06:54 -04:00
|
|
|
|
PREDICATE, whichever comes first. Raise an error when TIMEOUT is exceeded.
|
|
|
|
|
The error contains the recognized text along the preserved file name of the
|
2022-09-23 00:23:47 -04:00
|
|
|
|
screen dump, which is relative to the current working directory. If
|
|
|
|
|
PRE-ACTION is provided, it should be a thunk to call before each OCR attempt.
|
|
|
|
|
Likewise for POST-ACTION, except it runs at the end of a successful OCR."
|
2016-11-23 14:59:13 -05:00
|
|
|
|
(define start
|
|
|
|
|
(car (gettimeofday)))
|
|
|
|
|
|
|
|
|
|
(define end
|
|
|
|
|
(+ start timeout))
|
|
|
|
|
|
2022-09-19 22:06:54 -04:00
|
|
|
|
(let loop ((last-text #f)
|
|
|
|
|
(last-screendump #f))
|
2016-11-23 14:59:13 -05:00
|
|
|
|
(if (> (car (gettimeofday)) end)
|
2022-09-19 22:06:54 -04:00
|
|
|
|
(let ((screendump-backup (string-drop last-screendump 5)))
|
|
|
|
|
;; Move the file from /tmp/fileXXXXXX.pmm to the current working
|
|
|
|
|
;; directory, so that it is preserved in the test derivation output.
|
|
|
|
|
(copy-file last-screendump screendump-backup)
|
|
|
|
|
(delete-file last-screendump)
|
|
|
|
|
(error "'wait-for-screen-text' timeout"
|
|
|
|
|
'ocr-text: last-text
|
|
|
|
|
'screendump: screendump-backup))
|
2022-09-23 00:23:47 -04:00
|
|
|
|
(let* ((_ (and (procedure? pre-action) (pre-action)))
|
2023-06-30 09:58:12 -04:00
|
|
|
|
(text screendump
|
|
|
|
|
(marionette-screen-text marionette
|
|
|
|
|
#:ocr ocr
|
|
|
|
|
#:ocr-arguments ocr-arguments))
|
2022-09-23 00:23:47 -04:00
|
|
|
|
(_ (and (procedure? post-action) (post-action)))
|
2022-09-19 22:06:54 -04:00
|
|
|
|
(result (predicate text)))
|
|
|
|
|
(cond (result
|
|
|
|
|
(delete-file screendump)
|
|
|
|
|
result)
|
|
|
|
|
(else
|
|
|
|
|
(sleep 1)
|
|
|
|
|
(loop text screendump)))))))
|
2016-11-23 14:59:13 -05:00
|
|
|
|
|
2016-05-04 17:31:08 -04:00
|
|
|
|
(define %qwerty-us-keystrokes
|
|
|
|
|
;; Maps "special" characters to their keystrokes.
|
|
|
|
|
'((#\newline . "ret")
|
|
|
|
|
(#\space . "spc")
|
|
|
|
|
(#\- . "minus")
|
|
|
|
|
(#\+ . "shift-equal")
|
|
|
|
|
(#\* . "shift-8")
|
|
|
|
|
(#\= . "equal")
|
|
|
|
|
(#\? . "shift-slash")
|
|
|
|
|
(#\[ . "bracket_left")
|
|
|
|
|
(#\] . "bracket_right")
|
2019-05-15 06:06:34 -04:00
|
|
|
|
(#\{ . "shift-bracket_left")
|
|
|
|
|
(#\} . "shift-bracket_right")
|
2016-05-04 17:31:08 -04:00
|
|
|
|
(#\( . "shift-9")
|
|
|
|
|
(#\) . "shift-0")
|
|
|
|
|
(#\/ . "slash")
|
2020-02-21 09:04:17 -05:00
|
|
|
|
(#\< . "shift-comma")
|
|
|
|
|
(#\> . "shift-dot")
|
2016-05-04 17:31:08 -04:00
|
|
|
|
(#\. . "dot")
|
|
|
|
|
(#\, . "comma")
|
2022-09-20 11:00:59 -04:00
|
|
|
|
(#\: . "shift-semicolon")
|
2016-05-04 17:31:08 -04:00
|
|
|
|
(#\; . "semicolon")
|
2017-08-27 16:01:22 -04:00
|
|
|
|
(#\' . "apostrophe")
|
2022-09-20 11:00:59 -04:00
|
|
|
|
(#\! . "shift-1")
|
2017-08-27 16:01:22 -04:00
|
|
|
|
(#\" . "shift-apostrophe")
|
|
|
|
|
(#\` . "grave_accent")
|
2016-05-04 17:31:08 -04:00
|
|
|
|
(#\bs . "backspace")
|
|
|
|
|
(#\tab . "tab")))
|
|
|
|
|
|
2017-08-27 16:00:19 -04:00
|
|
|
|
(define (character->keystroke chr keystrokes)
|
|
|
|
|
"Return the keystroke for CHR according to the keyboard layout defined by
|
|
|
|
|
KEYSTROKES."
|
|
|
|
|
(if (char-set-contains? char-set:upper-case chr)
|
|
|
|
|
(string-append "shift-" (string (char-downcase chr)))
|
|
|
|
|
(or (assoc-ref keystrokes chr)
|
|
|
|
|
(string chr))))
|
|
|
|
|
|
2016-05-04 17:31:08 -04:00
|
|
|
|
(define* (string->keystroke-commands str
|
|
|
|
|
#:optional
|
|
|
|
|
(keystrokes
|
|
|
|
|
%qwerty-us-keystrokes))
|
|
|
|
|
"Return a list of QEMU monitor commands to send the keystrokes corresponding
|
|
|
|
|
to STR. KEYSTROKES is an alist specifying a mapping from characters to
|
|
|
|
|
keystrokes."
|
|
|
|
|
(string-fold-right (lambda (chr result)
|
2017-08-27 16:00:19 -04:00
|
|
|
|
(cons (string-append
|
|
|
|
|
"sendkey "
|
|
|
|
|
(character->keystroke chr keystrokes))
|
2016-05-04 17:31:08 -04:00
|
|
|
|
result))
|
|
|
|
|
'()
|
|
|
|
|
str))
|
|
|
|
|
|
|
|
|
|
(define* (marionette-type str marionette
|
|
|
|
|
#:key (keystrokes %qwerty-us-keystrokes))
|
|
|
|
|
"Type STR on MARIONETTE's keyboard, using the KEYSTROKES alist to map characters
|
|
|
|
|
to actual keystrokes."
|
|
|
|
|
(for-each (cut marionette-control <> marionette)
|
|
|
|
|
(string->keystroke-commands str keystrokes)))
|
|
|
|
|
|
tests: Adjust to SRFI-64 as found in Guile 3.0.7.
In Guile 3.0.7, 'test-runner-current' is set to #f upon 'test-end'.
Consequently, the previous strategy, where we'd call
'test-runner-current' after 'test-end', no longer works. Instead, set
the test runner in each test right before 'test-begin'.
* gnu/build/marionette.scm (system-test-runner): New procedure.
* gnu/tests/audio.scm (run-mpd-test): Replace (exit (= ...)) idiom
by (test-runner-current (system-test-runner)).
* gnu/tests/base.scm (run-basic-test)
(run-cleanup-test, run-mcron-test, run-nss-mdns-test): Likewise.
* gnu/tests/ci.scm (run-laminar-test): Likewise.
* gnu/tests/cups.scm (run-cups-test): Likewise.
* gnu/tests/databases.scm (run-memcached-test)
(run-postgresql-test, run-mysql-test): Likewise.
* gnu/tests/desktop.scm (run-elogind-test): Likewise.
* gnu/tests/dict.scm (run-dicod-test): Likewise.
* gnu/tests/docker.scm (run-docker-test): Likewise.
(run-docker-system-test): Likewise.
* gnu/tests/file-sharing.scm (run-transmission-daemon-test): Likewise.
* gnu/tests/ganeti.scm (run-ganeti-test): Likewise.
* gnu/tests/guix.scm (run-guix-build-coordinator-test): Likewise.
(run-guix-data-service-test): Likewise.
* gnu/tests/ldap.scm (run-ldap-test): Likewise.
* gnu/tests/linux-modules.scm (run-loadable-kernel-modules-test-base): Likewise.
* gnu/tests/mail.scm (run-opensmtpd-test)
(run-exim-test, run-dovecot-test, run-getmail-test): Likewise.
* gnu/tests/messaging.scm (run-xmpp-test)
(run-bitlbee-test, run-quassel-test): Likewise.
* gnu/tests/monitoring.scm (run-prometheus-node-exporter-server-test)
(run-zabbix-server-test): Likewise.
* gnu/tests/networking.scm (run-inetd-test, run-openvswitch-test)
(run-dhcpd-test, run-tor-test, run-iptables-test, run-ipfs-test): Likewise.
* gnu/tests/nfs.scm (run-nfs-test)
(run-nfs-server-test, run-nfs-root-fs-test): Likewise.
* gnu/tests/package-management.scm (run-nix-test): Likewise.
* gnu/tests/reconfigure.scm (run-switch-to-system-test)
(run-upgrade-services-test, run-install-bootloader-test): Likewise.
* gnu/tests/rsync.scm (run-rsync-test): Likewise.
* gnu/tests/security-token.scm (run-pcscd-test): Likewise.
* gnu/tests/singularity.scm (run-singularity-test): Likewise.
* gnu/tests/ssh.scm (run-ssh-test): Likewise.
* gnu/tests/telephony.scm (run-jami-test): Likewise.
* gnu/tests/version-control.scm (run-cgit-test): Likewise.
(run-git-http-test, run-gitolite-test, run-gitile-test): Likewise.
* gnu/tests/virtualization.scm (run-libvirt-test, run-childhurd-test): Likewise.
* gnu/tests/web.scm (run-webserver-test, run-php-fpm-test)
(run-hpcguix-web-server-test, run-tailon-test, run-patchwork-test): Likewise.
2021-09-25 12:36:04 -04:00
|
|
|
|
|
|
|
|
|
;;;
|
|
|
|
|
;;; Test helper.
|
|
|
|
|
;;;
|
|
|
|
|
|
2021-09-26 17:10:29 -04:00
|
|
|
|
(define* (system-test-runner #:optional log-directory)
|
|
|
|
|
"Return a SRFI-64 test runner that calls 'exit' upon 'test-end'. When
|
|
|
|
|
LOG-DIRECTORY is specified, create log file within it."
|
tests: Adjust to SRFI-64 as found in Guile 3.0.7.
In Guile 3.0.7, 'test-runner-current' is set to #f upon 'test-end'.
Consequently, the previous strategy, where we'd call
'test-runner-current' after 'test-end', no longer works. Instead, set
the test runner in each test right before 'test-begin'.
* gnu/build/marionette.scm (system-test-runner): New procedure.
* gnu/tests/audio.scm (run-mpd-test): Replace (exit (= ...)) idiom
by (test-runner-current (system-test-runner)).
* gnu/tests/base.scm (run-basic-test)
(run-cleanup-test, run-mcron-test, run-nss-mdns-test): Likewise.
* gnu/tests/ci.scm (run-laminar-test): Likewise.
* gnu/tests/cups.scm (run-cups-test): Likewise.
* gnu/tests/databases.scm (run-memcached-test)
(run-postgresql-test, run-mysql-test): Likewise.
* gnu/tests/desktop.scm (run-elogind-test): Likewise.
* gnu/tests/dict.scm (run-dicod-test): Likewise.
* gnu/tests/docker.scm (run-docker-test): Likewise.
(run-docker-system-test): Likewise.
* gnu/tests/file-sharing.scm (run-transmission-daemon-test): Likewise.
* gnu/tests/ganeti.scm (run-ganeti-test): Likewise.
* gnu/tests/guix.scm (run-guix-build-coordinator-test): Likewise.
(run-guix-data-service-test): Likewise.
* gnu/tests/ldap.scm (run-ldap-test): Likewise.
* gnu/tests/linux-modules.scm (run-loadable-kernel-modules-test-base): Likewise.
* gnu/tests/mail.scm (run-opensmtpd-test)
(run-exim-test, run-dovecot-test, run-getmail-test): Likewise.
* gnu/tests/messaging.scm (run-xmpp-test)
(run-bitlbee-test, run-quassel-test): Likewise.
* gnu/tests/monitoring.scm (run-prometheus-node-exporter-server-test)
(run-zabbix-server-test): Likewise.
* gnu/tests/networking.scm (run-inetd-test, run-openvswitch-test)
(run-dhcpd-test, run-tor-test, run-iptables-test, run-ipfs-test): Likewise.
* gnu/tests/nfs.scm (run-nfs-test)
(run-nfs-server-test, run-nfs-root-fs-test): Likewise.
* gnu/tests/package-management.scm (run-nix-test): Likewise.
* gnu/tests/reconfigure.scm (run-switch-to-system-test)
(run-upgrade-services-test, run-install-bootloader-test): Likewise.
* gnu/tests/rsync.scm (run-rsync-test): Likewise.
* gnu/tests/security-token.scm (run-pcscd-test): Likewise.
* gnu/tests/singularity.scm (run-singularity-test): Likewise.
* gnu/tests/ssh.scm (run-ssh-test): Likewise.
* gnu/tests/telephony.scm (run-jami-test): Likewise.
* gnu/tests/version-control.scm (run-cgit-test): Likewise.
(run-git-http-test, run-gitolite-test, run-gitile-test): Likewise.
* gnu/tests/virtualization.scm (run-libvirt-test, run-childhurd-test): Likewise.
* gnu/tests/web.scm (run-webserver-test, run-php-fpm-test)
(run-hpcguix-web-server-test, run-tailon-test, run-patchwork-test): Likewise.
2021-09-25 12:36:04 -04:00
|
|
|
|
(let ((runner (test-runner-simple)))
|
2021-09-26 17:10:29 -04:00
|
|
|
|
;; Log to a file under LOG-DIRECTORY.
|
|
|
|
|
(test-runner-on-group-begin! runner
|
|
|
|
|
(let ((on-begin (test-runner-on-group-begin runner)))
|
|
|
|
|
(lambda (runner suite-name count)
|
|
|
|
|
(when log-directory
|
|
|
|
|
(catch 'system-error
|
|
|
|
|
(lambda ()
|
|
|
|
|
(mkdir log-directory))
|
|
|
|
|
(lambda args
|
|
|
|
|
(unless (= (system-error-errno args) EEXIST)
|
|
|
|
|
(apply throw args))))
|
|
|
|
|
(set! test-log-to-file
|
|
|
|
|
(string-append log-directory "/" suite-name ".log")))
|
|
|
|
|
(on-begin runner suite-name count))))
|
|
|
|
|
|
2021-09-26 17:35:58 -04:00
|
|
|
|
;; The default behavior on 'test-end' is to only write a line if the test
|
|
|
|
|
;; failed. Arrange to also write a line on success.
|
|
|
|
|
(test-runner-on-test-end! runner
|
|
|
|
|
(let ((on-end (test-runner-on-test-end runner)))
|
|
|
|
|
(lambda (runner)
|
|
|
|
|
(let* ((kind (test-result-ref runner 'result-kind))
|
|
|
|
|
(results (test-result-alist runner))
|
|
|
|
|
(test-name (assq-ref results 'test-name)))
|
|
|
|
|
(unless (memq kind '(fail xpass))
|
|
|
|
|
(format (current-output-port) "~a: ~a~%"
|
|
|
|
|
(string-upcase (symbol->string kind))
|
|
|
|
|
test-name)))
|
|
|
|
|
|
|
|
|
|
(on-end runner))))
|
|
|
|
|
|
tests: Adjust to SRFI-64 as found in Guile 3.0.7.
In Guile 3.0.7, 'test-runner-current' is set to #f upon 'test-end'.
Consequently, the previous strategy, where we'd call
'test-runner-current' after 'test-end', no longer works. Instead, set
the test runner in each test right before 'test-begin'.
* gnu/build/marionette.scm (system-test-runner): New procedure.
* gnu/tests/audio.scm (run-mpd-test): Replace (exit (= ...)) idiom
by (test-runner-current (system-test-runner)).
* gnu/tests/base.scm (run-basic-test)
(run-cleanup-test, run-mcron-test, run-nss-mdns-test): Likewise.
* gnu/tests/ci.scm (run-laminar-test): Likewise.
* gnu/tests/cups.scm (run-cups-test): Likewise.
* gnu/tests/databases.scm (run-memcached-test)
(run-postgresql-test, run-mysql-test): Likewise.
* gnu/tests/desktop.scm (run-elogind-test): Likewise.
* gnu/tests/dict.scm (run-dicod-test): Likewise.
* gnu/tests/docker.scm (run-docker-test): Likewise.
(run-docker-system-test): Likewise.
* gnu/tests/file-sharing.scm (run-transmission-daemon-test): Likewise.
* gnu/tests/ganeti.scm (run-ganeti-test): Likewise.
* gnu/tests/guix.scm (run-guix-build-coordinator-test): Likewise.
(run-guix-data-service-test): Likewise.
* gnu/tests/ldap.scm (run-ldap-test): Likewise.
* gnu/tests/linux-modules.scm (run-loadable-kernel-modules-test-base): Likewise.
* gnu/tests/mail.scm (run-opensmtpd-test)
(run-exim-test, run-dovecot-test, run-getmail-test): Likewise.
* gnu/tests/messaging.scm (run-xmpp-test)
(run-bitlbee-test, run-quassel-test): Likewise.
* gnu/tests/monitoring.scm (run-prometheus-node-exporter-server-test)
(run-zabbix-server-test): Likewise.
* gnu/tests/networking.scm (run-inetd-test, run-openvswitch-test)
(run-dhcpd-test, run-tor-test, run-iptables-test, run-ipfs-test): Likewise.
* gnu/tests/nfs.scm (run-nfs-test)
(run-nfs-server-test, run-nfs-root-fs-test): Likewise.
* gnu/tests/package-management.scm (run-nix-test): Likewise.
* gnu/tests/reconfigure.scm (run-switch-to-system-test)
(run-upgrade-services-test, run-install-bootloader-test): Likewise.
* gnu/tests/rsync.scm (run-rsync-test): Likewise.
* gnu/tests/security-token.scm (run-pcscd-test): Likewise.
* gnu/tests/singularity.scm (run-singularity-test): Likewise.
* gnu/tests/ssh.scm (run-ssh-test): Likewise.
* gnu/tests/telephony.scm (run-jami-test): Likewise.
* gnu/tests/version-control.scm (run-cgit-test): Likewise.
(run-git-http-test, run-gitolite-test, run-gitile-test): Likewise.
* gnu/tests/virtualization.scm (run-libvirt-test, run-childhurd-test): Likewise.
* gnu/tests/web.scm (run-webserver-test, run-php-fpm-test)
(run-hpcguix-web-server-test, run-tailon-test, run-patchwork-test): Likewise.
2021-09-25 12:36:04 -04:00
|
|
|
|
;; On 'test-end', display test results and exit with zero if and only if
|
|
|
|
|
;; there were no test failures.
|
|
|
|
|
(test-runner-on-final! runner
|
|
|
|
|
(lambda (runner)
|
|
|
|
|
(let ((success? (= (test-runner-fail-count runner) 0)))
|
|
|
|
|
(test-on-final-simple runner)
|
2021-09-26 17:09:23 -04:00
|
|
|
|
|
|
|
|
|
(when (not success?)
|
|
|
|
|
(let* ((log-port (test-runner-aux-value runner))
|
|
|
|
|
(log-file (port-filename log-port)))
|
|
|
|
|
(format (current-error-port)
|
|
|
|
|
"\nTests failed, dumping log file '~a'.\n\n"
|
|
|
|
|
log-file)
|
|
|
|
|
|
|
|
|
|
;; At this point LOG-PORT is not closed yet; flush it.
|
|
|
|
|
(force-output log-port)
|
|
|
|
|
|
|
|
|
|
;; Brute force to avoid dependency on (guix build utils) for
|
|
|
|
|
;; 'dump-port'.
|
|
|
|
|
(let ((content (call-with-input-file log-file
|
|
|
|
|
get-bytevector-all)))
|
|
|
|
|
(put-bytevector (current-error-port) content))))
|
|
|
|
|
|
tests: Adjust to SRFI-64 as found in Guile 3.0.7.
In Guile 3.0.7, 'test-runner-current' is set to #f upon 'test-end'.
Consequently, the previous strategy, where we'd call
'test-runner-current' after 'test-end', no longer works. Instead, set
the test runner in each test right before 'test-begin'.
* gnu/build/marionette.scm (system-test-runner): New procedure.
* gnu/tests/audio.scm (run-mpd-test): Replace (exit (= ...)) idiom
by (test-runner-current (system-test-runner)).
* gnu/tests/base.scm (run-basic-test)
(run-cleanup-test, run-mcron-test, run-nss-mdns-test): Likewise.
* gnu/tests/ci.scm (run-laminar-test): Likewise.
* gnu/tests/cups.scm (run-cups-test): Likewise.
* gnu/tests/databases.scm (run-memcached-test)
(run-postgresql-test, run-mysql-test): Likewise.
* gnu/tests/desktop.scm (run-elogind-test): Likewise.
* gnu/tests/dict.scm (run-dicod-test): Likewise.
* gnu/tests/docker.scm (run-docker-test): Likewise.
(run-docker-system-test): Likewise.
* gnu/tests/file-sharing.scm (run-transmission-daemon-test): Likewise.
* gnu/tests/ganeti.scm (run-ganeti-test): Likewise.
* gnu/tests/guix.scm (run-guix-build-coordinator-test): Likewise.
(run-guix-data-service-test): Likewise.
* gnu/tests/ldap.scm (run-ldap-test): Likewise.
* gnu/tests/linux-modules.scm (run-loadable-kernel-modules-test-base): Likewise.
* gnu/tests/mail.scm (run-opensmtpd-test)
(run-exim-test, run-dovecot-test, run-getmail-test): Likewise.
* gnu/tests/messaging.scm (run-xmpp-test)
(run-bitlbee-test, run-quassel-test): Likewise.
* gnu/tests/monitoring.scm (run-prometheus-node-exporter-server-test)
(run-zabbix-server-test): Likewise.
* gnu/tests/networking.scm (run-inetd-test, run-openvswitch-test)
(run-dhcpd-test, run-tor-test, run-iptables-test, run-ipfs-test): Likewise.
* gnu/tests/nfs.scm (run-nfs-test)
(run-nfs-server-test, run-nfs-root-fs-test): Likewise.
* gnu/tests/package-management.scm (run-nix-test): Likewise.
* gnu/tests/reconfigure.scm (run-switch-to-system-test)
(run-upgrade-services-test, run-install-bootloader-test): Likewise.
* gnu/tests/rsync.scm (run-rsync-test): Likewise.
* gnu/tests/security-token.scm (run-pcscd-test): Likewise.
* gnu/tests/singularity.scm (run-singularity-test): Likewise.
* gnu/tests/ssh.scm (run-ssh-test): Likewise.
* gnu/tests/telephony.scm (run-jami-test): Likewise.
* gnu/tests/version-control.scm (run-cgit-test): Likewise.
(run-git-http-test, run-gitolite-test, run-gitile-test): Likewise.
* gnu/tests/virtualization.scm (run-libvirt-test, run-childhurd-test): Likewise.
* gnu/tests/web.scm (run-webserver-test, run-php-fpm-test)
(run-hpcguix-web-server-test, run-tailon-test, run-patchwork-test): Likewise.
2021-09-25 12:36:04 -04:00
|
|
|
|
(exit success?))))
|
|
|
|
|
runner))
|
|
|
|
|
|
2021-12-16 07:32:11 -05:00
|
|
|
|
(define* (qemu-command #:optional (system %host-type))
|
|
|
|
|
"Return the default name of the QEMU command for SYSTEM."
|
|
|
|
|
(let ((cpu (substring system 0
|
|
|
|
|
(string-index system #\-))))
|
|
|
|
|
(string-append "qemu-system-"
|
|
|
|
|
(cond
|
|
|
|
|
((string-match "^i[3456]86$" cpu) "i386")
|
|
|
|
|
((string-match "armhf" cpu) "arm")
|
|
|
|
|
(else cpu)))))
|
|
|
|
|
|
2016-05-04 17:31:08 -04:00
|
|
|
|
;;; marionette.scm ends here
|