From a6f41c35e337db192e612ee6e1545fcae4c69ac7 Mon Sep 17 00:00:00 2001 From: Philip Hands Date: Thu, 29 Jun 2017 22:11:09 +0200 Subject: lvc: grab updates from tails (01371c19bd..6ae59c49e5) Signed-off-by: Holger Levsen --- cucumber/features/step_definitions/common_steps.rb | 579 +++++++++++---------- 1 file changed, 291 insertions(+), 288 deletions(-) (limited to 'cucumber/features/step_definitions/common_steps.rb') diff --git a/cucumber/features/step_definitions/common_steps.rb b/cucumber/features/step_definitions/common_steps.rb index feec90e2..ca7604f8 100644 --- a/cucumber/features/step_definitions/common_steps.rb +++ b/cucumber/features/step_definitions/common_steps.rb @@ -8,24 +8,6 @@ def post_vm_start_hook @screen.click_point(@screen.w-1, @screen.h/2) end -def activate_filesystem_shares - # XXX-9p: First of all, filesystem shares cannot be mounted while we - # do a snapshot save+restore, so unmounting+remounting them seems - # like a good idea. However, the 9p modules get into a broken state - # during the save+restore, so we also would like to unload+reload - # them, but loading of 9pnet_virtio fails after a restore with - # "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}") - #end - - $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) @@ -41,64 +23,12 @@ def context_menu_helper(top, bottom, menu_item) end end -def deactivate_filesystem_shares - $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}") - #end -end - -# 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 # 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 - # XXX-9p: See XXX-9p above - #activate_filesystem_shares - # 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. @@ -106,18 +36,10 @@ def post_snapshot_restore_hook #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") + # $vm.execute("systemctl start tor@default.service") # wait_until_tor_is_working - # 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 @@ -137,10 +59,6 @@ Given /^a computer$/ do $vm = VM.new($virt, VM_XML_PATH, $vmnet, $vmstorage, DISPLAY) end -Given /^the computer has (\d+) ([[:alpha:]]+) of RAM$/ do |size, unit| - $vm.set_ram_size(size, unit) -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 @@ -156,7 +74,7 @@ Given /^the computer is set to boot from (.+?) drive$/ do |type| $vm.set_disk_boot(JOB_NAME, type.downcase) end -Given /^I (temporarily )?create a (\d+) ([[:alpha:]]+) disk named "([^"]+)"$/ do |temporary, size, unit, name| +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 @@ -184,6 +102,11 @@ 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 @@ -220,51 +143,43 @@ 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.nil? - step "the network is plugged" - else + 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" - step "Tails seems to have booted normally" - if network_unplugged.nil? - step "Tor is ready" + if network_unplugged step "all notifications have disappeared" - step "available upgrades have been checked" 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(| read-only) persistence enabled))?$/ do |drive_type, drive_name, network_unplugged, do_login, persistence_on, persistence_ro| +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.empty? - step "the network is plugged" - else + 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 - if ! persistence_on.empty? - if persistence_ro.empty? - step "I enable persistence" - else - step "I enable read-only persistence" - end - end + step "I enable persistence" if persistence_on step "I log in to a new session" - step "Tails seems to have booted normally" - if network_unplugged.empty? - step "Tor is ready" + if network_unplugged step "all notifications have disappeared" - step "available upgrades have been checked" else + step "Tor is ready" step "all notifications have disappeared" + step "available upgrades have been checked" end end end @@ -691,16 +606,16 @@ Given /^I should see a ([a-zA-Z]*) Login prompt$/ do |style| @screen.waitAny(loginPrompt[style], 20 * 60) end -def bootsplash +def boot_menu_cmdline_image case @os_loader when "UEFI" - 'TailsBootSplashUEFI.png' + 'TailsBootMenuKernelCmdlineUEFI.png' else 'd-i8_bootsplash.png' end end -def bootsplash_tab_msg +def boot_menu_tab_msg_image case @os_loader when "UEFI" 'TailsBootSplashTabMsgUEFI.png' @@ -719,21 +634,12 @@ def bootsplash_tab_msg 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) - + 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("DebianLive#{version}Greeter.png", 5*60) - @vm.wait_until_remote_shell_is_up - activate_filesystem_shares + @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| @@ -741,37 +647,60 @@ Given /^I log in to a new session(?: in )?(|German)$/ do |lang| when 'German' @language = "German" @screen.wait_and_click('TailsGreeterLanguage.png', 10) - @screen.wait_and_click("TailsGreeterLanguage#{@language}.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 -Given /^I set sudo password "([^"]*)"$/ do |password| - @sudo_password = password - next if @skip_steps_while_restoring_background - #@screen.wait("TailsGreeterAdminPassword.png", 20) +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 dealt with the sudo password$/ do - f1 = "/etc/sudoers.d/tails-greeter" - f2 = "#{f1}-no-password-lecture" - try_for(20) { - $vm.execute("test -e '#{f1}' -o -e '#{f2}'").success? +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" - # 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) + # 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. @@ -779,14 +708,22 @@ Given /^the Tails desktop is ready$/ do '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 -Then /^Tails seems to have booted normally$/ do - step "the Tails desktop is ready" -end - -When /^I see the 'Tor is ready' notification$/ do - robust_notification_wait('TorIsReadyNotification.png', 300) +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 @@ -803,43 +740,63 @@ Given /^Tor has built a circuit$/ do end Given /^the time has synced$/ do - ["/var/run/tordate/done", "/var/run/htpdate/success"].each do |file| + ["/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 '/var/run/tails-upgrader/checked_upgrades'").success? + $vm.execute("test -e '/run/tails-upgrader/checked_upgrades'").success? } end -Given /^the Tor Browser has started$/ do - tor_browser_picture = "TorBrowserWindow.png" - @screen.wait(tor_browser_picture, 60) +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 and )?load(?:ed|s) the (startup page|Tails roadmap)$/ do |page| +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" - picture = "TorBrowserStartupPage.png" + title = 'Tails - News' when "Tails roadmap" - picture = "TorBrowserTailsRoadmap.png" + title = 'Roadmap - Tails - RiseupLabs Code Repository' else raise "Unsupported page: #{page}" end - step "the Tor Browser has started" - @screen.wait(picture, 120) + step "\"#{title}\" has loaded in the Tor Browser" end -Given /^the Tor Browser has started in offline mode$/ do - @screen.wait("TorBrowserOffline.png", 60) +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" - @screen.wait("TorBrowserOffline.png", 5) + 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) @@ -851,24 +808,18 @@ Given /^the Tor Browser has a bookmark to eff.org$/ do end Given /^all notifications have disappeared$/ do - 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) + # 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 - rescue FindFailed - # No notifications, so we're good to go. + gnome_shell.child?('No Notifications', roleName: 'label') end - @screen.hide_cursor - # Click anywhere to close the notification applet - @screen.click("GnomeApplicationsMenu.png") - @screen.hide_cursor + @screen.type(Sikuli::Key.ESC) end Then /^I (do not )?see "([^"]*)" after at most (\d+) seconds$/ do |negation, image, time| @@ -890,24 +841,31 @@ Then /^I (do not )?see the "([^"]*)" screen, after at most (\d+) seconds$/ do |n end 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 + 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 (image, password) +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) - @screen.waitVanish(image, 10) + 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('PolicyKitAuthPrompt.png', password) + deal_with_polkit_prompt(password) end Given /^process "([^"]+)" is (not )?running$/ do |process, not_running| @@ -939,19 +897,17 @@ Given /^I kill the process "([^"]+)"$/ do |process| } end -Then /^Tails eventually shuts down$/ do - 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? +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 -Then /^Tails eventually restarts$/ do - 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 $vm.spawn("poweroff") step 'Tails eventually shuts down' @@ -960,6 +916,11 @@ 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 @@ -970,57 +931,38 @@ 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 /^package "([^"]+)" is installed$/ do |package| +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 -When /^I start the Tor Browser$/ do - step 'I start "TorBrowser" via the GNOME "Internet" applications menu' -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 - -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| - con_content = <> /tmp/NM.#{con_name}") + 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 - 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 connection show").stdout nm_con_list.split("\n").include? "#{con_name}" @@ -1035,8 +977,8 @@ Given /^I switch to the "([^"]+)" NetworkManager connection$/ do |con_name| end When /^I start and focus GNOME Terminal$/ do - step 'I start "Terminal" via the GNOME "Utilities" applications menu' - @screen.wait('GnomeTerminalWindow.png', 20) + step 'I start "GNOME Terminal" via GNOME Activities Overview' + @screen.wait('GnomeTerminalWindow.png', 40) end When /^I run "([^"]+)" in GNOME Terminal$/ do |command| @@ -1091,57 +1033,12 @@ Then /^persistence for "([^"]+)" is (|not )enabled$/ do |app, 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 +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| @@ -1198,8 +1095,14 @@ When /^(no|\d+) application(?:s?) (?:is|are) playing audio(?:| after (\d+) secon 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) +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 @@ -1265,9 +1168,9 @@ When /^I can print the current page as "([^"]+[.]pdf)" to the (default downloads 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_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 @@ -1282,14 +1185,15 @@ Given /^a web server is running on the LAN$/ do 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}, + 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 @@ -1365,8 +1269,7 @@ When /^AppArmor has (not )?denied "([^"]+)" from opening "([^"]+)"(?: after at m 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') + force_new_tor_circuit end When /^I eject the boot medium$/ do @@ -1374,7 +1277,7 @@ When /^I eject the boot medium$/ do dev_type = device_info(dev)['ID_TYPE'] case dev_type when 'cd' - $vm.remove_cdrom + $vm.eject_cdrom when 'disk' boot_disk_name = $vm.disk_name(dev) $vm.unplug_drive(boot_disk_name) @@ -1382,3 +1285,103 @@ When /^I eject the boot medium$/ do 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 -- cgit v1.2.3-70-g09d2