summaryrefslogtreecommitdiffstats
path: root/cucumber/features/step_definitions
diff options
context:
space:
mode:
Diffstat (limited to 'cucumber/features/step_definitions')
-rw-r--r--cucumber/features/step_definitions/apt.rb124
-rw-r--r--cucumber/features/step_definitions/browser.rb219
-rw-r--r--cucumber/features/step_definitions/build.rb153
-rw-r--r--cucumber/features/step_definitions/checks.rb230
-rw-r--r--cucumber/features/step_definitions/common_steps.rb1388
-rw-r--r--cucumber/features/step_definitions/dhcp.rb23
-rw-r--r--cucumber/features/step_definitions/electrum.rb54
-rw-r--r--cucumber/features/step_definitions/encryption.rb133
-rw-r--r--cucumber/features/step_definitions/evince.rb25
-rw-r--r--cucumber/features/step_definitions/firewall_leaks.rb33
-rw-r--r--cucumber/features/step_definitions/git.rb32
-rw-r--r--cucumber/features/step_definitions/mac_spoofing.rb119
-rw-r--r--cucumber/features/step_definitions/pidgin.rb497
-rw-r--r--cucumber/features/step_definitions/po.rb8
-rw-r--r--cucumber/features/step_definitions/root_access_control.rb41
-rw-r--r--cucumber/features/step_definitions/snapshots.rb119
-rw-r--r--cucumber/features/step_definitions/ssh.rb156
-rw-r--r--cucumber/features/step_definitions/time_syncing.rb86
-rw-r--r--cucumber/features/step_definitions/tor.rb406
-rw-r--r--cucumber/features/step_definitions/torified_browsing.rb5
-rw-r--r--cucumber/features/step_definitions/torified_gnupg.rb263
-rw-r--r--cucumber/features/step_definitions/torified_misc.rb51
-rw-r--r--cucumber/features/step_definitions/totem.rb44
-rw-r--r--cucumber/features/step_definitions/unsafe_browser.rb204
-rw-r--r--cucumber/features/step_definitions/untrusted_partitions.rb61
-rw-r--r--cucumber/features/step_definitions/usb.rb747
26 files changed, 0 insertions, 5221 deletions
diff --git a/cucumber/features/step_definitions/apt.rb b/cucumber/features/step_definitions/apt.rb
deleted file mode 100644
index 52ef9f7f..00000000
--- a/cucumber/features/step_definitions/apt.rb
+++ /dev/null
@@ -1,124 +0,0 @@
-require 'uri'
-
-Given /^the only hosts in APT sources are "([^"]*)"$/ do |hosts_str|
- hosts = hosts_str.split(',')
- apt_sources = $vm.execute_successfully(
- "cat /etc/apt/sources.list /etc/apt/sources.list.d/*"
- ).stdout.chomp
- apt_sources.each_line do |line|
- next if ! line.start_with? "deb"
- source_host = URI(line.split[1]).host
- if !hosts.include?(source_host)
- raise "Bad APT source '#{line}'"
- end
- end
-end
-
-Given /^no proposed-updates APT suite is enabled$/ do
- apt_sources = $vm.execute_successfully(
- 'cat /etc/apt/sources.list /etc/apt/sources.list.d/*'
- ).stdout
- assert_no_match(/\s\S+-proposed-updates\s/, apt_sources)
-end
-
-When /^I configure APT to use non-onion sources$/ do
- script = <<-EOF
- use strict;
- use warnings FATAL => "all";
- s{vwakviie2ienjx6t[.]onion}{ftp.us.debian.org};
- s{sgvtcaew4bxjd7ln[.]onion}{security.debian.org};
- s{sdscoq7snqtznauu[.]onion}{deb.torproject.org};
- s{jenw7xbd6tf7vfhp[.]onion}{deb.tails.boum.org};
-EOF
- # VMCommand:s cannot handle newlines, and they're irrelevant in the
- # above perl script any way
- script.delete!("\n")
- $vm.execute_successfully(
- "perl -pi -E '#{script}' /etc/apt/sources.list /etc/apt/sources.list.d/*"
- )
-end
-
-When /^I update APT using apt$/ do
- recovery_proc = Proc.new do
- step 'I kill the process "apt"'
- $vm.execute('rm -rf /var/lib/apt/lists/*')
- end
- retry_tor(recovery_proc) do
- Timeout::timeout(15*60) do
- $vm.execute_successfully("echo #{@sudo_password} | " +
- "sudo -S apt update", :user => LIVE_USER)
- end
- end
-end
-
-Then /^I install "(.+)" using apt$/ do |package_name|
- recovery_proc = Proc.new do
- step 'I kill the process "apt"'
- $vm.execute("apt purge #{package_name}")
- end
- retry_tor(recovery_proc) do
- Timeout::timeout(2*60) do
- $vm.execute_successfully("echo #{@sudo_password} | " +
- "sudo -S apt install #{package_name}",
- :user => LIVE_USER)
- end
- end
-end
-
-When /^I start Synaptic$/ do
- step 'I start "Synaptic Package Manager" via GNOME Activities Overview'
- deal_with_polkit_prompt(@sudo_password)
- @synaptic = Dogtail::Application.new('synaptic')
- # The seemingly spurious space is needed because that is how this
- # frame is named...
- @synaptic.child(
- 'Synaptic Package Manager ', roleName: 'frame', recursive: false
- )
-end
-
-When /^I update APT using Synaptic$/ do
- recovery_proc = Proc.new do
- step 'I kill the process "synaptic"'
- step "I start Synaptic"
- end
- retry_tor(recovery_proc) do
- @synaptic.button('Reload').click
- sleep 10 # It might take some time before APT starts downloading
- try_for(15*60, :msg => "Took too much time to download the APT data") {
- !$vm.has_process?("/usr/lib/apt/methods/tor+http")
- }
- assert_raise(RuntimeError) do
- @synaptic.child(roleName: 'dialog', recursive: false)
- .child('Error', roleName: 'icon', retry: false)
- end
- if !$vm.has_process?("synaptic")
- raise "Synaptic process vanished, did it segfault again?"
- end
- end
-end
-
-Then /^I install "(.+)" using Synaptic$/ do |package_name|
- recovery_proc = Proc.new do
- step 'I kill the process "synaptic"'
- $vm.execute("apt -y purge #{package_name}")
- step "I start Synaptic"
- end
- retry_tor(recovery_proc) do
- @synaptic.button('Search').click
- find_dialog = @synaptic.dialog('Find')
- find_dialog.child(roleName: 'text').typeText(package_name)
- find_dialog.button('Search').click
- package_list = @synaptic.child('Installed Version',
- roleName: 'table column header').parent
- package_entry = package_list.child(package_name, roleName: 'table cell')
- package_entry.doubleClick
- @synaptic.button('Apply').click
- apply_prompt = nil
- try_for(60) { apply_prompt = @synaptic.dialog('Summary'); true }
- apply_prompt.button('Apply').click
- try_for(4*60) do
- @synaptic.child('Changes applied', roleName: 'frame', recursive: false)
- true
- end
- end
-end
diff --git a/cucumber/features/step_definitions/browser.rb b/cucumber/features/step_definitions/browser.rb
deleted file mode 100644
index 68d1bca4..00000000
--- a/cucumber/features/step_definitions/browser.rb
+++ /dev/null
@@ -1,219 +0,0 @@
-Then /^the Unsafe Browser has started$/ do
- @screen.wait("UnsafeBrowserHomepage.png", 360)
-end
-
-When /^I start the Unsafe Browser(?: through the GNOME menu)?$/ do
- step "I start \"Unsafe Browser\" via GNOME Activities Overview"
-end
-
-When /^I successfully start the Unsafe Browser$/ do
- step "I start the Unsafe Browser"
- step "I see and accept the Unsafe Browser start verification"
- step "I see the \"Starting the Unsafe Browser...\" notification after at most 60 seconds"
- step "the Unsafe Browser has started"
-end
-
-When /^I close the Unsafe Browser$/ do
- @screen.type("q", Sikuli::KeyModifier.CTRL)
-end
-
-def xul_application_info(application)
- binary = $vm.execute_successfully(
- 'echo ${TBB_INSTALL}/firefox', :libs => 'tor-browser'
- ).stdout.chomp
- address_bar_image = "BrowserAddressBar.png"
- unused_tbb_libs = ['libnssdbm3.so', "libmozavcodec.so", "libmozavutil.so"]
- case application
- when "Tor Browser"
- user = LIVE_USER
- cmd_regex = "#{binary} .* -profile /home/#{user}/\.tor-browser/profile\.default"
- chroot = ""
- new_tab_button_image = "TorBrowserNewTabButton.png"
- when "Unsafe Browser"
- user = "clearnet"
- cmd_regex = "#{binary} .* -profile /home/#{user}/\.unsafe-browser/profile\.default"
- chroot = "/var/lib/unsafe-browser/chroot"
- new_tab_button_image = "UnsafeBrowserNewTabButton.png"
- when "Tor Launcher"
- user = "tor-launcher"
- # We do not enable AppArmor confinement for the Tor Launcher.
- binary = "#{binary}-unconfined"
- tor_launcher_install = $vm.execute_successfully(
- 'echo ${TOR_LAUNCHER_INSTALL}', :libs => 'tor-browser'
- ).stdout.chomp
- cmd_regex = "#{binary}\s+-app #{tor_launcher_install}/application\.ini.*"
- chroot = ""
- new_tab_button_image = nil
- address_bar_image = nil
- # The standalone Tor Launcher uses fewer libs than the full
- # browser.
- unused_tbb_libs.concat(["libfreebl3.so", "libnssckbi.so", "libsoftokn3.so"])
- else
- raise "Invalid browser or XUL application: #{application}"
- end
- return {
- :user => user,
- :cmd_regex => cmd_regex,
- :chroot => chroot,
- :new_tab_button_image => new_tab_button_image,
- :address_bar_image => address_bar_image,
- :unused_tbb_libs => unused_tbb_libs,
- }
-end
-
-When /^I open a new tab in the (.*)$/ do |browser|
- info = xul_application_info(browser)
- @screen.click(info[:new_tab_button_image])
- @screen.wait(info[:address_bar_image], 10)
-end
-
-When /^I open the address "([^"]*)" in the (.*)$/ do |address, browser|
- step "I open a new tab in the #{browser}"
- info = xul_application_info(browser)
- open_address = Proc.new do
- @screen.click(info[:address_bar_image])
- # This static here since we have no reliable visual indicators
- # that we can watch to know when typing is "safe".
- sleep 5
- # The browser sometimes loses keypresses when suggestions are
- # shown, which we work around by pasting the address from the
- # clipboard, in one go.
- $vm.set_clipboard(address)
- @screen.type('v', Sikuli::KeyModifier.CTRL)
- @screen.type(Sikuli::Key.ENTER)
- end
- recovery_on_failure = Proc.new do
- @screen.type(Sikuli::Key.ESC)
- @screen.waitVanish('BrowserReloadButton.png', 3)
- open_address.call
- end
- if browser == "Tor Browser"
- retry_method = method(:retry_tor)
- else
- retry_method = Proc.new { |p, &b| retry_action(10, recovery_proc: p, &b) }
- end
- open_address.call
- retry_method.call(recovery_on_failure) do
- @screen.wait('BrowserReloadButton.png', 120)
- end
-end
-
-# This step is limited to the Tor Browser due to #7502 since dogtail
-# uses the same interface.
-Then /^"([^"]+)" has loaded in the Tor Browser$/ do |title|
- if @language == 'German'
- browser_name = 'Tor-Browser'
- reload_action = 'Aktuelle Seite neu laden'
- else
- browser_name = 'Tor Browser'
- reload_action = 'Reload current page'
- end
- expected_title = "#{title} - #{browser_name}"
- try_for(60) { @torbrowser.child(expected_title, roleName: 'frame') }
- # The 'Reload current page' button (graphically shown as a looping
- # arrow) is only shown when a page has loaded, so once we see the
- # expected title *and* this button has appeared, then we can be sure
- # that the page has fully loaded.
- try_for(60) { @torbrowser.child(reload_action, roleName: 'push button') }
-end
-
-Then /^the (.*) has no plugins installed$/ do |browser|
- step "I open the address \"about:plugins\" in the #{browser}"
- step "I see \"TorBrowserNoPlugins.png\" after at most 30 seconds"
-end
-
-def xul_app_shared_lib_check(pid, chroot, expected_absent_tbb_libs = [])
- absent_tbb_libs = []
- unwanted_native_libs = []
- tbb_libs = $vm.execute_successfully("ls -1 #{chroot}${TBB_INSTALL}/*.so",
- :libs => 'tor-browser').stdout.split
- firefox_pmap_info = $vm.execute("pmap --show-path #{pid}").stdout
- for lib in tbb_libs do
- lib_name = File.basename lib
- if not /\W#{lib}$/.match firefox_pmap_info
- absent_tbb_libs << lib_name
- end
- native_libs = $vm.execute_successfully(
- "find /usr/lib /lib -name \"#{lib_name}\""
- ).stdout.split
- for native_lib in native_libs do
- if /\W#{native_lib}$"/.match firefox_pmap_info
- unwanted_native_libs << lib_name
- end
- end
- end
- absent_tbb_libs -= expected_absent_tbb_libs
- assert(absent_tbb_libs.empty? && unwanted_native_libs.empty?,
- "The loaded shared libraries for the firefox process are not the " +
- "way we expect them.\n" +
- "Expected TBB libs that are absent: #{absent_tbb_libs}\n" +
- "Native libs that we don't want: #{unwanted_native_libs}")
-end
-
-Then /^the (.*) uses all expected TBB shared libraries$/ do |application|
- info = xul_application_info(application)
- pid = $vm.execute_successfully("pgrep --uid #{info[:user]} --full --exact '#{info[:cmd_regex]}'").stdout.chomp
- assert(/\A\d+\z/.match(pid), "It seems like #{application} is not running")
- xul_app_shared_lib_check(pid, info[:chroot], info[:unused_tbb_libs])
-end
-
-Then /^the (.*) chroot is torn down$/ do |browser|
- info = xul_application_info(browser)
- try_for(30, :msg => "The #{browser} chroot '#{info[:chroot]}' was " \
- "not removed") do
- !$vm.execute("test -d '#{info[:chroot]}'").success?
- end
-end
-
-Then /^the (.*) runs as the expected user$/ do |browser|
- info = xul_application_info(browser)
- assert_vmcommand_success($vm.execute(
- "pgrep --full --exact '#{info[:cmd_regex]}'"),
- "The #{browser} is not running")
- assert_vmcommand_success($vm.execute(
- "pgrep --uid #{info[:user]} --full --exact '#{info[:cmd_regex]}'"),
- "The #{browser} is not running as the #{info[:user]} user")
-end
-
-When /^I download some file in the Tor Browser$/ do
- @some_file = 'tails-signing.key'
- some_url = "https://tails.boum.org/#{@some_file}"
- step "I open the address \"#{some_url}\" in the Tor Browser"
-end
-
-Then /^I get the browser download dialog$/ do
- @screen.wait('BrowserDownloadDialog.png', 60)
- @screen.wait('BrowserDownloadDialogSaveAsButton.png', 10)
-end
-
-When /^I save the file to the default Tor Browser download directory$/ do
- @screen.click('BrowserDownloadDialogSaveAsButton.png')
- @screen.wait('BrowserDownloadFileToDialog.png', 10)
- @screen.type(Sikuli::Key.ENTER)
-end
-
-Then /^the file is saved to the default Tor Browser download directory$/ do
- assert_not_nil(@some_file)
- expected_path = "/home/#{LIVE_USER}/Tor Browser/#{@some_file}"
- try_for(10) { $vm.file_exist?(expected_path) }
-end
-
-When /^I open Tails homepage in the (.+)$/ do |browser|
- step "I open the address \"https://tails.boum.org\" in the #{browser}"
-end
-
-Then /^Tails homepage loads in the Tor Browser$/ do
- title = 'Tails - Privacy for anyone anywhere'
- step "\"#{title}\" has loaded in the Tor Browser"
-end
-
-Then /^Tails homepage loads in the Unsafe Browser$/ do
- @screen.wait('TailsHomepage.png', 60)
-end
-
-Then /^the Tor Browser shows the "([^"]+)" error$/ do |error|
- page = @torbrowser.child("Problem loading page", roleName: "document frame")
- headers = page.children(roleName: "heading")
- found = headers.any? { |heading| heading.text == error }
- raise "Could not find the '#{error}' error in the Tor Browser" unless found
-end
diff --git a/cucumber/features/step_definitions/build.rb b/cucumber/features/step_definitions/build.rb
deleted file mode 100644
index e02edc62..00000000
--- a/cucumber/features/step_definitions/build.rb
+++ /dev/null
@@ -1,153 +0,0 @@
-Given /^Tails ([[:alnum:]~.]+) has been released$/ do |version|
- create_git unless git_exists?
-
- old_branch = current_branch
-
- fatal_system "git checkout --quiet stable"
- old_entries = File.open('debian/changelog') { |f| f.read }
- File.open('debian/changelog', 'w') do |changelog|
- changelog.write(<<END_OF_CHANGELOG)
-tails (#{version}) stable; urgency=low
-
- * New upstream release.
-
- -- Tails developers <tails@boum.org> Tue, 31 Jan 2012 15:12:57 +0100
-
-#{old_entries}
-END_OF_CHANGELOG
- end
- fatal_system "git commit --quiet debian/changelog -m 'Release #{version}'"
- fatal_system "git tag '#{version.gsub('~', '-')}'"
-
- if old_branch != 'stable'
- fatal_system "git checkout --quiet '#{old_branch}'"
- fatal_system "git merge --quiet 'stable'"
- end
-end
-
-Given /^Tails ([[:alnum:].-]+) has been tagged$/ do |version|
- fatal_system "git tag '#{version}'"
-end
-
-Given /^Tails ([[:alnum:].]+) has not been released yet$/ do |version|
- !File.exists? ".git/refs/tags/#{version}"
-end
-
-Given /^the last version mentioned in debian\/changelog is ([[:alnum:]~.]+)$/ do |version|
- last = `dpkg-parsechangelog | awk '/^Version: / { print $2 }'`.strip
- raise StandardError.new('dpkg-parsechangelog failed.') if $? != 0
-
- if last != version
- fatal_system "debchange -v '#{version}' 'New upstream release'"
- end
-end
-
-Given /^the last versions mentioned in debian\/changelog are ([[:alnum:]~.]+) and ([[:alnum:]~.]+)$/ do |version_a, version_b|
- step "the last version mentioned in debian/changelog is #{version_a}"
- step "the last version mentioned in debian/changelog is #{version_b}"
-end
-
-Given(/^no frozen APT snapshot is encoded in config\/APT_snapshots\.d$/) do
- ['debian', 'debian-security', 'torproject'].map do |origin|
- File.open("config/APT_snapshots.d/#{origin}/serial", 'w+') do |serial|
- serial.write("latest\n")
- end
- end
-end
-
-Given(/^frozen APT snapshots are encoded in config\/APT_snapshots\.d$/) do
- ['debian', 'torproject'].map do |origin|
- File.open("config/APT_snapshots.d/#{origin}/serial", 'w+') do |serial|
- serial.write("2016060602\n")
- end
- end
- # We never freeze debian-security
- File.open("config/APT_snapshots.d/debian-security/serial", 'w+') do |serial|
- serial.write("latest\n")
- end
-end
-
-Given %r{I am working on the ([[:alnum:]./_-]+) base branch$} do |branch|
- create_git unless git_exists?
-
- if current_branch != branch
- fatal_system "git checkout --quiet '#{branch}'"
- end
-
- File.open('config/base_branch', 'w+') do |base_branch_file|
- base_branch_file.write("#{branch}\n")
- end
-end
-
-Given %r{^I checkout the ([[:alnum:]~.-]+) tag$} do |tag|
- create_git unless git_exists?
- fatal_system "git checkout --quiet #{tag}"
-end
-
-Given %r{I am working on the ([[:alnum:]./_-]+) branch based on ([[:alnum:]./_-]+)$} do |branch, base|
- create_git unless git_exists?
-
- if current_branch != branch
- fatal_system "git checkout --quiet -b '#{branch}' '#{base}'"
- end
-
- File.open('config/base_branch', 'w+') do |base_branch_file|
- base_branch_file.write("#{base}\n")
- end
-end
-
-When /^I successfully run "?([[:alnum:] -]+)"?$/ do |command|
- @output = `#{File.expand_path("../../../auto/scripts/#{command}", __FILE__)}`
- raise StandardError.new("#{command} failed. Exit code: #{$?}") if $? != 0
-end
-
-When /^I run "?([[:alnum:] -]+)"?$/ do |command|
- @output = `#{File.expand_path("../../../auto/scripts/#{command}", __FILE__)}`
- @exit_code = $?.exitstatus
-end
-
-Then /^I should see the ['"]?([[:alnum:].-]+)['"]? suite$/ do |suite|
- @output.should have_suite(suite)
-end
-
-Then /^I should see only the ['"]?([[:alnum:].-]+)['"]? suite$/ do |suite|
- assert_equal(1, @output.lines.count)
- @output.should have_suite(suite)
-end
-
-Then /^I should not see the ['"]?([[:alnum:].-]+)['"]? suite$/ do |suite|
- @output.should_not have_suite(suite)
-end
-
-Given(/^the config\/APT_overlays\.d directory is empty$/) do
- Dir.glob('config/APT_overlays.d/*').empty? \
- or raise "config/APT_overlays.d/ is not empty"
-end
-
-Given(/^config\/APT_overlays\.d contains ['"]?([[:alnum:].-]+)['"]?$/) do |suite|
- FileUtils.touch("config/APT_overlays.d/#{suite}")
-end
-
-Then(/^it should fail$/) do
- assert_not_equal(0, @exit_code)
-end
-
-Given(/^the (config\/base_branch) file does not exist$/) do |file|
- File.delete(file)
-end
-
-Given(/^the (config\/APT_overlays\.d) directory does not exist$/) do |dir|
- Dir.rmdir(dir)
-end
-
-Given(/^the config\/base_branch file is empty$/) do
- File.truncate('config/base_branch', 0)
-end
-
-Then(/^I should see the ([[:alnum:].-]+) tagged snapshot$/) do |tag|
- @output.should have_tagged_snapshot(tag)
-end
-
-Then(/^I should see a time\-based snapshot$/) do
- @output.should have_time_based_snapshot()
-end
diff --git a/cucumber/features/step_definitions/checks.rb b/cucumber/features/step_definitions/checks.rb
deleted file mode 100644
index 142141a8..00000000
--- a/cucumber/features/step_definitions/checks.rb
+++ /dev/null
@@ -1,230 +0,0 @@
-def shipped_openpgp_keys
- shipped_gpg_keys = $vm.execute_successfully('gpg --batch --with-colons --fingerprint --list-key', :user => LIVE_USER).stdout
- openpgp_fingerprints = shipped_gpg_keys.scan(/^fpr:::::::::([A-Z0-9]+):$/).flatten
- return openpgp_fingerprints
-end
-
-Then /^the OpenPGP keys shipped with Tails will be valid for the next (\d+) months$/ do |months|
- invalid = Array.new
- shipped_openpgp_keys.each do |key|
- begin
- step "the shipped OpenPGP key #{key} will be valid for the next #{months} months"
- rescue Test::Unit::AssertionFailedError
- invalid << key
- next
- end
- end
- assert(invalid.empty?, "The following key(s) will not be valid in #{months} months: #{invalid.join(', ')}")
-end
-
-Then /^the shipped (?:Debian repository key|OpenPGP key ([A-Z0-9]+)) will be valid for the next (\d+) months$/ do |fingerprint, max_months|
- if fingerprint
- cmd = 'gpg'
- user = LIVE_USER
- else
- fingerprint = TAILS_DEBIAN_REPO_KEY
- cmd = 'apt-key adv'
- user = 'root'
- end
- shipped_sig_key_info = $vm.execute_successfully("#{cmd} --batch --list-key #{fingerprint}", :user => user).stdout
- m = /\[expire[ds]: ([0-9-]*)\]/.match(shipped_sig_key_info)
- if m
- expiration_date = Date.parse(m[1])
- assert((expiration_date << max_months.to_i) > DateTime.now,
- "The shipped key #{fingerprint} will not be valid #{max_months} months from now.")
- end
-end
-
-Then /^the live user has been setup by live\-boot$/ do
- assert($vm.execute("test -e /var/lib/live/config/user-setup").success?,
- "live-boot failed its user-setup")
- actual_username = $vm.execute(". /etc/live/config/username.conf; " +
- "echo $LIVE_USERNAME").stdout.chomp
- assert_equal(LIVE_USER, actual_username)
-end
-
-Then /^the live user is a member of only its own group and "(.*?)"$/ do |groups|
- expected_groups = groups.split(" ") << LIVE_USER
- actual_groups = $vm.execute("groups #{LIVE_USER}").stdout.chomp.sub(/^#{LIVE_USER} : /, "").split(" ")
- unexpected = actual_groups - expected_groups
- missing = expected_groups - actual_groups
- assert_equal(0, unexpected.size,
- "live user in unexpected groups #{unexpected}")
- assert_equal(0, missing.size,
- "live user not in expected groups #{missing}")
-end
-
-Then /^the live user owns its home dir and it has normal permissions$/ do
- home = "/home/#{LIVE_USER}"
- assert($vm.execute("test -d #{home}").success?,
- "The live user's home doesn't exist or is not a directory")
- owner = $vm.execute("stat -c %U:%G #{home}").stdout.chomp
- perms = $vm.execute("stat -c %a #{home}").stdout.chomp
- assert_equal("#{LIVE_USER}:#{LIVE_USER}", owner)
- assert_equal("700", perms)
-end
-
-Then /^no unexpected services are listening for network connections$/ do
- for line in $vm.execute_successfully("ss -ltupn").stdout.chomp.split("\n") do
- splitted = line.split(/[[:blank:]]+/)
- proto = splitted[0]
- next unless ['tcp', 'udp'].include?(proto)
- laddr, lport = splitted[4].split(":")
- proc = /users:\(\("([^"]+)"/.match(splitted[6])[1]
- # Services listening on loopback is not a threat
- if /127(\.[[:digit:]]{1,3}){3}/.match(laddr).nil?
- if SERVICES_EXPECTED_ON_ALL_IFACES.include? [proc, laddr, lport] or
- SERVICES_EXPECTED_ON_ALL_IFACES.include? [proc, laddr, "*"]
- puts "Service '#{proc}' is listening on #{laddr}:#{lport} " +
- "but has an exception"
- else
- raise "Unexpected service '#{proc}' listening on #{laddr}:#{lport}"
- end
- end
- end
-end
-
-When /^Tails has booted a 64-bit kernel$/ do
- assert($vm.execute("uname -r | grep -qs 'amd64$'").success?,
- "Tails has not booted a 64-bit kernel.")
-end
-
-Then /^the VirtualBox guest modules are available$/ do
- assert($vm.execute("modinfo vboxguest").success?,
- "The vboxguest module is not available.")
-end
-
-Then /^the support documentation page opens in Tor Browser$/ do
- if @language == 'German'
- expected_title = 'Tails - Hilfe & Support'
- expected_heading = 'Die Dokumentation durchsuchen'
- else
- expected_title = 'Tails - Support'
- expected_heading = 'Search the documentation'
- end
- step "\"#{expected_title}\" has loaded in the Tor Browser"
- headings = @torbrowser
- .child(expected_title, roleName: 'document frame')
- .children(roleName: 'heading')
- assert(
- headings.any? { |heading| heading.text == expected_heading }
- )
-end
-
-Given /^I plug and mount a USB drive containing a sample PNG$/ do
- @png_dir = share_host_files(Dir.glob("#{MISC_FILES_DIR}/*.png"))
-end
-
-Then /^MAT can clean some sample PNG file$/ do
- for png_on_host in Dir.glob("#{MISC_FILES_DIR}/*.png") do
- png_name = File.basename(png_on_host)
- png_on_guest = "/home/#{LIVE_USER}/#{png_name}"
- step "I copy \"#{@png_dir}/#{png_name}\" to \"#{png_on_guest}\" as user \"#{LIVE_USER}\""
- raw_check_cmd = "grep --quiet --fixed-strings --text " +
- "'Created with GIMP' '#{png_on_guest}'"
- assert($vm.execute(raw_check_cmd, user: LIVE_USER).success?,
- 'The comment is not present in the PNG')
- check_before = $vm.execute_successfully("mat --check '#{png_on_guest}'",
- :user => LIVE_USER).stdout
- assert(check_before.include?("#{png_on_guest} is not clean"),
- "MAT failed to see that '#{png_on_host}' is dirty")
- $vm.execute_successfully("mat '#{png_on_guest}'", :user => LIVE_USER)
- check_after = $vm.execute_successfully("mat --check '#{png_on_guest}'",
- :user => LIVE_USER).stdout
- assert(check_after.include?("#{png_on_guest} is clean"),
- "MAT failed to clean '#{png_on_host}'")
- assert($vm.execute(raw_check_cmd, user: LIVE_USER).failure?,
- 'The comment is still present in the PNG')
- $vm.execute_successfully("rm '#{png_on_guest}'")
- end
-end
-
-
-
-Then /^AppArmor is enabled$/ do
- assert($vm.execute("aa-status").success?, "AppArmor is not enabled")
-end
-
-Then /^some AppArmor profiles are enforced$/ do
- assert($vm.execute("aa-status --enforced").stdout.chomp.to_i > 0,
- "No AppArmor profile is enforced")
-end
-
-def get_seccomp_status(process)
- assert($vm.has_process?(process), "Process #{process} not running.")
- pid = $vm.pidof(process)[0]
- status = $vm.file_content("/proc/#{pid}/status")
- return status.match(/^Seccomp:\s+([0-9])/)[1].chomp.to_i
-end
-
-def get_apparmor_status(pid)
- apparmor_status = $vm.file_content("/proc/#{pid}/attr/current").chomp
- if apparmor_status.include?(')')
- # matches something like /usr/sbin/cupsd (enforce)
- # and only returns what's in the parentheses
- return apparmor_status.match(/[^\s]+\s+\((.+)\)$/)[1].chomp
- else
- return apparmor_status
- end
-end
-
-Then /^the running process "(.+)" is confined with AppArmor in (complain|enforce) mode$/ do |process, mode|
- assert($vm.has_process?(process), "Process #{process} not running.")
- pid = $vm.pidof(process)[0]
- assert_equal(mode, get_apparmor_status(pid))
-end
-
-Then /^the running process "(.+)" is confined with Seccomp in (filter|strict) mode$/ do |process,mode|
- status = get_seccomp_status(process)
- if mode == 'strict'
- assert_equal(1, status, "#{process} not confined with Seccomp in strict mode")
- elsif mode == 'filter'
- assert_equal(2, status, "#{process} not confined with Seccomp in filter mode")
- else
- raise "Unsupported mode #{mode} passed"
- end
-end
-
-Then /^tails-debugging-info is not susceptible to symlink attacks$/ do
- secret_file = '/secret'
- secret_contents = 'T0P S3Cr1t -- 3yEs oN1y'
- $vm.file_append(secret_file, secret_contents)
- $vm.execute_successfully("chmod u=rw,go= #{secret_file}")
- $vm.execute_successfully("chown root:root #{secret_file}")
- script_path = '/usr/local/sbin/tails-debugging-info'
- script_lines = $vm.file_content(script_path).split("\n")
- script_lines.grep(/^debug_file\s+/).each do |line|
- _, user, debug_file = line.split
- # root can always mount symlink attacks
- next if user == 'root'
- # Remove quoting around the file
- debug_file.gsub!(/["']/, '')
- # Skip files that do not exist, or cannot be removed (e.g. the
- # ones in /proc).
- next if not($vm.execute("rm #{debug_file}").success?)
- # Check what would happen *if* the amnesia user managed to replace
- # the debugging file with a symlink to the secret.
- $vm.execute_successfully("ln -s #{secret_file} #{debug_file}")
- $vm.execute_successfully("chown --no-dereference #{LIVE_USER}:#{LIVE_USER} #{debug_file}")
- if $vm.execute("sudo /usr/local/sbin/tails-debugging-info | " +
- "grep '#{secret_contents}'",
- :user => LIVE_USER).success?
- raise "The secret was leaked by tails-debugging-info via '#{debug_file}'"
- end
- # Remove the secret so it cannot possibly interfere with the
- # following iterations (even though it should not).
- $vm.execute_successfully("echo > #{debug_file}")
- end
-end
-
-When /^I disable all networking in the Tails Greeter$/ do
- open_greeter_additional_settings()
- @screen.wait_and_click('TailsGreeterNetworkConnection.png', 30)
- @screen.wait_and_click('TailsGreeterDisableAllNetworking.png', 10)
- @screen.wait_and_click("TailsGreeterAdditionalSettingsAdd.png", 10)
-end
-
-Then /^the Tor Status icon tells me that Tor is( not)? usable$/ do |not_usable|
- picture = not_usable ? 'TorStatusNotUsable' : 'TorStatusUsable'
- @screen.find("#{picture}.png")
-end
diff --git a/cucumber/features/step_definitions/common_steps.rb b/cucumber/features/step_definitions/common_steps.rb
deleted file mode 100644
index e92309e4..00000000
--- a/cucumber/features/step_definitions/common_steps.rb
+++ /dev/null
@@ -1,1388 +0,0 @@
-require 'fileutils'
-
-def post_vm_start_hook
- # Sometimes the first click is lost (presumably it's used to give
- # focus to virt-viewer or similar) so we do that now rather than
- # having an important click lost. The point we click should be
- # somewhere where no clickable elements generally reside.
- @screen.click_point(@screen.w-1, @screen.h/2)
-end
-
-def context_menu_helper(top, bottom, menu_item)
- try_for(60) do
- t = @screen.wait(top, 10)
- b = @screen.wait(bottom, 10)
- # In Sikuli, lower x == closer to the left, lower y == closer to the top
- assert(t.y < b.y)
- center = Sikuli::Location.new(((t.x + t.w) + b.x)/2,
- ((t.y + t.h) + b.y)/2)
- @screen.right_click(center)
- @screen.hide_cursor
- @screen.wait_and_click(menu_item, 10)
- return
- end
-end
-
-def post_snapshot_restore_hook
- # FIXME -- we've got a brain-damaged version of this, unlike Tails, so it breaks after restores at present
- # that being the case, let's not worry until we actually miss the feature
- #$vm.wait_until_remote_shell_is_up
- post_vm_start_hook
-
- # debian-TODO: move to tor feature
- # The guest's Tor's circuits' states are likely to get out of sync
- # with the other relays, so we ensure that we have fresh circuits.
- # Time jumps and incorrect clocks also confuses Tor in many ways.
- #if $vm.has_network?
- # if $vm.execute("systemctl --quiet is-active tor@default.service").success?
- # $vm.execute("systemctl stop tor@default.service")
- # $vm.execute("systemctl --no-block restart tails-tor-has-bootstrapped.target")
- # $vm.host_to_guest_time_sync
- # $vm.execute("systemctl start tor@default.service")
- # wait_until_tor_is_working
- # end
- #else
- # $vm.host_to_guest_time_sync
- #end
-end
-
-Given /^I intend to use ([a-z]*) mode$/ do |ui_mode|
- @ui_mode = ui_mode
-end
-
-def diui_png(name)
- return "d-i_#{@ui_mode}_#{name}.png"
-end
-
-Given /^a computer$/ do
- $vm.destroy_and_undefine if $vm
- $vm = VM.new($virt, VM_XML_PATH, $vmnet, $vmstorage, DISPLAY)
-end
-
-Then /^the VM shuts down within (\d+) minutes$/ do |mins|
- timeout = 60*mins.to_i
- try_for(timeout, :msg => "VM is still running after #{timeout} seconds") do
- ! $vm.is_running?
- end
-end
-
-Given /^the computer is set to boot from the Tails DVD$/ do
- $vm.set_cdrom_boot(TAILS_ISO)
-end
-
-Given /^the computer is set to boot from (.+?) drive$/ do |type|
- $vm.set_disk_boot(JOB_NAME, type.downcase)
-end
-
-Given /^I (temporarily )?create an? (\d+) ([[:alpha:]]+) disk named "([^"]+)"$/ do |temporary, size, unit, name|
- $vm.storage.create_new_disk(name, {:size => size, :unit => unit,
- :type => "qcow2"})
- add_after_scenario_hook { $vm.storage.delete_volume(name) } if temporary
-end
-
-Given /^I plug (.+) drive "([^"]+)"$/ do |bus, name|
- $vm.plug_drive(name, bus.downcase)
- if $vm.is_running?
- step "drive \"#{name}\" is detected by Tails"
- end
-end
-
-Then /^drive "([^"]+)" is detected by Tails$/ do |name|
- 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
-
-Given /^the network is plugged$/ do
- $vm.plug_network
-end
-
-Given /^the network is unplugged$/ do
- $vm.unplug_network
-end
-
-Given /^the network connection is ready(?: within (\d+) seconds)?$/ do |timeout|
- timeout ||= 30
- try_for(timeout.to_i) { $vm.has_network? }
-end
-
-Given /^the hardware clock is set to "([^"]*)"$/ do |time|
- $vm.set_hardware_clock(DateTime.parse(time).to_time)
-end
-
-Given /^I capture all network traffic$/ do
- @sniffer = Sniffer.new("sniffer", $vmnet)
- @sniffer.capture
- add_after_scenario_hook do
- @sniffer.stop
- @sniffer.clear
- end
-end
-
-Given /^I intend to boot with options: (.*)$/ do |options|
- @boot_options = options
-end
-
-When /^I start the computer$/ do
- assert(!$vm.is_running?,
- "Trying to start a VM that is already running")
- $vm.start
- post_vm_start_hook
-end
-
-When /^I execute "([^"]*)"$/ do |cmd|
- info_log($vm.execute(cmd))
-end
-
-When /^running "([^"]*)" (.*)$/ do |cmd, outcome|
- result = $vm.execute(cmd)
- assert(result.success? == ('succeeds' == outcome),
- "Attempting to run '#{cmd}' did not give the expected outcome of: #{outcome}\n" + result.to_s )
-end
-
-Given /^I start Tails( from DVD)?( with network unplugged)?( and I login)?$/ do |dvd_boot, network_unplugged, do_login|
- step "the computer is set to boot from the Tails DVD" if dvd_boot
- if network_unplugged
- step "the network is unplugged"
- else
- step "the network is plugged"
- end
- step "I start the computer"
- step "the computer boots Tails"
- if do_login
- step "I log in to a new session"
- if network_unplugged
- step "all notifications have disappeared"
- else
- step "Tor is ready"
- step "all notifications have disappeared"
- step "available upgrades have been checked"
- end
- end
-end
-
-Given /^I start Tails from (.+?) drive "(.+?)"( with network unplugged)?( and I login( with persistence enabled)?)?$/ do |drive_type, drive_name, network_unplugged, do_login, persistence_on|
- step "the computer is set to boot from #{drive_type} drive \"#{drive_name}\""
- if network_unplugged
- step "the network is unplugged"
- else
- step "the network is plugged"
- end
- step "I start the computer"
- step "the computer boots Tails"
- if do_login
- step "I enable persistence" if persistence_on
- step "I log in to a new session"
- if network_unplugged
- step "all notifications have disappeared"
- else
- step "Tor is ready"
- step "all notifications have disappeared"
- step "available upgrades have been checked"
- end
- end
-end
-
-When /^I power off the computer$/ do
- assert($vm.is_running?,
- "Trying to power off an already powered off VM")
- $vm.power_off
-end
-
-When /^I cold reboot the computer$/ do
- step "I power off the computer"
- step "I start the computer"
-end
-
-When /^I destroy the computer$/ do
- $vm.destroy_and_undefine
-end
-
-Given /^I select the install mode$/ do
- boot_timeout = 60
-
- debug_log("debug: CWD = " + Dir.getwd, :color => :blue)
-
- on_screen, _ = @screen.waitAny(["d-i_boot_graphical-default.png","d-i_boot_graphical-default_9alpha.png","d-i_boot_text-default.png","d-i_boot_miniiso.png"], boot_timeout * PATIENCE)
- debug_log("debug: found '#{on_screen}' in the bootspash", :color => :blue)
- if "d-i_boot_miniiso.png" != on_screen
- if ("d-i_boot_text-default.png" == on_screen) == ("gui" == @ui_mode)
- @screen.type(Sikuli::Key.DOWN)
- end
- end
-
- @screen.type(Sikuli::Key.TAB)
- @boot_options = "" if @boot_options.nil?
- initcmd = 'printf \\\\\\\\07; while read x; do printf \\\\\\\\02; eval $x 2>/tmp/.remcmd_stderr; printf \\\\\\\\03%%s\\\\\\\\037 $?; cat /tmp/.remcmd_stderr; printf \\\\\\\\00; done'.gsub(/([$\\;>])/, '\\\\\0')
- inittab_line = ('ttyS0::respawn:-/bin/sh -c \\\\047' + initcmd + '\\\\047').gsub(/ /, '\\ ')
- @screen.type( ' preseed/early_command="echo DPMS=-s\\\\ 0 > /lib/debian-installer.d/S61Xnoblank;' +
- ' sed -i /XF86_Switch_VT_/s/F/XF86_Switch_VT_/ /usr/share/X11/xkb/symbols/srvr_ctrl;' +
- ' set -x;' +
- ' { printf ' + inittab_line + ';echo;}>>/etc/inittab;' +
- ' kill -HUP 1" blacklist=psmouse ' +
- @boot_options +
- Sikuli::Key.ENTER)
- debug_log("debug: wait for the remote shell to respond...", :color => :blue)
- #$vm.wait_until_remote_shell_is_up
- sleep(60)
-end
-
-Given /^I expect package installation to start$/ do
- @screen.wait(diui_png("InstallSoftware"), 20 * 60)
-end
-
-Given /^I select British English$/ do
- @screen.wait(diui_png("English"), 30 * PATIENCE)
- @screen.type(Sikuli::Key.ENTER)
- @screen.wait(diui_png("SelectYourLocation"), 10 * PATIENCE)
- @screen.type(Sikuli::Key.UP)
- @screen.wait(diui_png("UnitedKingdom"), 10 * PATIENCE)
- @screen.type(Sikuli::Key.ENTER)
- @screen.click_point(@screen.w-2, @screen.h*2/3)
- @screen.wait(diui_png("BritishEnglish"), 10 * PATIENCE)
- @screen.type(Sikuli::Key.ENTER)
-end
-
-Given /^I accept the hostname, using "([^"]*)" as the domain$/ do |domain|
- @screen.wait(diui_png("EnterTheHostname"), 5*60 * PATIENCE)
- @screen.type(Sikuli::Key.ENTER)
- @screen.wait(diui_png("DomainName"), 10 * PATIENCE)
- @screen.type(domain + Sikuli::Key.ENTER)
- @screen.waitVanish(diui_png("DomainName"), 10 * PATIENCE)
-end
-
-Given /^I set the root password to "([^"]*)"$/ do |rootpw|
-# Root Password, twice
- on_screen, _ = @screen.waitAny([diui_png("ShowRootPassword"),diui_png("RootPassword"),diui_png("MirrorCountry")], 60 * PATIENCE)
- on_screen, _ = @screen.waitAny([diui_png("ShowRootPassword"),diui_png("RootPassword"),diui_png("MirrorCountry")], 60 * PATIENCE)
- if diui_png("MirrorCountry") == on_screen
- step("I accept the default mirror")
- on_screen, _ = @screen.waitAny([diui_png("ShowRootPassword"),diui_png("RootPassword")], 60 * PATIENCE)
- on_screen, _ = @screen.waitAny([diui_png("ShowRootPassword"),diui_png("RootPassword")], 60 * PATIENCE)
- end
- @screen.type(rootpw)
- if "gui" == @ui_mode
- @screen.type(Sikuli::Key.TAB)
- @screen.type(Sikuli::Key.TAB) if on_screen == diui_png("ShowRootPassword")
- else
- @screen.type(Sikuli::Key.ENTER)
- @screen.waitVanish(diui_png("RootPassword"), 10 * PATIENCE)
- end
- @screen.type(rootpw + Sikuli::Key.ENTER)
-end
-
-Given /^I set the password for "([^"]*)" to be "([^"]*)"$/ do |fullname,password|
-# Username, and password twice
- @screen.wait(diui_png("NameOfUser"), 10 * PATIENCE)
- @screen.type(fullname + Sikuli::Key.ENTER)
- @screen.waitVanish(diui_png("NameOfUser"), 10 * PATIENCE)
- @screen.type(Sikuli::Key.ENTER)
- on_screen, _ = @screen.waitAny([diui_png("ShowUserPassword"),diui_png("UserPassword")], 10 * PATIENCE)
- on_screen, _ = @screen.waitAny([diui_png("ShowUserPassword"),diui_png("UserPassword")], 10 * PATIENCE)
- @screen.type(password)
- if "gui" == @ui_mode
- @screen.type(Sikuli::Key.TAB)
- @screen.type(Sikuli::Key.TAB) if on_screen == diui_png("ShowUserPassword")
- else
- @screen.type(Sikuli::Key.ENTER)
- end
- @screen.type(password + Sikuli::Key.ENTER)
-end
-
- #@screen.wait(diui_png("NoDiskFound"), 60)
-
-Given /^I select full-disk, single-filesystem partitioning$/ do
- @screen.wait(diui_png("PartitioningMethod"), 60 * PATIENCE)
- @screen.type(Sikuli::Key.ENTER)
- @screen.wait(diui_png("SelectDiskToPartition"), 10 * PATIENCE)
- @screen.type(Sikuli::Key.ENTER)
- @screen.wait(diui_png("PartitioningScheme"), 10 * PATIENCE)
- @screen.type(Sikuli::Key.ENTER)
- @screen.wait(diui_png("FinishPartitioning"), 10 * PATIENCE)
- @screen.type(Sikuli::Key.ENTER)
- # prompt about Writing Partitions to disk:
- @screen.wait(diui_png("No"), 10 * PATIENCE)
- if "gui" == @ui_mode
- @screen.type(Sikuli::Key.DOWN)
- else
- @screen.type(Sikuli::Key.TAB)
- end
- @screen.wait(diui_png("Yes"), 10 * PATIENCE)
- @screen.type(Sikuli::Key.ENTER)
-end
-
-Given /^I note that the Base system is being installed$/ do
- on_screen, _ = @screen.waitAny([diui_png("CheckSyslog"),diui_png("InstallingBaseSystem")], 30 * PATIENCE)
- on_screen, _ = @screen.waitAny([diui_png("CheckSyslog"),diui_png("InstallingBaseSystem")], 30 * PATIENCE)
- if diui_png("CheckSyslog") == on_screen
- if "gui" == @ui_mode
- @screen.type(Sikuli::Key.F4) # for this to work, we need to remap the keyboard -- CtrlAltF4 is apparently untypable :-(
- else
- @screen.type(Sikuli::Key.F4, Sikuli::KeyModifier.ALT)
- end
- sleep(10)
- raise "Failed to install the Base system"
- end
- debug_log("debug: Found InstallingBaseSystem. Wait for it to vanish", :color => :blue)
- @screen.waitVanish(diui_png("InstallingBaseSystem"), 15 * 60 * PATIENCE)
- debug_log("debug: InstallingBaseSystem vanished", :color => :blue)
-end
-
-Given /^I accept the default mirror$/ do
- on_screen, _ = @screen.waitAny([diui_png("popcon"),diui_png("BadMirror"),diui_png("MirrorCountry"),diui_png("ScanCD")], 10 * 60 * PATIENCE)
- if diui_png("MirrorCountry") == on_screen
- @screen.wait(diui_png("MirrorCountry"), 10 * 60 * PATIENCE)
- @screen.type(Sikuli::Key.ENTER)
- @screen.wait(diui_png("ArchiveMirror"), 5 * PATIENCE)
- @screen.type(Sikuli::Key.ENTER)
- @screen.wait(diui_png("HttpProxy"), 5 * PATIENCE)
- @screen.type("http://local-http-proxy:3128/" + Sikuli::Key.ENTER)
- #@screen.type(Sikuli::Key.ENTER)
- elsif diui_png("ScanCD") == on_screen
- @screen.type(Sikuli::Key.ENTER)
- step("I accept the default mirror")
- else
- step("I ignore Popcon")
- end
-end
-
-Given /^I neglect to scan more CDs$/ do
- @screen.wait(diui_png("ScanCD"), 15 * 60 * PATIENCE)
- @screen.type(Sikuli::Key.ENTER)
- @screen.wait(diui_png("UseNetMirror"), 10 * PATIENCE)
- @screen.wait(diui_png("Yes"), 10 * PATIENCE)
- if "gui" == @ui_mode
- @screen.type(Sikuli::Key.DOWN)
- else
- @screen.type(Sikuli::Key.TAB)
- end
- @screen.wait(diui_png("No"), 10 * PATIENCE)
- @screen.type(Sikuli::Key.ENTER)
-end
-
-Given /^I ignore Popcon$/ do
- on_screen, _ = @screen.waitAny([diui_png("popcon"),diui_png("BadMirror"),diui_png("ChooseSoftware")], 10 * 60 * PATIENCE)
- if diui_png("ChooseSoftware") != on_screen
- if on_screen == diui_png("BadMirror")
- if "gui" == @ui_mode
- @screen.type(Sikuli::Key.F4) # for this to work, we need to remap the keyboard -- CtrlAltF4 is apparently untypable :-(
- else
- @screen.type(Sikuli::Key.F4, Sikuli::KeyModifier.ALT)
- end
- sleep(10)
- raise "Failed to access the mirror (perhaps a duff proxy?)"
- end
- @screen.type(Sikuli::Key.ENTER)
- @screen.waitVanish(diui_png("popcon"), 10 * PATIENCE)
- end
-end
-
-Given /^we reach the Tasksel prompt$/ do
- @screen.wait(diui_png("ChooseSoftware"), 5 * 60 * PATIENCE)
-end
-
-Given /^I select the non-GUI task$/ do
- @screen.wait(diui_png("DesktopTask_Yes"), 2 * 60 * PATIENCE)
-
- @screen.type(Sikuli::Key.SPACE)
- @screen.waitVanish(diui_png("DesktopTask_Yes"), 10 * PATIENCE)
-
- if "gui" == @ui_mode
- @screen.wait(diui_png("CONTINUEunselected"), 10 * PATIENCE)
- @screen.type(Sikuli::Key.TAB)
- @screen.wait(diui_png("CONTINUEselected"), 10 * PATIENCE)
- end
- @screen.type(Sikuli::Key.ENTER)
-end
-
-Given /^I select Combi Debian-Edu profile$/ do
- on_screen, _ = @screen.waitAny([diui_png("Edu-Profile"),diui_png("NoKernelModules")], 2 * 60 * PATIENCE)
- # just acept the default combination
- if on_screen == diui_png("NoKernelModules")
- if "gui" == @ui_mode
- @screen.type(Sikuli::Key.F4) # for this to work, we need to remap the keyboard -- CtrlAltF4 is apparently untypable :-(
- else
- @screen.type(Sikuli::Key.F4, Sikuli::KeyModifier.ALT)
- end
- sleep(10)
- raise "No Kernel Modules Found (probably version skew between the .iso and the archive)"
- end
-
- if "gui" == @ui_mode
- @screen.wait(diui_png("CONTINUEunselected"), 10 * PATIENCE)
- @screen.type(Sikuli::Key.TAB)
- @screen.wait(diui_png("CONTINUEselected"), 10 * PATIENCE)
- end
- @screen.type(Sikuli::Key.ENTER)
-end
-
-Given /^I select Main Server Debian-Edu profile$/ do
- @screen.wait(diui_png("Edu-Profile"), 2 * 60 * PATIENCE)
- @screen.type(Sikuli::Key.DOWN)
- @screen.type(Sikuli::Key.SPACE)
- @screen.type(Sikuli::Key.DOWN)
- @screen.type(Sikuli::Key.DOWN)
- @screen.type(Sikuli::Key.SPACE)
-
- if "gui" == @ui_mode
- @screen.wait(diui_png("CONTINUEunselected"), 10 * PATIENCE)
- @screen.type(Sikuli::Key.TAB)
- @screen.wait(diui_png("CONTINUEselected"), 10 * PATIENCE)
- end
- @screen.type(Sikuli::Key.ENTER)
-end
-
-Given /^I select Standalone Debian-Edu profile$/ do
- @screen.wait(diui_png("Edu-Profile"), 2 * 60 * PATIENCE)
- @screen.type(Sikuli::Key.SPACE)
- @screen.type(Sikuli::Key.DOWN)
- @screen.type(Sikuli::Key.SPACE)
- @screen.type(Sikuli::Key.DOWN)
- @screen.type(Sikuli::Key.DOWN)
- @screen.type(Sikuli::Key.SPACE)
- @screen.type(Sikuli::Key.DOWN)
- @screen.type(Sikuli::Key.SPACE)
-
- if "gui" == @ui_mode
- @screen.wait(diui_png("CONTINUEunselected"), 10 * PATIENCE)
- @screen.type(Sikuli::Key.TAB)
- @screen.wait(diui_png("CONTINUEselected"), 10 * PATIENCE)
- end
- @screen.type(Sikuli::Key.ENTER)
-end
-
-Given /^I use the Debian-Edu Automatic Partitioning$/ do
- @screen.wait(diui_png("Edu-AutoPartition"), 10 * PATIENCE)
- # prompt about Writing Partitions to disk:
- @screen.wait(diui_png("No"), 10 * PATIENCE)
- if "gui" == @ui_mode
- @screen.type(Sikuli::Key.DOWN)
- else
- @screen.type(Sikuli::Key.TAB)
- end
- @screen.wait(diui_png("Yes"), 10 * PATIENCE)
- @screen.type(Sikuli::Key.ENTER)
-end
-
-Given /^I select the minimal task$/ do
- @screen.wait(diui_png("DesktopTask_Yes"), 2 * 60 * PATIENCE)
-
- @screen.type(Sikuli::Key.SPACE)
- 8.times do
- @screen.type(Sikuli::Key.DOWN)
- end
- @screen.type(Sikuli::Key.SPACE)
- @screen.waitVanish(diui_png("DesktopTask_Yes"), 10 * PATIENCE)
-
- if "gui" == @ui_mode
- @screen.wait(diui_png("CONTINUEunselected"), 10 * PATIENCE)
- @screen.type(Sikuli::Key.TAB)
- @screen.wait(diui_png("CONTINUEselected"), 10 * PATIENCE)
- end
- @screen.type(Sikuli::Key.ENTER)
-end
-
-Given /^I select the ([A-Z][[:alpha:]]*) task$/ do |desktop|
- @screen.wait(diui_png("DesktopTask_Yes"), 2 * 60 * PATIENCE)
- debug_log("debug: Found DesktopTask_Yes", :color => :blue)
-
- menu = [ '_', 'Gnome', 'XFCE', 'KDE', 'Cinamon', 'MATE', 'LXDE' ]
-
-
- menu.index(desktop).times do
- @screen.type(Sikuli::Key.DOWN)
- end
- @screen.type(Sikuli::Key.SPACE)
- expected_result = "Desktop+" + desktop
- @screen.wait(diui_png(expected_result), 10 * PATIENCE)
-
- if "gui" == @ui_mode
- @screen.wait(diui_png("CONTINUEunselected"), 10 * PATIENCE)
- @screen.type(Sikuli::Key.TAB)
- @screen.wait(diui_png("CONTINUEselected"), 10 * PATIENCE)
- end
- @screen.type(Sikuli::Key.ENTER)
- @screen.waitVanish(diui_png(expected_result), 10 * PATIENCE)
-end
-
-Given /^I wait while the partitions are made$/ do
- @screen.wait(diui_png("PartitionDisks"), 60)
- debug_log("debug: we see PartitionDisks", :color => :blue)
- @screen.waitVanish(diui_png("PartitionDisks"), 2 * 60)
-end
-
-Given /^I wait patiently for the package installation to start$/ do
- @screen.wait(diui_png("InstallSoftware"), 10 * 60)
-end
-
-Given /^I wait while the bulk of the packages are installed$/ do
- @screen.wait(diui_png("InstallSoftware"), 10)
- debug_log("debug: we see InstallSoftware", :color => :blue)
- failed = false
- try_for(180*60, :msg => "it seems that the install stalled (timing-out after 3 hours)") do
- found = false
- sleep(30)
- debug_log("debug: check for Install GRUB/Software", :color => :blue)
- if $vm.is_running?
- hit, _ = @screen.waitAny([diui_png("InstallGRUB"),diui_png("InstallGRUB-heading"),diui_png("InstallComplete"),diui_png("InstallationStepFailed"),diui_png("InstallSoftware"),diui_png("Edu-LTSPchroot")], 10)
- else
- found = true
- hit = ''
- end
- debug_log("debug: found #{hit}", :color => :blue)
- case hit
- when diui_png("InstallSoftware"), diui_png("Edu-LTSPchroot"), diui_png("InstallationStepFailed")
- debug_log("debug: so let's glance at tty4", :color => :blue)
- if "gui" == @ui_mode
- @screen.type(Sikuli::Key.F4) # for this to work, we need to remap the keyboard -- CtrlAltF4 is apparently untypable :-(
- else
- @screen.type(Sikuli::Key.F4, Sikuli::KeyModifier.ALT)
- end
- debug_log("debug: typed F4, pausing...", :color => :blue)
- sleep(5)
- debug_log("debug: slept 5", :color => :blue)
- if diui_png("InstallationStepFailed") == hit
- failed = true
- sleep(30*60) # pause for 30-mins, for debugging
- else
- if "gui" == @ui_mode
- @screen.type(Sikuli::Key.F5, Sikuli::KeyModifier.ALT)
- else
- @screen.type(Sikuli::Key.F1, Sikuli::KeyModifier.ALT)
- end
- debug_log("debug: pressed F1", :color => :blue)
- sleep(20)
- end
- when diui_png("InstallGRUB"), diui_png("InstallGRUB-heading"), diui_png("InstallComplete")
- found = true
- end
-
- found || failed
- end
- raise "an Instalation Step Failed -- see the screenshot (may need to be in text-mode)" if failed
-end
-
-Given /^I install GRUB$/ do
- @screen.wait(diui_png("InstallGRUB"), 2 * 60 * PATIENCE)
- debug_log("debug: Found InstallGRUB", :color => :blue)
-
- if "gui" == @ui_mode
- debug_log("debug: We're in GUI mode", :color => :blue)
- @screen.wait(diui_png("CONTINUEunselected"), 10 * PATIENCE)
- debug_log("debug: Found CONTINUEunselected", :color => :blue)
- debug_log("debug: Press TAB", :color => :blue)
- @screen.type(Sikuli::Key.TAB)
- @screen.wait(diui_png("CONTINUEselected"), 10 * PATIENCE)
- debug_log("debug: Found CONTINUEselected", :color => :blue)
- end
- debug_log("debug: Press ENTER", :color => :blue)
- @screen.type(Sikuli::Key.ENTER)
- @screen.wait(diui_png("GRUBEnterDev"), 10 * 60 * PATIENCE)
- @screen.type(Sikuli::Key.DOWN)
- @screen.wait(diui_png("GRUBdev"), 10 * PATIENCE)
- @screen.type(Sikuli::Key.ENTER)
-end
-
-Given /^I allow reboot after the install is complete$/ do
- step('I see the "InstallComplete" screen, after at most 240 seconds')
- @screen.type(Sikuli::Key.ENTER)
-end
-
-Given /^I wait for the reboot$/ do
- @screen.waitAny(["d-i_boot_graphical-default.png","d-i_boot_graphical-default_9alpha.png","d-i_boot_text-default.png","d-i_boot_miniiso.png"], 10 * 60 * PATIENCE)
-end
-
-Given /^I should see a ([a-zA-Z]*) Login prompt$/ do |style|
- def loginPrompt
- {
- 'XFCE' => [ 'DebianLoginPromptXFCE.png', 'DebianLoginPromptXFCE_d9.png' ],
- 'KDE' => [ 'DebianLoginPromptKDE.png', 'DebianLoginPromptKDE_d9.png' ],
- 'LXDE' => [ 'DebianLoginPromptLXDE.png', 'DebianLoginPromptLXDE_d9.png' ],
- 'Gnome' => [ 'DebianLoginPromptGnome.png' ],
- 'VT' => [ 'DebianLoginPromptVT.png', 'DebianLoginPromptVT_d9.png', 'DebianLoginPromptVT_Edu.png' ],
- }
- end
-
- @screen.waitAny(loginPrompt[style], 20 * 60)
-end
-
-def boot_menu_cmdline_image
- case @os_loader
- when "UEFI"
- 'TailsBootMenuKernelCmdlineUEFI.png'
- else
- 'd-i8_bootsplash.png'
- end
-end
-
-def boot_menu_tab_msg_image
- case @os_loader
- when "UEFI"
- 'TailsBootSplashTabMsgUEFI.png'
- else
- #if reboot
- # bootsplash = 'TailsBootSplashPostReset.png'
- # bootsplash_tab_msg = 'TailsBootSplashTabMsgPostReset.png'
- # boot_timeout = 120
- #else
- #bootsplash = "DebianLive#{version}BootSplash.png"
- bootsplash = "DebianLiveBootSplash.png"
- bootsplash_tab_msg = "DebianLiveBootSplashTabMsg.png"
- boot_timeout = 30
- #end
- end
-end
-
-Given /^the computer (re)?boots Tails$/ do |reboot|
- step "Tails is at the boot menu's cmdline" + (reboot ? ' after rebooting' : '')
- @screen.type(" autotest_never_use_this_option blacklist=psmouse #{@boot_options}" +
- Sikuli::Key.ENTER)
- @screen.wait('TailsGreeter.png', 5*60)
- $vm.wait_until_remote_shell_is_up
- step 'I configure Tails to use a simulated Tor network'
-end
-
-Given /^I log in to a new session(?: in )?(|German)$/ do |lang|
- case lang
- when 'German'
- @language = "German"
- @screen.wait_and_click('TailsGreeterLanguage.png', 10)
- @screen.wait('TailsGreeterLanguagePopover.png', 10)
- @screen.type(@language)
- sleep(2) # Gtk needs some time to filter the results
- @screen.type(Sikuli::Key.ENTER)
- @screen.wait_and_click("TailsGreeterLoginButton#{@language}.png", 10)
- when ''
- @screen.wait_and_click('TailsGreeterLoginButton.png', 10)
- else
- raise "Unsupported language: #{lang}"
- end
- step 'Tails Greeter has applied all settings'
- step 'the Tails desktop is ready'
-end
-
-def open_greeter_additional_settings
- @screen.click('TailsGreeterAddMoreOptions.png')
- @screen.wait('TailsGreeterAdditionalSettingsDialog.png', 10)
-end
-
-Given /^I open Tails Greeter additional settings dialog$/ do
- open_greeter_additional_settings()
-end
-
-Given /^I enable the specific Tor configuration option$/ do
- open_greeter_additional_settings()
- @screen.wait_and_click('TailsGreeterNetworkConnection.png', 30)
- @screen.wait_and_click("TailsGreeterSpecificTorConfiguration.png", 10)
- @screen.wait_and_click("TailsGreeterAdditionalSettingsAdd.png", 10)
-end
-
-Given /^I set an administration password$/ do
- open_greeter_additional_settings()
- @screen.wait_and_click("TailsGreeterAdminPassword.png", 20)
- @screen.type(@sudo_password)
- @screen.type(Sikuli::Key.TAB)
- @screen.type(@sudo_password)
- @screen.type(Sikuli::Key.ENTER)
-end
-
-Given /^Tails Greeter has applied all settings$/ do
- # I.e. it is done with PostLogin, which is ensured to happen before
- # a logind session is opened for LIVE_USER.
- try_for(120) {
- $vm.execute_successfully("loginctl").stdout
- .match(/^\s*\S+\s+\d+\s+#{LIVE_USER}\s+seat\d+\s+\S+\s*$/) != nil
- }
-end
-
-Given /^the Tails desktop is ready$/ do
- desktop_started_picture = "GnomeApplicationsMenu#{@language}.png"
- @screen.wait(desktop_started_picture, 180)
- # We wait for the Florence icon to be displayed to ensure reliable systray icon clicking.
- @screen.wait("GnomeSystrayFlorence.png", 30)
- @screen.wait("DesktopTailsDocumentation.png", 30)
- # Disable screen blanking since we sometimes need to wait long
- # enough for it to activate, which can mess with Sikuli wait():ing
- # for some image.
- $vm.execute_successfully(
- 'gsettings set org.gnome.desktop.session idle-delay 0',
- :user => LIVE_USER
- )
- # We need to enable the accessibility toolkit for dogtail.
- $vm.execute_successfully(
- 'gsettings set org.gnome.desktop.interface toolkit-accessibility true',
- :user => LIVE_USER,
- )
-end
-
-When /^I see the "(.+)" notification(?: after at most (\d+) seconds)?$/ do |title, timeout|
- timeout = timeout ? timeout.to_i : nil
- gnome_shell = Dogtail::Application.new('gnome-shell')
- notification_list = gnome_shell.child(
- 'No Notifications', roleName: 'label', showingOnly: false
- ).parent.parent
- try_for(timeout) do
- notification_list.child?(title, roleName: 'label', showingOnly: false)
- end
-end
-
-Given /^Tor is ready$/ do
- step "Tor has built a circuit"
- step "the time has synced"
- if $vm.execute('systemctl is-system-running').failure?
- units_status = $vm.execute('systemctl').stdout
- raise "At least one system service failed to start:\n#{units_status}"
- end
-end
-
-Given /^Tor has built a circuit$/ do
- wait_until_tor_is_working
-end
-
-Given /^the time has synced$/ do
- ["/run/tordate/done", "/run/htpdate/success"].each do |file|
- try_for(300) { $vm.execute("test -e #{file}").success? }
- end
-end
-
-Given /^available upgrades have been checked$/ do
- try_for(300) {
- $vm.execute("test -e '/run/tails-upgrader/checked_upgrades'").success?
- }
-end
-
-When /^I start the Tor Browser( in offline mode)?$/ do |offline|
- step 'I start "Tor Browser" via GNOME Activities Overview'
- if offline
- offline_prompt = Dogtail::Application.new('zenity')
- .dialog('Tor is not ready')
- offline_prompt.button('Start Tor Browser').click
- end
- step "the Tor Browser has started#{offline}"
- if offline
- step 'the Tor Browser shows the "The proxy server is refusing connections" error'
- end
-end
-
-Given /^the Tor Browser has started( in offline mode)?$/ do |offline|
- try_for(60) do
- @torbrowser = Dogtail::Application.new('Firefox')
- @torbrowser.child?(roleName: 'frame', recursive: false)
- end
-end
-
-Given /^the Tor Browser loads the (startup page|Tails roadmap)$/ do |page|
- case page
- when "startup page"
- title = 'Tails - News'
- when "Tails roadmap"
- title = 'Roadmap - Tails - RiseupLabs Code Repository'
- else
- raise "Unsupported page: #{page}"
- end
- step "\"#{title}\" has loaded in the Tor Browser"
-end
-
-When /^I request a new identity using Torbutton$/ do
- @screen.wait_and_click('TorButtonIcon.png', 30)
- @screen.wait_and_click('TorButtonNewIdentity.png', 30)
-end
-
-When /^I acknowledge Torbutton's New Identity confirmation prompt$/ do
- @screen.wait('GnomeQuestionDialogIcon.png', 30)
- step 'I type "y"'
-end
-
-Given /^I add a bookmark to eff.org in the Tor Browser$/ do
- url = "https://www.eff.org"
- step "I open the address \"#{url}\" in the Tor Browser"
- step 'the Tor Browser shows the "The proxy server is refusing connections" error'
- @screen.type("d", Sikuli::KeyModifier.CTRL)
- @screen.wait("TorBrowserBookmarkPrompt.png", 10)
- @screen.type(url + Sikuli::Key.ENTER)
-end
-
-Given /^the Tor Browser has a bookmark to eff.org$/ do
- @screen.type("b", Sikuli::KeyModifier.ALT)
- @screen.wait("TorBrowserEFFBookmark.png", 10)
-end
-
-Given /^all notifications have disappeared$/ do
- # These magic coordinates always locates GNOME's clock in the top
- # bar, which when clicked opens the calendar.
- x, y = 512, 10
- gnome_shell = Dogtail::Application.new('gnome-shell')
- retry_action(10, recovery_proc: Proc.new { @screen.type(Sikuli::Key.ESC) }) do
- @screen.click_point(x, y)
- unless gnome_shell.child?('No Notifications', roleName: 'label')
- @screen.click('GnomeCloseAllNotificationsButton.png')
- end
- gnome_shell.child?('No Notifications', roleName: 'label')
- end
- @screen.type(Sikuli::Key.ESC)
-end
-
-Then /^I (do not )?see "([^"]*)" after at most (\d+) seconds$/ do |negation, image, time|
- begin
- @screen.wait(image, time.to_i)
- raise "found '#{image}' while expecting not to" if negation
- rescue FindFailed => e
- raise e if not(negation)
- end
-end
-
-Then /^I (do not )?see the "([^"]*)" screen, after at most (\d+) seconds$/ do |negation, image, time|
- begin
- @screen.wait(diui_png(image), time.to_i)
- raise "found '" + diui_png(image) + "' while expecting not to" if negation
- rescue FindFailed => e
- raise e if not(negation)
- end
-end
-
-Then /^all Internet traffic has only flowed through Tor$/ do
- allowed_hosts = allowed_hosts_under_tor_enforcement
- assert_all_connections(@sniffer.pcap_file) do |c|
- allowed_hosts.include?({ address: c.daddr, port: c.dport })
- end
-end
-
-Given /^I enter the sudo password in the pkexec prompt$/ do
- step "I enter the \"#{@sudo_password}\" password in the pkexec prompt"
-end
-
-def deal_with_polkit_prompt(password, opts = {})
- opts[:expect_success] ||= true
- image = 'PolicyKitAuthPrompt.png'
- @screen.wait(image, 60)
- @screen.type(password)
- @screen.type(Sikuli::Key.ENTER)
- if opts[:expect_success]
- @screen.waitVanish(image, 20)
- else
- @screen.wait('PolicyKitAuthFailure.png', 20)
- end
-end
-
-Given /^I enter the "([^"]*)" password in the pkexec prompt$/ do |password|
- deal_with_polkit_prompt(password)
-end
-
-Given /^process "([^"]+)" is (not )?running$/ do |process, not_running|
- if not_running
- assert(!$vm.has_process?(process), "Process '#{process}' is running")
- else
- assert($vm.has_process?(process), "Process '#{process}' is not running")
- end
-end
-
-Given /^process "([^"]+)" is running within (\d+) seconds$/ do |process, time|
- try_for(time.to_i, :msg => "Process '#{process}' is not running after " +
- "waiting for #{time} seconds") do
- $vm.has_process?(process)
- end
-end
-
-Given /^process "([^"]+)" has stopped running after at most (\d+) seconds$/ do |process, time|
- try_for(time.to_i, :msg => "Process '#{process}' is still running after " +
- "waiting for #{time} seconds") do
- not $vm.has_process?(process)
- end
-end
-
-Given /^I kill the process "([^"]+)"$/ do |process|
- $vm.execute("killall #{process}")
- try_for(10, :msg => "Process '#{process}' could not be killed") {
- !$vm.has_process?(process)
- }
-end
-
-Then /^Tails eventually (shuts down|restarts)$/ do |mode|
- try_for(3*60) do
- if mode == 'restarts'
- @screen.find('TailsGreeter.png')
- true
- else
- ! $vm.is_running?
- end
- end
-end
-
-Given /^I shutdown Tails and wait for the computer to power off$/ do
- $vm.spawn("poweroff")
- step 'Tails eventually shuts down'
-end
-
-When /^I request a shutdown using the emergency shutdown applet$/ do
- @screen.hide_cursor
- @screen.wait_and_click('TailsEmergencyShutdownButton.png', 10)
- # Sometimes the next button too fast, before the menu has settled
- # down to its final size and the icon we want to click is in its
- # final position. dogtail might allow us to fix that, but given how
- # rare this problem is, it's not worth the effort.
- step 'I wait 5 seconds'
- @screen.wait_and_click('TailsEmergencyShutdownHalt.png', 10)
-end
-
-When /^I warm reboot the computer$/ do
- $vm.spawn("reboot")
-end
-
-When /^I request a reboot using the emergency shutdown applet$/ do
- @screen.hide_cursor
- @screen.wait_and_click('TailsEmergencyShutdownButton.png', 10)
- # See comment on /^I request a shutdown using the emergency shutdown applet$/
- # that explains why we need to wait.
- step 'I wait 5 seconds'
- @screen.wait_and_click('TailsEmergencyShutdownReboot.png', 10)
-end
-
-Given /^the package "([^"]+)" is installed$/ do |package|
- assert($vm.execute("dpkg -s '#{package}' 2>/dev/null | grep -qs '^Status:.*installed$'").success?,
- "Package '#{package}' is not installed")
-end
-
-Given /^I add a ([a-z0-9.]+ |)wired DHCP NetworkManager connection called "([^"]+)"$/ do |version, con_name|
- if version and version == '2.x'
- con_content = <<EOF
-[connection]
-id=#{con_name}
-uuid=b04afa94-c3a1-41bf-aa12-1a743d964162
-interface-name=eth0
-type=ethernet
-EOF
- con_file = "/etc/NetworkManager/system-connections/#{con_name}"
- $vm.file_overwrite(con_file, con_content)
- $vm.execute_successfully("chmod 600 '#{con_file}'")
- $vm.execute_successfully("nmcli connection load '#{con_file}'")
- elsif version and version == '3.x'
- raise "Unsupported version '#{version}'"
- else
- $vm.execute_successfully(
- "nmcli connection add con-name #{con_name} " + \
- "type ethernet autoconnect yes ifname eth0"
- )
- end
- try_for(10) {
- nm_con_list = $vm.execute("nmcli --terse --fields NAME connection show").stdout
- nm_con_list.split("\n").include? "#{con_name}"
- }
-end
-
-Given /^I switch to the "([^"]+)" NetworkManager connection$/ do |con_name|
- $vm.execute("nmcli connection up id #{con_name}")
- try_for(60) do
- $vm.execute("nmcli --terse --fields NAME,STATE connection show").stdout.chomp.split("\n").include?("#{con_name}:activated")
- end
-end
-
-When /^I start and focus GNOME Terminal$/ do
- step 'I start "GNOME Terminal" via GNOME Activities Overview'
- @screen.wait('GnomeTerminalWindow.png', 40)
-end
-
-When /^I run "([^"]+)" in GNOME Terminal$/ do |command|
- if !$vm.has_process?("gnome-terminal-server")
- step "I start and focus GNOME Terminal"
- else
- @screen.wait_and_click('GnomeTerminalWindow.png', 20)
- end
- @screen.type(command + Sikuli::Key.ENTER)
-end
-
-When /^the file "([^"]+)" exists(?:| after at most (\d+) seconds)$/ do |file, timeout|
- timeout = 0 if timeout.nil?
- try_for(
- timeout.to_i,
- :msg => "The file #{file} does not exist after #{timeout} seconds"
- ) {
- $vm.file_exist?(file)
- }
-end
-
-When /^the file "([^"]+)" does not exist$/ do |file|
- assert(! ($vm.file_exist?(file)))
-end
-
-When /^the directory "([^"]+)" exists$/ do |directory|
- assert($vm.directory_exist?(directory))
-end
-
-When /^the directory "([^"]+)" does not exist$/ do |directory|
- assert(! ($vm.directory_exist?(directory)))
-end
-
-When /^I copy "([^"]+)" to "([^"]+)" as user "([^"]+)"$/ do |source, destination, user|
- c = $vm.execute("cp \"#{source}\" \"#{destination}\"", :user => LIVE_USER)
- assert(c.success?, "Failed to copy file:\n#{c.stdout}\n#{c.stderr}")
-end
-
-def is_persistent?(app)
- conf = get_persistence_presets(true)["#{app}"]
- c = $vm.execute("findmnt --noheadings --output SOURCE --target '#{conf}'")
- # This check assumes that we haven't enabled read-only persistence.
- c.success? and c.stdout.chomp != "aufs"
-end
-
-Then /^persistence for "([^"]+)" is (|not )enabled$/ do |app, enabled|
- case enabled
- when ''
- assert(is_persistent?(app), "Persistence should be enabled.")
- when 'not '
- assert(!is_persistent?(app), "Persistence should not be enabled.")
- end
-end
-
-Given /^I start "([^"]+)" via GNOME Activities Overview$/ do |app_name|
- @screen.wait('GnomeApplicationsMenu.png', 10)
- $vm.execute_successfully('xdotool key Super', user: LIVE_USER)
- @screen.wait('GnomeActivitiesOverview.png', 10)
- @screen.type(app_name)
- @screen.type(Sikuli::Key.ENTER, Sikuli::KeyModifier.CTRL)
-end
-
-When /^I type "([^"]+)"$/ do |string|
- @screen.type(string)
-end
-
-When /^I press the "([^"]+)" key$/ do |key|
- begin
- @screen.type(eval("Sikuli::Key.#{key}"))
- rescue RuntimeError
- raise "unsupported key #{key}"
- end
-end
-
-Then /^the (amnesiac|persistent) Tor Browser directory (exists|does not exist)$/ do |persistent_or_not, mode|
- case persistent_or_not
- when "amnesiac"
- dir = "/home/#{LIVE_USER}/Tor Browser"
- when "persistent"
- dir = "/home/#{LIVE_USER}/Persistent/Tor Browser"
- end
- step "the directory \"#{dir}\" #{mode}"
-end
-
-Then /^there is a GNOME bookmark for the (amnesiac|persistent) Tor Browser directory$/ do |persistent_or_not|
- case persistent_or_not
- when "amnesiac"
- bookmark_image = 'TorBrowserAmnesicFilesBookmark.png'
- when "persistent"
- bookmark_image = 'TorBrowserPersistentFilesBookmark.png'
- end
- @screen.wait_and_click('GnomePlaces.png', 10)
- @screen.wait(bookmark_image, 40)
- @screen.type(Sikuli::Key.ESC)
-end
-
-Then /^there is no GNOME bookmark for the persistent Tor Browser directory$/ do
- try_for(65) do
- @screen.wait_and_click('GnomePlaces.png', 10)
- @screen.wait("GnomePlacesWithoutTorBrowserPersistent.png", 10)
- @screen.type(Sikuli::Key.ESC)
- end
-end
-
-def pulseaudio_sink_inputs
- pa_info = $vm.execute_successfully('pacmd info', :user => LIVE_USER).stdout
- sink_inputs_line = pa_info.match(/^\d+ sink input\(s\) available\.$/)[0]
- return sink_inputs_line.match(/^\d+/)[0].to_i
-end
-
-When /^(no|\d+) application(?:s?) (?:is|are) playing audio(?:| after (\d+) seconds)$/ do |nb, wait_time|
- nb = 0 if nb == "no"
- sleep wait_time.to_i if ! wait_time.nil?
- assert_equal(nb.to_i, pulseaudio_sink_inputs)
-end
-
-When /^I double-click on the (Tails documentation|Report an Error) launcher on the desktop$/ do |launcher|
- image = 'Desktop' + launcher.split.map { |s| s.capitalize } .join + '.png'
- info = xul_application_info('Tor Browser')
- # Sometimes the double-click is lost (#12131).
- retry_action(10) do
- @screen.wait_and_double_click(image, 10) if $vm.execute("pgrep --uid #{info[:user]} --full --exact '#{info[:cmd_regex]}'").failure?
- step 'the Tor Browser has started'
- end
-end
-
-When /^I click the blocked video icon$/ do
- @screen.wait_and_click("TorBrowserBlockedVideo.png", 30)
-end
-
-When /^I accept to temporarily allow playing this video$/ do
- @screen.wait_and_click("TorBrowserOkButton.png", 10)
-end
-
-When /^I click the HTML5 play button$/ do
- @screen.wait_and_click("TorBrowserHtml5PlayButton.png", 30)
-end
-
-When /^I (can|cannot) save the current page as "([^"]+[.]html)" to the (.*) directory$/ do |should_work, output_file, output_dir|
- should_work = should_work == 'can' ? true : false
- @screen.type("s", Sikuli::KeyModifier.CTRL)
- @screen.wait("TorBrowserSaveDialog.png", 10)
- if output_dir == "persistent Tor Browser"
- output_dir = "/home/#{LIVE_USER}/Persistent/Tor Browser"
- @screen.wait_and_click("GtkTorBrowserPersistentBookmark.png", 10)
- @screen.wait("GtkTorBrowserPersistentBookmarkSelected.png", 10)
- # The output filename (without its extension) is already selected,
- # let's use the keyboard shortcut to focus its field
- @screen.type("n", Sikuli::KeyModifier.ALT)
- @screen.wait("TorBrowserSaveOutputFileSelected.png", 10)
- elsif output_dir == "default downloads"
- output_dir = "/home/#{LIVE_USER}/Tor Browser"
- else
- @screen.type(output_dir + '/')
- end
- # Only the part of the filename before the .html extension can be easily replaced
- # so we have to remove it before typing it into the arget filename entry widget.
- @screen.type(output_file.sub(/[.]html$/, ''))
- @screen.type(Sikuli::Key.ENTER)
- if should_work
- try_for(10, :msg => "The page was not saved to #{output_dir}/#{output_file}") {
- $vm.file_exist?("#{output_dir}/#{output_file}")
- }
- else
- @screen.wait("TorBrowserCannotSavePage.png", 10)
- end
-end
-
-When /^I can print the current page as "([^"]+[.]pdf)" to the (default downloads|persistent Tor Browser) directory$/ do |output_file, output_dir|
- if output_dir == "persistent Tor Browser"
- output_dir = "/home/#{LIVE_USER}/Persistent/Tor Browser"
- else
- output_dir = "/home/#{LIVE_USER}/Tor Browser"
- end
- @screen.type("p", Sikuli::KeyModifier.CTRL)
- @screen.wait("TorBrowserPrintDialog.png", 20)
- @screen.wait_and_click("BrowserPrintToFile.png", 10)
- @screen.wait_and_double_click("TorBrowserPrintOutputFile.png", 10)
- @screen.hide_cursor
- @screen.wait("TorBrowserPrintOutputFileSelected.png", 10)
- # Only the file's basename is selected by double-clicking,
- # so we type only the desired file's basename to replace it
- @screen.type(output_dir + '/' + output_file.sub(/[.]pdf$/, '') + Sikuli::Key.ENTER)
- try_for(30, :msg => "The page was not printed to #{output_dir}/#{output_file}") {
- $vm.file_exist?("#{output_dir}/#{output_file}")
- }
-end
-
-Given /^a web server is running on the LAN$/ do
- @web_server_ip_addr = $vmnet.bridge_ip_addr
- @web_server_port = 8000
- @web_server_url = "http://#{@web_server_ip_addr}:#{@web_server_port}"
- web_server_hello_msg = "Welcome to the LAN web server!"
-
- # I've tested ruby Thread:s, fork(), etc. but nothing works due to
- # various strange limitations in the ruby interpreter. For instance,
- # apparently concurrent IO has serious limits in the thread
- # scheduler (e.g. sikuli's wait() would block WEBrick from reading
- # from its socket), and fork():ing results in a lot of complex
- # cucumber stuff (like our hooks!) ending up in the child process,
- # breaking stuff in the parent process. After asking some supposed
- # ruby pros, I've settled on the following.
- code = <<-EOF
- require "webrick"
- STDOUT.reopen("/dev/null", "w")
- STDERR.reopen("/dev/null", "w")
- server = WEBrick::HTTPServer.new(:BindAddress => "#{@web_server_ip_addr}",
- :Port => #{@web_server_port},
- :DocumentRoot => "/dev/null")
- server.mount_proc("/") do |req, res|
- res.body = "#{web_server_hello_msg}"
- end
- server.start
-EOF
- add_lan_host(@web_server_ip_addr, @web_server_port)
- proc = IO.popen(['ruby', '-e', code])
- try_for(10, :msg => "It seems the LAN web server failed to start") do
- Process.kill(0, proc.pid) == 1
- end
-
- add_after_scenario_hook { Process.kill("TERM", proc.pid) }
-
- # It seems necessary to actually check that the LAN server is
- # serving, possibly because it isn't doing so reliably when setting
- # up. If e.g. the Unsafe Browser (which *should* be able to access
- # the web server) tries to access it too early, Firefox seems to
- # take some random amount of time to retry fetching. Curl gives a
- # more consistent result, so let's rely on that instead. Note that
- # this forces us to capture traffic *after* this step in case
- # accessing this server matters, like when testing the Tor Browser..
- try_for(30, :msg => "Something is wrong with the LAN web server") do
- msg = $vm.execute_successfully("curl #{@web_server_url}",
- :user => LIVE_USER).stdout.chomp
- web_server_hello_msg == msg
- end
-end
-
-When /^I open a page on the LAN web server in the (.*)$/ do |browser|
- step "I open the address \"#{@web_server_url}\" in the #{browser}"
-end
-
-Given /^I wait (?:between (\d+) and )?(\d+) seconds$/ do |min, max|
- if min
- time = rand(max.to_i - min.to_i + 1) + min.to_i
- else
- time = max.to_i
- end
- puts "Slept for #{time} seconds"
- sleep(time)
-end
-
-Given /^I (?:re)?start monitoring the AppArmor log of "([^"]+)"$/ do |profile|
- # AppArmor log entries may be dropped if printk rate limiting is
- # enabled.
- $vm.execute_successfully('sysctl -w kernel.printk_ratelimit=0')
- # We will only care about entries for this profile from this time
- # and on.
- guest_time = $vm.execute_successfully(
- 'date +"%Y-%m-%d %H:%M:%S"').stdout.chomp
- @apparmor_profile_monitoring_start ||= Hash.new
- @apparmor_profile_monitoring_start[profile] = guest_time
-end
-
-When /^AppArmor has (not )?denied "([^"]+)" from opening "([^"]+)"(?: after at most (\d+) seconds)?$/ do |anti_test, profile, file, time|
- assert(@apparmor_profile_monitoring_start &&
- @apparmor_profile_monitoring_start[profile],
- "It seems the profile '#{profile}' isn't being monitored by the " +
- "'I monitor the AppArmor log of ...' step")
- audit_line_regex = 'apparmor="DENIED" operation="open" profile="%s" name="%s"' % [profile, file]
- block = Proc.new do
- audit_log = $vm.execute(
- "journalctl --full --no-pager " +
- "--since='#{@apparmor_profile_monitoring_start[profile]}' " +
- "SYSLOG_IDENTIFIER=kernel | grep -w '#{audit_line_regex}'"
- ).stdout.chomp
- assert(audit_log.empty? == (anti_test ? true : false))
- true
- end
- begin
- if time
- try_for(time.to_i) { block.call }
- else
- block.call
- end
- rescue Timeout::Error, Test::Unit::AssertionFailedError => e
- raise e, "AppArmor has #{anti_test ? "" : "not "}denied the operation"
- end
-end
-
-Then /^I force Tor to use a new circuit$/ do
- force_new_tor_circuit
-end
-
-When /^I eject the boot medium$/ do
- dev = boot_device
- dev_type = device_info(dev)['ID_TYPE']
- case dev_type
- when 'cd'
- $vm.eject_cdrom
- when 'disk'
- boot_disk_name = $vm.disk_name(dev)
- $vm.unplug_drive(boot_disk_name)
- else
- raise "Unsupported medium type '#{dev_type}' for boot device '#{dev}'"
- end
-end
-
-Given /^Tails is fooled to think it is running version (.+)$/ do |version|
- $vm.execute_successfully(
- "sed -i " +
- "'s/^TAILS_VERSION_ID=.*$/TAILS_VERSION_ID=\"#{version}\"/' " +
- "/etc/os-release"
- )
-end
-
-Then /^Tails is running version (.+)$/ do |version|
- v1 = $vm.execute_successfully('tails-version').stdout.split.first
- assert_equal(version, v1, "The version doesn't match tails-version's output")
- v2 = $vm.file_content('/etc/os-release')
- .scan(/TAILS_VERSION_ID="(#{version})"/).flatten.first
- assert_equal(version, v2, "The version doesn't match /etc/os-release")
-end
-
-def share_host_files(files)
- files = [files] if files.class == String
- assert_equal(Array, files.class)
- disk_size = files.map { |f| File.new(f).size } .inject(0, :+)
- # Let's add some extra space for filesysten overhead etc.
- disk_size += [convert_to_bytes(1, 'MiB'), (disk_size * 0.10).ceil].max
- disk = random_alpha_string(10)
- step "I temporarily create an #{disk_size} bytes disk named \"#{disk}\""
- step "I create a gpt partition labeled \"#{disk}\" with an ext4 " +
- "filesystem on disk \"#{disk}\""
- $vm.storage.guestfs_disk_helper(disk) do |g, _|
- partition = g.list_partitions().first
- g.mount(partition, "/")
- files.each { |f| g.upload(f, "/" + File.basename(f)) }
- end
- step "I plug USB drive \"#{disk}\""
- mount_dir = $vm.execute_successfully('mktemp -d').stdout.chomp
- dev = $vm.disk_dev(disk)
- partition = dev + '1'
- $vm.execute_successfully("mount #{partition} #{mount_dir}")
- $vm.execute_successfully("chmod -R a+rX '#{mount_dir}'")
- return mount_dir
-end
-
-def mount_USB_drive(disk, fs_options = {})
- fs_options[:encrypted] ||= false
- @tmp_usb_drive_mount_dir = $vm.execute_successfully('mktemp -d').stdout.chomp
- dev = $vm.disk_dev(disk)
- partition = dev + '1'
- if fs_options[:encrypted]
- password = fs_options[:password]
- assert_not_nil(password)
- luks_mapping = "#{disk}_unlocked"
- $vm.execute_successfully(
- "echo #{password} | " +
- "cryptsetup luksOpen #{partition} #{luks_mapping}"
- )
- $vm.execute_successfully(
- "mount /dev/mapper/#{luks_mapping} #{@tmp_usb_drive_mount_dir}"
- )
- else
- $vm.execute_successfully("mount #{partition} #{@tmp_usb_drive_mount_dir}")
- end
- @tmp_filesystem_disk = disk
- @tmp_filesystem_options = fs_options
- @tmp_filesystem_partition = partition
- return @tmp_usb_drive_mount_dir
-end
-
-When(/^I plug and mount a (\d+) MiB USB drive with an? (.*)$/) do |size_MiB, fs|
- disk_size = convert_to_bytes(size_MiB.to_i, 'MiB')
- disk = random_alpha_string(10)
- step "I temporarily create an #{disk_size} bytes disk named \"#{disk}\""
- step "I create a gpt partition labeled \"#{disk}\" with " +
- "an #{fs} on disk \"#{disk}\""
- step "I plug USB drive \"#{disk}\""
- fs_options = {}
- fs_options[:filesystem] = /(.*) filesystem/.match(fs)[1]
- if /\bencrypted with password\b/.match(fs)
- fs_options[:encrypted] = true
- fs_options[:password] = /encrypted with password "([^"]+)"/.match(fs)[1]
- end
- mount_dir = mount_USB_drive(disk, fs_options)
- @tmp_filesystem_size_b = convert_to_bytes(
- avail_space_in_mountpoint_kB(mount_dir),
- 'KB'
- )
-end
-
-When(/^I mount the USB drive again$/) do
- mount_USB_drive(@tmp_filesystem_disk, @tmp_filesystem_options)
-end
-
-When(/^I umount the USB drive$/) do
- $vm.execute_successfully("umount #{@tmp_usb_drive_mount_dir}")
- if @tmp_filesystem_options[:encrypted]
- $vm.execute_successfully("cryptsetup luksClose #{@tmp_filesystem_disk}_unlocked")
- end
-end
-
-When /^Tails system time is magically synchronized$/ do
- $vm.host_to_guest_time_sync
-end
diff --git a/cucumber/features/step_definitions/dhcp.rb b/cucumber/features/step_definitions/dhcp.rb
deleted file mode 100644
index 3c834224..00000000
--- a/cucumber/features/step_definitions/dhcp.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-Then /^the hostname should not have been leaked on the network$/ do
- begin
- hostname = $vm.execute("hostname").stdout.chomp
- packets = PacketFu::PcapFile.new.file_to_array(filename: @sniffer.pcap_file)
- packets.each do |p|
- # if PacketFu::TCPPacket.can_parse?(p)
- # ipv4_tcp_packets << PacketFu::TCPPacket.parse(p)
- if PacketFu::IPPacket.can_parse?(p)
- payload = PacketFu::IPPacket.parse(p).payload
- elsif PacketFu::IPv6Packet.can_parse?(p)
- payload = PacketFu::IPv6Packet.parse(p).payload
- else
- raise "Found something in the pcap file that either is non-IP, or cannot be parsed"
- end
- if payload.match(hostname)
- raise "Hostname leak detected"
- end
- end
- rescue Exception => e
- save_failure_artifact("Network capture", @sniffer.pcap_file)
- raise e
- end
-end
diff --git a/cucumber/features/step_definitions/electrum.rb b/cucumber/features/step_definitions/electrum.rb
deleted file mode 100644
index eaeb22aa..00000000
--- a/cucumber/features/step_definitions/electrum.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-Then /^I start Electrum through the GNOME menu$/ do
- step "I start \"Electrum Bitcoin Wallet\" via GNOME Activities Overview"
-end
-
-When /^a bitcoin wallet is (|not )present$/ do |existing|
- wallet = "/home/#{LIVE_USER}/.electrum/wallets/default_wallet"
- case existing
- when ""
- step "the file \"#{wallet}\" exists after at most 30 seconds"
- when "not "
- step "the file \"#{wallet}\" does not exist"
- else
- raise "Unknown value specified for #{existing}"
- end
-end
-
-When /^I create a new bitcoin wallet$/ do
- @screen.wait("ElectrumNoWallet.png", 10)
- @screen.wait_and_click("ElectrumNextButton.png", 10)
- @screen.wait("ElectrumCreateNewSeed.png", 10)
- @screen.wait_and_click("ElectrumNextButton.png", 10)
- @screen.wait("ElectrumWalletGenerationSeed.png", 15)
- @screen.wait_and_click("ElectrumWalletSeedTextbox.png", 15)
- @screen.type('a', Sikuli::KeyModifier.CTRL) # select wallet seed
- @screen.type('c', Sikuli::KeyModifier.CTRL) # copy seed to clipboard
- seed = $vm.get_clipboard
- @screen.wait_and_click("ElectrumNextButton.png", 15)
- @screen.wait("ElectrumSeedVerificationPrompt.png", 15)
- @screen.wait_and_click("ElectrumWalletSeedTextbox.png", 15)
- @screen.type(seed) # Confirm seed
- @screen.wait_and_click("ElectrumNextButton.png", 10)
- @screen.wait("ElectrumEncryptWallet.png", 10)
- @screen.type(Sikuli::Key.TAB) # focus first password field
- @screen.type("asdf" + Sikuli::Key.TAB) # set password
- @screen.type("asdf" + Sikuli::Key.TAB) # confirm password
- @screen.wait_and_click("ElectrumNextButton.png", 10)
- @screen.wait("ElectrumPreferencesButton.png", 30)
-end
-
-Then /^I see a warning that Electrum is not persistent$/ do
- @screen.wait('GnomeQuestionDialogIcon.png', 30)
-end
-
-Then /^I am prompted to configure Electrum$/ do
- @screen.wait("ElectrumNoWallet.png", 60)
-end
-
-Then /^I see the main Electrum client window$/ do
- @screen.wait('ElectrumPreferencesButton.png', 20)
-end
-
-Then /^Electrum successfully connects to the network$/ do
- @screen.wait('ElectrumStatus.png', 180)
-end
diff --git a/cucumber/features/step_definitions/encryption.rb b/cucumber/features/step_definitions/encryption.rb
deleted file mode 100644
index 3b20a5b4..00000000
--- a/cucumber/features/step_definitions/encryption.rb
+++ /dev/null
@@ -1,133 +0,0 @@
-def seahorse_menu_click_helper(main, sub, verify = nil)
- try_for(60) do
- step "process \"#{verify}\" is running" if verify
- @screen.hide_cursor
- @screen.wait_and_click(main, 10)
- @screen.wait_and_click(sub, 10)
- return
- end
-end
-
-Given /^I generate an OpenPGP key named "([^"]+)" with password "([^"]+)"$/ do |name, pwd|
- @passphrase = pwd
- @key_name = name
- gpg_key_recipie = <<EOF
- Key-Type: RSA
- Key-Length: 4096
- Subkey-Type: RSA
- Subkey-Length: 4096
- Name-Real: #{@key_name}
- Name-Comment: Blah
- Name-Email: #{@key_name}@test.org
- Expire-Date: 0
- Passphrase: #{pwd}
- %commit
-EOF
- recipe_path = '/tmp/gpg_key_recipe'
- $vm.file_overwrite(recipe_path, gpg_key_recipie)
- $vm.execute("chown #{LIVE_USER}:#{LIVE_USER} #{recipe_path}")
- c = $vm.execute("gpg --batch --gen-key < #{recipe_path}",
- :user => LIVE_USER)
- assert(c.success?, "Failed to generate OpenPGP key:\n#{c.stderr}")
-end
-
-When /^I type a message into gedit$/ do
- step 'I start "gedit" via GNOME Activities Overview'
- @screen.wait_and_click("GeditWindow.png", 20)
- # We don't have a good visual indicator for when we can continue. Without the
- # sleep we may start typing in the gedit window far too soon, causing
- # keystrokes to go missing.
- sleep 5
- @screen.type("ATTACK AT DAWN")
-end
-
-def maybe_deal_with_pinentry
- begin
- @screen.wait_and_click("PinEntryPrompt.png", 10)
- # Without this sleep here (and reliable visual indicators) we can sometimes
- # miss keystrokes by typing too soon. This sleep prevents this problem from
- # coming up.
- sleep 5
- @screen.type(@passphrase + Sikuli::Key.ENTER)
- rescue FindFailed
- # The passphrase was cached or we wasn't prompted at all (e.g. when
- # only encrypting to a public key)
- end
-end
-
-def gedit_copy_all_text
- context_menu_helper('GeditWindow.png', 'GeditStatusBar.png', 'GeditSelectAll.png')
- context_menu_helper('GeditWindow.png', 'GeditStatusBar.png', 'GeditCopy.png')
-end
-
-def gedit_paste_into_a_new_tab
- @screen.wait_and_click("GeditNewTab.png", 20)
- context_menu_helper('GeditWindow.png', 'GeditStatusBar.png', 'GeditPaste.png')
-end
-
-def encrypt_sign_helper
- gedit_copy_all_text
- seahorse_menu_click_helper('GpgAppletIconNormal.png', 'GpgAppletSignEncrypt.png')
- @screen.wait_and_click("GpgAppletChooseKeyWindow.png", 30)
- # We don't have a good visual indicator for when we can continue without
- # keystrokes being lost.
- sleep 5
- yield
- maybe_deal_with_pinentry
- gedit_paste_into_a_new_tab
-end
-
-def decrypt_verify_helper(icon)
- gedit_copy_all_text
- seahorse_menu_click_helper(icon, 'GpgAppletDecryptVerify.png')
- maybe_deal_with_pinentry
- @screen.wait("GpgAppletResults.png", 20)
- @screen.wait("GpgAppletResultsMsg.png", 20)
-end
-
-When /^I encrypt the message using my OpenPGP key$/ do
- encrypt_sign_helper do
- @screen.type(@key_name + Sikuli::Key.ENTER + Sikuli::Key.ENTER)
- end
-end
-
-Then /^I can decrypt the encrypted message$/ do
- decrypt_verify_helper("GpgAppletIconEncrypted.png")
- @screen.wait("GpgAppletResultsEncrypted.png", 20)
-end
-
-When /^I sign the message using my OpenPGP key$/ do
- encrypt_sign_helper do
- @screen.type(Sikuli::Key.TAB + Sikuli::Key.DOWN + Sikuli::Key.ENTER)
- end
-end
-
-Then /^I can verify the message's signature$/ do
- decrypt_verify_helper("GpgAppletIconSigned.png")
- @screen.wait("GpgAppletResultsSigned.png", 20)
-end
-
-When /^I both encrypt and sign the message using my OpenPGP key$/ do
- encrypt_sign_helper do
- @screen.wait_and_click('GpgAppletEncryptionKey.png', 20)
- @screen.type(Sikuli::Key.SPACE)
- @screen.wait('GpgAppletKeySelected.png', 10)
- @screen.type(Sikuli::Key.TAB + Sikuli::Key.DOWN + Sikuli::Key.ENTER)
- @screen.type(Sikuli::Key.ENTER)
- end
-end
-
-Then /^I can decrypt and verify the encrypted message$/ do
- decrypt_verify_helper("GpgAppletIconEncrypted.png")
- @screen.wait("GpgAppletResultsEncrypted.png", 20)
- @screen.wait("GpgAppletResultsSigned.png", 20)
-end
-
-When /^I symmetrically encrypt the message with password "([^"]+)"$/ do |pwd|
- @passphrase = pwd
- gedit_copy_all_text
- seahorse_menu_click_helper('GpgAppletIconNormal.png', 'GpgAppletEncryptPassphrase.png')
- maybe_deal_with_pinentry # enter password
- maybe_deal_with_pinentry # confirm password
- gedit_paste_into_a_new_tab
-end
diff --git a/cucumber/features/step_definitions/evince.rb b/cucumber/features/step_definitions/evince.rb
deleted file mode 100644
index 9411ac4d..00000000
--- a/cucumber/features/step_definitions/evince.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-When /^I(?:| try to) open "([^"]+)" with Evince$/ do |filename|
- step "I run \"evince #{filename}\" in GNOME Terminal"
-end
-
-Then /^I can print the current document to "([^"]+)"$/ do |output_file|
- @screen.type("p", Sikuli::KeyModifier.CTRL)
- @screen.wait("EvincePrintDialog.png", 10)
- @screen.wait_and_click("EvincePrintToFile.png", 10)
- @screen.wait_and_click("EvincePrintOutputFileButton.png", 10)
- @screen.wait("EvincePrintFileDialog.png", 10)
- # Only the file's basename is selected by double-clicking,
- # so we type only the desired file's basename to replace it
- $vm.set_clipboard(output_file.sub(/[.]pdf$/, ''))
- @screen.type('v', Sikuli::KeyModifier.CTRL)
- @screen.type(Sikuli::Key.ENTER)
- @screen.wait_and_click("EvincePrintButton.png", 10)
- try_for(10, :msg => "The document was not printed to #{output_file}") {
- $vm.file_exist?(output_file)
- }
-end
-
-When /^I close Evince$/ do
- @screen.type("w", Sikuli::KeyModifier.CTRL)
- step 'process "evince" has stopped running after at most 20 seconds'
-end
diff --git a/cucumber/features/step_definitions/firewall_leaks.rb b/cucumber/features/step_definitions/firewall_leaks.rb
deleted file mode 100644
index 0cd94cca..00000000
--- a/cucumber/features/step_definitions/firewall_leaks.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-Then(/^the firewall leak detector has detected leaks$/) do
- assert_raise(FirewallAssertionFailedError) do
- step 'all Internet traffic has only flowed through Tor'
- end
-end
-
-Given(/^I disable Tails' firewall$/) do
- $vm.execute("/usr/local/lib/do_not_ever_run_me")
- iptables = $vm.execute("iptables -L -n -v").stdout.chomp.split("\n")
- for line in iptables do
- if !line[/Chain (INPUT|OUTPUT|FORWARD) \(policy ACCEPT/] and
- !line[/pkts[[:blank:]]+bytes[[:blank:]]+target/] and
- !line.empty?
- raise "The Tails firewall was not successfully disabled:\n#{iptables}"
- end
- end
-end
-
-When(/^I do a TCP DNS lookup of "(.*?)"$/) do |host|
- lookup = $vm.execute("host -T -t A #{host} #{SOME_DNS_SERVER}", :user => LIVE_USER)
- assert(lookup.success?, "Failed to resolve #{host}:\n#{lookup.stdout}")
-end
-
-When(/^I do a UDP DNS lookup of "(.*?)"$/) do |host|
- lookup = $vm.execute("host -t A #{host} #{SOME_DNS_SERVER}", :user => LIVE_USER)
- assert(lookup.success?, "Failed to resolve #{host}:\n#{lookup.stdout}")
-end
-
-When(/^I send some ICMP pings$/) do
- # We ping an IP address to avoid a DNS lookup
- ping = $vm.execute("ping -c 5 #{SOME_DNS_SERVER}")
- assert(ping.success?, "Failed to ping #{SOME_DNS_SERVER}:\n#{ping.stderr}")
-end
diff --git a/cucumber/features/step_definitions/git.rb b/cucumber/features/step_definitions/git.rb
deleted file mode 100644
index bd8fcf7d..00000000
--- a/cucumber/features/step_definitions/git.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-When /^I clone the Git repository "([\S]+)" in GNOME Terminal$/ do |repo|
- repo_directory = /[\S]+\/([\S]+)(\.git)?$/.match(repo)[1]
- assert(!$vm.directory_exist?("/home/#{LIVE_USER}/#{repo_directory}"))
-
- recovery_proc = Proc.new do
- $vm.execute("rm -rf /home/#{LIVE_USER}/#{repo_directory}",
- :user => LIVE_USER)
- step 'I kill the process "git"'
- @screen.type('clear' + Sikuli::Key.ENTER)
- end
-
- retry_tor(recovery_proc) do
- step "I run \"git clone #{repo}\" in GNOME Terminal"
- m = /^(https?|git):\/\//.match(repo)
- unless m
- step 'I verify the SSH fingerprint for the Git repository'
- end
- try_for(180, :msg => 'Git process took too long') {
- !$vm.has_process?('/usr/bin/git')
- }
- Dogtail::Application.new('gnome-terminal-server')
- .child('Terminal', roleName: 'terminal')
- .text['Unpacking objects: 100%']
- end
-end
-
-Then /^the Git repository "([\S]+)" has been cloned successfully$/ do |repo|
- assert($vm.directory_exist?("/home/#{LIVE_USER}/#{repo}/.git"))
- assert($vm.file_exist?("/home/#{LIVE_USER}/#{repo}/.git/config"))
- $vm.execute_successfully("cd '/home/#{LIVE_USER}/#{repo}/' && git status",
- :user => LIVE_USER)
-end
diff --git a/cucumber/features/step_definitions/mac_spoofing.rb b/cucumber/features/step_definitions/mac_spoofing.rb
deleted file mode 100644
index 260b28fd..00000000
--- a/cucumber/features/step_definitions/mac_spoofing.rb
+++ /dev/null
@@ -1,119 +0,0 @@
-def all_ethernet_nics
- $vm.execute_successfully(
- "get_all_ethernet_nics", :libs => 'hardware'
- ).stdout.split
-end
-
-When /^I disable MAC spoofing in Tails Greeter$/ do
- open_greeter_additional_settings()
- @screen.wait_and_click("TailsGreeterMACSpoofing.png", 30)
- @screen.wait_and_click("TailsGreeterDisableMACSpoofing.png", 10)
- @screen.wait_and_click("TailsGreeterAdditionalSettingsAdd.png", 10)
-end
-
-Then /^the (\d+)(?:st|nd|rd|th) network device has (its real|a spoofed) MAC address configured$/ do |dev_nr, mode|
- is_spoofed = (mode == "a spoofed")
- alias_name = "net#{dev_nr.to_i - 1}"
- nic_real_mac = $vm.real_mac(alias_name)
- nic = "eth#{dev_nr.to_i - 1}"
- nic_current_mac = $vm.execute_successfully(
- "get_current_mac_of_nic #{nic}", :libs => 'hardware'
- ).stdout.chomp
- begin
- if is_spoofed
- if nic_real_mac == nic_current_mac
- raise "The MAC address was expected to be spoofed but wasn't"
- end
- else
- if nic_real_mac != nic_current_mac
- raise "The MAC address is spoofed but was expected to not be"
- end
- end
- rescue Exception => e
- save_failure_artifact("Network capture", @sniffer.pcap_file)
- raise e
- end
-end
-
-Then /^no network device leaked the real MAC address$/ do
- macs = $vm.all_real_macs
- assert_all_connections(@sniffer.pcap_file) do |c|
- macs.all? do |mac|
- not [c.mac_saddr, c.mac_daddr].include?(mac)
- end
- end
-end
-
-Then /^some network device leaked the real MAC address$/ do
- assert_raise(FirewallAssertionFailedError) do
- step 'no network device leaked the real MAC address'
- end
-end
-
-Given /^macchanger will fail by not spoofing and always returns ([\S]+)$/ do |mode|
- $vm.execute_successfully("mv /usr/bin/macchanger /usr/bin/macchanger.orig")
- $vm.execute_successfully("ln -s /bin/#{mode} /usr/bin/macchanger")
-end
-
-Given /^no network interface modules can be unloaded$/ do
- # Note that the real /sbin/modprobe is a symlink to /bin/kmod, and
- # for it to run in modprobe compatibility mode the name must be
- # exactly "modprobe", so we just move it somewhere our of the path
- # instead of renaming it ".real" or whatever we usuablly do when
- # diverting executables for wrappers.
- modprobe_divert = "/usr/local/lib/modprobe"
- $vm.execute_successfully(
- "dpkg-divert --add --rename --divert '#{modprobe_divert}' /sbin/modprobe"
- )
- fake_modprobe_wrapper = <<EOF
-#!/bin/sh
-if echo "${@}" | grep -q -- -r; then
- exit 1
-fi
-exec '#{modprobe_divert}' "${@}"
-EOF
- $vm.file_append('/sbin/modprobe', fake_modprobe_wrapper)
- $vm.execute_successfully("chmod a+rx /sbin/modprobe")
-end
-
-Then /^(\d+|no) network interface(?:s)? (?:is|are) enabled$/ do |expected_nr_nics|
- # note that "no".to_i => 0 in Ruby.
- expected_nr_nics = expected_nr_nics.to_i
- nr_nics = all_ethernet_nics.size
- assert_equal(expected_nr_nics, nr_nics)
-end
-
-Then /^the MAC spoofing panic mode disabled networking$/ do
- nm_state = $vm.execute_successfully('systemctl show NetworkManager').stdout
- nm_is_disabled = $vm.pidof('NetworkManager').empty? &&
- nm_state[/^LoadState=masked$/] &&
- nm_state[/^ActiveState=inactive$/]
- assert(nm_is_disabled, "NetworkManager was not disabled")
- all_ethernet_nics.each do |nic|
- ["nic_ipv4_addr", "nic_ipv6_addr"].each do |function|
- addr = $vm.execute_successfully(
- "#{function} #{nic}", :libs => 'hardware'
- ).stdout.chomp
- assert_equal("", addr, "NIC #{nic} was assigned address #{addr}")
- end
- end
-end
-
-When /^I hotplug a network device( and wait for it to be initialized)?$/ do |wait|
- initial_nr_nics = wait ? all_ethernet_nics.size : nil
- xml = <<-EOF
- <interface type='network'>
- <alias name='net1'/>
- <mac address='52:54:00:11:22:33'/>
- <source network='TailsToasterNet'/>
- <model type='virtio'/>
- <link state='up'/>
- </interface>
- EOF
- $vm.plug_device(xml)
- if wait
- try_for(20) do
- all_ethernet_nics.size >= initial_nr_nics + 1
- end
- end
-end
diff --git a/cucumber/features/step_definitions/pidgin.rb b/cucumber/features/step_definitions/pidgin.rb
deleted file mode 100644
index 43949b68..00000000
--- a/cucumber/features/step_definitions/pidgin.rb
+++ /dev/null
@@ -1,497 +0,0 @@
-# Extracts the secrets for the XMMP account `account_name`.
-def xmpp_account(account_name, required_options = [])
- begin
- account = $config["Pidgin"]["Accounts"]["XMPP"][account_name]
- check_keys = ["username", "domain", "password"] + required_options
- for key in check_keys do
- assert(account.has_key?(key))
- assert_not_nil(account[key])
- assert(!account[key].empty?)
- end
- rescue NoMethodError, Test::Unit::AssertionFailedError
- raise(
-<<EOF
-Your Pidgin:Accounts:XMPP:#{account} is incorrect or missing from your local configuration file (#{LOCAL_CONFIG_FILE}). See wiki/src/contribute/release_process/test/usage.mdwn for the format.
-EOF
-)
- end
- return account
-end
-
-def wait_and_focus(img, time = 10, window)
- begin
- @screen.wait(img, time)
- rescue FindFailed
- $vm.focus_window(window)
- @screen.wait(img, time)
- end
-end
-
-def focus_pidgin_irc_conversation_window(account)
- account = account.sub(/^irc\./, '')
- try_for(20) do
- $vm.focus_window(".*#{Regexp.escape(account)}$")
- end
-end
-
-def pidgin_dbus_call(method, *args)
- dbus_send(
- 'im.pidgin.purple.PurpleService',
- '/im/pidgin/purple/PurpleObject',
- "im.pidgin.purple.PurpleInterface.#{method}",
- *args, user: LIVE_USER
- )
-end
-
-def pidgin_account_connected?(account, prpl_protocol)
- account_id = pidgin_dbus_call('PurpleAccountsFind', account, prpl_protocol)
- pidgin_dbus_call('PurpleAccountIsConnected', account_id) == 1
-end
-
-When /^I create my XMPP account$/ do
- account = xmpp_account("Tails_account")
- @screen.click("PidginAccountManagerAddButton.png")
- @screen.wait("PidginAddAccountWindow.png", 20)
- @screen.click_mid_right_edge("PidginAddAccountProtocolLabel.png")
- @screen.click("PidginAddAccountProtocolXMPP.png")
- # We first wait for some field that is shown for XMPP but not the
- # default (IRC) since we otherwise may decide where we click before
- # the GUI has updated after switching protocol.
- @screen.wait("PidginAddAccountXMPPDomain.png", 5)
- @screen.click_mid_right_edge("PidginAddAccountXMPPUsername.png")
- @screen.type(account["username"])
- @screen.click_mid_right_edge("PidginAddAccountXMPPDomain.png")
- @screen.type(account["domain"])
- @screen.click_mid_right_edge("PidginAddAccountXMPPPassword.png")
- @screen.type(account["password"])
- @screen.click("PidginAddAccountXMPPRememberPassword.png")
- if account["connect_server"]
- @screen.click("PidginAddAccountXMPPAdvancedTab.png")
- @screen.click_mid_right_edge("PidginAddAccountXMPPConnectServer.png")
- @screen.type(account["connect_server"])
- end
- @screen.click("PidginAddAccountXMPPAddButton.png")
-end
-
-Then /^Pidgin automatically enables my XMPP account$/ do
- account = xmpp_account("Tails_account")
- jid = account["username"] + '@' + account["domain"]
- try_for(3*60) do
- pidgin_account_connected?(jid, 'prpl-jabber')
- end
- $vm.focus_window('Buddy List')
- @screen.wait("PidginAvailableStatus.png", 60*3)
-end
-
-Given /^my XMPP friend goes online( and joins the multi-user chat)?$/ do |join_chat|
- account = xmpp_account("Friend_account", ["otr_key"])
- bot_opts = account.select { |k, v| ["connect_server"].include?(k) }
- if join_chat
- bot_opts["auto_join"] = [@chat_room_jid]
- end
- @friend_name = account["username"]
- @chatbot = ChatBot.new(account["username"] + "@" + account["domain"],
- account["password"], account["otr_key"], bot_opts)
- @chatbot.start
- add_after_scenario_hook { @chatbot.stop }
- $vm.focus_window('Buddy List')
- @screen.wait("PidginFriendOnline.png", 60)
-end
-
-When /^I start a conversation with my friend$/ do
- $vm.focus_window('Buddy List')
- # Clicking the middle, bottom of this image should query our
- # friend, given it's the only subscribed user that's online, which
- # we assume.
- r = @screen.find("PidginFriendOnline.png")
- bottom_left = r.getBottomLeft()
- x = bottom_left.getX + r.getW/2
- y = bottom_left.getY
- @screen.doubleClick_point(x, y)
- # Since Pidgin sets the window name to the contact, we have no good
- # way to identify the conversation window. Let's just look for the
- # expected menu bar.
- @screen.wait("PidginConversationWindowMenuBar.png", 10)
-end
-
-And /^I say (.*) to my friend( in the multi-user chat)?$/ do |msg, multi_chat|
- msg = "ping" if msg == "something"
- msg = msg + Sikuli::Key.ENTER
- if multi_chat
- $vm.focus_window(@chat_room_jid.split("@").first)
- msg = @friend_name + ": " + msg
- else
- $vm.focus_window(@friend_name)
- end
- @screen.type(msg)
-end
-
-Then /^I receive a response from my friend( in the multi-user chat)?$/ do |multi_chat|
- if multi_chat
- $vm.focus_window(@chat_room_jid.split("@").first)
- else
- $vm.focus_window(@friend_name)
- end
- try_for(60) do
- if @screen.exists('PidginServerMessage.png')
- @screen.click('PidginDialogCloseButton.png')
- end
- @screen.find('PidginFriendExpectedAnswer.png')
- end
-end
-
-When /^I start an OTR session with my friend$/ do
- $vm.focus_window(@friend_name)
- @screen.click("PidginConversationOTRMenu.png")
- @screen.hide_cursor
- @screen.click("PidginOTRMenuStartSession.png")
-end
-
-Then /^Pidgin automatically generates an OTR key$/ do
- @screen.wait("PidginOTRKeyGenPrompt.png", 30)
- @screen.wait_and_click("PidginOTRKeyGenPromptDoneButton.png", 30)
-end
-
-Then /^an OTR session was successfully started with my friend$/ do
- $vm.focus_window(@friend_name)
- @screen.wait("PidginConversationOTRUnverifiedSessionStarted.png", 10)
-end
-
-# The reason the chat must be empty is to guarantee that we don't mix
-# up messages/events from other users with the ones we expect from the
-# bot.
-When /^I join some empty multi-user chat$/ do
- $vm.focus_window('Buddy List')
- @screen.click("PidginBuddiesMenu.png")
- @screen.wait_and_click("PidginBuddiesMenuJoinChat.png", 10)
- @screen.wait_and_click("PidginJoinChatWindow.png", 10)
- @screen.click_mid_right_edge("PidginJoinChatRoomLabel.png")
- account = xmpp_account("Tails_account")
- if account.has_key?("chat_room") && \
- !account["chat_room"].nil? && \
- !account["chat_room"].empty?
- chat_room = account["chat_room"]
- else
- chat_room = random_alnum_string(10, 15)
- end
- @screen.type(chat_room)
-
- # We will need the conference server later, when starting the bot.
- @screen.click_mid_right_edge("PidginJoinChatServerLabel.png")
- @screen.type("a", Sikuli::KeyModifier.CTRL)
- @screen.type("c", Sikuli::KeyModifier.CTRL)
- conference_server =
- $vm.execute_successfully("xclip -o", :user => LIVE_USER).stdout.chomp
- @chat_room_jid = chat_room + "@" + conference_server
-
- @screen.click("PidginJoinChatButton.png")
- # The following will both make sure that the we joined the chat, and
- # that it is empty. We'll also deal with the *potential* "Create New
- # Room" prompt that Pidgin shows for some server configurations.
- images = ["PidginCreateNewRoomPrompt.png",
- "PidginChat1UserInRoom.png"]
- image_found, _ = @screen.waitAny(images, 30)
- if image_found == "PidginCreateNewRoomPrompt.png"
- @screen.click("PidginCreateNewRoomAcceptDefaultsButton.png")
- end
- $vm.focus_window(@chat_room_jid)
- @screen.wait("PidginChat1UserInRoom.png", 10)
-end
-
-# Since some servers save the scrollback, and sends it when joining,
-# it's safer to clear it so we do not get false positives from old
-# messages when looking for a particular response, or similar.
-When /^I clear the multi-user chat's scrollback$/ do
- $vm.focus_window(@chat_room_jid)
- @screen.click("PidginConversationMenu.png")
- @screen.wait_and_click("PidginConversationMenuClearScrollback.png", 10)
-end
-
-Then /^I can see that my friend joined the multi-user chat$/ do
- $vm.focus_window(@chat_room_jid)
- @screen.wait("PidginChat2UsersInRoom.png", 60)
-end
-
-def configured_pidgin_accounts
- accounts = Hash.new
- xml = REXML::Document.new(
- $vm.file_content("/home/#{LIVE_USER}/.purple/accounts.xml")
- )
- xml.elements.each("account/account") do |e|
- account = e.elements["name"].text
- account_name, network = account.split("@")
- protocol = e.elements["protocol"].text
- port = e.elements["settings/setting[@name='port']"].text
- username_element = e.elements["settings/setting[@name='username']"]
- realname_elemenet = e.elements["settings/setting[@name='realname']"]
- if username_element
- nickname = username_element.text
- else
- nickname = nil
- end
- if realname_elemenet
- real_name = realname_elemenet.text
- else
- real_name = nil
- end
- accounts[network] = {
- 'name' => account_name,
- 'network' => network,
- 'protocol' => protocol,
- 'port' => port,
- 'nickname' => nickname,
- 'real_name' => real_name,
- }
- end
-
- return accounts
-end
-
-def chan_image (account, channel, image)
- images = {
- 'conference.riseup.net' => {
- 'tails' => {
- 'conversation_tab' => 'PidginTailsConversationTab',
- 'welcome' => 'PidginTailsChannelWelcome',
- }
- },
- }
- return images[account][channel][image] + ".png"
-end
-
-def default_chan (account)
- chans = {
- 'conference.riseup.net' => 'tails',
- }
- return chans[account]
-end
-
-def pidgin_otr_keys
- return $vm.file_content("/home/#{LIVE_USER}/.purple/otr.private_key")
-end
-
-Given /^Pidgin has the expected accounts configured with random nicknames$/ do
- expected = [
- ["irc.oftc.net", "prpl-irc", "6697"],
- ["127.0.0.1", "prpl-irc", "6668"],
- ]
- configured_pidgin_accounts.values.each() do |account|
- assert(account['nickname'] != "XXX_NICK_XXX", "Nickname was no randomised")
- assert_equal(account['nickname'], account['real_name'],
- "Nickname and real name are not identical: " +
- account['nickname'] + " vs. " + account['real_name'])
- assert_equal(account['name'], account['nickname'],
- "Account name and nickname are not identical: " +
- account['name'] + " vs. " + account['nickname'])
- candidate = [account['network'], account['protocol'], account['port']]
- assert(expected.include?(candidate), "Unexpected account: #{candidate}")
- expected.delete(candidate)
- end
- assert(expected.empty?, "These Pidgin accounts are not configured: " +
- "#{expected}")
-end
-
-When /^I open Pidgin's account manager window$/ do
- @screen.wait_and_click('PidginMenuAccounts.png', 20)
- @screen.wait_and_click('PidginMenuManageAccounts.png', 20)
- step "I see Pidgin's account manager window"
-end
-
-When /^I see Pidgin's account manager window$/ do
- @screen.wait("PidginAccountWindow.png", 40)
-end
-
-When /^I close Pidgin's account manager window$/ do
- @screen.wait_and_click("PidginDialogCloseButton.png", 10)
-end
-
-When /^I close Pidgin$/ do
- $vm.focus_window('Buddy List')
- @screen.type("q", Sikuli::KeyModifier.CTRL)
- @screen.waitVanish('PidginAvailableStatus.png', 10)
-end
-
-When /^I (de)?activate the "([^"]+)" Pidgin account$/ do |deactivate, account|
- @screen.click("PidginAccount_#{account}.png")
- @screen.type(Sikuli::Key.LEFT + Sikuli::Key.SPACE)
- if deactivate
- @screen.waitVanish('PidginAccountEnabledCheckbox.png', 5)
- else
- # wait for the Pidgin to be connecting, otherwise sometimes the step
- # that closes the account management dialog happens before the account
- # is actually enabled
- @screen.waitAny(['PidginConnecting.png', 'PidginAvailableStatus.png'], 5)
- end
-end
-
-def deactivate_and_activate_pidgin_account(account)
- debug_log("Deactivating and reactivating Pidgin account #{account}")
- step "I open Pidgin's account manager window"
- step "I deactivate the \"#{account}\" Pidgin account"
- step "I close Pidgin's account manager window"
- step "I open Pidgin's account manager window"
- step "I activate the \"#{account}\" Pidgin account"
- step "I close Pidgin's account manager window"
-end
-
-
-
-Then /^Pidgin successfully connects to the "([^"]+)" account$/ do |account|
- expected_channel_entry = chan_image(account, default_chan(account), 'roster')
- reconnect_button = 'PidginReconnect.png'
- recovery_on_failure = Proc.new do
- if @screen.exists('PidginReconnect.png')
- @screen.click('PidginReconnect.png')
- else
- deactivate_and_activate_pidgin_account(account)
- end
- end
- retry_tor(recovery_on_failure) do
- begin
- $vm.focus_window('Buddy List')
- rescue ExecutionFailedInVM
- # Sometimes focusing the window with xdotool will fail with the
- # conversation window right on top of it. We'll try to close the
- # conversation window. At worst, the test will still fail...
- close_pidgin_conversation_window(account)
- end
- on_screen, _ = @screen.waitAny([expected_channel_entry, reconnect_button], 60)
- unless on_screen == expected_channel_entry
- raise "Connecting to account #{account} failed."
- end
- end
-end
-
-Then /^the "([^"]*)" account only responds to PING and VERSION CTCP requests$/ do |irc_server|
- ctcp_cmds = [
- "CLIENTINFO", "DATE", "ERRMSG", "FINGER", "PING", "SOURCE", "TIME",
- "USERINFO", "VERSION"
- ]
- expected_ctcp_replies = {
- "PING" => /^\d+$/,
- "VERSION" => /^Purple IRC$/
- }
- spam_target = configured_pidgin_accounts[irc_server]["nickname"]
- ctcp_check = CtcpChecker.new(irc_server, 6667, spam_target, ctcp_cmds,
- expected_ctcp_replies)
- ctcp_check.verify_ctcp_responses
-end
-
-Then /^I can join the( pre-configured)? "([^"]+)" channel on "([^"]+)"$/ do |preconfigured, channel, account|
- if preconfigured
- @screen.doubleClick(chan_image(account, channel, 'roster'))
- focus_pidgin_irc_conversation_window(account)
- else
- $vm.focus_window('Buddy List')
- @screen.wait_and_click("PidginBuddiesMenu.png", 20)
- @screen.wait_and_click("PidginBuddiesMenuJoinChat.png", 10)
- @screen.wait_and_click("PidginJoinChatWindow.png", 10)
- @screen.click_mid_right_edge("PidginJoinChatRoomLabel.png")
- @screen.type(channel)
- @screen.click("PidginJoinChatButton.png")
- @chat_room_jid = channel + "@" + account
- $vm.focus_window(@chat_room_jid)
- end
- @screen.hide_cursor
- try_for(60) do
- begin
- @screen.wait_and_click(chan_image(account, channel, 'conversation_tab'), 5)
- rescue FindFailed => e
- # If the channel tab can't be found it could be because there were
- # multiple connection attempts and the channel tab we want is off the
- # screen. We'll try closing tabs until the one we want can be found.
- @screen.type("w", Sikuli::KeyModifier.CTRL)
- raise e
- end
- end
- @screen.hide_cursor
- @screen.wait( chan_image(account, channel, 'welcome'), 10)
-end
-
-Then /^I take note of the configured Pidgin accounts$/ do
- @persistent_pidgin_accounts = configured_pidgin_accounts
-end
-
-Then /^I take note of the OTR key for Pidgin's "([^"]+)" account$/ do |account_name|
- @persistent_pidgin_otr_keys = pidgin_otr_keys
-end
-
-Then /^Pidgin has the expected persistent accounts configured$/ do
- current_accounts = configured_pidgin_accounts
- assert(current_accounts <=> @persistent_pidgin_accounts,
- "Currently configured Pidgin accounts do not match the persistent ones:\n" +
- "Current:\n#{current_accounts}\n" +
- "Persistent:\n#{@persistent_pidgin_accounts}"
- )
-end
-
-Then /^Pidgin has the expected persistent OTR keys$/ do
- assert_equal(pidgin_otr_keys, @persistent_pidgin_otr_keys)
-end
-
-def pidgin_add_certificate_from (cert_file)
- # Here, we need a certificate that is not already in the NSS database
- step "I copy \"/usr/share/ca-certificates/mozilla/CNNIC_ROOT.crt\" to \"#{cert_file}\" as user \"amnesia\""
-
- $vm.focus_window('Buddy List')
- @screen.wait_and_click('PidginToolsMenu.png', 10)
- @screen.wait_and_click('PidginCertificatesMenuItem.png', 10)
- @screen.wait('PidginCertificateManagerDialog.png', 10)
- @screen.wait_and_click('PidginCertificateAddButton.png', 10)
- begin
- @screen.wait_and_click('GtkFileChooserDesktopButton.png', 10)
- rescue FindFailed
- # The first time we're run, the file chooser opens in the Recent
- # view, so we have to browse a directory before we can use the
- # "Type file name" button. But on subsequent runs, the file
- # chooser is already in the Desktop directory, so we don't need to
- # do anything. Hence, this noop exception handler.
- end
- @screen.wait_and_click('GtkFileTypeFileNameButton.png', 10)
- @screen.type("l", Sikuli::KeyModifier.ALT) # "Location" field
- @screen.type(cert_file + Sikuli::Key.ENTER)
-end
-
-Then /^I can add a certificate from the "([^"]+)" directory to Pidgin$/ do |cert_dir|
- pidgin_add_certificate_from("#{cert_dir}/test.crt")
- wait_and_focus('PidginCertificateAddHostnameDialog.png', 10, 'Certificate Import')
- @screen.type("XXX test XXX" + Sikuli::Key.ENTER)
- wait_and_focus('PidginCertificateTestItem.png', 10, 'Certificate Manager')
-end
-
-Then /^I cannot add a certificate from the "([^"]+)" directory to Pidgin$/ do |cert_dir|
- pidgin_add_certificate_from("#{cert_dir}/test.crt")
- wait_and_focus('PidginCertificateImportFailed.png', 10, 'Import Error')
-end
-
-When /^I close Pidgin's certificate manager$/ do
- wait_and_focus('PidginCertificateManagerDialog.png', 10, 'Certificate Manager')
- @screen.type(Sikuli::Key.ESC)
- # @screen.wait_and_click('PidginCertificateManagerClose.png', 10)
- @screen.waitVanish('PidginCertificateManagerDialog.png', 10)
-end
-
-When /^I close Pidgin's certificate import failure dialog$/ do
- @screen.type(Sikuli::Key.ESC)
- # @screen.wait_and_click('PidginCertificateManagerClose.png', 10)
- @screen.waitVanish('PidginCertificateImportFailed.png', 10)
-end
-
-When /^I see the Tails roadmap URL$/ do
- try_for(60) do
- if @screen.exists('PidginServerMessage.png')
- @screen.click('PidginDialogCloseButton.png')
- end
- begin
- @screen.find('PidginTailsRoadmapUrl.png')
- rescue FindFailed => e
- @screen.type(Sikuli::Key.PAGE_UP)
- raise e
- end
- end
-end
-
-When /^I click on the Tails roadmap URL$/ do
- @screen.click('PidginTailsRoadmapUrl.png')
- try_for(60) { @torbrowser = Dogtail::Application.new('Firefox') }
-end
diff --git a/cucumber/features/step_definitions/po.rb b/cucumber/features/step_definitions/po.rb
deleted file mode 100644
index c73bacef..00000000
--- a/cucumber/features/step_definitions/po.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-Given /^I am in the Git branch being tested$/ do
- Dir.chdir(GIT_DIR)
-end
-
-Then /^all the PO files should be correct$/ do
- File.exists?('./submodules/jenkins-tools/slaves/check_po')
- cmd_helper(['./submodules/jenkins-tools/slaves/check_po'])
-end
diff --git a/cucumber/features/step_definitions/root_access_control.rb b/cucumber/features/step_definitions/root_access_control.rb
deleted file mode 100644
index 8362342d..00000000
--- a/cucumber/features/step_definitions/root_access_control.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-Then /^I should be able to run administration commands as the live user$/ do
- stdout = $vm.execute("echo #{@sudo_password} | sudo -S whoami",
- :user => LIVE_USER).stdout
- actual_user = stdout.sub(/^\[sudo\] password for #{LIVE_USER}: /, "").chomp
- assert_equal("root", actual_user, "Could not use sudo")
-end
-
-Then /^I should not be able to run administration commands as the live user with the "([^"]*)" password$/ do |password|
- stderr = $vm.execute("echo #{password} | sudo -S whoami",
- :user => LIVE_USER).stderr
- sudo_failed = stderr.include?("The administration password is disabled") || stderr.include?("is not allowed to execute")
- assert(sudo_failed, "The administration password is not disabled:" + stderr)
-end
-
-When /^running a command as root with pkexec requires PolicyKit administrator privileges$/ do
- action = 'org.freedesktop.policykit.exec'
- action_details = $vm.execute("pkaction --verbose --action-id #{action}").stdout
- assert(action_details[/\s+implicit any:\s+auth_admin$/],
- "Expected 'auth_admin' for 'any':\n#{action_details}")
- assert(action_details[/\s+implicit inactive:\s+auth_admin$/],
- "Expected 'auth_admin' for 'inactive':\n#{action_details}")
- assert(action_details[/\s+implicit active:\s+auth_admin$/],
- "Expected 'auth_admin' for 'active':\n#{action_details}")
-end
-
-Then /^I should be able to run a command as root with pkexec$/ do
- step "I run \"pkexec touch /root/pkexec-test\" in GNOME Terminal"
- step 'I enter the sudo password in the pkexec prompt'
- try_for(10, :msg => 'The /root/pkexec-test file was not created.') {
- $vm.execute('ls /root/pkexec-test').success?
- }
-end
-
-Then /^I should not be able to run a command as root with pkexec and the standard passwords$/ do
- step "I run \"pkexec touch /root/pkexec-test\" in GNOME Terminal"
- ['', 'live', 'amnesia'].each do |password|
- deal_with_polkit_prompt(password, expect_success: false)
- end
- @screen.type(Sikuli::Key.ESC)
- @screen.wait('PolicyKitAuthCompleteFailure.png', 20)
-end
diff --git a/cucumber/features/step_definitions/snapshots.rb b/cucumber/features/step_definitions/snapshots.rb
deleted file mode 100644
index 16e59a4b..00000000
--- a/cucumber/features/step_definitions/snapshots.rb
+++ /dev/null
@@ -1,119 +0,0 @@
-def checkpoints
- cp = Hash.new
- cp['disk-for-d-i'] = {
- :temporary => true,
- :description => "a disk is created for Debian Installer tests",
- :parent_checkpoint => nil,
- :steps => [
- 'I create a 8 GiB disk named "'+JOB_NAME+'"',
- 'I plug sata drive "'+JOB_NAME+'"',
- ]
- }
-
- cp['disk-for-deb-edu'] = {
- :temporary => true,
- :description => "a disk is created for Debian Edu tests",
- :parent_checkpoint => nil,
- :steps => [
- 'I create a 64 GiB disk named "'+JOB_NAME+'"',
- 'I plug sata drive "'+JOB_NAME+'"',
- ]
- }
-
- ['text', 'gui'].each do |m|
- cp["boot-d-i-#{m}-to-tasksel"] = {
- :temporary => true,
- :description => "I have started Debian Installer in #{m} mode and stopped at the Tasksel prompt",
- :parent_checkpoint => 'disk-for-d-i',
- :steps => [
- "I intend to use #{m} mode",
- 'I start the computer',
- 'I select the install mode',
- 'I select British English',
- 'I accept the hostname, using "example.com" as the domain',
- 'I set the root password to "rootme"',
- 'I set the password for "Philip Hands" to be "verysecret"',
- 'I select full-disk, single-filesystem partitioning',
- 'I note that the Base system is being installed',
- 'I accept the default mirror',
- 'I ignore Popcon',
- 'we reach the Tasksel prompt',
- ]
- }
-
- ['minimal', 'non-GUI', 'Gnome', 'XFCE', 'LXDE', 'KDE'].each do |de|
- cp["debian-#{m}-#{de}-install"] = {
- :temporary => true,
- :description => "I install a #{de} Debian system, in #{m} mode",
- :parent_checkpoint => "boot-d-i-#{m}-to-tasksel",
- :steps => [
- "I intend to use #{m} mode",
- "I select the #{de} task",
- 'I wait while the bulk of the packages are installed',
- 'I install GRUB',
- 'I allow reboot after the install is complete',
- 'I wait for the reboot',
- 'I power off the computer',
- 'the computer is set to boot from sata drive',
- ]
- }
- end
- end
-
- return cp
-end
-
-def reach_checkpoint(name)
- scenario_indent = " "*4
- step_indent = " "*6
-
- step "a computer"
- if VM.snapshot_exists?(name)
- $vm.restore_snapshot(name)
- post_snapshot_restore_hook
- else
- checkpoint = checkpoints[name]
- checkpoint_description = checkpoint[:description]
- parent_checkpoint = checkpoint[:parent_checkpoint]
- steps = checkpoint[:steps]
- if parent_checkpoint
- if VM.snapshot_exists?(parent_checkpoint)
- $vm.restore_snapshot(parent_checkpoint)
- else
- reach_checkpoint(parent_checkpoint)
- end
- post_snapshot_restore_hook
- end
- debug_log(scenario_indent + "Checkpoint: #{checkpoint_description}",
- color: :white, timestamp: false)
- step_action = "Given"
- if parent_checkpoint
- parent_description = checkpoints[parent_checkpoint][:description]
- debug_log(step_indent + "#{step_action} #{parent_description}",
- color: :green, timestamp: false)
- step_action = "And"
- end
- steps.each do |s|
- begin
- step(s)
- rescue Exception => e
- debug_log(scenario_indent +
- "Step failed while creating checkpoint: #{s}",
- color: :red, timestamp: false)
- raise e
- end
- debug_log(step_indent + "#{step_action} #{s}",
- color: :green, timestamp: false)
- step_action = "And"
- end
- $vm.save_snapshot(name)
- end
-end
-
-# For each checkpoint we generate a step to reach it.
-checkpoints.each do |name, desc|
- step_regex = Regexp.new("^#{Regexp.escape(desc[:description])}$")
- Given step_regex do
- reach_checkpoint(name)
- end
-end
diff --git a/cucumber/features/step_definitions/ssh.rb b/cucumber/features/step_definitions/ssh.rb
deleted file mode 100644
index 1fd0efaf..00000000
--- a/cucumber/features/step_definitions/ssh.rb
+++ /dev/null
@@ -1,156 +0,0 @@
-require 'socket'
-
-def assert_not_ipaddr(s)
- err_msg = "'#{s}' looks like a LAN IP address."
- assert_raise(IPAddr::InvalidAddressError, err_msg) do
- IPAddr.new(s)
- end
-end
-
-def read_and_validate_ssh_config srv_type
- conf = $config[srv_type]
- begin
- required_settings = ["private_key", "public_key", "username", "hostname"]
- required_settings.each do |key|
- assert(conf.has_key?(key))
- assert_not_nil(conf[key])
- assert(!conf[key].empty?)
- end
- rescue NoMethodError
- raise(
- <<EOF
-Your #{srv_type} config is incorrect or missing from your local configuration file (#{LOCAL_CONFIG_FILE}). See wiki/src/contribute/release_process/test/usage.mdwn for the format.
-EOF
- )
- end
-
- case srv_type
- when 'SSH'
- @ssh_host = conf["hostname"]
- @ssh_port = conf["port"].to_i if conf["port"]
- @ssh_username = conf["username"]
- assert_not_ipaddr(@ssh_host)
- when 'SFTP'
- @sftp_host = conf["hostname"]
- @sftp_port = conf["port"].to_i if conf["port"]
- @sftp_username = conf["username"]
- assert_not_ipaddr(@sftp_host)
- end
-end
-
-Given /^I have the SSH key pair for an? (Git|SSH|SFTP) (?:repository|server)( on the LAN)?$/ do |server_type, lan|
- $vm.execute_successfully("install -m 0700 -d '/home/#{LIVE_USER}/.ssh/'",
- :user => LIVE_USER)
- unless server_type == 'Git' || lan
- read_and_validate_ssh_config server_type
- secret_key = $config[server_type]["private_key"]
- public_key = $config[server_type]["public_key"]
- else
- secret_key = $config["Unsafe_SSH_private_key"]
- public_key = $config["Unsafe_SSH_public_key"]
- end
-
- $vm.execute_successfully("echo '#{secret_key}' > '/home/#{LIVE_USER}/.ssh/id_rsa'",
- :user => LIVE_USER)
- $vm.execute_successfully("echo '#{public_key}' > '/home/#{LIVE_USER}/.ssh/id_rsa.pub'",
- :user => LIVE_USER)
- $vm.execute_successfully("chmod 0600 '/home/#{LIVE_USER}/.ssh/'id*",
- :user => LIVE_USER)
-end
-
-Given /^I (?:am prompted to )?verify the SSH fingerprint for the (?:Git|SSH) (?:repository|server)$/ do
- @screen.wait("SSHFingerprint.png", 60)
- sleep 1 # brief pause to ensure that the following keystrokes do not get lost
- @screen.type('yes' + Sikuli::Key.ENTER)
-end
-
-def get_free_tcp_port
- server = TCPServer.new('127.0.0.1', 0)
- return server.addr[1]
-ensure
- server.close
-end
-
-Given /^an SSH server is running on the LAN$/ do
- @sshd_server_port = get_free_tcp_port
- @sshd_server_host = $vmnet.bridge_ip_addr
- sshd = SSHServer.new(@sshd_server_host, @sshd_server_port)
- sshd.start
- add_lan_host(@sshd_server_host, @sshd_server_port)
- add_after_scenario_hook { sshd.stop }
-end
-
-When /^I connect to an SSH server on the (Internet|LAN)$/ do |location|
-
- case location
- when 'Internet'
- read_and_validate_ssh_config "SSH"
- when 'LAN'
- @ssh_port = @sshd_server_port
- @ssh_username = 'user'
- @ssh_host = @sshd_server_host
- end
-
- ssh_port_suffix = "-p #{@ssh_port}" if @ssh_port
-
- cmd = "ssh #{@ssh_username}@#{@ssh_host} #{ssh_port_suffix}"
-
- step 'process "ssh" is not running'
-
- recovery_proc = Proc.new do
- step 'I kill the process "ssh"' if $vm.has_process?("ssh")
- step 'I run "clear" in GNOME Terminal'
- end
-
- retry_tor(recovery_proc) do
- step "I run \"#{cmd}\" in GNOME Terminal"
- step 'process "ssh" is running within 10 seconds'
- step 'I verify the SSH fingerprint for the SSH server'
- end
-end
-
-Then /^I have sucessfully logged into the SSH server$/ do
- @screen.wait('SSHLoggedInPrompt.png', 60)
-end
-
-Then /^I connect to an SFTP server on the Internet$/ do
- read_and_validate_ssh_config "SFTP"
-
- @sftp_port ||= 22
- @sftp_port = @sftp_port.to_s
-
- recovery_proc = Proc.new do
- step 'I kill the process "ssh"'
- step 'I kill the process "nautilus"'
- end
-
- retry_tor(recovery_proc) do
- step 'I start "Nautilus" via GNOME Activities Overview'
- nautilus = Dogtail::Application.new('nautilus')
- nautilus.child(roleName: 'frame')
- nautilus.child('Other Locations', roleName: 'label').click
- connect_bar = nautilus.child('Connect to Server', roleName: 'label').parent
- connect_bar
- .child(roleName: 'filler', recursive: false)
- .child(roleName: 'text', recursive: false)
- .text = "sftp://" + @sftp_username + "@" + @sftp_host + ":" + @sftp_port
- connect_bar.button('Connect', recursive: false).click
- step "I verify the SSH fingerprint for the SFTP server"
- end
-end
-
-Then /^I verify the SSH fingerprint for the SFTP server$/ do
- try_for(30) do
- Dogtail::Application.new('gnome-shell').child?('Log In Anyway')
- end
- # Here we'd like to click on the button using Dogtail, but something
- # is buggy so let's just use the keyboard.
- @screen.type(Sikuli::Key.ENTER)
-end
-
-Then /^I successfully connect to the SFTP server$/ do
- try_for(60) do
- Dogtail::Application.new('nautilus')
- .child?("#{@sftp_username} on #{@sftp_host}")
- end
-end
diff --git a/cucumber/features/step_definitions/time_syncing.rb b/cucumber/features/step_definitions/time_syncing.rb
deleted file mode 100644
index d1b81073..00000000
--- a/cucumber/features/step_definitions/time_syncing.rb
+++ /dev/null
@@ -1,86 +0,0 @@
-# In some steps below we allow some slack when verifying that the date
-# was set appropriately because it may take time to send the `date`
-# command over the remote shell and get the answer back, parsing and
-# post-processing of the result, etc.
-def max_time_drift
- 10
-end
-
-When /^I set the system time to "([^"]+)"$/ do |time|
- $vm.execute_successfully("date -s '#{time}'")
- new_time = DateTime.parse($vm.execute_successfully("date").stdout).to_time
- expected_time_lower_bound = DateTime.parse(time).to_time
- expected_time_upper_bound = expected_time_lower_bound + max_time_drift
- assert(expected_time_lower_bound <= new_time &&
- new_time <= expected_time_upper_bound,
- "The guest's time was supposed to be set to " \
- "'#{expected_time_lower_bound}' but is '#{new_time}'")
-end
-
-When /^I bump the (hardware clock's|system) time with "([^"]+)"$/ do |clock_type, timediff|
- case clock_type
- when "hardware clock's"
- old_time = DateTime.parse($vm.execute_successfully("hwclock -r").stdout).to_time
- $vm.execute_successfully("hwclock --set --date 'now #{timediff}'")
- new_time = DateTime.parse($vm.execute_successfully("hwclock -r").stdout).to_time
- when 'system'
- old_time = DateTime.parse($vm.execute_successfully("date").stdout).to_time
- $vm.execute_successfully("date -s 'now #{timediff}'")
- new_time = DateTime.parse($vm.execute_successfully("date").stdout).to_time
- end
- expected_time_lower_bound = DateTime.parse(
- cmd_helper(["date", "-d", "#{old_time} #{timediff}"])).to_time
- expected_time_upper_bound = expected_time_lower_bound + max_time_drift
- assert(expected_time_lower_bound <= new_time &&
- new_time <= expected_time_upper_bound,
- "The #{clock_type} time was supposed to be bumped to " \
- "'#{expected_time_lower_bound}' but is '#{new_time}'")
-end
-
-Then /^Tails clock is less than (\d+) minutes incorrect$/ do |max_diff_mins|
- guest_time_str = $vm.execute("date --rfc-2822").stdout.chomp
- guest_time = Time.rfc2822(guest_time_str)
- host_time = Time.now
- diff = (host_time - guest_time).abs
- assert(diff < max_diff_mins.to_i*60,
- "The guest's clock is off by #{diff} seconds (#{guest_time})")
- puts "Time was #{diff} seconds off"
-end
-
-Then /^the system clock is just past Tails' source date$/ do
- system_time_str = $vm.execute_successfully('date').to_s
- system_time = DateTime.parse(system_time_str).to_time
- source_time_cmd = 'sed -n -e "1s/^.* - \([0-9]\+\)$/\1/p;q" ' +
- '/etc/amnesia/version'
- source_time_str = $vm.execute_successfully(source_time_cmd).to_s
- source_time = DateTime.parse(source_time_str).to_time
- diff = system_time - source_time # => in seconds
- # Half an hour should be enough to boot Tails on any reasonable
- # hardware and VM setup.
- max_diff = 30*60
- assert(diff > 0,
- "The system time (#{system_time}) is before the Tails " +
- "source date (#{source_time})")
- assert(diff <= max_diff,
- "The system time (#{system_time}) is more than #{max_diff} seconds " +
- "past the source date (#{source_time})")
-end
-
-Then /^Tails' hardware clock is close to the host system's time$/ do
- host_time = Time.now
- hwclock_time_str = $vm.execute('hwclock -r').stdout.chomp
- hwclock_time = DateTime.parse(hwclock_time_str).to_time
- diff = (hwclock_time - host_time).abs
- assert(diff <= max_time_drift)
-end
-
-Then /^the hardware clock is still off by "([^"]+)"$/ do |timediff|
- hwclock = DateTime.parse($vm.execute_successfully("hwclock -r").stdout.chomp).to_time
- expected_time_lower_bound = DateTime.parse(
- cmd_helper(["date", "-d", "now #{timediff}"])).to_time - max_time_drift
- expected_time_upper_bound = expected_time_lower_bound + max_time_drift
- assert(expected_time_lower_bound <= hwclock &&
- hwclock <= expected_time_upper_bound,
- "The host's hwclock should be approximately " \
- "'#{expected_time_lower_bound}' but is actually '#{hwclock}'")
-end
diff --git a/cucumber/features/step_definitions/tor.rb b/cucumber/features/step_definitions/tor.rb
deleted file mode 100644
index 04852f76..00000000
--- a/cucumber/features/step_definitions/tor.rb
+++ /dev/null
@@ -1,406 +0,0 @@
-def iptables_chains_parse(iptables, table = "filter", &block)
- assert(block_given?)
- cmd = "#{iptables}-save -c -t #{table} | iptables-xml"
- xml_str = $vm.execute_successfully(cmd).stdout
- rexml = REXML::Document.new(xml_str)
- rexml.get_elements('iptables-rules/table/chain').each do |element|
- yield(
- element.attribute('name').to_s,
- element.attribute('policy').to_s,
- element.get_elements('rule')
- )
- end
-end
-
-def ip4tables_chains(table = "filter", &block)
- iptables_chains_parse('iptables', table, &block)
-end
-
-def ip6tables_chains(table = "filter", &block)
- iptables_chains_parse('ip6tables', table, &block)
-end
-
-def iptables_rules_parse(iptables, chain, table)
- iptables_chains_parse(iptables, table) do |name, _, rules|
- return rules if name == chain
- end
- return nil
-end
-
-def iptables_rules(chain, table = "filter")
- iptables_rules_parse("iptables", chain, table)
-end
-
-def ip6tables_rules(chain, table = "filter")
- iptables_rules_parse("ip6tables", chain, table)
-end
-
-def ip4tables_packet_counter_sum(filters = {})
- pkts = 0
- ip4tables_chains do |name, _, rules|
- next if filters[:tables] && not(filters[:tables].include?(name))
- rules.each do |rule|
- next if filters[:uid] && not(rule.elements["conditions/owner/uid-owner[text()=#{filters[:uid]}]"])
- pkts += rule.attribute('packet-count').to_s.to_i
- end
- end
- return pkts
-end
-
-def try_xml_element_text(element, xpath, default = nil)
- node = element.elements[xpath]
- (node.nil? or not(node.has_text?)) ? default : node.text
-end
-
-Then /^the firewall's policy is to (.+) all IPv4 traffic$/ do |expected_policy|
- expected_policy.upcase!
- ip4tables_chains do |name, policy, _|
- if ["INPUT", "FORWARD", "OUTPUT"].include?(name)
- assert_equal(expected_policy, policy,
- "Chain #{name} has unexpected policy #{policy}")
- end
- end
-end
-
-Then /^the firewall is configured to only allow the (.+) users? to connect directly to the Internet over IPv4$/ do |users_str|
- users = users_str.split(/, | and /)
- expected_uids = Set.new
- users.each do |user|
- expected_uids << $vm.execute_successfully("id -u #{user}").stdout.to_i
- end
- allowed_output = iptables_rules("OUTPUT").find_all do |rule|
- out_iface = rule.elements['conditions/match/o']
- is_maybe_accepted = rule.get_elements('actions/*').find do |action|
- not(["DROP", "REJECT", "LOG"].include?(action.name))
- end
- is_maybe_accepted &&
- (
- # nil => match all interfaces according to iptables-xml
- out_iface.nil? ||
- ((out_iface.text == 'lo') == (out_iface.attribute('invert').to_s == '1'))
- )
- end
- uids = Set.new
- allowed_output.each do |rule|
- rule.elements.each('actions/*') do |action|
- destination = try_xml_element_text(rule, "conditions/match/d")
- if action.name == "ACCEPT"
- # nil == 0.0.0.0/0 according to iptables-xml
- assert(destination == '0.0.0.0/0' || destination.nil?,
- "The following rule has an unexpected destination:\n" +
- rule.to_s)
- state_cond = try_xml_element_text(rule, "conditions/state/state")
- next if state_cond == "ESTABLISHED"
- assert_not_nil(rule.elements['conditions/owner/uid-owner'])
- rule.elements.each('conditions/owner/uid-owner') do |owner|
- uid = owner.text.to_i
- uids << uid
- assert(expected_uids.include?(uid),
- "The following rule allows uid #{uid} to access the " +
- "network, but we only expect uids #{expected_uids.to_a} " +
- "(#{users_str}) to have such access:\n#{rule.to_s}")
- end
- elsif action.name == "call" && action.elements[1].name == "lan"
- lan_subnets = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
- assert(lan_subnets.include?(destination),
- "The following lan-targeted rule's destination is " +
- "#{destination} which may not be a private subnet:\n" +
- rule.to_s)
- else
- raise "Unexpected iptables OUTPUT chain rule:\n#{rule.to_s}"
- end
- end
- end
- uids_not_found = expected_uids - uids
- assert(uids_not_found.empty?,
- "Couldn't find rules allowing uids #{uids_not_found.to_a.to_s} " \
- "access to the network")
-end
-
-Then /^the firewall's NAT rules only redirect traffic for Tor's TransPort and DNSPort$/ do
- loopback_address = "127.0.0.1/32"
- tor_onion_addr_space = "127.192.0.0/10"
- tor_trans_port = "9040"
- dns_port = "53"
- tor_dns_port = "5353"
- ip4tables_chains('nat') do |name, _, rules|
- if name == "OUTPUT"
- good_rules = rules.find_all do |rule|
- redirect = rule.get_elements('actions/*').all? do |action|
- action.name == "REDIRECT"
- end
- destination = try_xml_element_text(rule, "conditions/match/d")
- redir_port = try_xml_element_text(rule, "actions/REDIRECT/to-ports")
- redirected_to_trans_port = redir_port == tor_trans_port
- udp_destination_port = try_xml_element_text(rule, "conditions/udp/dport")
- dns_redirected_to_tor_dns_port = (udp_destination_port == dns_port) &&
- (redir_port == tor_dns_port)
- redirect &&
- (
- (destination == tor_onion_addr_space && redirected_to_trans_port) ||
- (destination == loopback_address && dns_redirected_to_tor_dns_port)
- )
- end
- bad_rules = rules - good_rules
- assert(bad_rules.empty?,
- "The NAT table's OUTPUT chain contains some unexpected " +
- "rules:\n#{bad_rules}")
- else
- assert(rules.empty?,
- "The NAT table contains unexpected rules for the #{name} " +
- "chain:\n#{rules}")
- end
- end
-end
-
-Then /^the firewall is configured to block all external IPv6 traffic$/ do
- ip6_loopback = '::1/128'
- expected_policy = "DROP"
- ip6tables_chains do |name, policy, rules|
- assert_equal(expected_policy, policy,
- "The IPv6 #{name} chain has policy #{policy} but we " \
- "expected #{expected_policy}")
- good_rules = rules.find_all do |rule|
- ["DROP", "REJECT", "LOG"].any? do |target|
- rule.elements["actions/#{target}"]
- end \
- ||
- ["s", "d"].all? do |x|
- try_xml_element_text(rule, "conditions/match/#{x}") == ip6_loopback
- end
- end
- bad_rules = rules - good_rules
- assert(bad_rules.empty?,
- "The IPv6 table's #{name} chain contains some unexpected rules:\n" +
- (bad_rules.map { |r| r.to_s }).join("\n"))
- end
-end
-
-def firewall_has_dropped_packet_to?(proto, host, port)
- regex = "^Dropped outbound packet: .* "
- regex += "DST=#{Regexp.escape(host)} .* "
- regex += "PROTO=#{Regexp.escape(proto)} "
- regex += ".* DPT=#{port} " if port
- $vm.execute("journalctl --dmesg --output=cat | grep -qP '#{regex}'").success?
-end
-
-When /^I open an untorified (TCP|UDP|ICMP) connection to (\S*)(?: on port (\d+))?$/ do |proto, host, port|
- assert(!firewall_has_dropped_packet_to?(proto, host, port),
- "A #{proto} packet to #{host}" +
- (port.nil? ? "" : ":#{port}") +
- " has already been dropped by the firewall")
- @conn_proto = proto
- @conn_host = host
- @conn_port = port
- case proto
- when "TCP"
- assert_not_nil(port)
- cmd = "echo | nc.traditional #{host} #{port}"
- user = LIVE_USER
- when "UDP"
- assert_not_nil(port)
- cmd = "echo | nc.traditional -u #{host} #{port}"
- user = LIVE_USER
- when "ICMP"
- cmd = "ping -c 5 #{host}"
- user = 'root'
- end
- @conn_res = $vm.execute(cmd, :user => user)
-end
-
-Then /^the untorified connection fails$/ do
- case @conn_proto
- when "TCP"
- expected_in_stderr = "Connection refused"
- conn_failed = !@conn_res.success? &&
- @conn_res.stderr.chomp.end_with?(expected_in_stderr)
- when "UDP", "ICMP"
- conn_failed = !@conn_res.success?
- end
- assert(conn_failed,
- "The untorified #{@conn_proto} connection didn't fail as expected:\n" +
- @conn_res.to_s)
-end
-
-Then /^the untorified connection is logged as dropped by the firewall$/ do
- assert(firewall_has_dropped_packet_to?(@conn_proto, @conn_host, @conn_port),
- "No #{@conn_proto} packet to #{@conn_host}" +
- (@conn_port.nil? ? "" : ":#{@conn_port}") +
- " was dropped by the firewall")
-end
-
-When /^the system DNS is(?: still)? using the local DNS resolver$/ do
- resolvconf = $vm.file_content("/etc/resolv.conf")
- bad_lines = resolvconf.split("\n").find_all do |line|
- !line.start_with?("#") && !/^nameserver\s+127\.0\.0\.1$/.match(line)
- end
- assert_empty(bad_lines,
- "The following bad lines were found in /etc/resolv.conf:\n" +
- bad_lines.join("\n"))
-end
-
-def stream_isolation_info(application)
- case application
- when "htpdate"
- {
- :grep_monitor_expr => 'users:(("curl"',
- :socksport => 9062
- }
- when "tails-security-check"
- {
- :grep_monitor_expr => 'users:(("tails-security-"',
- :socksport => 9062
- }
- when "tails-upgrade-frontend-wrapper"
- {
- :grep_monitor_expr => 'users:(("tails-iuk-get-u"',
- :socksport => 9062
- }
- when "Tor Browser"
- {
- :grep_monitor_expr => 'users:(("firefox"',
- :socksport => 9150,
- :controller => true,
- }
- when "Gobby"
- {
- :grep_monitor_expr => 'users:(("gobby-0.5"',
- :socksport => 9050
- }
- when "SSH"
- {
- :grep_monitor_expr => 'users:(("\(nc\|ssh\)"',
- :socksport => 9050
- }
- when "whois"
- {
- :grep_monitor_expr => 'users:(("whois"',
- :socksport => 9050
- }
- else
- raise "Unknown application '#{application}' for the stream isolation tests"
- end
-end
-
-When /^I monitor the network connections of (.*)$/ do |application|
- @process_monitor_log = "/tmp/ss.log"
- info = stream_isolation_info(application)
- $vm.spawn("while true; do " +
- " ss -taupen | grep '#{info[:grep_monitor_expr]}'; " +
- " sleep 0.1; " +
- "done > #{@process_monitor_log}")
-end
-
-Then /^I see that (.+) is properly stream isolated$/ do |application|
- info = stream_isolation_info(application)
- expected_ports = [info[:socksport]]
- expected_ports << 9051 if info[:controller]
- assert_not_nil(@process_monitor_log)
- log_lines = $vm.file_content(@process_monitor_log).split("\n")
- assert(log_lines.size > 0,
- "Couldn't see any connection made by #{application} so " \
- "something is wrong")
- log_lines.each do |line|
- ip_port = line.split(/\s+/)[5]
- assert(expected_ports.map { |port| "127.0.0.1:#{port}" }.include?(ip_port),
- "#{application} should only connect to #{expected_ports} but " \
- "was seen connecting to #{ip_port}")
- end
-end
-
-And /^I re-run tails-security-check$/ do
- $vm.execute_successfully("tails-security-check", :user => LIVE_USER)
-end
-
-And /^I re-run htpdate$/ do
- $vm.execute_successfully("service htpdate stop && " \
- "rm -f /run/htpdate/* && " \
- "systemctl --no-block start htpdate.service")
- step "the time has synced"
-end
-
-And /^I re-run tails-upgrade-frontend-wrapper$/ do
- $vm.execute_successfully("tails-upgrade-frontend-wrapper", :user => LIVE_USER)
-end
-
-When /^I connect Gobby to "([^"]+)"$/ do |host|
- gobby = Dogtail::Application.new('gobby-0.5')
- gobby.child('Welcome to Gobby', roleName: 'label')
- gobby.button('Close').click
- # This indicates that Gobby has finished initializing itself
- # (generating DH parameters, etc.) -- before, the UI is not responsive
- # and our CTRL-t is lost.
- gobby.child('Failed to share documents', roleName: 'label')
- gobby.menu('File').click
- gobby.menuItem('Connect to Server...').click
- @screen.type("t", Sikuli::KeyModifier.CTRL)
- connect_dialog = gobby.dialog('Connect to Server')
- connect_dialog.child('', roleName: 'text').typeText(host)
- connect_dialog.button('Connect').click
- # This looks for the live user's presence entry in the chat, which
- # will only be shown if the connection succeeded.
- try_for(60) { gobby.child(LIVE_USER, roleName: 'table cell'); true }
-end
-
-When /^the Tor Launcher autostarts$/ do
- @screen.wait('TorLauncherWindow.png', 60)
-end
-
-When /^I configure some (\w+) pluggable transports in Tor Launcher$/ do |bridge_type|
- @screen.wait_and_click('TorLauncherConfigureButton.png', 10)
- @screen.wait('TorLauncherBridgePrompt.png', 10)
- @screen.wait_and_click('TorLauncherYesRadioOption.png', 10)
- @screen.wait_and_click('TorLauncherNextButton.png', 10)
- @screen.wait_and_click('TorLauncherBridgeList.png', 10)
- @bridge_hosts = []
- chutney_src_dir = "#{GIT_DIR}/submodules/chutney"
- bridge_dirs = Dir.glob(
- "#{$config['TMPDIR']}/chutney-data/nodes/*#{bridge_type}/"
- )
- bridge_dirs.each do |bridge_dir|
- address = $vmnet.bridge_ip_addr
- port = nil
- fingerprint = nil
- extra = nil
- if bridge_type == 'bridge'
- open(bridge_dir + "/torrc") do |f|
- port = f.grep(/^OrPort\b/).first.split.last
- end
- else
- # This is the pluggable transport case. While we could set a
- # static port via ServerTransportListenAddr we instead let it be
- # picked randomly so an already used port is not picked --
- # Chutney already has issues with that for OrPort selection.
- pt_re = /Registered server transport '#{bridge_type}' at '[^']*:(\d+)'/
- open(bridge_dir + "/notice.log") do |f|
- pt_lines = f.grep(pt_re)
- port = pt_lines.last.match(pt_re)[1]
- end
- if bridge_type == 'obfs4'
- open(bridge_dir + "/pt_state/obfs4_bridgeline.txt") do |f|
- extra = f.readlines.last.chomp.sub(/^.* cert=/, 'cert=')
- end
- end
- end
- open(bridge_dir + "/fingerprint") do |f|
- fingerprint = f.read.chomp.split.last
- end
- @bridge_hosts << { address: address, port: port.to_i }
- bridge_line = bridge_type + " " + address + ":" + port
- [fingerprint, extra].each { |e| bridge_line += " " + e.to_s if e }
- @screen.type(bridge_line + Sikuli::Key.ENTER)
- end
- @screen.wait_and_click('TorLauncherNextButton.png', 10)
- @screen.hide_cursor
- @screen.wait_and_click('TorLauncherFinishButton.png', 10)
- @screen.wait('TorLauncherConnectingWindow.png', 10)
- @screen.waitVanish('TorLauncherConnectingWindow.png', 120)
-end
-
-When /^all Internet traffic has only flowed through the configured pluggable transports$/ do
- assert_not_nil(@bridge_hosts, "No bridges has been configured via the " +
- "'I configure some ... bridges in Tor Launcher' step")
- assert_all_connections(@sniffer.pcap_file) do |c|
- @bridge_hosts.include?({ address: c.daddr, port: c.dport })
- end
-end
diff --git a/cucumber/features/step_definitions/torified_browsing.rb b/cucumber/features/step_definitions/torified_browsing.rb
deleted file mode 100644
index 76760789..00000000
--- a/cucumber/features/step_definitions/torified_browsing.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-Then /^no traffic was sent to the web server on the LAN$/ do
- assert_no_connections(@sniffer.pcap_file) do |c|
- c.daddr == @web_server_ip_addr and c.dport == @web_server_port
- end
-end
diff --git a/cucumber/features/step_definitions/torified_gnupg.rb b/cucumber/features/step_definitions/torified_gnupg.rb
deleted file mode 100644
index f5f61cef..00000000
--- a/cucumber/features/step_definitions/torified_gnupg.rb
+++ /dev/null
@@ -1,263 +0,0 @@
-require 'resolv'
-
-class OpenPGPKeyserverCommunicationError < StandardError
-end
-
-def count_gpg_signatures(key)
- output = $vm.execute_successfully("gpg --batch --list-sigs #{key}",
- :user => LIVE_USER).stdout
- output.scan(/^sig/).count
-end
-
-def check_for_seahorse_error
- if @screen.exists('GnomeCloseButton.png')
- raise OpenPGPKeyserverCommunicationError.new(
- "Found GnomeCloseButton.png' on the screen"
- )
- end
-end
-
-def start_or_restart_seahorse
- assert_not_nil(@withgpgapplet)
- if @withgpgapplet
- seahorse_menu_click_helper('GpgAppletIconNormal.png', 'GpgAppletManageKeys.png')
- else
- step 'I start "Passwords and Keys" via GNOME Activities Overview'
- end
- step 'Seahorse has opened'
-end
-
-Then /^the key "([^"]+)" has (only|more than) (\d+) signatures$/ do |key, qualifier, num|
- count = count_gpg_signatures(key)
- case qualifier
- when 'only'
- assert_equal(count, num.to_i, "Expected #{num} signatures but instead found #{count}")
- when 'more than'
- assert(count > num.to_i, "Expected more than #{num} signatures but found #{count}")
- else
- raise "Unknown operator #{qualifier} passed"
- end
-end
-
-When /^the "([^"]+)" OpenPGP key is not in the live user's public keyring$/ do |keyid|
- assert(!$vm.execute("gpg --batch --list-keys '#{keyid}'",
- :user => LIVE_USER).success?,
- "The '#{keyid}' key is in the live user's public keyring.")
-end
-
-def setup_onion_keyserver
- resolver = Resolv::DNS.new
- keyservers = resolver.getaddresses('pool.sks-keyservers.net').select do |addr|
- addr.class == Resolv::IPv4
- end
- onion_keyserver_address = keyservers.sample
- hkp_port = 11371
- @onion_keyserver_job = chutney_onionservice_redir(
- onion_keyserver_address, hkp_port
- )
-end
-
-When /^I fetch the "([^"]+)" OpenPGP key using the GnuPG CLI( without any signatures)?$/ do |keyid, without|
- # Make keyid an instance variable so we can reference it in the Seahorse
- # keysyncing step.
- @fetched_openpgp_keyid = keyid
- if without
- importopts = '--keyserver-options import-clean'
- else
- importopts = ''
- end
- retry_tor(Proc.new { setup_onion_keyserver }) do
- @gnupg_recv_key_res = $vm.execute_successfully(
- "timeout 120 gpg --batch #{importopts} --recv-key '#{@fetched_openpgp_keyid}'",
- :user => LIVE_USER)
- if @gnupg_recv_key_res.failure?
- raise "Fetching keys with the GnuPG CLI failed with:\n" +
- "#{@gnupg_recv_key_res.stdout}\n" +
- "#{@gnupg_recv_key_res.stderr}"
- end
- end
-end
-
-When /^the GnuPG fetch is successful$/ do
- assert(@gnupg_recv_key_res.success?,
- "gpg keyserver fetch failed:\n#{@gnupg_recv_key_res.stderr}")
-end
-
-When /^the Seahorse operation is successful$/ do
- !@screen.exists('GnomeCloseButton.png')
- $vm.has_process?('seahorse')
-end
-
-When /^the "([^"]+)" key is in the live user's public keyring(?: after at most (\d) seconds)?$/ do |keyid, delay|
- delay = 10 unless delay
- try_for(delay.to_i, :msg => "The '#{keyid}' key is not in the live user's public keyring") {
- $vm.execute("gpg --batch --list-keys '#{keyid}'",
- :user => LIVE_USER).success?
- }
-end
-
-When /^I start Seahorse( via the OpenPGP Applet)?$/ do |withgpgapplet|
- @withgpgapplet = !!withgpgapplet
- start_or_restart_seahorse
-end
-
-Then /^Seahorse has opened$/ do
- @screen.wait('SeahorseWindow.png', 20)
-end
-
-Then /^I enable key synchronization in Seahorse$/ do
- step 'process "seahorse" is running'
- @screen.wait_and_click("SeahorseWindow.png", 10)
- seahorse_menu_click_helper('GnomeEditMenu.png', 'SeahorseEditPreferences.png', 'seahorse')
- @screen.wait('SeahorsePreferences.png', 20)
- @screen.type("p", Sikuli::KeyModifier.ALT) # Option: "Publish keys to...".
- @screen.type(Sikuli::Key.DOWN) # select HKP server
- @screen.type("c", Sikuli::KeyModifier.ALT) # Button: "Close"
-end
-
-Then /^I synchronize keys in Seahorse$/ do
- recovery_proc = Proc.new do
- setup_onion_keyserver
- # The version of Seahorse in Jessie will abort with a
- # segmentation fault whenever there's any sort of network error while
- # syncing keys. This will usually happens after clicking away the error
- # message. This does not appear to be a problem in Stretch.
- #
- # We'll kill the Seahorse process to avoid waiting for the inevitable
- # segfault. We'll also make sure the process is still running (= hasn't
- # yet segfaulted) before terminating it.
- if @screen.exists('GnomeCloseButton.png') || !$vm.has_process?('seahorse')
- step 'I kill the process "seahorse"' if $vm.has_process?('seahorse')
- debug_log('Restarting Seahorse.')
- start_or_restart_seahorse
- end
- end
-
- def change_of_status?
- # Due to a lack of visual feedback in Seahorse we'll break out of the
- # try_for loop below by returning "true" when there's something we can act
- # upon.
- if count_gpg_signatures(@fetched_openpgp_keyid) > 2 || \
- @screen.exists('GnomeCloseButton.png') || \
- !$vm.has_process?('seahorse')
- true
- end
- end
-
- retry_tor(recovery_proc) do
- @screen.wait_and_click("SeahorseWindow.png", 10)
- seahorse_menu_click_helper('SeahorseRemoteMenu.png',
- 'SeahorseRemoteMenuSync.png',
- 'seahorse')
- @screen.wait('SeahorseSyncKeys.png', 20)
- @screen.type("s", Sikuli::KeyModifier.ALT) # Button: Sync
- # There's no visual feedback of Seahorse in Tails/Jessie, except on error.
- try_for(120) {
- change_of_status?
- }
- check_for_seahorse_error
- raise OpenPGPKeyserverCommunicationError.new(
- 'Seahorse crashed with a segfault.') unless $vm.has_process?('seahorse')
- end
-end
-
-When /^I fetch the "([^"]+)" OpenPGP key using Seahorse( via the OpenPGP Applet)?$/ do |keyid, withgpgapplet|
- step "I start Seahorse#{withgpgapplet}"
-
- def change_of_status?(keyid)
- # Due to a lack of visual feedback in Seahorse we'll break out of the
- # try_for loop below by returning "true" when there's something we can act
- # upon.
- if $vm.execute_successfully(
- "gpg --batch --list-keys '#{keyid}'", :user => LIVE_USER) ||
- @screen.exists('GnomeCloseButton.png')
- true
- end
- end
-
- recovery_proc = Proc.new do
- setup_onion_keyserver
- @screen.click('GnomeCloseButton.png') if @screen.exists('GnomeCloseButton.png')
- @screen.type("w", Sikuli::KeyModifier.CTRL)
- end
- retry_tor(recovery_proc) do
- @screen.wait_and_click("SeahorseWindow.png", 10)
- seahorse_menu_click_helper('SeahorseRemoteMenu.png',
- 'SeahorseRemoteMenuFind.png',
- 'seahorse')
- @screen.wait('SeahorseFindKeysWindow.png', 10)
- # Seahorse doesn't seem to support searching for fingerprints
- @screen.type(keyid + Sikuli::Key.ENTER)
- begin
- @screen.waitAny(['SeahorseFoundKeyResult.png',
- 'GnomeCloseButton.png'], 120)
- rescue FindAnyFailed
- # We may end up here if Seahorse appears to be "frozen".
- # Sometimes--but not always--if we click another window
- # the main Seahorse window will unfreeze, allowing us
- # to continue normally.
- @screen.click("SeahorseSearch.png")
- end
- check_for_seahorse_error
- @screen.click("SeahorseKeyResultWindow.png")
- @screen.click("SeahorseFoundKeyResult.png")
- @screen.click("SeahorseImport.png")
- try_for(120) do
- change_of_status?(keyid)
- end
- check_for_seahorse_error
- end
-end
-
-Given /^(GnuPG|Seahorse) is configured to use Chutney's onion keyserver$/ do |app|
- setup_onion_keyserver unless @onion_keyserver_job
- _, _, onion_address, onion_port = chutney_onionservice_info
- case app
- when 'GnuPG'
- # Validate the shipped configuration ...
- server = /keyserver\s+(\S+)$/.match($vm.file_content("/home/#{LIVE_USER}/.gnupg/dirmngr.conf"))[1]
- assert_equal(
- "hkp://#{CONFIGURED_KEYSERVER_HOSTNAME}", server,
- "GnuPG's dirmngr does not use the correct keyserver"
- )
- # ... before replacing it
- $vm.execute_successfully(
- "sed -i 's/#{CONFIGURED_KEYSERVER_HOSTNAME}/#{onion_address}:#{onion_port}/' " +
- "'/home/#{LIVE_USER}/.gnupg/dirmngr.conf'"
- )
- when 'Seahorse'
- # Validate the shipped configuration ...
- @gnome_keyservers = YAML.load(
- $vm.execute_successfully(
- 'gsettings get org.gnome.crypto.pgp keyservers',
- user: LIVE_USER
- ).stdout
- )
- assert_equal(1, @gnome_keyservers.count,
- 'Seahorse should only have one keyserver configured.')
- assert_equal(
- 'hkp://' + CONFIGURED_KEYSERVER_HOSTNAME, @gnome_keyservers[0],
- "GnuPG's dirmngr does not use the correct keyserver"
- )
- # ... before replacing it
- $vm.execute_successfully(
- "gsettings set org.gnome.crypto.pgp keyservers \"['hkp://#{onion_address}:#{onion_port}']\"",
- user: LIVE_USER
- )
- end
-end
-
-Then /^GnuPG's dirmngr uses the configured keyserver$/ do
- _, _, onion_keyserver_address, _ = chutney_onionservice_info
- dirmngr_request = $vm.execute_successfully(
- 'gpg-connect-agent --dirmngr "keyserver --hosttable" /bye', user: LIVE_USER
- )
- server = dirmngr_request.stdout.chomp.lines[1].split[4]
- server = /keyserver\s+(\S+)$/.match(
- $vm.file_content("/home/#{LIVE_USER}/.gnupg/dirmngr.conf")
- )[1]
- assert_equal(
- "hkp://#{onion_keyserver_address}:5858", server,
- "GnuPG's dirmngr does not use the correct keyserver"
- )
-end
diff --git a/cucumber/features/step_definitions/torified_misc.rb b/cucumber/features/step_definitions/torified_misc.rb
deleted file mode 100644
index 7ccdb227..00000000
--- a/cucumber/features/step_definitions/torified_misc.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-require 'resolv'
-
-When /^I query the whois directory service for "([^"]+)"$/ do |domain|
- retry_tor do
- @vm_execute_res = $vm.execute("whois '#{domain}'", :user => LIVE_USER)
- if @vm_execute_res.failure? || @vm_execute_res.stdout['LIMIT EXCEEDED']
- raise "Looking up whois info for #{domain} failed with:\n" +
- "#{@vm_execute_res.stdout}\n" +
- "#{@vm_execute_res.stderr}"
- end
- end
-end
-
-When /^I wget "([^"]+)" to stdout(?:| with the '([^']+)' options)$/ do |target, options|
- retry_tor do
- if target == "some Tails mirror"
- host = 'dl.amnesia.boum.org'
- address = Resolv.new.getaddresses(host).sample
- puts "Resolved #{host} to #{address}"
- url = "http://#{address}/tails/stable/"
- else
- url = target
- end
- arguments = "-O - '#{url}'"
- arguments = "#{options} #{arguments}" if options
- @vm_execute_res = $vm.execute("wget #{arguments}", :user => LIVE_USER)
- if @vm_execute_res.failure?
- raise "wget:ing #{url} with options #{options} failed with:\n" +
- "#{@vm_execute_res.stdout}\n" +
- "#{@vm_execute_res.stderr}"
- end
- end
-end
-
-Then /^the (wget|whois) command is successful$/ do |command|
- assert(
- @vm_execute_res.success?,
- "#{command} failed:\n" +
- "#{@vm_execute_res.stdout}\n" +
- "#{@vm_execute_res.stderr}"
- )
-end
-
-Then /^the (wget|whois) standard output contains "([^"]+)"$/ do |command, text|
- assert(
- @vm_execute_res.stdout[text],
- "The #{command} standard output does not contain #{text}:\n" +
- "#{@vm_execute_res.stdout}\n" +
- "#{@vm_execute_res.stderr}"
- )
-end
diff --git a/cucumber/features/step_definitions/totem.rb b/cucumber/features/step_definitions/totem.rb
deleted file mode 100644
index a5b88d14..00000000
--- a/cucumber/features/step_definitions/totem.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-Given /^I create sample videos$/ do
- @video_dir_on_host = "#{$config["TMPDIR"]}/video_dir"
- FileUtils.mkdir_p(@video_dir_on_host)
- add_after_scenario_hook { FileUtils.rm_r(@video_dir_on_host) }
- fatal_system("avconv -loop 1 -t 30 -f image2 " +
- "-i 'features/images/USBTailsLogo.png' " +
- "-an -vcodec libx264 -y " +
- '-filter:v "crop=in_w-mod(in_w\,2):in_h-mod(in_h\,2)" ' +
- "'#{@video_dir_on_host}/video.mp4' >/dev/null 2>&1")
-end
-
-Given /^I plug and mount a USB drive containing sample videos$/ do
- @video_dir_on_guest = share_host_files(
- Dir.glob("#{@video_dir_on_host}/*")
- )
-end
-
-Given /^I copy the sample videos to "([^"]+)" as user "([^"]+)"$/ do |destination, user|
- for video_on_host in Dir.glob("#{@video_dir_on_host}/*.mp4") do
- video_name = File.basename(video_on_host)
- src_on_guest = "#{@video_dir_on_guest}/#{video_name}"
- dst_on_guest = "#{destination}/#{video_name}"
- step "I copy \"#{src_on_guest}\" to \"#{dst_on_guest}\" as user \"amnesia\""
- end
-end
-
-When /^I(?:| try to) open "([^"]+)" with Totem$/ do |filename|
- step "I run \"totem #{filename}\" in GNOME Terminal"
-end
-
-When /^I close Totem$/ do
- step 'I kill the process "totem"'
-end
-
-Then /^I can watch a WebM video over HTTPs$/ do
- test_url = 'https://tails.boum.org/lib/test_suite/test.webm'
- recovery_on_failure = Proc.new do
- step 'I close Totem'
- end
- retry_tor(recovery_on_failure) do
- step "I open \"#{test_url}\" with Totem"
- @screen.wait("SampleRemoteWebMVideoFrame.png", 120)
- end
-end
diff --git a/cucumber/features/step_definitions/unsafe_browser.rb b/cucumber/features/step_definitions/unsafe_browser.rb
deleted file mode 100644
index 160279ca..00000000
--- a/cucumber/features/step_definitions/unsafe_browser.rb
+++ /dev/null
@@ -1,204 +0,0 @@
-When /^I see and accept the Unsafe Browser start verification(?:| in the "([^"]+)" locale)$/ do |locale|
- @screen.wait('GnomeQuestionDialogIcon.png', 30)
- if ['ar_EG.utf8', 'fa_IR'].include?(locale)
- # Take into account button ordering in RTL languages
- @screen.type(Sikuli::Key.LEFT + Sikuli::Key.ENTER)
- else
- @screen.type(Sikuli::Key.RIGHT + Sikuli::Key.ENTER)
- end
-end
-
-def supported_torbrowser_languages
- localization_descriptions = "#{Dir.pwd}/config/chroot_local-includes/usr/share/tails/browser-localization/descriptions"
- File.read(localization_descriptions).split("\n").map do |line|
- # The line will be of the form "xx:YY:..." or "xx-YY:YY:..."
- first, second = line.sub('-', '_').split(':')
- candidates = ["#{first}_#{second}.UTF-8", "#{first}_#{second}.utf8",
- "#{first}.UTF-8", "#{first}.utf8",
- "#{first}_#{second}", first]
- when_not_found = Proc.new { raise "Could not find a locale for '#{line}'" }
- candidates.find(when_not_found) do |candidate|
- $vm.directory_exist?("/usr/lib/locale/#{candidate}")
- end
- end
-end
-
-Then /^I start the Unsafe Browser in the "([^"]+)" locale$/ do |loc|
- step "I run \"LANG=#{loc} LC_ALL=#{loc} sudo unsafe-browser\" in GNOME Terminal"
- step "I see and accept the Unsafe Browser start verification in the \"#{loc}\" locale"
-end
-
-Then /^the Unsafe Browser works in all supported languages$/ do
- failed = Array.new
- supported_torbrowser_languages.sample(3).each do |lang|
- step "I start the Unsafe Browser in the \"#{lang}\" locale"
- begin
- step "the Unsafe Browser has started"
- rescue RuntimeError
- failed << lang
- next
- end
- step "I close the Unsafe Browser"
- step "the Unsafe Browser chroot is torn down"
- end
- assert(failed.empty?, "Unsafe Browser failed to launch in the following locale(s): #{failed.join(', ')}")
-end
-
-Then /^the Unsafe Browser has no add-ons installed$/ do
- step "I open the address \"about:addons\" in the Unsafe Browser"
- step "I see \"UnsafeBrowserNoAddons.png\" after at most 30 seconds"
-end
-
-Then /^the Unsafe Browser has only Firefox's default bookmarks configured$/ do
- info = xul_application_info("Unsafe Browser")
- # "Show all bookmarks"
- @screen.type("o", Sikuli::KeyModifier.SHIFT + Sikuli::KeyModifier.CTRL)
- @screen.wait_and_click("UnsafeBrowserExportBookmarksButton.png", 20)
- @screen.wait_and_click("UnsafeBrowserExportBookmarksMenuEntry.png", 20)
- @screen.wait("UnsafeBrowserExportBookmarksSavePrompt.png", 20)
- path = "/home/#{info[:user]}/bookmarks"
- @screen.type(path + Sikuli::Key.ENTER)
- chroot_path = "#{info[:chroot]}/#{path}.json"
- try_for(10) { $vm.file_exist?(chroot_path) }
- dump = JSON.load($vm.file_content(chroot_path))
-
- def check_bookmarks_helper(a)
- mozilla_uris_counter = 0
- places_uris_counter = 0
- a.each do |h|
- h.each_pair do |k, v|
- if k == "children"
- m, p = check_bookmarks_helper(v)
- mozilla_uris_counter += m
- places_uris_counter += p
- elsif k == "uri"
- uri = v
- if uri.match("^https://www\.mozilla\.org/")
- mozilla_uris_counter += 1
- elsif uri.match("^place:(sort|folder|type)=")
- places_uris_counter += 1
- else
- raise "Unexpected Unsafe Browser bookmark for '#{uri}'"
- end
- end
- end
- end
- return [mozilla_uris_counter, places_uris_counter]
- end
-
- mozilla_uris_counter, places_uris_counter =
- check_bookmarks_helper(dump["children"])
- assert_equal(5, mozilla_uris_counter,
- "Unexpected number (#{mozilla_uris_counter}) of mozilla " \
- "bookmarks")
- assert_equal(2, places_uris_counter,
- "Unexpected number (#{places_uris_counter}) of places " \
- "bookmarks")
- @screen.type(Sikuli::Key.F4, Sikuli::KeyModifier.ALT)
-end
-
-Then /^the Unsafe Browser has a red theme$/ do
- @screen.wait("UnsafeBrowserRedTheme.png", 10)
-end
-
-Then /^the Unsafe Browser shows a warning as its start page$/ do
- @screen.wait("UnsafeBrowserStartPage.png", 10)
-end
-
-Then /^I see a warning about another instance already running$/ do
- @screen.wait('UnsafeBrowserWarnAlreadyRunning.png', 10)
-end
-
-Then /^I can start the Unsafe Browser again$/ do
- step "I start the Unsafe Browser"
-end
-
-Then /^I cannot configure the Unsafe Browser to use any local proxies$/ do
- socks_proxy = 'C' # Alt+Shift+c for socks proxy
- no_proxy = 'y' # Alt+y for no proxy
- proxies = [[no_proxy, nil, nil]]
- socksport_lines =
- $vm.execute_successfully('grep -w "^SocksPort" /etc/tor/torrc').stdout
- assert(socksport_lines.size >= 4, "We got fewer than four Tor SocksPorts")
- socksports = socksport_lines.scan(/^SocksPort\s([^:]+):(\d+)/)
- proxies += socksports.map { |host, port| [socks_proxy, host, port] }
-
- proxies.each do |proxy_type, proxy_host, proxy_port|
- @screen.hide_cursor
-
- # Open proxy settings
- @screen.click('UnsafeBrowserMenuButton.png')
- @screen.wait_and_click('UnsafeBrowserPreferencesButton.png', 10)
- @screen.wait_and_click('UnsafeBrowserAdvancedSettingsButton.png', 10)
- hit, _ = @screen.waitAny(['UnsafeBrowserNetworkTabAlreadySelected.png',
- 'UnsafeBrowserNetworkTab.png'], 10)
- @screen.click(hit) if hit == 'UnsafeBrowserNetworkTab.png'
- @screen.wait_and_click('UnsafeBrowserNetworkTabSettingsButton.png', 10)
- @screen.wait_and_click('UnsafeBrowserProxySettingsWindow.png', 10)
-
- # Ensure the desired proxy configuration
- if proxy_type == no_proxy
- @screen.type(proxy_type, Sikuli::KeyModifier.ALT)
- @screen.wait('UnsafeBrowserNoProxySelected.png', 10)
- else
- @screen.type("M", Sikuli::KeyModifier.ALT)
- @screen.type(proxy_type, Sikuli::KeyModifier.ALT)
- @screen.type(proxy_host + Sikuli::Key.TAB + proxy_port)
- end
-
- # Close settings
- @screen.click('UnsafeBrowserProxySettingsOkButton.png')
- @screen.waitVanish('UnsafeBrowserProxySettingsWindow.png', 10)
-
- # Test that the proxy settings work as they should
- step 'I open Tails homepage in the Unsafe Browser'
- if proxy_type == no_proxy
- step 'Tails homepage loads in the Unsafe Browser'
- else
- @screen.wait('UnsafeBrowserProxyRefused.png', 60)
- end
- end
-end
-
-Then /^the Unsafe Browser has no proxy configured$/ do
- @screen.click('UnsafeBrowserMenuButton.png')
- @screen.wait_and_click('UnsafeBrowserPreferencesButton.png', 10)
- @screen.wait_and_click('UnsafeBrowserAdvancedSettingsButton.png', 10)
- @screen.wait_and_click('UnsafeBrowserNetworkTab.png', 10)
- @screen.wait_and_click('UnsafeBrowserNetworkTabSettingsButton.png', 10)
- @screen.wait('UnsafeBrowserProxySettingsWindow.png', 10)
- @screen.wait('UnsafeBrowserNoProxySelected.png', 10)
- @screen.type(Sikuli::Key.F4, Sikuli::KeyModifier.ALT)
- @screen.type("w", Sikuli::KeyModifier.CTRL)
-end
-
-Then /^the Unsafe Browser complains that no DNS server is configured$/ do
- assert_not_nil(
- Dogtail::Application.new('zenity')
- .child(roleName: 'label')
- .text['No DNS server was obtained']
- )
-end
-
-Then /^I configure the Unsafe Browser to check for updates more frequently$/ do
- prefs = '/usr/share/tails/chroot-browsers/unsafe-browser/prefs.js'
- $vm.file_append(prefs, 'pref("app.update.idletime", 1);')
- $vm.file_append(prefs, 'pref("app.update.promptWaitTime", 1);')
- $vm.file_append(prefs, 'pref("app.update.interval", 5);')
-end
-
-But /^checking for updates is disabled in the Unsafe Browser's configuration$/ do
- prefs = '/usr/share/tails/chroot-browsers/common/prefs.js'
- assert($vm.file_content(prefs).include?('pref("app.update.enabled", false)'))
-end
-
-Then /^the clearnet user has (|not )sent packets out to the Internet$/ do |sent|
- uid = $vm.execute_successfully("id -u clearnet").stdout.chomp.to_i
- pkts = ip4tables_packet_counter_sum(:tables => ['OUTPUT'], :uid => uid)
- case sent
- when ''
- assert(pkts > 0, "Packets have not gone out to the internet.")
- when 'not'
- assert_equal(pkts, 0, "Packets have gone out to the internet.")
- end
-end
diff --git a/cucumber/features/step_definitions/untrusted_partitions.rb b/cucumber/features/step_definitions/untrusted_partitions.rb
deleted file mode 100644
index 603c8b4f..00000000
--- a/cucumber/features/step_definitions/untrusted_partitions.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-Given /^I create an? ([[:alnum:]]+) swap partition on disk "([^"]+)"$/ do |parttype, name|
- $vm.storage.disk_mkswap(name, parttype)
-end
-
-Then /^an? "([^"]+)" partition was detected by Tails on drive "([^"]+)"$/ do |type, name|
- part_info = $vm.execute_successfully(
- "blkid '#{$vm.disk_dev(name)}'").stdout.strip
- assert(part_info.split.grep(/^TYPE=\"#{Regexp.escape(type)}\"$/),
- "No #{type} partition was detected by Tails on disk '#{name}'")
-end
-
-Then /^Tails has no disk swap enabled$/ do
- # Skip first line which contain column headers
- swap_info = $vm.execute_successfully("tail -n+2 /proc/swaps").stdout
- assert(swap_info.empty?,
- "Disk swapping is enabled according to /proc/swaps:\n" + swap_info)
- mem_info = $vm.execute_successfully("grep '^Swap' /proc/meminfo").stdout
- assert(mem_info.match(/^SwapTotal:\s+0 kB$/),
- "Disk swapping is enabled according to /proc/meminfo:\n" +
- mem_info)
-end
-
-Given /^I create an? ([[:alnum:]]+) partition( labeled "([^"]+)")? with an? ([[:alnum:]]+) filesystem( encrypted with password "([^"]+)")? on disk "([^"]+)"$/ do |parttype, has_label, label, fstype, is_encrypted, luks_password, name|
- opts = {}
- opts.merge!(:label => label) if has_label
- opts.merge!(:luks_password => luks_password) if is_encrypted
- $vm.storage.disk_mkpartfs(name, parttype, fstype, opts)
-end
-
-Given /^I write the Tails ISO image to disk "([^"]+)"$/ do |name|
- src_disk = {
- :path => TAILS_ISO,
- :opts => {
- :format => "raw",
- :readonly => true
- }
- }
- dest_disk = {
- :path => $vm.storage.disk_path(name),
- :opts => {
- :format => $vm.storage.disk_format(name)
- }
- }
- $vm.storage.guestfs_disk_helper(src_disk, dest_disk) do |g, src_disk_handle, dest_disk_handle|
- g.copy_device_to_device(src_disk_handle, dest_disk_handle, {})
- end
-end
-
-Then /^drive "([^"]+)" is not mounted$/ do |name|
- dev = $vm.disk_dev(name)
- assert(!$vm.execute("grep -qs '^#{dev}' /proc/mounts").success?,
- "an untrusted partition from drive '#{name}' was automounted")
-end
-
-Then /^Tails Greeter has( not)? detected a persistence partition$/ do |no_persistence|
- expecting_persistence = no_persistence.nil?
- @screen.find('TailsGreeter.png')
- found_persistence = ! @screen.exists('TailsGreeterPersistencePassphrase.png').nil?
- assert_equal(expecting_persistence, found_persistence,
- "Persistence is unexpectedly#{no_persistence} enabled")
-end
diff --git a/cucumber/features/step_definitions/usb.rb b/cucumber/features/step_definitions/usb.rb
deleted file mode 100644
index e030f68e..00000000
--- a/cucumber/features/step_definitions/usb.rb
+++ /dev/null
@@ -1,747 +0,0 @@
-# Returns a hash that for each preset the running Tails is aware of
-# maps the source to the destination.
-def get_persistence_presets(skip_links = false)
- # Perl script that prints all persistence presets (one per line) on
- # the form: <mount_point>:<comma-separated-list-of-options>
- script = <<-EOF
- use strict;
- use warnings FATAL => "all";
- use Tails::Persistence::Configuration::Presets;
- foreach my $preset (Tails::Persistence::Configuration::Presets->new()->all) {
- say $preset->destination, ":", join(",", @{$preset->options});
- }
-EOF
- # VMCommand:s cannot handle newlines, and they're irrelevant in the
- # above perl script any way
- script.delete!("\n")
- presets = $vm.execute_successfully("perl -E '#{script}'").stdout.chomp.split("\n")
- assert presets.size >= 10, "Got #{presets.size} persistence presets, " +
- "which is too few"
- persistence_mapping = Hash.new
- for line in presets
- destination, options_str = line.split(":")
- options = options_str.split(",")
- is_link = options.include? "link"
- next if is_link and skip_links
- source_str = options.find { |option| /^source=/.match option }
- # If no source is given as an option, live-boot's persistence
- # feature defaults to the destination minus the initial "/".
- if source_str.nil?
- source = destination.partition("/").last
- else
- source = source_str.split("=")[1]
- end
- persistence_mapping[source] = destination
- end
- return persistence_mapping
-end
-
-def persistent_dirs
- get_persistence_presets
-end
-
-def persistent_mounts
- get_persistence_presets(true)
-end
-
-def persistent_volumes_mountpoints
- $vm.execute("ls -1 -d /live/persistence/*_unlocked/").stdout.chomp.split
-end
-
-def recover_from_upgrader_failure
- $vm.execute('killall tails-upgrade-frontend tails-upgrade-frontend-wrapper zenity')
- # Remove unnecessary sleep for retry
- $vm.execute_successfully('sed -i "/^sleep 30$/d" ' +
- '/usr/local/bin/tails-upgrade-frontend-wrapper')
- $vm.spawn('tails-upgrade-frontend-wrapper', user: LIVE_USER)
-end
-
-Given /^I clone USB drive "([^"]+)" to a new USB drive "([^"]+)"$/ do |from, to|
- $vm.storage.clone_to_new_disk(from, to)
-end
-
-Given /^I unplug USB drive "([^"]+)"$/ do |name|
- $vm.unplug_drive(name)
-end
-
-Given /^the computer is set to boot from the old Tails DVD$/ do
- $vm.set_cdrom_boot(OLD_TAILS_ISO)
-end
-
-Given /^the computer is set to boot in UEFI mode$/ do
- $vm.set_os_loader('UEFI')
- @os_loader = 'UEFI'
-end
-
-def tails_installer_selected_device
- @installer.child('Target Device:', roleName: 'label').parent
- .child('', roleName: 'combo box', recursive: false).name
-end
-
-def tails_installer_is_device_selected?(name)
- device = $vm.disk_dev(name)
- tails_installer_selected_device[/#{device}\d*$/]
-end
-
-def tails_installer_match_status(pattern)
- @installer.child('', roleName: 'text').text[pattern]
-end
-
-class UpgradeNotSupported < StandardError
-end
-
-def usb_install_helper(name)
- if tails_installer_match_status(/It is impossible to upgrade the device .+ #{$vm.disk_dev(name)}\d* /)
- raise UpgradeNotSupported
- end
- assert(tails_installer_is_device_selected?(name))
- begin
- @installer.button('Install Tails').click
- @installer.child('Question', roleName: 'alert').button('Yes').click
- try_for(30*60) do
- @installer
- .child('Information', roleName: 'alert')
- .child('Installation complete!', roleName: 'label')
- true
- end
- rescue FindFailed => e
- path = $vm.execute_successfully('ls -1 /tmp/tails-installer-*').stdout.chomp
- debug_log("Tails Installer debug log:\n" + $vm.file_content(path))
- raise e
- end
-end
-
-When /^I start Tails Installer in "([^"]+)" mode$/ do |mode|
- step 'I run "export DEBUG=1 ; tails-installer-launcher" in GNOME Terminal'
- installer_launcher = Dogtail::Application.new('tails-installer-launcher')
- .child('Tails Installer', roleName: 'frame')
- # Sometimes Dogtail will find the button and click it before it is
- # shown (searchShowingOnly is not perfect) which generally means
- # clicking somewhere on the Terminal => the click is lost *and* the
- # installer does no go to the foreground. So let's wait a bit extra.
- sleep 3
- installer_launcher.button(mode).click
- @installer = Dogtail::Application.new('tails-installer')
- @installer.child('Tails Installer', roleName: 'frame')
- # ... and something similar (for consecutive steps) again.
- sleep 3
- $vm.focus_window('Tails Installer')
-end
-
-Then /^Tails Installer detects that a device is too small$/ do
- try_for(10) do
- tails_installer_match_status(/^The device .* is too small to install Tails/)
- end
-end
-
-When /^I am told that the destination device cannot be upgraded$/ do
- try_for(10) do
- tails_installer_match_status(/^It is impossible to upgrade the device/)
- end
-end
-
-When /^I am suggested to do a "Install by cloning"$/ do
- try_for(10) do
- tails_installer_match_status(
- /You should instead use "Install by cloning" to upgrade Tails/
- )
- end
-end
-
-Then /^a suitable USB device is (?:still )?not found$/ do
- @installer.child(
- 'No device suitable to install Tails could be found', roleName: 'label'
- )
-end
-
-Then /^(no|the "([^"]+)") USB drive is selected$/ do |mode, name|
- try_for(30) do
- if mode == 'no'
- tails_installer_selected_device == ''
- else
- tails_installer_is_device_selected?(name)
- end
- end
-end
-
-When /^I "([^"]*)" Tails to USB drive "([^"]+)"$/ do |mode, name|
- step "I start Tails Installer in \"#{mode}\" mode"
- usb_install_helper(name)
-end
-
-When /^I fail to "([^"]*)" Tails to USB drive "([^"]+)"$/ do |mode, name|
- begin
- step "I \"#{mode}\" Tails to USB drive \"#{name}\""
- rescue UpgradeNotSupported
- # this is what we expect
- else
- raise "The USB installer should not succeed"
- end
-end
-
-Given /^I plug and mount a USB drive containing the Tails ISO$/ do
- iso_dir = share_host_files(TAILS_ISO)
- @iso_path = "#{iso_dir}/#{File.basename(TAILS_ISO)}"
-end
-
-When /^I do a "Upgrade from ISO" on USB drive "([^"]+)"$/ do |name|
- step 'I start Tails Installer in "Upgrade from ISO" mode'
- @installer.child('Use existing Live system ISO:', roleName: 'label')
- .parent.button('(None)').click
- file_chooser = @installer.child('Select a File', roleName: 'file chooser')
- @screen.type("l", Sikuli::KeyModifier.CTRL)
- # The only visible text element will be the path entry
- file_chooser.child(roleName: 'text').typeText(@iso_path + '\n')
- file_chooser.button('Open').click
- usb_install_helper(name)
-end
-
-Given /^I enable all persistence presets$/ do
- @screen.wait('PersistenceWizardPresets.png', 20)
- # Select the "Persistent" folder preset, which is checked by default.
- @screen.type(Sikuli::Key.TAB)
- # Check all non-default persistence presets, i.e. all *after* the
- # "Persistent" folder, which are unchecked by default.
- (persistent_dirs.size - 1).times do
- @screen.type(Sikuli::Key.TAB + Sikuli::Key.SPACE)
- end
- @screen.wait_and_click('PersistenceWizardSave.png', 10)
- @screen.wait('PersistenceWizardDone.png', 60)
- @screen.type(Sikuli::Key.F4, Sikuli::KeyModifier.ALT)
-end
-
-When /^I disable the first persistence preset$/ do
- step 'I start "Configure persistent volume" via GNOME Activities Overview'
- @screen.wait('PersistenceWizardPresets.png', 300)
- @screen.type(Sikuli::Key.SPACE)
- @screen.wait_and_click('PersistenceWizardSave.png', 10)
- @screen.wait('PersistenceWizardDone.png', 30)
- @screen.type(Sikuli::Key.F4, Sikuli::KeyModifier.ALT)
-end
-
-Given /^I create a persistent partition$/ do
- step 'I start "Configure persistent volume" via GNOME Activities Overview'
- @screen.wait('PersistenceWizardStart.png', 60)
- @screen.type(@persistence_password + "\t" + @persistence_password + Sikuli::Key.ENTER)
- @screen.wait('PersistenceWizardPresets.png', 300)
- step "I enable all persistence presets"
-end
-
-def check_disk_integrity(name, dev, scheme)
- info = $vm.execute("udisksctl info --block-device '#{dev}'").stdout
- info_split = info.split("\n org\.freedesktop\.UDisks2\.PartitionTable:\n")
- dev_info = info_split[0]
- part_table_info = info_split[1]
- assert(part_table_info.match("^ Type: +#{scheme}$"),
- "Unexpected partition scheme on USB drive '#{name}', '#{dev}'")
-end
-
-def check_part_integrity(name, dev, usage, fs_type, part_label, part_type = nil)
- info = $vm.execute("udisksctl info --block-device '#{dev}'").stdout
- info_split = info.split("\n org\.freedesktop\.UDisks2\.Partition:\n")
- dev_info = info_split[0]
- part_info = info_split[1]
- assert(dev_info.match("^ IdUsage: +#{usage}$"),
- "Unexpected device field 'usage' on USB drive '#{name}', '#{dev}'")
- assert(dev_info.match("^ IdType: +#{fs_type}$"),
- "Unexpected device field 'IdType' on USB drive '#{name}', '#{dev}'")
- assert(part_info.match("^ Name: +#{part_label}$"),
- "Unexpected partition label on USB drive '#{name}', '#{dev}'")
- if part_type
- assert(part_info.match("^ Type: +#{part_type}$"),
- "Unexpected partition type on USB drive '#{name}', '#{dev}'")
- end
-end
-
-def tails_is_installed_helper(name, tails_root, loader)
- disk_dev = $vm.disk_dev(name)
- part_dev = disk_dev + "1"
- check_disk_integrity(name, disk_dev, "gpt")
- check_part_integrity(name, part_dev, "filesystem", "vfat", "Tails",
- # EFI System Partition
- 'c12a7328-f81f-11d2-ba4b-00a0c93ec93b')
-
- target_root = "/mnt/new"
- $vm.execute("mkdir -p #{target_root}")
- $vm.execute("mount #{part_dev} #{target_root}")
-
- c = $vm.execute("diff -qr '#{tails_root}/live' '#{target_root}/live'")
- assert(c.success?,
- "USB drive '#{name}' has differences in /live:\n#{c.stdout}\n#{c.stderr}")
-
- syslinux_files = $vm.execute("ls -1 #{target_root}/syslinux").stdout.chomp.split
- # We deal with these files separately
- ignores = ["syslinux.cfg", "exithelp.cfg", "ldlinux.c32", "ldlinux.sys"]
- for f in syslinux_files - ignores do
- c = $vm.execute("diff -q '#{tails_root}/#{loader}/#{f}' " +
- "'#{target_root}/syslinux/#{f}'")
- assert(c.success?, "USB drive '#{name}' has differences in " +
- "'/syslinux/#{f}'")
- end
-
- # The main .cfg is named differently vs isolinux
- c = $vm.execute("diff -q '#{tails_root}/#{loader}/#{loader}.cfg' " +
- "'#{target_root}/syslinux/syslinux.cfg'")
- assert(c.success?, "USB drive '#{name}' has differences in " +
- "'/syslinux/syslinux.cfg'")
-
- $vm.execute("umount #{target_root}")
- $vm.execute("sync")
-end
-
-Then /^the running Tails is installed on USB drive "([^"]+)"$/ do |target_name|
- loader = boot_device_type == "usb" ? "syslinux" : "isolinux"
- tails_is_installed_helper(target_name, "/lib/live/mount/medium", loader)
-end
-
-Then /^the ISO's Tails is installed on USB drive "([^"]+)"$/ do |target_name|
- iso_root = "/mnt/iso"
- $vm.execute("mkdir -p #{iso_root}")
- $vm.execute("mount -o loop #{@iso_path} #{iso_root}")
- tails_is_installed_helper(target_name, iso_root, "isolinux")
- $vm.execute("umount #{iso_root}")
-end
-
-Then /^there is no persistence partition on USB drive "([^"]+)"$/ do |name|
- data_part_dev = $vm.disk_dev(name) + "2"
- assert(!$vm.execute("test -b #{data_part_dev}").success?,
- "USB drive #{name} has a partition '#{data_part_dev}'")
-end
-
-Then /^a Tails persistence partition exists on USB drive "([^"]+)"$/ do |name|
- dev = $vm.disk_dev(name) + "2"
- check_part_integrity(name, dev, "crypto", "crypto_LUKS", "TailsData")
-
- # The LUKS container may already be opened, e.g. by udisks after
- # we've run tails-persistence-setup.
- c = $vm.execute("ls -1 --hide 'control' /dev/mapper/")
- if c.success?
- for candidate in c.stdout.split("\n")
- luks_info = $vm.execute("cryptsetup status '#{candidate}'")
- if luks_info.success? and luks_info.stdout.match("^\s+device:\s+#{dev}$")
- luks_dev = "/dev/mapper/#{candidate}"
- break
- end
- end
- end
- if luks_dev.nil?
- c = $vm.execute("echo #{@persistence_password} | " +
- "cryptsetup luksOpen #{dev} #{name}")
- assert(c.success?, "Couldn't open LUKS device '#{dev}' on drive '#{name}'")
- luks_dev = "/dev/mapper/#{name}"
- end
-
- # Adapting check_part_integrity() seems like a bad idea so here goes
- info = $vm.execute("udisksctl info --block-device '#{luks_dev}'").stdout
- assert info.match("^ CryptoBackingDevice: +'/[a-zA-Z0-9_/]+'$")
- assert info.match("^ IdUsage: +filesystem$")
- assert info.match("^ IdType: +ext[34]$")
- assert info.match("^ IdLabel: +TailsData$")
-
- mount_dir = "/mnt/#{name}"
- $vm.execute("mkdir -p #{mount_dir}")
- c = $vm.execute("mount '#{luks_dev}' #{mount_dir}")
- assert(c.success?,
- "Couldn't mount opened LUKS device '#{dev}' on drive '#{name}'")
-
- $vm.execute("umount #{mount_dir}")
- $vm.execute("sync")
- $vm.execute("cryptsetup luksClose #{name}")
-end
-
-Given /^I enable persistence$/ do
- @screen.wait_and_click('TailsGreeterPersistencePassphrase.png', 10)
- @screen.type(@persistence_password + Sikuli::Key.ENTER)
- @screen.wait('TailsGreeterPersistenceUnlocked.png', 30)
-end
-
-def tails_persistence_enabled?
- persistence_state_file = "/var/lib/live/config/tails.persistence"
- return $vm.execute("test -e '#{persistence_state_file}'").success? &&
- $vm.execute(". '#{persistence_state_file}' && " +
- 'test "$TAILS_PERSISTENCE_ENABLED" = true').success?
-end
-
-Given /^all persistence presets(| from the old Tails version)(| but the first one) are enabled$/ do |old_tails, except_first|
- assert(old_tails.empty? || except_first.empty?, "Unsupported case.")
- try_for(120, :msg => "Persistence is disabled") do
- tails_persistence_enabled?
- end
- unexpected_mounts = Array.new
- # Check that all persistent directories are mounted
- if old_tails.empty?
- expected_mounts = persistent_mounts
- if ! except_first.empty?
- first_expected_mount_source = expected_mounts.keys[0]
- first_expected_mount_destination = expected_mounts[first_expected_mount_source]
- expected_mounts.delete(first_expected_mount_source)
- unexpected_mounts = [first_expected_mount_destination]
- end
- else
- assert_not_nil($remembered_persistence_mounts)
- expected_mounts = $remembered_persistence_mounts
- end
- mount = $vm.execute("mount").stdout.chomp
- for _, dir in expected_mounts do
- assert(mount.include?("on #{dir} "),
- "Persistent directory '#{dir}' is not mounted")
- end
- for dir in unexpected_mounts do
- assert(! mount.include?("on #{dir} "),
- "Persistent directory '#{dir}' is mounted")
- end
-end
-
-Given /^persistence is disabled$/ do
- assert(!tails_persistence_enabled?, "Persistence is enabled")
-end
-
-def boot_device
- # Approach borrowed from
- # config/chroot_local_includes/lib/live/config/998-permissions
- boot_dev_id = $vm.execute("udevadm info --device-id-of-file=/lib/live/mount/medium").stdout.chomp
- boot_dev = $vm.execute("readlink -f /dev/block/'#{boot_dev_id}'").stdout.chomp
- return boot_dev
-end
-
-def device_info(dev)
- # Approach borrowed from
- # config/chroot_local_includes/lib/live/config/998-permissions
- info = $vm.execute("udevadm info --query=property --name='#{dev}'").stdout.chomp
- info.split("\n").map { |e| e.split('=') } .to_h
-end
-
-def boot_device_type
- device_info(boot_device)['ID_BUS']
-end
-
-Then /^Tails is running from (.*) drive "([^"]+)"$/ do |bus, name|
- bus = bus.downcase
- case bus
- when "sata"
- expected_bus = "ata"
- else
- expected_bus = bus
- end
- assert_equal(expected_bus, boot_device_type)
- actual_dev = boot_device
- # The boot partition differs between an using Tails installer and
- # isohybrids. There's also a strange case isohybrids are thought to
- # be booting from the "raw" device, and not a partition of it
- # (#10504).
- expected_devs = ['', '1', '4'].map { |e| $vm.disk_dev(name) + e }
- assert(expected_devs.include?(actual_dev),
- "We are running from device #{actual_dev}, but for #{bus} drive " +
- "'#{name}' we expected to run from one of #{expected_devs}")
-end
-
-Then /^the boot device has safe access rights$/ do
-
- super_boot_dev = boot_device.sub(/[[:digit:]]+$/, "")
- devs = $vm.execute("ls -1 #{super_boot_dev}*").stdout.chomp.split
- assert(devs.size > 0, "Could not determine boot device")
- all_users = $vm.execute("cut -d':' -f1 /etc/passwd").stdout.chomp.split
- all_users_with_groups = all_users.collect do |user|
- groups = $vm.execute("groups #{user}").stdout.chomp.sub(/^#{user} : /, "").split(" ")
- [user, groups]
- end
- for dev in devs do
- dev_owner = $vm.execute("stat -c %U #{dev}").stdout.chomp
- dev_group = $vm.execute("stat -c %G #{dev}").stdout.chomp
- dev_perms = $vm.execute("stat -c %a #{dev}").stdout.chomp
- assert_equal("root", dev_owner)
- assert(dev_group == "disk" || dev_group == "root",
- "Boot device '#{dev}' owned by group '#{dev_group}', expected " +
- "'disk' or 'root'.")
- assert_equal("660", dev_perms)
- for user, groups in all_users_with_groups do
- next if user == "root"
- assert(!(groups.include?(dev_group)),
- "Unprivileged user '#{user}' is in group '#{dev_group}' which " +
- "owns boot device '#{dev}'")
- end
- end
-
- info = $vm.execute("udisksctl info --block-device '#{super_boot_dev}'").stdout
- assert(info.match("^ HintSystem: +true$"),
- "Boot device '#{super_boot_dev}' is not system internal for udisks")
-end
-
-Then /^all persistent filesystems have safe access rights$/ do
- persistent_volumes_mountpoints.each do |mountpoint|
- fs_owner = $vm.execute("stat -c %U #{mountpoint}").stdout.chomp
- fs_group = $vm.execute("stat -c %G #{mountpoint}").stdout.chomp
- fs_perms = $vm.execute("stat -c %a #{mountpoint}").stdout.chomp
- assert_equal("root", fs_owner)
- assert_equal("root", fs_group)
- assert_equal('775', fs_perms)
- end
-end
-
-Then /^all persistence configuration files have safe access rights$/ do
- persistent_volumes_mountpoints.each do |mountpoint|
- assert($vm.execute("test -e #{mountpoint}/persistence.conf").success?,
- "#{mountpoint}/persistence.conf does not exist, while it should")
- assert($vm.execute("test ! -e #{mountpoint}/live-persistence.conf").success?,
- "#{mountpoint}/live-persistence.conf does exist, while it should not")
- $vm.execute(
- "ls -1 #{mountpoint}/persistence.conf #{mountpoint}/live-*.conf"
- ).stdout.chomp.split.each do |f|
- file_owner = $vm.execute("stat -c %U '#{f}'").stdout.chomp
- file_group = $vm.execute("stat -c %G '#{f}'").stdout.chomp
- file_perms = $vm.execute("stat -c %a '#{f}'").stdout.chomp
- assert_equal("tails-persistence-setup", file_owner)
- assert_equal("tails-persistence-setup", file_group)
- assert_equal("600", file_perms)
- end
- end
-end
-
-Then /^all persistent directories(| from the old Tails version) have safe access rights$/ do |old_tails|
- if old_tails.empty?
- expected_dirs = persistent_dirs
- else
- assert_not_nil($remembered_persistence_dirs)
- expected_dirs = $remembered_persistence_dirs
- end
- persistent_volumes_mountpoints.each do |mountpoint|
- expected_dirs.each do |src, dest|
- full_src = "#{mountpoint}/#{src}"
- assert_vmcommand_success $vm.execute("test -d #{full_src}")
- dir_perms = $vm.execute_successfully("stat -c %a '#{full_src}'").stdout.chomp
- dir_owner = $vm.execute_successfully("stat -c %U '#{full_src}'").stdout.chomp
- if dest.start_with?("/home/#{LIVE_USER}")
- expected_perms = "700"
- expected_owner = LIVE_USER
- else
- expected_perms = "755"
- expected_owner = "root"
- end
- assert_equal(expected_perms, dir_perms,
- "Persistent source #{full_src} has permission " \
- "#{dir_perms}, expected #{expected_perms}")
- assert_equal(expected_owner, dir_owner,
- "Persistent source #{full_src} has owner " \
- "#{dir_owner}, expected #{expected_owner}")
- end
- end
-end
-
-When /^I write some files expected to persist$/ do
- persistent_mounts.each do |_, dir|
- owner = $vm.execute("stat -c %U #{dir}").stdout.chomp
- assert($vm.execute("touch #{dir}/XXX_persist", :user => owner).success?,
- "Could not create file in persistent directory #{dir}")
- end
-end
-
-When /^I write some dotfile expected to persist$/ do
- assert($vm.execute("touch /live/persistence/TailsData_unlocked/dotfiles/.XXX_persist",
- :user => LIVE_USER).success?,
- "Could not create a file in the dotfiles persistence.")
-end
-
-When /^I remove some files expected to persist$/ do
- persistent_mounts.each do |_, dir|
- owner = $vm.execute("stat -c %U #{dir}").stdout.chomp
- assert($vm.execute("rm #{dir}/XXX_persist", :user => owner).success?,
- "Could not remove file in persistent directory #{dir}")
- end
-end
-
-When /^I write some files not expected to persist$/ do
- persistent_mounts.each do |_, dir|
- owner = $vm.execute("stat -c %U #{dir}").stdout.chomp
- assert($vm.execute("touch #{dir}/XXX_gone", :user => owner).success?,
- "Could not create file in persistent directory #{dir}")
- end
-end
-
-When /^I take note of which persistence presets are available$/ do
- $remembered_persistence_mounts = persistent_mounts
- $remembered_persistence_dirs = persistent_dirs
-end
-
-Then /^the expected persistent files(| created with the old Tails version) are present in the filesystem$/ do |old_tails|
- if old_tails.empty?
- expected_mounts = persistent_mounts
- else
- assert_not_nil($remembered_persistence_mounts)
- expected_mounts = $remembered_persistence_mounts
- end
- expected_mounts.each do |_, dir|
- assert($vm.execute("test -e #{dir}/XXX_persist").success?,
- "Could not find expected file in persistent directory #{dir}")
- assert(!$vm.execute("test -e #{dir}/XXX_gone").success?,
- "Found file that should not have persisted in persistent directory #{dir}")
- end
-end
-
-Then /^the expected persistent dotfile is present in the filesystem$/ do
- expected_dirs = persistent_dirs
- assert($vm.execute("test -L #{expected_dirs['dotfiles']}/.XXX_persist").success?,
- "Could not find expected persistent dotfile link.")
- assert($vm.execute("test -e $(readlink -f #{expected_dirs['dotfiles']}/.XXX_persist)").success?,
- "Could not find expected persistent dotfile link target.")
-end
-
-Then /^only the expected files are present on the persistence partition on USB drive "([^"]+)"$/ do |name|
- assert(!$vm.is_running?)
- disk = {
- :path => $vm.storage.disk_path(name),
- :opts => {
- :format => $vm.storage.disk_format(name),
- :readonly => true
- }
- }
- $vm.storage.guestfs_disk_helper(disk) do |g, disk_handle|
- partitions = g.part_list(disk_handle).map do |part_desc|
- disk_handle + part_desc["part_num"].to_s
- end
- partition = partitions.find do |part|
- g.blkid(part)["PART_ENTRY_NAME"] == "TailsData"
- end
- assert_not_nil(partition, "Could not find the 'TailsData' partition " \
- "on disk '#{disk_handle}'")
- luks_mapping = File.basename(partition) + "_unlocked"
- g.luks_open(partition, @persistence_password, luks_mapping)
- luks_dev = "/dev/mapper/#{luks_mapping}"
- mount_point = "/"
- g.mount(luks_dev, mount_point)
- assert_not_nil($remembered_persistence_mounts)
- $remembered_persistence_mounts.each do |dir, _|
- # Guestfs::exists may have a bug; if the file exists, 1 is
- # returned, but if it doesn't exist false is returned. It seems
- # the translation of C types into Ruby types is glitchy.
- assert(g.exists("/#{dir}/XXX_persist") == 1,
- "Could not find expected file in persistent directory #{dir}")
- assert(g.exists("/#{dir}/XXX_gone") != 1,
- "Found file that should not have persisted in persistent directory #{dir}")
- end
- g.umount(mount_point)
- g.luks_close(luks_dev)
- end
-end
-
-When /^I delete the persistent partition$/ do
- step 'I start "Delete persistent volume" via GNOME Activities Overview'
- @screen.wait("PersistenceWizardDeletionStart.png", 120)
- @screen.type(" ")
- @screen.wait("PersistenceWizardDone.png", 120)
-end
-
-Then /^Tails has started in UEFI mode$/ do
- assert($vm.execute("test -d /sys/firmware/efi").success?,
- "/sys/firmware/efi does not exist")
- end
-
-Given /^I create a ([[:alpha:]]+) label on disk "([^"]+)"$/ do |type, name|
- $vm.storage.disk_mklabel(name, type)
-end
-
-Given /^the file system changes introduced in version (.+) are (not )?present(?: in the (\S+) Browser's chroot)?$/ do |version, not_present, chroot_browser|
- assert_equal('1.1~test', version)
- upgrade_applied = not_present.nil?
- chroot_browser = "#{chroot_browser.downcase}-browser" if chroot_browser
- changes = [
- {
- filesystem: :rootfs,
- path: 'some_new_file',
- status: :added,
- new_content: <<-EOF
-Some content
- EOF
- },
- {
- filesystem: :rootfs,
- path: 'etc/amnesia/version',
- status: :modified,
- new_content: <<-EOF
-#{version} - 20380119
-ffffffffffffffffffffffffffffffffffffffff
-live-build: 3.0.5+really+is+2.0.12-0.tails2
-live-boot: 4.0.2-1
-live-config: 4.0.4-1
- EOF
- },
- {
- filesystem: :rootfs,
- path: 'etc/os-release',
- status: :modified,
- new_content: <<-EOF
-TAILS_PRODUCT_NAME="Tails"
-TAILS_VERSION_ID="#{version}"
- EOF
- },
- {
- filesystem: :rootfs,
- path: 'usr/share/common-licenses/BSD',
- status: :removed
- },
- {
- filesystem: :medium,
- path: 'utils/linux/syslinux',
- status: :removed
- },
- ]
- changes.each do |change|
- case change[:filesystem]
- when :rootfs
- path = '/'
- path += "var/lib/#{chroot_browser}/chroot/" if chroot_browser
- path += change[:path]
- when :medium
- path = '/lib/live/mount/medium/' + change[:path]
- else
- raise "Unknown filesysten '#{change[:filesystem]}'"
- end
- case change[:status]
- when :removed
- assert_equal(!upgrade_applied, $vm.file_exist?(path))
- when :added
- assert_equal(upgrade_applied, $vm.file_exist?(path))
- if upgrade_applied && change[:new_content]
- assert_equal(change[:new_content], $vm.file_content(path))
- end
- when :modified
- assert($vm.file_exist?(path))
- if upgrade_applied
- assert_not_nil(change[:new_content])
- assert_equal(change[:new_content], $vm.file_content(path))
- end
- else
- raise "Unknown status '#{change[:status]}'"
- end
- end
-end
-
-Then /^I am proposed to install an incremental upgrade to version (.+)$/ do |version|
- recovery_proc = Proc.new do
- recover_from_upgrader_failure
- end
- failure_pic = 'TailsUpgraderFailure.png'
- success_pic = "TailsUpgraderUpgradeTo#{version}.png"
- retry_tor(recovery_proc) do
- match, _ = @screen.waitAny([success_pic, failure_pic], 2*60)
- assert_equal(success_pic, match)
- end
-end
-
-When /^I agree to install the incremental upgrade$/ do
- @screen.click('TailsUpgraderUpgradeNowButton.png')
-end
-
-Then /^I can successfully install the incremental upgrade to version (.+)$/ do |version|
- step 'I agree to install the incremental upgrade'
- recovery_proc = Proc.new do
- recover_from_upgrader_failure
- step "I am proposed to install an incremental upgrade to version #{version}"
- step 'I agree to install the incremental upgrade'
- end
- failure_pic = 'TailsUpgraderFailure.png'
- success_pic = "TailsUpgraderDone.png"
- retry_tor(recovery_proc) do
- match, _ = @screen.waitAny([success_pic, failure_pic], 2*60)
- assert_equal(success_pic, match)
- end
-end