Commit 03f2d9c0 authored by anonym's avatar anonym
Browse files

Merge remote-tracking branch 'origin/stable' into test/9633-fix-waitAny

parents f09d05a7 4ddbc9da
......@@ -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);
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" unless @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,14 @@ 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.
def bind_java_to_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)
end
def findfailed_hook(pic)
STDERR.puts ""
STDERR.puts "FindFailed for: #{pic}"
......@@ -193,7 +206,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|
if File.exist?($config["TMPDIR"])
if !File.directory?($config["TMPDIR"])
raise "Temporary directory '#{$config["TMPDIR"]}' exists but is not a " +
"directory"
end
if !File.owned?($config["TMPDIR"])
raise "Temporary directory '#{$config["TMPDIR"]}' must be owned by the " +
"current user"
end
FileUtils.chmod(0755, $config["TMPDIR"])
else
begin
FileUtils.mkdir_p($config["TMPDIR"])
rescue Errno::EACCES => e
raise "Cannot create temporary directory: #{e.to_s}"
end
end
# 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
# Fix Sikuli's debug_log():ing.
bind_java_to_pseudo_fifo_logger
end
# For @product tests
####################
......@@ -25,23 +69,6 @@ def add_after_scenario_hook(&block)
end
BeforeFeature('@product') do |feature|
if File.exist?($config["TMPDIR"])
if !File.directory?($config["TMPDIR"])
raise "Temporary directory '#{$config["TMPDIR"]}' exists but is not a " +
"directory"
end
if !File.owned?($config["TMPDIR"])
raise "Temporary directory '#{$config["TMPDIR"]}' must be owned by the " +
"current user"
end
FileUtils.chmod(0755, $config["TMPDIR"])
else
begin
Dir.mkdir($config["TMPDIR"])
rescue Errno::EACCES => e
raise "Cannot create temporary directory: #{e.to_s}"
end
end
delete_all_snapshots if !KEEP_SNAPSHOTS
if TAILS_ISO.nil?
raise "No Tails ISO image specified, and none could be found in the " +
......@@ -65,6 +92,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 +110,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
@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
......@@ -52,8 +53,6 @@ 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 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.
......@@ -73,17 +72,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`.
"
}
......@@ -128,9 +129,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() {
......@@ -165,24 +163,6 @@ capture_session() {
-vcodec libvpx -y "${CAPTURE_FILE}" >/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"
}
# main script
# Unset all environment variables used by this script to pass options
......@@ -192,14 +172,13 @@ CAPTURE_FILE=
LOG_FILE=
VNC_VIEWER=
VNC_SERVER=
DEBUG=
PAUSE_ON_FAIL=
KEEP_SNAPSHOTS=
SIKULI_RETRY_FINDFAILED=
TAILS_ISO=
OLD_TAILS_ISO=
LONGOPTS="view,vnc-server-only,capture:,help,tmpdir:,keep-snapshots,retry-find,iso:,old-iso:,debug,pause-on-fail,log-to-file:"
LONGOPTS="view,vnc-server-only,capture:,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
......@@ -216,13 +195,6 @@ while [ $# -gt 0 ]; do
shift
CAPTURE_FILE="$1"
;;
--log-to-file)
shift
LOG_FILE="$1"
;;