Commit 3e29392b authored by anonym's avatar anonym
Browse files

Merge remote-tracking branch 'origin/stable' into test/10148-capture-per-scenario-videos

Conflicts:
	features/config/defaults.yml
	run_test_suite
parents b90c9dbd beedeb04
......@@ -33,7 +33,7 @@ install_torbrowser_AppArmor_profile() {
tmpdir="$(mktemp -d)"
(
cd "$tmpdir"
apt-get source torbrowser-launcher/testing
apt-get source torbrowser-launcher/sid
install -m 0644 \
torbrowser-launcher-*/apparmor/torbrowser.Browser.firefox \
"$PROFILE"
......
......@@ -14,6 +14,14 @@ set -e
TEXTDOMAIN="tails"
export TEXTDOMAIN
disable_networking() {
service network-manager stop || :
for f in /etc/init.d/network-manager /usr/sbin/NetworkManager; do
[ -e "${f}" ] && mv "${f}" "${f}.disabled"
done
log "Networking disabled"
}
show_notification() {
# We must wait until all the facilities necessary for showing the
# notification to the Live user is available to prevent it from
......@@ -63,8 +71,8 @@ mac_spoof_panic() {
echo "blacklist ${module}" >> /etc/modprobe.d/"${module}"-blacklist.conf
unload_module_and_rev_deps "${module}" || :
if nic_exists "${nic}"; then
log "Failed to unload module ${module} of NIC ${nic}. Stopping NetworkManager."
service network-manager stop
log "Failed to unload module ${module} of NIC ${nic}"
disable_networking
notify_panic_failure "${nic}" "${nic_name}" &
else
log "Successfully unloaded module ${module} of NIC ${nic}."
......@@ -130,8 +138,8 @@ then
# If mac_spoof_panic() fails we're quite screwed, so we kill
# NetworkManager without notification to do our best to
# prevent a MAC address leak.
log "Panic mode failed for NIC ${NIC}. Killing NetworkManager."
service network-manager stop
log "Panic mode failed for NIC ${NIC}"
disable_networking
fi
exit 1
fi
......
......@@ -19,4 +19,10 @@ rm -f "${BLACKLIST}"
# if NM wins, the udev trigger's run of tails-spoof-mac will fail.
/sbin/udevadm settle
service network-manager start
# If tails-spoof-mac goes into panic mode but fails to disable the
# problematic device, networking will be disabled by having these
# removed.
if [ -e "/etc/init.d/network-manager" ] && \
[ -e "/usr/sbin/NetworkManager" ]; then
service network-manager start
fi
......@@ -22,3 +22,6 @@ pref("browser.newtabpage.directory.source", "");
pref("browser.newtabpage.directory.ping", "");
// ... and disable the explanation shown the first time
pref("browser.newtabpage.introShown", true);
// Never add 'www' or '.com' to hostnames in I2P Browser.
pref("browser.fixup.alternate.enabled", false);
CAPTURE: false
CAPTURE_ALL: false
DEBUG: false
PAUSE_ON_FAIL: false
SIKULI_RETRY_FINDFAILED: false
MAX_NEW_TOR_CIRCUIT_RETRIES: 5
......
......@@ -3,6 +3,7 @@ import sys
import jabberbot
import xmpp
import potr
import logging
from argparse import ArgumentParser
class OtrContext(potr.context.Context):
......@@ -51,9 +52,11 @@ class OtrBot(jabberbot.JabberBot):
PING_FREQUENCY = 60
def __init__(self, account, password, otr_key_path, connect_server = None):
def __init__(self, account, password, otr_key_path,
connect_server = None, log_file = None):
self.__connect_server = connect_server
self.__password = password
self.__log_file = log_file
super(OtrBot, self).__init__(account, password)
self.__otr_manager = OtrContextManager(account, otr_key_path)
self.send_raw_message_fn = super(OtrBot, self).send_message
......@@ -71,6 +74,8 @@ class OtrBot(jabberbot.JabberBot):
# completely (copy-paste mostly) in order to add support for using
# an XMPP "Connect Server".
def connect(self):
logging.basicConfig(filename = self.__log_file,
level = logging.DEBUG)
if not self.conn:
conn = xmpp.Client(self.jid.getDomain(), debug=[])
if self.__connect_server:
......@@ -185,10 +190,14 @@ if __name__ == '__main__':
"(port defaults to 5222)")
parser.add_argument("-j", "--auto-join", nargs = '+', metavar = 'ROOMS',
help = "auto-join multi-user chatrooms on start")
parser.add_argument("-l", "--log-file", metavar = 'LOGFILE',
help = "Log to file instead of stderr")
args = parser.parse_args()
otr_bot_opt_args = dict()
if args.connect_server:
otr_bot_opt_args["connect_server"] = args.connect_server
if args.log_file:
otr_bot_opt_args["log_file"] = args.log_file
otr_bot = OtrBot(args.account, args.password, args.otr_key_path,
**otr_bot_opt_args)
if args.auto_join:
......
......@@ -7,7 +7,8 @@ rescue LoadError => e
raise "This script must be run from within Tails' Git directory."
end
$config = Hash.new
$config["DEBUG"] = false
def debug_log(*args) ; end
class FakeVM
def get_remote_shell_port
......
......@@ -110,13 +110,9 @@ end
Then /^drive "([^"]+)" is detected by Tails$/ do |name|
next if @skip_steps_while_restoring_background
if @vm.is_running?
try_for(10, :msg => "Drive '#{name}' is not detected by Tails") {
@vm.disk_detected?(name)
}
else
STDERR.puts "Cannot tell if drive '#{name}' is detected by Tails: " +
"Tails is not running"
raise "Tails is not running" if @vm.is_running?
try_for(10, :msg => "Drive '#{name}' is not detected by Tails") do
@vm.disk_detected?(name)
end
end
......@@ -1068,13 +1064,13 @@ def force_new_tor_circuit(with_vidalia=nil)
assert(!@new_circuit_tries.nil? && @new_circuit_tries >= 0,
'@new_circuit_tries was not initialized before it was used')
@new_circuit_tries += 1
STDERR.puts "Forcing new Tor circuit... (attempt ##{@new_circuit_tries})" if $config["DEBUG"]
debug_log("Forcing new Tor circuit... (attempt ##{@new_circuit_tries})")
if with_vidalia
assert_equal('gnome', @theme, "Vidalia is not available in the #{@theme} theme.")
begin
step 'process "vidalia" is running'
rescue Test::Unit::AssertionFailedError
STDERR.puts "Vidalia was not running. Attempting to start Vidalia..." if $config["DEBUG"]
debug_log("Vidalia was not running. Attempting to start Vidalia...")
@vm.spawn('restart-vidalia')
step 'process "vidalia" is running within 15 seconds'
end
......
......@@ -140,18 +140,19 @@ Given /^I fill the guest's memory with a known pattern(| without verifying)$/ do
@vm.pidof("fillram").each do |pid|
@vm.execute_successfully("echo 15 > /proc/#{pid}/oom_adj")
end
STDERR.print "Memory fill progress: "
ram_usage = ""
remove_chars = 0
prev_used_ram_ratio = -1
# ... and that it finishes
try_for(instances*2*60, { :msg => "fillram didn't complete, probably the VM crashed" }) do
used_ram = used_ram_in_MiB
remove_chars = ram_usage.size
ram_usage = "%3d%% " % ((used_ram.to_f/@detected_ram_m)*100)
STDERR.print "\b"*remove_chars + ram_usage
used_ram_ratio = (used_ram_in_MiB.to_f/@detected_ram_m)*100
# Round down to closest multiple of 10 to limit the logging a bit.
used_ram_ratio = (used_ram_ratio/10).round*10
if used_ram_ratio - prev_used_ram_ratio >= 10
debug_log("Memory fill progress: %3d%%" % used_ram_ratio)
prev_used_ram_ratio = used_ram_ratio
end
! @vm.has_process?("fillram")
end
STDERR.print "\b"*remove_chars + "finished.\n"
debug_log("Memory fill progress: finished")
if verify
coverage = pattern_coverage_in_guest_ram()
# Let's aim for having the pattern cover at least 80% of the free RAM.
......
......@@ -36,12 +36,13 @@ ENV['TMPDIR'] = $config['TMPDIR']
# Dynamic constants initialized through the environment or similar,
# e.g. options we do not want to be configurable through the YAML
# configuration files.
DEBUG_LOG_PSEUDO_FIFO = "#{$config["TMPDIR"]}/debug_log_pseudo_fifo"
DISPLAY = ENV['DISPLAY']
GIT_DIR = ENV['PWD']
KEEP_SNAPSHOTS = !ENV['KEEP_SNAPSHOTS'].nil?
LIVE_USER = cmd_helper(". config/chroot_local-includes/etc/live/config.d/username.conf; echo ${LIVE_USERNAME}").chomp
OLD_TAILS_ISO = ENV['OLD_TAILS_ISO']
TAILS_ISO = ENV['TAILS_ISO']
OLD_TAILS_ISO = ENV['OLD_TAILS_ISO'] || TAILS_ISO
TIME_AT_START = Time.now
# Constants that are statically initialized.
......
......@@ -46,20 +46,18 @@ def AfterFeature(*tag_expressions, &block)
$after_feature_hooks << SimpleHook.new(tag_expressions, block)
end
AfterConfiguration do |config|
# Cucumber may read this file multiple times, and hence run this
# AfterConfiguration hook multiple times. Patching the formatter
# more than once will lead to problems, so let's ensure we only do
# it once.
next if $formatters_are_patched
# Multiple formatters can be registered, but we only patch one of
# them, since we only want our hooks to run once in total, not once
# for each formatter.
formatter_name, _ = config.formats.first
formatter = config.formatter_class(formatter_name)
formatter.class_exec do
if method_defined?(:before_feature)
alias old_before_feature before_feature
def debug_log(message)
$debug_log_fns.each { |fn| fn.call(message) } if $debug_log_fns
end
require 'cucumber/formatter/pretty'
module ExtraFormatters
# This is a null formatter in the sense that it doesn't ever output
# anything. We only use it do hook into the correct events so we can
# add our extra hooks.
class ExtraHooks
def initialize(*args)
# We do not care about any of the arguments.
end
def before_feature(feature)
......@@ -68,19 +66,9 @@ AfterConfiguration do |config|
hook.invoke(feature) if feature.accept_hook?(hook)
end
end
if self.class.method_defined?(:old_before_feature)
old_before_feature(feature)
end
end
if method_defined?(:after_feature)
alias old_after_feature after_feature
end
def after_feature(feature)
if self.class.method_defined?(:old_after_feature)
old_after_feature(feature)
end
if $after_feature_hooks
$after_feature_hooks.each do |hook|
hook.invoke(feature) if feature.accept_hook?(hook)
......@@ -88,5 +76,40 @@ AfterConfiguration do |config|
end
end
end
$formatters_are_patched = true
# The pretty formatter with debug logging mixed into its output.
class PrettyDebug < Cucumber::Formatter::Pretty
def initialize(*args)
super(*args)
$debug_log_fns ||= []
$debug_log_fns << self.method(:debug_log)
end
def debug_log(message)
@io.puts(format_string(message, :blue))
end
end
end
module Cucumber
module Cli
class Options
BUILTIN_FORMATS['pretty_debug'] =
[
'ExtraFormatters::PrettyDebug',
'Prints the feature with debugging information - in colours.'
]
BUILTIN_FORMATS['debug'] = BUILTIN_FORMATS['pretty_debug']
end
end
end
AfterConfiguration do |config|
# Cucumber may read this file multiple times, and hence run this
# AfterConfiguration hook multiple times. We only want our
# ExtraHooks formatter to be loaded once, otherwise the hooks would
# be run miltiple times.
extra_hooks = ['ExtraFormatters::ExtraHooks', '/dev/null']
config.formats << extra_hooks if not(config.formats.include?(extra_hooks))
end
......@@ -27,6 +27,7 @@ class ChatBot
]
cmd += ["--connect-server", @opts["connect_server"]] if @opts["connect_server"]
cmd += ["--auto-join"] + @opts["auto_join"] if @opts["auto_join"]
cmd += ["--log-file", DEBUG_LOG_PSEUDO_FIFO]
job = IO.popen(cmd)
@pid = job.pid
......
......@@ -19,7 +19,7 @@ class CtcpChecker < Net::IRC::Client
:user => nickname,
:real => nickname,
}
opts[:logger] = Logger.new("/dev/null") if !$config["DEBUG"]
opts[:logger] = Logger.new(DEBUG_LOG_PSEUDO_FIFO)
super(host, port, opts)
end
......
......@@ -32,14 +32,14 @@ class VMCommand
options[:spawn] ||= false
type = options[:spawn] ? "spawn" : "call"
socket = TCPSocket.new("127.0.0.1", vm.get_remote_shell_port)
STDERR.puts "#{type}ing as #{options[:user]}: #{cmd}" if $config["DEBUG"]
debug_log("#{type}ing as #{options[:user]}: #{cmd}")
begin
socket.puts(JSON.dump([type, options[:user], cmd]))
s = socket.readline(sep = "\0").chomp("\0")
ensure
socket.close
end
STDERR.puts "#{type} returned: #{s}" if $config["DEBUG"]
debug_log("#{type} returned: #{s}") if not(options[:spawn])
begin
return JSON.load(s)
rescue JSON::ParserError
......
......@@ -5,6 +5,9 @@ require 'sikuli-script.jar'
Rjb::load
package_members = [
"java.io.FileOutputStream",
"java.io.PrintStream",
"java.lang.System",
"org.sikuli.script.Finder",
"org.sikuli.script.Key",
"org.sikuli.script.KeyModifier",
......@@ -18,6 +21,8 @@ package_members = [
translations = Hash[
"org.sikuli.script", "Sikuli",
"java.lang", "Java::Lang",
"java.io", "Java::Io",
]
for p in package_members
......@@ -36,6 +41,12 @@ for p in package_members
mod.const_set(class_name, imported_class)
end
# Bind Java's stdout to debug_log() via our magical pseudo fifo
# logger.
file_output_stream = Java::Io::FileOutputStream.new(DEBUG_LOG_PSEUDO_FIFO)
print_stream = Java::Io::PrintStream.new(file_output_stream)
Java::Lang::System.setOut(print_stream)
def findfailed_hook(pic)
STDERR.puts ""
STDERR.puts "FindFailed for: #{pic}"
......@@ -188,7 +199,7 @@ sikuli_settings.OcrDataPath = $config["TMPDIR"]
# Also, Sikuli's default of 0.7 is simply too low (many false
# positives).
sikuli_settings.MinSimilarity = 0.9
sikuli_settings.ActionLogs = $config["DEBUG"]
sikuli_settings.DebugLogs = $config["DEBUG"]
sikuli_settings.InfoLogs = $config["DEBUG"]
sikuli_settings.ProfileLogs = $config["DEBUG"]
sikuli_settings.ActionLogs = true
sikuli_settings.DebugLogs = true
sikuli_settings.InfoLogs = true
sikuli_settings.ProfileLogs = true
......@@ -179,7 +179,12 @@ class VMStorage
def guestfs_disk_helper(*disks)
assert(block_given?)
g = Guestfs::Guestfs.new()
g.set_trace(1) if $config["DEBUG"]
g.set_trace(1)
message_callback = Proc.new do |event, _, message, _|
debug_log("libguestfs: #{Guestfs.event_to_string(event)}: #{message}")
end
g.set_event_callback(message_callback,
Guestfs::EVENT_TRACE)
g.set_autosync(1)
disks.each do |disk|
g.add_drive_opts(disk[:path], disk[:opts])
......
require 'fileutils'
require 'rb-inotify'
require 'time'
require 'tmpdir'
# Run once, before any feature
AfterConfiguration do |config|
# Start a thread that monitors a pseudo fifo file and debug_log():s
# anything written to it "immediately" (well, as fast as inotify
# detects it). We're forced to a convoluted solution like this
# because CRuby's thread support is horribly as soon as IO is mixed
# in (other threads get blocked).
FileUtils.rm(DEBUG_LOG_PSEUDO_FIFO) if File.exist?(DEBUG_LOG_PSEUDO_FIFO)
FileUtils.touch(DEBUG_LOG_PSEUDO_FIFO)
at_exit do
FileUtils.rm(DEBUG_LOG_PSEUDO_FIFO) if File.exist?(DEBUG_LOG_PSEUDO_FIFO)
end
Thread.new do
File.open(DEBUG_LOG_PSEUDO_FIFO) do |fd|
watcher = INotify::Notifier.new
watcher.watch(DEBUG_LOG_PSEUDO_FIFO, :modify) do
line = fd.read.chomp
debug_log(line) if line and line.length > 0
end
watcher.run
end
end
end
# For @product tests
####################
......@@ -65,6 +90,10 @@ BeforeFeature('@product') do |feature|
raise "The specified Tails ISO image '#{TAILS_ISO}' does not exist"
end
puts "Testing ISO image: #{File.basename(TAILS_ISO)}"
if !File.exist?(OLD_TAILS_ISO)
raise "The specified old Tails ISO image '#{OLD_TAILS_ISO}' does not exist"
end
puts "Using old ISO image: #{File.basename(OLD_TAILS_ISO)}"
base = File.basename(feature.file, ".feature").to_s
$background_snapshot = "#{$config["TMPDIR"]}/#{base}_background.state"
$virt = Libvirt::open("qemu:///system")
......@@ -79,20 +108,6 @@ AfterFeature('@product') do
$virt.close
end
BeforeFeature('@product', '@old_iso') do
if OLD_TAILS_ISO.nil?
raise "No old Tails ISO image specified, and none could be found in the " +
"current directory"
end
if !File.exist?(OLD_TAILS_ISO)
raise "The specified old Tails ISO image '#{OLD_TAILS_ISO}' does not exist"
end
if TAILS_ISO == OLD_TAILS_ISO
raise "The old Tails ISO is the same as the Tails ISO we're testing"
end
puts "Using old ISO image: #{File.basename(OLD_TAILS_ISO)}"
end
# BeforeScenario
Before('@product') do |scenario|
@screen = Sikuli::Screen.new
......
@product @old_iso
@product
Feature: Installing Tails to a USB drive, upgrading it, and using persistence
As a Tails user
I may want to install Tails to a USB drive
......
......@@ -33,6 +33,7 @@ ruby-json
ruby-libvirt
ruby-net-irc
ruby-packetfu
ruby-rb-inotify
ruby-rjb
ruby-rspec
ruby-test-unit
......@@ -76,17 +77,19 @@ Options for '@product' features:
--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.
--log-to-file FILE Clone stdout and stderr to the specified file.
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. When it comes to formatters
there are some interesting things to consider, in particular that any debugging
output that we write to stderr will not be cloned to any file pointed to by
cucumbers --out.
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`.
"
}
......@@ -131,9 +134,6 @@ next_free_display() {
test_suite_cleanup() {
(kill -0 ${XVFB_PID} 2>/dev/null && kill ${XVFB_PID}) || /bin/true
if [ ! -z $LOG_FILE ]; then
remove_control_chars_from "$LOG_FILE"
fi
}
start_xvfb() {
......@@ -161,22 +161,11 @@ start_vnc_viewer() {
xtightvncviewer -viewonly localhost:${VNC_SERVER_PORT} 1>/dev/null 2>&1 &
}
remove_control_chars_from() {
local file="$1"
local tmpfile
# Sanity checks
[ -n "$file" ] || return 11
[ -r "$file" ] || return 13
[ -w "$(dirname "$file")" ] || return 17
# Remove control chars with `perl` and backspaces with `col`
tmpfile=$(mktemp)
cat "$file" \
| perl -pe 's/\e([^\[\]]|\[.*?[a-zA-Z]|\].*?\a)//g' \
| col -b \
> "$tmpfile"
mv "$tmpfile" "$file"
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
......@@ -196,7 +185,7 @@ SIKULI_RETRY_FINDFAILED=
TAILS_ISO=
OLD_TAILS_ISO=
LONGOPTS="view,vnc-server-only,capture,capture-all,help,tmpdir:,keep-snapshots,retry-find,iso:,old-iso:,debug,pause-on-fail,log-to-file:"
LONGOPTS="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
......@@ -218,13 +207,6 @@ while [ $# -gt 0 ]; do
export CAPTURE="yes"
export CAPTURE_ALL="yes"
;;
--log-to-file)
shift
LOG_FILE="$1"
;;
--debug)
export DEBUG="yes"
;;
--pause-on-fail)
export PAUSE_ON_FAIL="yes"
;;
......@@ -277,10 +259,4 @@ export JAVA_HOME="/usr/lib/jvm/java-7-openjdk-amd64"
export SIKULI_HOME="/usr/share/java"
export DISPLAY=${TARGET_DISPLAY}
CUCUMBER_COMMAND="cucumber ${@}"
if [ -z "$LOG_FILE" ]; then
$CUCUMBER_COMMAND
else
script --quiet --flush --return --command "$CUCUMBER_COMMAND" "$LOG_FILE" | cat
fi
cucumber ${@}
......@@ -24,7 +24,7 @@ The following packages are necessary on Debian Jessie:
apt install git i18nspector xvfb virt-viewer libsikuli-script-java \
libxslt1-dev tcpdump unclutter radvd x11-apps \
libcap2-bin devscripts ruby-libvirt ruby-rspec gawk ntp ovmf \
ruby-json x11vnc xtightvncviewer libav-tools \
ruby-json ruby-rb-inotify x11vnc xtightvncviewer libav-tools \
libvpx1 dnsmasq-base openjdk-7-jre ruby-guestfs ruby-net-irc \
ruby-test-unit qemu-kvm qemu-system-x86 libvirt0 libvirt-dev \
libvirt-daemon-system libvirt-clients seabios ruby-rjb \
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment