#!/bin/bash set -e set -u set -o pipefail NAME=$(basename ${0}) GENERAL_DEPENDENCIES=" cucumber devscripts dnsmasq-base gawk git i18nspector libav-tools libcap2-bin libsikuli-script-java libvirt-clients libvirt-daemon-system libvirt-dev libvirt0 openjdk-7-jre openssh-server ovmf python-jabberbot python-potr qemu-kvm qemu-system-x86 ruby-guestfs ruby-json ruby-libvirt ruby-net-irc ruby-packetfu ruby-rb-inotify ruby-rjb ruby-rspec ruby-test-unit seabios tcpdump unclutter virt-viewer xvfb " usage() { echo "Usage: $NAME [OPTION]... [--] [CUCUMBER_ARGS]... Sets up an appropriate environment and invokes cucumber. Note that this script must be run from the Tails source directory root. Options for '@product' features: --artifacts-base-uri URI Pretend that the artifact is located at URI when printing its location during a scenario failure. This is useful if you intend to serve the artifacts via the web, for instance. --capture Captures failed scenarios into videos stored in the temporary directory (see --tmpdir below) using x264 encoding. Requires x264. --capture-all Keep videos for all scenarios, including those that succeed (implies --capture). --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 any snapshots (including ones marked as temporary). This can be 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. --tmpdir 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 TMPDIR in the environment, and if unset, /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. --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, it defaults to the same IMAGE given by --iso, which will be good enough for most testing purposes. Note that '@source' features has no relevant options. CUCUMBER_ARGS can be used to specify which features to be run, but also any cucumber option, although then you must pass \`--\` first to let this wrapper script know that we're done with *its* options. For debugging purposes, a 'debug' formatter has been added so pretty debugging can be enabled with \`--format debug\`. You could even combine the default (pretty) formatter with pretty debugging printed to a file with \`--format pretty --format debug --out debug.log\`. " } error() { echo "${NAME}: error: ${*}" >&2 usage exit 1 } package_installed() { local ret set +o pipefail if dpkg -s "${1}" 2>/dev/null | grep -q "^Status:.*installed"; then ret=0 else ret=1 fi set -o pipefail return ${ret} } check_dependencies() { while [ -n "${1:-}" ]; do if ! which "${1}" >/dev/null && ! package_installed "${1}" ; then error "'${1}' is missing, please install it and run again." fi shift done } 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}" } test_suite_cleanup() { (kill -0 ${XVFB_PID} 2>/dev/null && kill ${XVFB_PID}) || /bin/true } start_xvfb() { Xvfb $TARGET_DISPLAY -screen 0 1024x768x24+32 >/dev/null 2>&1 & XVFB_PID=$! # 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_dependencies x11vnc VNC_SERVER_PORT="$(x11vnc -listen localhost -display ${TARGET_DISPLAY} \ -bg -nopw -forever 2>&1 | \ grep -m 1 "^PORT=[0-9]\+" | sed 's/^PORT=//')" echo "VNC server running on: localhost:${VNC_SERVER_PORT}" } start_vnc_viewer() { check_dependencies xtightvncviewer xtightvncviewer -viewonly localhost:${VNC_SERVER_PORT} 1>/dev/null 2>&1 & } capture_session() { check_dependencies libvpx1 echo "Capturing guest display into ${CAPTURE_FILE}" avconv -f x11grab -s 1024x768 -r 15 -i ${TARGET_DISPLAY}.0 -an \ -vcodec libvpx -y "${CAPTURE_FILE}" >/dev/null 2>&1 & } # main script # Unset all environment variables used by this script to pass options # to cucumber, except TMPDIR since we explicitly want to support # setting it that way. ARTIFACTS_BASE_URI= CAPTURE= CAPTURE_ALL= LOG_FILE= VNC_VIEWER= VNC_SERVER= PAUSE_ON_FAIL= KEEP_SNAPSHOTS= SIKULI_RETRY_FINDFAILED= ISO= OLD_ISO= LONGOPTS="artifacts-base-uri:,view,vnc-server-only,capture,capture-all,help,tmpdir:,keep-snapshots,retry-find,iso:,old-iso:,pause-on-fail" OPTS=$(getopt -o "" --longoptions $LONGOPTS -n "${NAME}" -- "$@") eval set -- "$OPTS" while [ $# -gt 0 ]; do case $1 in --artifacts-base-uri) shift export ARTIFACTS_BASE_URI="${1}" ;; --view) VNC_VIEWER=yes VNC_SERVER=yes ;; --vnc-server-only) VNC_VIEWER= VNC_SERVER=yes ;; --capture) check_dependencies x264 export CAPTURE="yes" ;; --capture-all) check_dependencies x264 export CAPTURE="yes" export CAPTURE_ALL="yes" ;; --pause-on-fail) export PAUSE_ON_FAIL="yes" ;; --keep-snapshots) export KEEP_SNAPSHOTS="yes" ;; --retry-find) export SIKULI_RETRY_FINDFAILED="yes" ;; --tmpdir) shift export TMPDIR="$(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 trap "test_suite_cleanup" EXIT HUP INT QUIT TERM check_dependencies ${GENERAL_DEPENDENCIES} TARGET_DISPLAY=$(next_free_display) start_xvfb if [ -n "${VNC_SERVER:-}" ]; then start_vnc_server fi if [ -n "${VNC_VIEWER:-}" ]; then start_vnc_viewer fi 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_dependencies cucumber set -x 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