summaryrefslogtreecommitdiffstats
path: root/features/step_definitions/common_steps.rb
diff options
context:
space:
mode:
Diffstat (limited to 'features/step_definitions/common_steps.rb')
-rw-r--r--features/step_definitions/common_steps.rb953
1 files changed, 608 insertions, 345 deletions
diff --git a/features/step_definitions/common_steps.rb b/features/step_definitions/common_steps.rb
index f173a94b..d7097a0d 100644
--- a/features/step_definitions/common_steps.rb
+++ b/features/step_definitions/common_steps.rb
@@ -17,29 +17,81 @@ def activate_filesystem_shares
# "probe of virtio2 failed with error -2" (in dmesg) which makes the
# shares unavailable. Hence we leave this code commented for now.
#for mod in ["9pnet_virtio", "9p"] do
- # @vm.execute("modprobe #{mod}")
+ # $vm.execute("modprobe #{mod}")
#end
- @vm.list_shares.each do |share|
- @vm.execute("mkdir -p #{share}")
- @vm.execute("mount -t 9p -o trans=virtio #{share} #{share}")
+ $vm.list_shares.each do |share|
+ $vm.execute("mkdir -p #{share}")
+ $vm.execute("mount -t 9p -o trans=virtio #{share} #{share}")
+ end
+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 deactivate_filesystem_shares
- @vm.list_shares.each do |share|
- @vm.execute("umount #{share}")
+ $vm.list_shares.each do |share|
+ $vm.execute("umount #{share}")
end
# XXX-9p: See XXX-9p above
#for mod in ["9p", "9pnet_virtio"] do
- # @vm.execute("modprobe -r #{mod}")
+ # $vm.execute("modprobe -r #{mod}")
#end
end
-def restore_background
- @vm.restore_snapshot($background_snapshot)
- @vm.wait_until_remote_shell_is_up
+# This helper requires that the notification image is the one shown in
+# the notification applet's list, not the notification pop-up.
+def robust_notification_wait(notification_image, time_to_wait)
+ error_msg = "Didn't not manage to open the notification applet"
+ wait_start = Time.now
+ try_for(time_to_wait, :delay => 0, :msg => error_msg) do
+ @screen.hide_cursor
+ @screen.click("GnomeNotificationApplet.png")
+ @screen.wait("GnomeNotificationAppletOpened.png", 10)
+ end
+
+ error_msg = "Didn't not see notification '#{notification_image}'"
+ time_to_wait -= (Time.now - wait_start).ceil
+ try_for(time_to_wait, :delay => 0, :msg => error_msg) do
+ found = false
+ entries = @screen.findAll("GnomeNotificationEntry.png")
+ while(entries.hasNext) do
+ entry = entries.next
+ @screen.hide_cursor
+ @screen.click(entry)
+ close_entry = @screen.wait("GnomeNotificationEntryClose.png", 10)
+ if @screen.exists(notification_image)
+ found = true
+ @screen.click(close_entry)
+ break
+ else
+ @screen.click(entry)
+ end
+ end
+ found
+ end
+
+ # Click anywhere to close the notification applet
+ @screen.hide_cursor
+ @screen.click("GnomeApplicationsMenu.png")
+ @screen.hide_cursor
+end
+
+def post_snapshot_restore_hook
+ $vm.wait_until_remote_shell_is_up
post_vm_start_hook
# XXX-9p: See XXX-9p above
@@ -49,115 +101,119 @@ def restore_background
# 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("service tor status").success?
- # @vm.execute("service tor stop")
- # @vm.execute("rm -f /var/log/tor/log")
- # @vm.execute("killall vidalia")
- # @vm.host_to_guest_time_sync
- # @vm.execute("service tor start")
+ #if $vm.has_network?
+ # if $vm.execute("systemctl --quiet is-active tor@default.service").success?
+ # $vm.execute("systemctl stop tor@default.service")
+ # $vm.execute("rm -f /var/log/tor/log")
+ # $vm.execute("systemctl --no-block restart tails-tor-has-bootstrapped.target")
+ # $vm.host_to_guest_time_sync
+ # $vm.spawn("restart-tor")
# wait_until_tor_is_working
- # @vm.spawn("/usr/local/sbin/restart-vidalia")
+ # if $vm.file_content('/proc/cmdline').include?(' i2p')
+ # $vm.execute_successfully('/usr/local/sbin/tails-i2p stop')
+ # # we "killall tails-i2p" to prevent multiple
+ # # copies of the script from running
+ # $vm.execute_successfully('killall tails-i2p')
+ # $vm.spawn('/usr/local/sbin/tails-i2p start')
+ # end
# end
+ #else
+ # $vm.host_to_guest_time_sync
#end
end
Given /^a computer$/ do
- @vm.destroy if @vm
- @vm = VM.new($vm_xml_path, $x_display)
+ $vm.destroy_and_undefine if $vm
+ $vm = VM.new($virt, VM_XML_PATH, $vmnet, $vmstorage, DISPLAY)
end
Given /^the computer has (\d+) ([[:alpha:]]+) of RAM$/ do |size, unit|
- next if @skip_steps_while_restoring_background
- @vm.set_ram_size(size, unit)
+ $vm.set_ram_size(size, unit)
end
Given /^the computer is set to boot from the Tails DVD$/ do
- next if @skip_steps_while_restoring_background
- @vm.set_cdrom_boot($tails_iso)
+ $vm.set_cdrom_boot(TAILS_ISO)
end
Given /^the computer is set to boot from (.+?) drive "(.+?)"$/ do |type, name|
- next if @skip_steps_while_restoring_background
- @vm.set_disk_boot(name, type.downcase)
+ $vm.set_disk_boot(name, type.downcase)
end
-Given /^I plug ([[:alpha:]]+) drive "([^"]+)"$/ do |bus, name|
- next if @skip_steps_while_restoring_background
- @vm.plug_drive(name, bus.downcase)
- if @vm.is_running?
+Given /^I (temporarily )?create a (\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|
- next if @skip_steps_while_restoring_background
- if @vm.is_running?
- try_for(10, :msg => "Drive '#{name}' is not detected by Tails") {
- @vm.disk_detected?(name)
- }
- else
- STDERR.puts "Cannot tell if drive '#{name}' is detected by Tails: " +
- "Tails is not running"
+ raise "Tails is not running" unless $vm.is_running?
+ try_for(10, :msg => "Drive '#{name}' is not detected by Tails") do
+ $vm.disk_detected?(name)
end
end
Given /^the network is plugged$/ do
- next if @skip_steps_while_restoring_background
- @vm.plug_network
+ $vm.plug_network
end
Given /^the network is unplugged$/ do
- next if @skip_steps_while_restoring_background
- @vm.unplug_network
+ $vm.unplug_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
- # Note: We don't want skip this particular stpe if
- # @skip_steps_while_restoring_background is set since it starts
- # something external to the VM state.
- @sniffer = Sniffer.new("TestSniffer", @vm.net.bridge_name)
+ @sniffer = Sniffer.new("sniffer", $vmnet)
@sniffer.capture
+ add_after_scenario_hook do
+ @sniffer.stop
+ @sniffer.clear
+ end
end
Given /^I set Tails to boot with options "([^"]*)"$/ do |options|
- next if @skip_steps_while_restoring_background
@boot_options = options
end
When /^I start the computer$/ do
- next if @skip_steps_while_restoring_background
- assert(!@vm.is_running?,
+ assert(!$vm.is_running?,
"Trying to start a VM that is already running")
- @vm.start
+ $vm.start
post_vm_start_hook
end
-Given /^I start Tails from DVD(| with network unplugged) and I login$/ do |network_unplugged|
- # we don't @skip_steps_while_restoring_background as we're only running
- # other steps, that are taking care of it *if* they have to
- step "the computer is set to boot from the Tails DVD"
- if network_unplugged.empty?
+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.nil?
step "the network is plugged"
else
step "the network is unplugged"
end
step "I start the computer"
step "the computer boots Tails"
- step "I log in to a new session"
- step "Tails seems to have booted normally"
- if network_unplugged.empty?
- step "Tor is ready"
- step "all notifications have disappeared"
- step "available upgrades have been checked"
- else
- step "all notifications have disappeared"
+ if do_login
+ step "I log in to a new session"
+ step "Tails seems to have booted normally"
+ if network_unplugged.nil?
+ step "Tor is ready"
+ step "all notifications have disappeared"
+ step "available upgrades have been checked"
+ else
+ step "all notifications have disappeared"
+ end
end
end
-Given /^I start Tails from (.+?) drive "(.+?)"(| with network unplugged) and I login(| with(| read-only) persistence password "([^"]+)")$/ do |drive_type, drive_name, network_unplugged, persistence_on, persistence_ro, persistence_pwd|
- # we don't @skip_steps_while_restoring_background as we're only running
- # other steps, that are taking care of it *if* they have to
+Given /^I start Tails from (.+?) drive "(.+?)"(| with network unplugged)( and I login(| with(| read-only) persistence enabled))?$/ do |drive_type, drive_name, network_unplugged, do_login, persistence_on, persistence_ro|
step "the computer is set to boot from #{drive_type} drive \"#{drive_name}\""
if network_unplugged.empty?
step "the network is plugged"
@@ -166,52 +222,57 @@ Given /^I start Tails from (.+?) drive "(.+?)"(| with network unplugged) and I l
end
step "I start the computer"
step "the computer boots Tails"
- if ! persistence_on.empty?
- assert(! persistence_pwd.empty?, "A password must be provided when enabling persistence")
- if persistence_ro.empty?
- step "I enable persistence with password \"#{persistence_pwd}\""
+ if do_login
+ if ! persistence_on.empty?
+ if persistence_ro.empty?
+ step "I enable persistence"
+ else
+ step "I enable read-only persistence"
+ end
+ end
+ step "I log in to a new session"
+ step "Tails seems to have booted normally"
+ if network_unplugged.empty?
+ step "Tor is ready"
+ step "all notifications have disappeared"
+ step "available upgrades have been checked"
else
- step "I enable read-only persistence with password \"#{persistence_pwd}\""
+ step "all notifications have disappeared"
end
end
- step "I log in to a new session"
- step "Tails seems to have booted normally"
- if network_unplugged.empty?
- step "Tor is ready"
- step "all notifications have disappeared"
- step "available upgrades have been checked"
- else
- step "all notifications have disappeared"
- end
end
When /^I power off the computer$/ do
- next if @skip_steps_while_restoring_background
- assert(@vm.is_running?,
+ assert($vm.is_running?,
"Trying to power off an already powered off VM")
- @vm.power_off
+ $vm.power_off
end
When /^I cold reboot the computer$/ do
- next if @skip_steps_while_restoring_background
step "I power off the computer"
step "I start the computer"
end
When /^I destroy the computer$/ do
- next if @skip_steps_while_restoring_background
- @vm.destroy
+ $vm.destroy_and_undefine
end
Given /^the computer (re)?boots DebianLive(|\d+)$/ do |reboot,version|
next if @skip_steps_while_restoring_background
+def bootsplash
case @os_loader
when "UEFI"
- assert(!reboot, "Testing of reboot with UEFI enabled is not implemented")
- bootsplash = 'TailsBootSplashUEFI.png'
- bootsplash_tab_msg = 'TailsBootSplashTabMsgUEFI.png'
- boot_timeout = 30
+ 'TailsBootSplashUEFI.png'
+ else
+ 'TailsBootSplash.png'
+ end
+end
+
+def bootsplash_tab_msg
+ case @os_loader
+ when "UEFI"
+ 'TailsBootSplashTabMsgUEFI.png'
else
if reboot
bootsplash = 'TailsBootSplashPostReset.png'
@@ -223,22 +284,38 @@ Given /^the computer (re)?boots DebianLive(|\d+)$/ do |reboot,version|
boot_timeout = 30
end
end
+end
+
+Given /^the computer (re)?boots Tails$/ do |reboot|
+
+ boot_timeout = 30
+ # We need some extra time for memory wiping if rebooting
+ boot_timeout += 90 if reboot
@screen.wait(bootsplash, boot_timeout)
@screen.wait(bootsplash_tab_msg, 10)
@screen.type(Sikuli::Key.TAB)
@screen.waitVanish(bootsplash_tab_msg, 1)
- @screen.type(" autotest_never_use_this_option #{@boot_options}" +
+ @screen.type(" autotest_never_use_this_option blacklist=psmouse #{@boot_options}" +
Sikuli::Key.ENTER)
@screen.wait("DebianLive#{version}Greeter.png", 5*60)
@vm.wait_until_remote_shell_is_up
activate_filesystem_shares
end
-Given /^I log in to a new session$/ do
- next if @skip_steps_while_restoring_background
- @screen.wait_and_click('TailsGreeterLoginButton.png', 10)
+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_and_click("TailsGreeterLanguage#{@language}.png", 10)
+ @screen.wait_and_click("TailsGreeterLoginButton#{@language}.png", 10)
+ when ''
+ @screen.wait_and_click('TailsGreeterLoginButton.png', 10)
+ else
+ raise "Unsupported language: #{lang}"
+ end
end
Given /^I set sudo password "([^"]*)"$/ do |password|
@@ -251,86 +328,83 @@ Given /^I set sudo password "([^"]*)"$/ do |password|
end
Given /^Tails Greeter has dealt with the sudo password$/ do
- next if @skip_steps_while_restoring_background
f1 = "/etc/sudoers.d/tails-greeter"
f2 = "#{f1}-no-password-lecture"
try_for(20) {
- @vm.execute("test -e '#{f1}' -o -e '#{f2}'").success?
+ $vm.execute("test -e '#{f1}' -o -e '#{f2}'").success?
}
end
-Given /^GNOME has started$/ do
- next if @skip_steps_while_restoring_background
- case @theme
- when "windows"
- desktop_started_picture = 'WindowsStartButton.png'
- else
- desktop_started_picture = 'GnomeApplicationsMenu.png'
- end
+Given /^the Tails desktop is ready$/ do
+ desktop_started_picture = "GnomeApplicationsMenu#{@language}.png"
+ # We wait for the Florence icon to be displayed to ensure reliable systray icon clicking.
+ @screen.wait("GnomeSystrayFlorence.png", 180)
@screen.wait(desktop_started_picture, 180)
+ # 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
+ )
end
Then /^Tails seems to have booted normally$/ do
- next if @skip_steps_while_restoring_background
- step "GNOME has started"
+ step "the Tails desktop is ready"
end
-Given /^Tor is ready$/ do
- next if @skip_steps_while_restoring_background
- @screen.wait("GnomeTorIsReady.png", 300)
- @screen.waitVanish("GnomeTorIsReady.png", 15)
+When /^I see the 'Tor is ready' notification$/ do
+ robust_notification_wait('TorIsReadyNotification.png', 300)
+end
- # Having seen the "Tor is ready" notification implies that Tor has
- # built a circuit, but let's check it directly to be on the safe side.
+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
- next if @skip_steps_while_restoring_background
wait_until_tor_is_working
end
Given /^the time has synced$/ do
- next if @skip_steps_while_restoring_background
["/var/run/tordate/done", "/var/run/htpdate/success"].each do |file|
- try_for(300) { @vm.execute("test -e #{file}").success? }
+ try_for(300) { $vm.execute("test -e #{file}").success? }
end
end
Given /^available upgrades have been checked$/ do
- next if @skip_steps_while_restoring_background
try_for(300) {
- @vm.execute("test -e '/var/run/tails-upgrader/checked_upgrades'").success?
+ $vm.execute("test -e '/var/run/tails-upgrader/checked_upgrades'").success?
}
end
Given /^the Tor Browser has started$/ do
- next if @skip_steps_while_restoring_background
- case @theme
- when "windows"
- tor_browser_picture = "WindowsTorBrowserWindow.png"
- else
- tor_browser_picture = "TorBrowserWindow.png"
- end
-
+ tor_browser_picture = "TorBrowserWindow.png"
@screen.wait(tor_browser_picture, 60)
end
-Given /^the Tor Browser has started and loaded the startup page$/ do
- next if @skip_steps_while_restoring_background
+Given /^the Tor Browser (?:has started and )?load(?:ed|s) the (startup page|Tails roadmap)$/ do |page|
+ case page
+ when "startup page"
+ picture = "TorBrowserStartupPage.png"
+ when "Tails roadmap"
+ picture = "TorBrowserTailsRoadmap.png"
+ else
+ raise "Unsupported page: #{page}"
+ end
step "the Tor Browser has started"
- @screen.wait("TorBrowserStartupPage.png", 120)
+ @screen.wait(picture, 120)
end
Given /^the Tor Browser has started in offline mode$/ do
- next if @skip_steps_while_restoring_background
@screen.wait("TorBrowserOffline.png", 60)
end
Given /^I add a bookmark to eff.org in the Tor Browser$/ do
- next if @skip_steps_while_restoring_background
url = "https://www.eff.org"
step "I open the address \"#{url}\" in the Tor Browser"
@screen.wait("TorBrowserOffline.png", 5)
@@ -340,271 +414,150 @@ Given /^I add a bookmark to eff.org in the Tor Browser$/ do
end
Given /^the Tor Browser has a bookmark to eff.org$/ do
- next if @skip_steps_while_restoring_background
@screen.type("b", Sikuli::KeyModifier.ALT)
@screen.wait("TorBrowserEFFBookmark.png", 10)
end
Given /^all notifications have disappeared$/ do
- next if @skip_steps_while_restoring_background
- case @theme
- when "windows"
- notification_picture = "WindowsNotificationX.png"
- else
- notification_picture = "GnomeNotificationX.png"
- end
- @screen.waitVanish(notification_picture, 60)
-end
-
-Given /^I save the state so the background can be restored next scenario$/ do
- if @skip_steps_while_restoring_background
- assert(File.size?($background_snapshot),
- "We have been skipping steps but there is no snapshot to restore")
- else
- # To be sure we run the feature from scratch we remove any
- # leftover snapshot that wasn't removed.
- if File.exist?($background_snapshot)
- File.delete($background_snapshot)
+ next if not(@screen.exists("GnomeNotificationApplet.png"))
+ @screen.click("GnomeNotificationApplet.png")
+ @screen.wait("GnomeNotificationAppletOpened.png", 10)
+ begin
+ entries = @screen.findAll("GnomeNotificationEntry.png")
+ while(entries.hasNext) do
+ entry = entries.next
+ @screen.hide_cursor
+ @screen.click(entry)
+ @screen.wait_and_click("GnomeNotificationEntryClose.png", 10)
end
- # Workaround: when libvirt takes ownership of the snapshot it may
- # become unwritable for the user running this script so it cannot
- # be removed during clean up.
- FileUtils.touch($background_snapshot)
- FileUtils.chmod(0666, $background_snapshot)
-
- # Snapshots cannot be saved while filesystem shares are mounted
- # XXX-9p: See XXX-9p above.
- #deactivate_filesystem_shares
-
- @vm.save_snapshot($background_snapshot)
+ rescue FindFailed
+ # No notifications, so we're good to go.
end
- restore_background
- # Now we stop skipping steps from the snapshot restore.
- @skip_steps_while_restoring_background = false
-end
-
-Then /^I see "([^"]*)" after at most (\d+) seconds$/ do |image, time|
- next if @skip_steps_while_restoring_background
- @screen.wait(image, time.to_i)
+ @screen.hide_cursor
+ # Click anywhere to close the notification applet
+ @screen.click("GnomeApplicationsMenu.png")
+ @screen.hide_cursor
end
-Then /^all Internet traffic has only flowed through Tor$/ do
- next if @skip_steps_while_restoring_background
- leaks = FirewallLeakCheck.new(@sniffer.pcap_file, get_tor_relays)
- if !leaks.empty?
- if !leaks.ipv4_tcp_leaks.empty?
- puts "The following IPv4 TCP non-Tor Internet hosts were contacted:"
- puts leaks.ipv4_tcp_leaks.join("\n")
- puts
- end
- if !leaks.ipv4_nontcp_leaks.empty?
- puts "The following IPv4 non-TCP Internet hosts were contacted:"
- puts leaks.ipv4_nontcp_leaks.join("\n")
- puts
- end
- if !leaks.ipv6_leaks.empty?
- puts "The following IPv6 Internet hosts were contacted:"
- puts leaks.ipv6_leaks.join("\n")
- puts
- end
- if !leaks.nonip_leaks.empty?
- puts "Some non-IP packets were sent\n"
- end
- save_pcap_file
- raise "There were network leaks!"
+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
-Given /^I enter the sudo password in the gksu prompt$/ do
- next if @skip_steps_while_restoring_background
- @screen.wait('GksuAuthPrompt.png', 60)
- sleep 1 # wait for weird fade-in to unblock the "Ok" button
- @screen.type(@sudo_password)
- @screen.type(Sikuli::Key.ENTER)
- @screen.waitVanish('GksuAuthPrompt.png', 10)
+Then /^all Internet traffic has only flowed through Tor$/ do
+ leaks = FirewallLeakCheck.new(@sniffer.pcap_file,
+ :accepted_hosts => get_all_tor_nodes)
+ leaks.assert_no_leaks
end
Given /^I enter the sudo password in the pkexec prompt$/ do
- next if @skip_steps_while_restoring_background
step "I enter the \"#{@sudo_password}\" password in the pkexec prompt"
end
def deal_with_polkit_prompt (image, password)
@screen.wait(image, 60)
- sleep 1 # wait for weird fade-in to unblock the "Ok" button
@screen.type(password)
@screen.type(Sikuli::Key.ENTER)
@screen.waitVanish(image, 10)
end
Given /^I enter the "([^"]*)" password in the pkexec prompt$/ do |password|
- next if @skip_steps_while_restoring_background
deal_with_polkit_prompt('PolicyKitAuthPrompt.png', password)
end
-Given /^process "([^"]+)" is running$/ do |process|
- next if @skip_steps_while_restoring_background
- assert(@vm.has_process?(process),
- "Process '#{process}' is not running")
+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|
- next if @skip_steps_while_restoring_background
try_for(time.to_i, :msg => "Process '#{process}' is not running after " +
"waiting for #{time} seconds") do
- @vm.has_process?(process)
+ $vm.has_process?(process)
end
end
-Given /^process "([^"]+)" is not running$/ do |process|
- next if @skip_steps_while_restoring_background
- assert(!@vm.has_process?(process),
- "Process '#{process}' is running")
+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|
- next if @skip_steps_while_restoring_background
- @vm.execute("killall #{process}")
+ $vm.execute("killall #{process}")
try_for(10, :msg => "Process '#{process}' could not be killed") {
- !@vm.has_process?(process)
+ !$vm.has_process?(process)
}
end
Then /^Tails eventually shuts down$/ do
- next if @skip_steps_while_restoring_background
- nr_gibs_of_ram = (detected_ram_in_MiB.to_f/(2**10)).ceil
+ nr_gibs_of_ram = convert_from_bytes($vm.get_ram_size_in_bytes, 'GiB').ceil
timeout = nr_gibs_of_ram*5*60
try_for(timeout, :msg => "VM is still running after #{timeout} seconds") do
- ! @vm.is_running?
+ ! $vm.is_running?
end
end
Then /^Tails eventually restarts$/ do
- next if @skip_steps_while_restoring_background
- nr_gibs_of_ram = (detected_ram_in_MiB.to_f/(2**10)).ceil
- @screen.wait('TailsBootSplashPostReset.png', nr_gibs_of_ram*5*60)
+ nr_gibs_of_ram = convert_from_bytes($vm.get_ram_size_in_bytes, 'GiB').ceil
+ @screen.wait('TailsBootSplash.png', nr_gibs_of_ram*5*60)
end
Given /^I shutdown Tails and wait for the computer to power off$/ do
- next if @skip_steps_while_restoring_background
- @vm.execute("poweroff")
+ $vm.spawn("poweroff")
step 'Tails eventually shuts down'
end
When /^I request a shutdown using the emergency shutdown applet$/ do
- next if @skip_steps_while_restoring_background
@screen.hide_cursor
@screen.wait_and_click('TailsEmergencyShutdownButton.png', 10)
- @screen.hide_cursor
@screen.wait_and_click('TailsEmergencyShutdownHalt.png', 10)
end
When /^I warm reboot the computer$/ do
- next if @skip_steps_while_restoring_background
- @vm.execute("reboot")
+ $vm.spawn("reboot")
end
When /^I request a reboot using the emergency shutdown applet$/ do
- next if @skip_steps_while_restoring_background
@screen.hide_cursor
@screen.wait_and_click('TailsEmergencyShutdownButton.png', 10)
- @screen.hide_cursor
@screen.wait_and_click('TailsEmergencyShutdownReboot.png', 10)
end
Given /^package "([^"]+)" is installed$/ do |package|
- next if @skip_steps_while_restoring_background
- assert(@vm.execute("dpkg -s '#{package}' 2>/dev/null | grep -qs '^Status:.*installed$'").success?,
+ assert($vm.execute("dpkg -s '#{package}' 2>/dev/null | grep -qs '^Status:.*installed$'").success?,
"Package '#{package}' is not installed")
end
When /^I start the Tor Browser$/ do
- next if @skip_steps_while_restoring_background
- case @theme
- when "windows"
- step 'I click the start menu'
- @screen.wait_and_click("WindowsApplicationsInternet.png", 10)
- @screen.wait_and_click("WindowsApplicationsTorBrowser.png", 10)
- else
- @screen.wait_and_click("GnomeApplicationsMenu.png", 10)
- @screen.wait_and_click("GnomeApplicationsInternet.png", 10)
- @screen.wait_and_click("GnomeApplicationsTorBrowser.png", 10)
- end
+ step 'I start "TorBrowser" via the GNOME "Internet" applications menu'
end
-When /^I start the Tor Browser in offline mode$/ do
- next if @skip_steps_while_restoring_background
- step "I start the Tor Browser"
- case @theme
- when "windows"
- @screen.wait_and_click("WindowsTorBrowserOfflinePrompt.png", 10)
- @screen.click("WindowsTorBrowserOfflinePromptStart.png")
- else
- @screen.wait_and_click("TorBrowserOfflinePrompt.png", 10)
- @screen.click("TorBrowserOfflinePromptStart.png")
- end
-end
-
-def xul_app_shared_lib_check(pid, chroot)
- expected_absent_tbb_libs = ['libnssdbm3.so']
- absent_tbb_libs = []
- unwanted_native_libs = []
- tbb_libs = @vm.execute_successfully(
- ". /usr/local/lib/tails-shell-library/tor-browser.sh; " +
- "ls -1 #{chroot}${TBB_INSTALL}/Browser/*.so"
- ).stdout.split
- firefox_pmap_info = @vm.execute("pmap #{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}")
+When /^I request a new identity using Torbutton$/ do
+ @screen.wait_and_click('TorButtonIcon.png', 30)
+ @screen.wait_and_click('TorButtonNewIdentity.png', 30)
end
-Then /^(.*) uses all expected TBB shared libraries$/ do |application|
- next if @skip_steps_while_restoring_background
- binary = @vm.execute_successfully(
- '. /usr/local/lib/tails-shell-library/tor-browser.sh; ' +
- 'echo ${TBB_INSTALL}/Browser/firefox'
- ).stdout.chomp
- case application
- when "the Tor Browser"
- user = $live_user
- cmd_regex = "#{binary} .* -profile /home/#{user}/\.tor-browser/profile\.default"
- chroot = ""
- when "the Unsafe Browser"
- user = "clearnet"
- cmd_regex = "#{binary} .* -profile /home/#{user}/\.tor-browser/profile\.default"
- chroot = "/var/lib/unsafe-browser/chroot"
- when "Tor Launcher"
- user = "tor-launcher"
- cmd_regex = "#{binary} -app /home/#{user}/\.tor-launcher/tor-launcher-standalone/application\.ini"
- chroot = ""
- else
- raise "Invalid browser or XUL application: #{application}"
- end
- pid = @vm.execute_successfully("pgrep --uid #{user} --full --exact '#{cmd_regex}'").stdout.chomp
- assert(/\A\d+\z/.match(pid), "It seems like #{application} is not running")
- xul_app_shared_lib_check(pid, chroot)
+When /^I acknowledge Torbutton's New Identity confirmation prompt$/ do
+ @screen.wait('GnomeQuestionDialogIcon.png', 30)
+ step 'I type "y"'
+end
+
+When /^I start the Tor Browser in offline mode$/ do
+ step "I start the Tor Browser"
+ @screen.wait_and_click("TorBrowserOfflinePrompt.png", 10)
+ @screen.click("TorBrowserOfflinePromptStart.png")
end
Given /^I add a wired DHCP NetworkManager connection called "([^"]+)"$/ do |con_name|
- next if @skip_steps_while_restoring_background
con_content = <<EOF
[802-3-ethernet]
duplex=full
@@ -622,59 +575,369 @@ method=auto
method=auto
EOF
con_content.split("\n").each do |line|
- @vm.execute("echo '#{line}' >> /tmp/NM.#{con_name}")
+ $vm.execute("echo '#{line}' >> /tmp/NM.#{con_name}")
end
- @vm.execute("install -m 0600 '/tmp/NM.#{con_name}' '/etc/NetworkManager/system-connections/#{con_name}'")
+ con_file = "/etc/NetworkManager/system-connections/#{con_name}"
+ $vm.execute("install -m 0600 '/tmp/NM.#{con_name}' '#{con_file}'")
+ $vm.execute_successfully("nmcli connection load '#{con_file}'")
try_for(10) {
- nm_con_list = @vm.execute("nmcli --terse --fields NAME con list").stdout
+ 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|
- next if @skip_steps_while_restoring_background
- @vm.execute("nmcli con up id #{con_name}")
- try_for(60) {
- @vm.execute("nmcli --terse --fields NAME,STATE con status").stdout.chomp == "#{con_name}:activated"
- }
+ $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
- next if @skip_steps_while_restoring_background
- @screen.wait_and_click("GnomeApplicationsMenu.png", 10)
- @screen.wait_and_click("GnomeApplicationsAccessories.png", 10)
- @screen.wait_and_click("GnomeApplicationsTerminal.png", 20)
- @screen.wait_and_click('GnomeTerminalWindow.png', 20)
+ step 'I start "Terminal" via the GNOME "Utilities" applications menu'
+ @screen.wait('GnomeTerminalWindow.png', 20)
end
When /^I run "([^"]+)" in GNOME Terminal$/ do |command|
- next if @skip_steps_while_restoring_background
- step "I start and focus GNOME Terminal"
+ 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$/ do |file|
- next if @skip_steps_while_restoring_background
- assert(@vm.file_exist?(file))
+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|
- next if @skip_steps_while_restoring_background
- c = @vm.execute("cp \"#{source}\" \"#{destination}\"", $live_user)
+ c = $vm.execute("cp \"#{source}\" \"#{destination}\"", :user => LIVE_USER)
assert(c.success?, "Failed to copy file:\n#{c.stdout}\n#{c.stderr}")
end
-Given /^the USB drive "([^"]+)" contains Tails with persistence configured and password "([^"]+)"$/ do |drive, password|
- step "a computer"
- step "I start Tails from DVD with network unplugged and I login"
- step "I create a new 4 GiB USB drive named \"#{drive}\""
- step "I plug USB drive \"#{drive}\""
- step "I \"Clone & Install\" Tails to USB drive \"#{drive}\""
- step "there is no persistence partition on USB drive \"#{drive}\""
- step "I shutdown Tails and wait for the computer to power off"
- step "a computer"
- step "I start Tails from USB drive \"#{drive}\" with network unplugged and I login"
- step "I create a persistent partition with password \"#{password}\""
- step "a Tails persistence partition with password \"#{password}\" exists on USB drive \"#{drive}\""
- step "I shutdown Tails and wait for the computer to power off"
+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
+
+def gnome_app_menu_click_helper(click_me, verify_me = nil)
+ try_for(30) do
+ @screen.hide_cursor
+ # The sensitivity for submenus to open by just hovering past them
+ # is extremely high, and may result in the wrong one
+ # opening. Hence we better avoid hovering over undesired submenus
+ # entirely by "approaching" the menu strictly horizontally.
+ r = @screen.wait(click_me, 10)
+ @screen.hover_point(@screen.w, r.getY)
+ @screen.click(r)
+ @screen.wait(verify_me, 10) if verify_me
+ return
+ end
+end
+
+Given /^I start "([^"]+)" via the GNOME "([^"]+)" applications menu$/ do |app, submenu|
+ menu_button = "GnomeApplicationsMenu.png"
+ sub_menu_entry = "GnomeApplications" + submenu + ".png"
+ application_entry = "GnomeApplications" + app + ".png"
+ try_for(120) do
+ begin
+ gnome_app_menu_click_helper(menu_button, sub_menu_entry)
+ gnome_app_menu_click_helper(sub_menu_entry, application_entry)
+ gnome_app_menu_click_helper(application_entry)
+ rescue Exception => e
+ # Close menu, if still open
+ @screen.type(Sikuli::Key.ESC)
+ raise e
+ end
+ true
+ end
+end
+
+Given /^I start "([^"]+)" via the GNOME "([^"]+)"\/"([^"]+)" applications menu$/ do |app, submenu, subsubmenu|
+ menu_button = "GnomeApplicationsMenu.png"
+ sub_menu_entry = "GnomeApplications" + submenu + ".png"
+ sub_sub_menu_entry = "GnomeApplications" + subsubmenu + ".png"
+ application_entry = "GnomeApplications" + app + ".png"
+ try_for(120) do
+ begin
+ gnome_app_menu_click_helper(menu_button, sub_menu_entry)
+ gnome_app_menu_click_helper(sub_menu_entry, sub_sub_menu_entry)
+ gnome_app_menu_click_helper(sub_sub_menu_entry, application_entry)
+ gnome_app_menu_click_helper(application_entry)
+ rescue Exception => e
+ # Close menu, if still open
+ @screen.type(Sikuli::Key.ESC)
+ raise e
+ end
+ true
+ end
+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" link on the Desktop$/ do
+ @screen.wait_and_double_click("DesktopTailsDocumentationIcon.png", 10)
+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
+ 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
+ debug_log("Forcing new Tor circuit...")
+ $vm.execute_successfully('tor_control_send "signal NEWNYM"', :libs => 'tor')
+end
+
+When /^I eject the boot medium$/ do
+ dev = boot_device
+ dev_type = device_info(dev)['ID_TYPE']
+ case dev_type
+ when 'cd'
+ $vm.remove_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