Add vertical space
[tails/matsa.git] / features / step_definitions / common_steps.rb
blob19496bb0d7080f5f09fe67c46bc5935e9b339d4f
1 require 'fileutils'
3 def post_vm_start_hook
4   # Sometimes the first click is lost (presumably it's used to give
5   # focus to virt-viewer or similar) so we do that now rather than
6   # having an important click lost. The point we click should be
7   # somewhere where no clickable elements generally reside.
8   @screen.click_point(@screen.w, @screen.h/2)
9 end
11 def activate_filesystem_shares
12   # XXX-9p: First of all, filesystem shares cannot be mounted while we
13   # do a snapshot save+restore, so unmounting+remounting them seems
14   # like a good idea. However, the 9p modules get into a broken state
15   # during the save+restore, so we also would like to unload+reload
16   # them, but loading of 9pnet_virtio fails after a restore with
17   # "probe of virtio2 failed with error -2" (in dmesg) which makes the
18   # shares unavailable. Hence we leave this code commented for now.
19   #for mod in ["9pnet_virtio", "9p"] do
20   #  @vm.execute("modprobe #{mod}")
21   #end
23   @vm.list_shares.each do |share|
24     @vm.execute("mkdir -p #{share}")
25     @vm.execute("mount -t 9p -o trans=virtio #{share} #{share}")
26   end
27 end
29 def deactivate_filesystem_shares
30   @vm.list_shares.each do |share|
31     @vm.execute("umount #{share}")
32   end
34   # XXX-9p: See XXX-9p above
35   #for mod in ["9p", "9pnet_virtio"] do
36   #  @vm.execute("modprobe -r #{mod}")
37   #end
38 end
40 def restore_background
41   @vm.restore_snapshot($background_snapshot)
42   @vm.wait_until_remote_shell_is_up
43   post_vm_start_hook
45   # XXX-9p: See XXX-9p above
46   #activate_filesystem_shares
48   # The guest's Tor's circuits' states are likely to get out of sync
49   # with the other relays, so we ensure that we have fresh circuits.
50   # Time jumps and incorrect clocks also confuses Tor in many ways.
51   if @vm.has_network?
52     if @vm.execute("service tor status").success?
53       @vm.execute("service tor stop")
54       @vm.execute("rm -f /var/log/tor/log")
55       @vm.execute("killall vidalia")
56       @vm.host_to_guest_time_sync
57       @vm.execute("service tor start")
58       wait_until_tor_is_working
59       @vm.spawn("/usr/local/sbin/restart-vidalia")
60     end
61   end
62 end
64 Given /^a computer$/ do
65   @vm.destroy if @vm
66   @vm = VM.new($vm_xml_path, $x_display)
67 end
69 Given /^the computer has (\d+) ([[:alpha:]]+) of RAM$/ do |size, unit|
70   next if @skip_steps_while_restoring_background
71   @vm.set_ram_size(size, unit)
72 end
74 Given /^the computer is set to boot from the Tails DVD$/ do
75   next if @skip_steps_while_restoring_background
76   @vm.set_cdrom_boot($tails_iso)
77 end
79 Given /^the computer is set to boot from (.+?) drive "(.+?)"$/ do |type, name|
80   next if @skip_steps_while_restoring_background
81   @vm.set_disk_boot(name, type.downcase)
82 end
84 Given /^I plug ([[:alpha:]]+) drive "([^"]+)"$/ do |bus, name|
85   next if @skip_steps_while_restoring_background
86   @vm.plug_drive(name, bus.downcase)
87   if @vm.is_running?
88     step "drive \"#{name}\" is detected by Tails"
89   end
90 end
92 Then /^drive "([^"]+)" is detected by Tails$/ do |name|
93   next if @skip_steps_while_restoring_background
94   if @vm.is_running?
95     try_for(10, :msg => "Drive '#{name}' is not detected by Tails") {
96       @vm.disk_detected?(name)
97     }
98   else
99     STDERR.puts "Cannot tell if drive '#{name}' is detected by Tails: " +
100                 "Tails is not running"
101   end
104 Given /^the network is plugged$/ do
105   next if @skip_steps_while_restoring_background
106   @vm.plug_network
109 Given /^the network is unplugged$/ do
110   next if @skip_steps_while_restoring_background
111   @vm.unplug_network
114 Given /^I capture all network traffic$/ do
115   # Note: We don't want skip this particular stpe if
116   # @skip_steps_while_restoring_background is set since it starts
117   # something external to the VM state.
118   @sniffer = Sniffer.new("TestSniffer", @vm.net.bridge_name)
119   @sniffer.capture
122 Given /^I set Tails to boot with options "([^"]*)"$/ do |options|
123   next if @skip_steps_while_restoring_background
124   @boot_options = options
127 When /^I start the computer$/ do
128   next if @skip_steps_while_restoring_background
129   assert(!@vm.is_running?,
130          "Trying to start a VM that is already running")
131   @vm.start
132   post_vm_start_hook
135 Given /^I start Tails from DVD(| with network unplugged) and I login$/ do |network_unplugged|
136   # we don't @skip_steps_while_restoring_background as we're only running
137   # other steps, that are taking care of it *if* they have to
138   step "the computer is set to boot from the Tails DVD"
139   if network_unplugged.empty?
140     step "the network is plugged"
141   else
142     step "the network is unplugged"
143   end
144   step "I start the computer"
145   step "the computer boots Tails"
146   step "I log in to a new session"
147   step "Tails seems to have booted normally"
148   if network_unplugged.empty?
149     step "Tor is ready"
150     step "all notifications have disappeared"
151     step "available upgrades have been checked"
152   else
153     step "all notifications have disappeared"
154   end
157 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|
158   # we don't @skip_steps_while_restoring_background as we're only running
159   # other steps, that are taking care of it *if* they have to
160   step "the computer is set to boot from #{drive_type} drive \"#{drive_name}\""
161   if network_unplugged.empty?
162     step "the network is plugged"
163   else
164     step "the network is unplugged"
165   end
166   step "I start the computer"
167   step "the computer boots Tails"
168   if ! persistence_on.empty?
169     assert(! persistence_pwd.empty?, "A password must be provided when enabling persistence")
170     if persistence_ro.empty?
171       step "I enable persistence with password \"#{persistence_pwd}\""
172     else
173       step "I enable read-only persistence with password \"#{persistence_pwd}\""
174     end
175   end
176   step "I log in to a new session"
177   step "Tails seems to have booted normally"
178   if network_unplugged.empty?
179     step "Tor is ready"
180     step "all notifications have disappeared"
181     step "available upgrades have been checked"
182   else
183     step "all notifications have disappeared"
184   end
187 When /^I power off the computer$/ do
188   next if @skip_steps_while_restoring_background
189   assert(@vm.is_running?,
190          "Trying to power off an already powered off VM")
191   @vm.power_off
194 When /^I cold reboot the computer$/ do
195   next if @skip_steps_while_restoring_background
196   step "I power off the computer"
197   step "I start the computer"
200 When /^I destroy the computer$/ do
201   next if @skip_steps_while_restoring_background
202   @vm.destroy
205 Given /^the computer (re)?boots Tails$/ do |reboot|
206   next if @skip_steps_while_restoring_background
208   case @os_loader
209   when "UEFI"
210     assert(!reboot, "Testing of reboot with UEFI enabled is not implemented")
211     bootsplash = 'TailsBootSplashUEFI.png'
212     bootsplash_tab_msg = 'TailsBootSplashTabMsgUEFI.png'
213     boot_timeout = 30
214   else
215     if reboot
216       bootsplash = 'TailsBootSplashPostReset.png'
217       bootsplash_tab_msg = 'TailsBootSplashTabMsgPostReset.png'
218       boot_timeout = 120
219     else
220       bootsplash = 'TailsBootSplash.png'
221       bootsplash_tab_msg = 'TailsBootSplashTabMsg.png'
222       boot_timeout = 30
223     end
224   end
226   @screen.wait(bootsplash, boot_timeout)
227   @screen.wait(bootsplash_tab_msg, 10)
228   @screen.type(Sikuli::Key.TAB)
229   @screen.waitVanish(bootsplash_tab_msg, 1)
231   @screen.type(" autotest_never_use_this_option #{@boot_options}" +
232                Sikuli::Key.ENTER)
233   @screen.wait('TailsGreeter.png', 30*60)
234   @vm.wait_until_remote_shell_is_up
235   activate_filesystem_shares
238 Given /^I log in to a new session$/ do
239   next if @skip_steps_while_restoring_background
240   @screen.wait_and_click('TailsGreeterLoginButton.png', 10)
243 Given /^I enable more Tails Greeter options$/ do
244   next if @skip_steps_while_restoring_background
245   match = @screen.find('TailsGreeterMoreOptions.png')
246   @screen.click(match.getCenter.offset(match.w/2, match.h*2))
247   @screen.wait_and_click('TailsGreeterForward.png', 10)
248   @screen.wait('TailsGreeterLoginButton.png', 20)
251 Given /^I set sudo password "([^"]*)"$/ do |password|
252   @sudo_password = password
253   next if @skip_steps_while_restoring_background
254   @screen.wait("TailsGreeterAdminPassword.png", 20)
255   @screen.type(@sudo_password)
256   @screen.type(Sikuli::Key.TAB)
257   @screen.type(@sudo_password)
260 Given /^Tails Greeter has dealt with the sudo password$/ do
261   next if @skip_steps_while_restoring_background
262   f1 = "/etc/sudoers.d/tails-greeter"
263   f2 = "#{f1}-no-password-lecture"
264   try_for(20) {
265     @vm.execute("test -e '#{f1}' -o -e '#{f2}'").success?
266   }
269 Given /^GNOME has started$/ do
270   next if @skip_steps_while_restoring_background
271   case @theme
272   when "windows"
273     desktop_started_picture = 'WindowsStartButton.png'
274   else
275     desktop_started_picture = 'GnomeApplicationsMenu.png'
276   end
277   @screen.wait(desktop_started_picture, 180)
280 Then /^Tails seems to have booted normally$/ do
281   next if @skip_steps_while_restoring_background
282   step "GNOME has started"
285 Given /^Tor is ready$/ do
286   next if @skip_steps_while_restoring_background
287   @screen.wait("GnomeTorIsReady.png", 300)
288   @screen.waitVanish("GnomeTorIsReady.png", 15)
290   # Having seen the "Tor is ready" notification implies that Tor has
291   # built a circuit, but let's check it directly to be on the safe side.
292   step "Tor has built a circuit"
294   step "the time has synced"
297 Given /^Tor has built a circuit$/ do
298   next if @skip_steps_while_restoring_background
299   wait_until_tor_is_working
302 Given /^the time has synced$/ do
303   next if @skip_steps_while_restoring_background
304   ["/var/run/tordate/done", "/var/run/htpdate/success"].each do |file|
305     try_for(300) { @vm.execute("test -e #{file}").success? }
306   end
309 Given /^available upgrades have been checked$/ do
310   next if @skip_steps_while_restoring_background
311   try_for(300) {
312     @vm.execute("test -e '/var/run/tails-upgrader/checked_upgrades'").success?
313   }
316 Given /^the Tor Browser has started$/ do
317   next if @skip_steps_while_restoring_background
318   case @theme
319   when "windows"
320     tor_browser_picture = "WindowsTorBrowserWindow.png"
321   else
322     tor_browser_picture = "TorBrowserWindow.png"
323   end
325   @screen.wait(tor_browser_picture, 60)
328 Given /^the Tor Browser has started and loaded the startup page$/ do
329   next if @skip_steps_while_restoring_background
330   step "the Tor Browser has started"
331   @screen.wait("TorBrowserStartupPage.png", 120)
334 Given /^the Tor Browser has started in offline mode$/ do
335   next if @skip_steps_while_restoring_background
336   @screen.wait("TorBrowserOffline.png", 60)
339 Given /^I add a bookmark to eff.org in the Tor Browser$/ do
340   next if @skip_steps_while_restoring_background
341   url = "https://www.eff.org"
342   step "I open the address \"#{url}\" in the Tor Browser"
343   @screen.wait("TorBrowserOffline.png", 5)
344   @screen.type("d", Sikuli::KeyModifier.CTRL)
345   @screen.wait("TorBrowserBookmarkPrompt.png", 10)
346   @screen.type(url + Sikuli::Key.ENTER)
349 Given /^the Tor Browser has a bookmark to eff.org$/ do
350   next if @skip_steps_while_restoring_background
351   @screen.type("b", Sikuli::KeyModifier.ALT)
352   @screen.wait("TorBrowserEFFBookmark.png", 10)
355 Given /^all notifications have disappeared$/ do
356   next if @skip_steps_while_restoring_background
357   case @theme
358   when "windows"
359     notification_picture = "WindowsNotificationX.png"
360   else
361     notification_picture = "GnomeNotificationX.png"
362   end
363   @screen.waitVanish(notification_picture, 60)
366 Given /^I save the state so the background can be restored next scenario$/ do
367   if @skip_steps_while_restoring_background
368     assert(File.size?($background_snapshot),
369            "We have been skipping steps but there is no snapshot to restore")
370   else
371     # To be sure we run the feature from scratch we remove any
372     # leftover snapshot that wasn't removed.
373     if File.exist?($background_snapshot)
374       File.delete($background_snapshot)
375     end
376     # Workaround: when libvirt takes ownership of the snapshot it may
377     # become unwritable for the user running this script so it cannot
378     # be removed during clean up.
379     FileUtils.touch($background_snapshot)
380     FileUtils.chmod(0666, $background_snapshot)
382     # Snapshots cannot be saved while filesystem shares are mounted
383     # XXX-9p: See XXX-9p above.
384     #deactivate_filesystem_shares
386     @vm.save_snapshot($background_snapshot)
387   end
388   restore_background
389   # Now we stop skipping steps from the snapshot restore.
390   @skip_steps_while_restoring_background = false
393 Then /^I see "([^"]*)" after at most (\d+) seconds$/ do |image, time|
394   next if @skip_steps_while_restoring_background
395   @screen.wait(image, time.to_i)
398 Then /^all Internet traffic has only flowed through Tor$/ do
399   next if @skip_steps_while_restoring_background
400   leaks = FirewallLeakCheck.new(@sniffer.pcap_file, get_tor_relays)
401   if !leaks.empty?
402     if !leaks.ipv4_tcp_leaks.empty?
403       puts "The following IPv4 TCP non-Tor Internet hosts were contacted:"
404       puts leaks.ipv4_tcp_leaks.join("\n")
405       puts
406     end
407     if !leaks.ipv4_nontcp_leaks.empty?
408       puts "The following IPv4 non-TCP Internet hosts were contacted:"
409       puts leaks.ipv4_nontcp_leaks.join("\n")
410       puts
411     end
412     if !leaks.ipv6_leaks.empty?
413       puts "The following IPv6 Internet hosts were contacted:"
414       puts leaks.ipv6_leaks.join("\n")
415       puts
416     end
417     if !leaks.nonip_leaks.empty?
418       puts "Some non-IP packets were sent\n"
419     end
420     save_pcap_file
421     raise "There were network leaks!"
422   end
425 Given /^I enter the sudo password in the gksu prompt$/ do
426   next if @skip_steps_while_restoring_background
427   @screen.wait('GksuAuthPrompt.png', 60)
428   sleep 1 # wait for weird fade-in to unblock the "Ok" button
429   @screen.type(@sudo_password)
430   @screen.type(Sikuli::Key.ENTER)
431   @screen.waitVanish('GksuAuthPrompt.png', 10)
434 Given /^I enter the sudo password in the pkexec prompt$/ do
435   next if @skip_steps_while_restoring_background
436   step "I enter the \"#{@sudo_password}\" password in the pkexec prompt"
439 def deal_with_polkit_prompt (image, password)
440   @screen.wait(image, 60)
441   sleep 1 # wait for weird fade-in to unblock the "Ok" button
442   @screen.type(password)
443   @screen.type(Sikuli::Key.ENTER)
444   @screen.waitVanish(image, 10)
447 Given /^I enter the "([^"]*)" password in the pkexec prompt$/ do |password|
448   next if @skip_steps_while_restoring_background
449   deal_with_polkit_prompt('PolicyKitAuthPrompt.png', password)
452 Given /^process "([^"]+)" is running$/ do |process|
453   next if @skip_steps_while_restoring_background
454   assert(@vm.has_process?(process),
455          "Process '#{process}' is not running")
458 Given /^process "([^"]+)" is running within (\d+) seconds$/ do |process, time|
459   next if @skip_steps_while_restoring_background
460   try_for(time.to_i, :msg => "Process '#{process}' is not running after " +
461                              "waiting for #{time} seconds") do
462     @vm.has_process?(process)
463   end
466 Given /^process "([^"]+)" is not running$/ do |process|
467   next if @skip_steps_while_restoring_background
468   assert(!@vm.has_process?(process),
469          "Process '#{process}' is running")
472 Given /^I kill the process "([^"]+)"$/ do |process|
473   next if @skip_steps_while_restoring_background
474   @vm.execute("killall #{process}")
475   try_for(10, :msg => "Process '#{process}' could not be killed") {
476     !@vm.has_process?(process)
477   }
480 Then /^Tails eventually shuts down$/ do
481   next if @skip_steps_while_restoring_background
482   nr_gibs_of_ram = (detected_ram_in_MiB.to_f/(2**10)).ceil
483   timeout = nr_gibs_of_ram*5*60
484   try_for(timeout, :msg => "VM is still running after #{timeout} seconds") do
485     ! @vm.is_running?
486   end
489 Then /^Tails eventually restarts$/ do
490   next if @skip_steps_while_restoring_background
491   nr_gibs_of_ram = (detected_ram_in_MiB.to_f/(2**10)).ceil
492   @screen.wait('TailsBootSplashPostReset.png', nr_gibs_of_ram*5*60)
495 Given /^I shutdown Tails and wait for the computer to power off$/ do
496   next if @skip_steps_while_restoring_background
497   @vm.execute("poweroff")
498   step 'Tails eventually shuts down'
501 When /^I request a shutdown using the emergency shutdown applet$/ do
502   next if @skip_steps_while_restoring_background
503   @screen.hide_cursor
504   @screen.wait_and_click('TailsEmergencyShutdownButton.png', 10)
505   @screen.hide_cursor
506   @screen.wait_and_click('TailsEmergencyShutdownHalt.png', 10)
509 When /^I warm reboot the computer$/ do
510   next if @skip_steps_while_restoring_background
511   @vm.execute("reboot")
514 When /^I request a reboot using the emergency shutdown applet$/ do
515   next if @skip_steps_while_restoring_background
516   @screen.hide_cursor
517   @screen.wait_and_click('TailsEmergencyShutdownButton.png', 10)
518   @screen.hide_cursor
519   @screen.wait_and_click('TailsEmergencyShutdownReboot.png', 10)
522 Given /^package "([^"]+)" is installed$/ do |package|
523   next if @skip_steps_while_restoring_background
524   assert(@vm.execute("dpkg -s '#{package}' 2>/dev/null | grep -qs '^Status:.*installed$'").success?,
525          "Package '#{package}' is not installed")
528 When /^I start the Tor Browser$/ do
529   next if @skip_steps_while_restoring_background
530   step 'I start "TorBrowser" via the GNOME "Internet" applications menu'
533 When /^I start the Tor Browser in offline mode$/ do
534   next if @skip_steps_while_restoring_background
535   step "I start the Tor Browser"
536   case @theme
537   when "windows"
538     @screen.wait_and_click("WindowsTorBrowserOfflinePrompt.png", 10)
539     @screen.click("WindowsTorBrowserOfflinePromptStart.png")
540   else
541     @screen.wait_and_click("TorBrowserOfflinePrompt.png", 10)
542     @screen.click("TorBrowserOfflinePromptStart.png")
543   end
546 def xul_app_shared_lib_check(pid, chroot)
547   expected_absent_tbb_libs = ['libnssdbm3.so']
548   absent_tbb_libs = []
549   unwanted_native_libs = []
550   tbb_libs = @vm.execute_successfully(
551                  ". /usr/local/lib/tails-shell-library/tor-browser.sh; " +
552                  "ls -1 #{chroot}${TBB_INSTALL}/*.so"
553                                       ).stdout.split
554   firefox_pmap_info = @vm.execute("pmap #{pid}").stdout
555   for lib in tbb_libs do
556     lib_name = File.basename lib
557     if not /\W#{lib}$/.match firefox_pmap_info
558       absent_tbb_libs << lib_name
559     end
560     native_libs = @vm.execute_successfully(
561                        "find /usr/lib /lib -name \"#{lib_name}\""
562                                            ).stdout.split
563     for native_lib in native_libs do
564       if /\W#{native_lib}$"/.match firefox_pmap_info
565         unwanted_native_libs << lib_name
566       end
567     end
568   end
569   absent_tbb_libs -= expected_absent_tbb_libs
570   assert(absent_tbb_libs.empty? && unwanted_native_libs.empty?,
571          "The loaded shared libraries for the firefox process are not the " +
572          "way we expect them.\n" +
573          "Expected TBB libs that are absent: #{absent_tbb_libs}\n" +
574          "Native libs that we don't want: #{unwanted_native_libs}")
577 Then /^(.*) uses all expected TBB shared libraries$/ do |application|
578   next if @skip_steps_while_restoring_background
579   binary = @vm.execute_successfully(
580                 '. /usr/local/lib/tails-shell-library/tor-browser.sh; ' +
581                 'echo ${TBB_INSTALL}/firefox'
582                                     ).stdout.chomp
583   case application
584   when "the Tor Browser"
585     user = $live_user
586     cmd_regex = "#{binary} .* -profile /home/#{user}/\.tor-browser/profile\.default"
587     chroot = ""
588   when "the Unsafe Browser"
589     user = "clearnet"
590     cmd_regex = "#{binary} .* -profile /home/#{user}/\.tor-browser/profile\.default"
591     chroot = "/var/lib/unsafe-browser/chroot"
592   when "Tor Launcher"
593     user = "tor-launcher"
594     cmd_regex = "#{binary} -app /home/#{user}/\.tor-launcher/tor-launcher-standalone/application\.ini"
595     chroot = ""
596   else
597     raise "Invalid browser or XUL application: #{application}"
598   end
599   pid = @vm.execute_successfully("pgrep --uid #{user} --full --exact '#{cmd_regex}'").stdout.chomp
600   assert(/\A\d+\z/.match(pid), "It seems like #{application} is not running")
601   xul_app_shared_lib_check(pid, chroot)
604 Given /^I add a wired DHCP NetworkManager connection called "([^"]+)"$/ do |con_name|
605   next if @skip_steps_while_restoring_background
606   con_content = <<EOF
607 [802-3-ethernet]
608 duplex=full
610 [connection]
611 id=#{con_name}
612 uuid=bbc60668-1be0-11e4-a9c6-2f1ce0e75bf1
613 type=802-3-ethernet
614 timestamp=1395406011
616 [ipv6]
617 method=auto
619 [ipv4]
620 method=auto
622   con_content.split("\n").each do |line|
623     @vm.execute("echo '#{line}' >> /tmp/NM.#{con_name}")
624   end
625   @vm.execute("install -m 0600 '/tmp/NM.#{con_name}' '/etc/NetworkManager/system-connections/#{con_name}'")
626   try_for(10) {
627     nm_con_list = @vm.execute("nmcli --terse --fields NAME con list").stdout
628     nm_con_list.split("\n").include? "#{con_name}"
629   }
632 Given /^I switch to the "([^"]+)" NetworkManager connection$/ do |con_name|
633   next if @skip_steps_while_restoring_background
634   @vm.execute("nmcli con up id #{con_name}")
635   try_for(60) {
636     @vm.execute("nmcli --terse --fields NAME,STATE con status").stdout.chomp == "#{con_name}:activated"
637   }
640 When /^I start and focus GNOME Terminal$/ do
641   next if @skip_steps_while_restoring_background
642   step 'I start "Terminal" via the GNOME "Accessories" applications menu'
643   @screen.wait_and_click('GnomeTerminalWindow.png', 20)
646 When /^I run "([^"]+)" in GNOME Terminal$/ do |command|
647   next if @skip_steps_while_restoring_background
648   step "I start and focus GNOME Terminal"
649   @screen.type(command + Sikuli::Key.ENTER)
652 When /^the file "([^"]+)" exists$/ do |file|
653   next if @skip_steps_while_restoring_background
654   assert(@vm.file_exist?(file))
657 When /^I copy "([^"]+)" to "([^"]+)" as user "([^"]+)"$/ do |source, destination, user|
658   next if @skip_steps_while_restoring_background
659   c = @vm.execute("cp \"#{source}\" \"#{destination}\"", $live_user)
660   assert(c.success?, "Failed to copy file:\n#{c.stdout}\n#{c.stderr}")
663 Given /^the USB drive "([^"]+)" contains Tails with persistence configured and password "([^"]+)"$/ do |drive, password|
664     step "a computer"
665     step "I start Tails from DVD with network unplugged and I login"
666     step "I create a new 4 GiB USB drive named \"#{drive}\""
667     step "I plug USB drive \"#{drive}\""
668     step "I \"Clone & Install\" Tails to USB drive \"#{drive}\""
669     step "there is no persistence partition on USB drive \"#{drive}\""
670     step "I shutdown Tails and wait for the computer to power off"
671     step "a computer"
672     step "I start Tails from USB drive \"#{drive}\" with network unplugged and I login"
673     step "I create a persistent partition with password \"#{password}\""
674     step "a Tails persistence partition with password \"#{password}\" exists on USB drive \"#{drive}\""
675     step "I shutdown Tails and wait for the computer to power off"
678 Given /^I start "([^"]+)" via the GNOME "([^"]+)" applications menu$/ do |app, submenu|
679   next if @skip_steps_while_restoring_background
680   case @theme
681   when "windows"
682     prefix = 'Windows'
683   else
684     prefix = 'Gnome'
685   end
686   @screen.wait_and_click(prefix + "ApplicationsMenu.png", 10)
687   @screen.wait_and_hover(prefix + "Applications" + submenu + ".png", 40)
688   @screen.wait_and_click(prefix + "Applications" + app + ".png", 40)
691 Given /^I start "([^"]+)" via the GNOME "([^"]+)"\/"([^"]+)" applications menu$/ do |app, submenu, subsubmenu|
692   next if @skip_steps_while_restoring_background
693   case @theme
694   when "windows"
695     prefix = 'Windows'
696   else
697     prefix = 'Gnome'
698   end
699   @screen.wait_and_click(prefix + "ApplicationsMenu.png", 10)
700   @screen.wait_and_hover(prefix + "Applications" + submenu + ".png", 20)
701   @screen.wait_and_hover(prefix + "Applications" + subsubmenu + ".png", 20)
702   @screen.wait_and_click(prefix + "Applications" + app + ".png", 20)