#!/bin/sh set -e set -u NAME=$(basename ${0}) usage() { echo "Usage: $NAME [OPTION]... [FEATURE]... Sets up an appropriate environment and tests FEATUREs (all by default). Note that this script must be run from the Tails source directory root. Options for '@product' features: --capture FILE Captures the test session into FILE using VP8 encoding. Requires ffmpeg and libvpx1. --debug Display various debugging information while running the test suite. --pause-on-fail On failure, pause test suite until pressing Enter. This is useful for investigating the state of the VM guest to see exactly why a test failed. --keep-snapshots Don't ever delete the background snapshots. This can a big time saver when debugging new features. --retry-find Print a warning whenever Sikuli fails to find an image and allow *one* retry after pressing ENTER. This is useful for updating outdated images. --temp-dir Directory where various temporary files are written during a test, e.g. VM snapshots and memory dumps, failure screenshots, pcap files and disk images (default is /tmp/DebianToaster). --view Shows the test session in a windows. Requires x11vnc and xtightvncviewer. --vnc-server-only Starts a VNC server for the test session. Requires x11vnc. --iso IMAGE Test '@product' features using IMAGE. If none is given, the ISO with most recent creation date (according to the ISO's label) in the current directory will be used. --old-iso IMAGE For some '@product' features (e.g. usb_install) we need an older version of Tails, which this options sets to IMAGE. If none is given, the ISO with the least recent creation date will be used. Note that '@source' features has no relevant options. " } error() { echo "${NAME}: error: ${*}" >&2 usage exit 1 } check_dependency() { if ! which "${1}" >/dev/null && \ ! dpkg -s "${1}" 2>/dev/null | grep -q "^Status:.*installed"; then error "'${1}' is missing, please install it and run again. Aborting..." fi } display_in_use() { [ -e "/tmp/.X${1#:}-lock" ] || [ -e "/tmp/.X11-unix/X${1#:}" ] } next_free_display() { display_nr=0 while display_in_use ":${display_nr}"; do display_nr=$((display_nr+1)) done echo ":${display_nr}" } start_xvfb() { Xvfb $TARGET_DISPLAY -screen 0 1024x768x24+32 >/dev/null 2>&1 & XVFB_PID=$! trap "kill -0 ${XVFB_PID} 2>/dev/null && kill -9 ${XVFB_PID}; \ rm -f /tmp/.X${TARGET_DISPLAY#:}-lock" EXIT # Wait for Xvfb to run on TARGET_DISPLAY until display_in_use $TARGET_DISPLAY; do sleep 1 done echo "Virtual X framebuffer started on display ${TARGET_DISPLAY}" # Hide the mouse cursor so it won't mess up Sikuli's screen scanning unclutter -display $TARGET_DISPLAY -root -idle 0 >/dev/null 2>&1 & } start_vnc_server() { check_dependency x11vnc VNC_SERVER_PORT="$(x11vnc -listen localhost -display ${TARGET_DISPLAY} \ -bg -nopw 2>&1 | \ grep -m 1 "^PORT=[0-9]\+" | sed 's/^PORT=//')" echo "VNC server running on: localhost:${VNC_SERVER_PORT}" } start_vnc_viewer() { check_dependency xtightvncviewer xtightvncviewer -viewonly localhost:${VNC_SERVER_PORT} 1>/dev/null 2>&1 & } capture_session() { echo "Capturing guest display into ${CAPTURE_FILE}" ffmpeg -f x11grab -s 1024x768 -r 15 -i ${TARGET_DISPLAY}.0 -an \ -vcodec libvpx -y "${CAPTURE_FILE}" >/dev/null 2>&1 & } # main script CAPTURE_FILE= VNC_VIEWER= VNC_SERVER= DEBUG= PAUSE_ON_FAIL= KEEP_SNAPSHOTS= SIKULI_RETRY_FINDFAILED= TEMP_DIR= ISO= OLD_ISO= LONGOPTS="view,vnc-server-only,capture:,help,temp-dir:,keep-snapshots,retry-find,iso:,old-iso:,debug,pause-on-fail" OPTS=$(getopt -o "" --longoptions $LONGOPTS -n "${NAME}" -- "$@") eval set -- "$OPTS" while [ $# -gt 0 ]; do case $1 in --view) VNC_VIEWER=yes VNC_SERVER=yes ;; --vnc-server-only) VNC_VIEWER= VNC_SERVER=yes ;; --capture) shift CAPTURE_FILE="$1" ;; --debug) export DEBUG="yes" ;; --pause-on-fail) export PAUSE_ON_FAIL="yes" ;; --keep-snapshots) export KEEP_SNAPSHOTS="yes" ;; --retry-find) export SIKULI_RETRY_FINDFAILED="yes" ;; --temp-dir) shift export TEMP_DIR="$(readlink -f $1)" ;; --iso) shift export ISO="$(readlink -f $1)" ;; --old-iso) shift export OLD_ISO="$(readlink -f $1)" ;; --help) usage exit 0 ;; --) shift break ;; esac shift done for dep in ffmpeg git libvirt-bin libvirt-dev libavcodec-extra-53 libvpx1 \ virt-viewer libsikuli-script-java ovmf tcpdump xvfb; do check_dependency "${dep}" done TARGET_DISPLAY=$(next_free_display) start_xvfb if [ -n "${CAPTURE_FILE}" ]; then capture_session fi if [ -n "${VNC_SERVER}" ]; then start_vnc_server fi if [ -n "${VNC_VIEWER}" ]; then start_vnc_viewer fi export JAVA_HOME="/usr/lib/jvm/java-7-openjdk-amd64" export SIKULI_HOME="/usr/share/java" export SIKULI_IMAGE_PATH="/srv/jenkins/features/images/" export RUBYLIB="/srv/jenkins" export FEATURE_PATH="/srv/jenkins/features" export VM_XML_PATH="/srv/jenkins/features/domains" export DISPLAY=${TARGET_DISPLAY} CUCUMBEROPTS="--verbose --backtrace --expand" check_dependency cucumber if [ -z "${*}" ]; then cucumber $CUCUMBEROPTS --format ExtraHooks::Pretty $FEATURE_PATH else FEATURES="" for f in ${*} ; do FEATURES="$FEATURES $FEATURE_PATH/$f" done cucumber $CUCUMBEROPTS --format ExtraHooks::Pretty $FEATURE_PATH/step_definitions $FEATURE_PATH/support $FEATURES fi