Merge remote-tracking branch 'origin/test/7712-hostname-leaks' into stable
[tails-test.git] / features / step_definitions / common_steps.rb
blob3732bde51796db7ad814e82589a08bf9abebfc17
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("killall vidalia")
55       @vm.host_to_guest_time_sync
56       @vm.execute("service tor start")
57       wait_until_tor_is_working
58       @vm.spawn("/usr/local/sbin/restart-vidalia")
59     end
60   end
61 end
63 Given /^a computer$/ do
64   @vm.destroy if @vm
65   @vm = VM.new($vm_xml_path, $x_display)
66 end
68 Given /^the computer has (\d+) ([[:alpha:]]+) of RAM$/ do |size, unit|
69   next if @skip_steps_while_restoring_background
70   @vm.set_ram_size(size, unit)
71 end
73 Given /^the computer is set to boot from the Tails DVD$/ do
74   next if @skip_steps_while_restoring_background
75   @vm.set_cdrom_boot($tails_iso)
76 end
78 Given /^the computer is set to boot from (.+?) drive "(.+?)"$/ do |type, name|
79   next if @skip_steps_while_restoring_background
80   @vm.set_disk_boot(name, type.downcase)
81 end
83 Given /^I plug ([[:alpha:]]+) drive "([^"]+)"$/ do |bus, name|
84   next if @skip_steps_while_restoring_background
85   @vm.plug_drive(name, bus.downcase)
86   if @vm.is_running?
87     step "drive \"#{name}\" is detected by Tails"
88   end
89 end
91 Then /^drive "([^"]+)" is detected by Tails$/ do |name|
92   next if @skip_steps_while_restoring_background
93   if @vm.is_running?
94     try_for(10, :msg => "Drive '#{name}' is not detected by Tails") {
95       @vm.disk_detected?(name)
96     }
97   else
98     STDERR.puts "Cannot tell if drive '#{name}' is detected by Tails: " +
99                 "Tails is not running"
100   end
103 Given /^the network is plugged$/ do
104   next if @skip_steps_while_restoring_background
105   @vm.plug_network
108 Given /^the network is unplugged$/ do
109   next if @skip_steps_while_restoring_background
110   @vm.unplug_network
113 Given /^I capture all network traffic$/ do
114   # Note: We don't want skip this particular stpe if
115   # @skip_steps_while_restoring_background is set since it starts
116   # something external to the VM state.
117   @sniffer = Sniffer.new("TestSniffer", @vm.net.bridge_name)
118   @sniffer.capture
121 Given /^I set Tails to boot with options "([^"]*)"$/ do |options|
122   next if @skip_steps_while_restoring_background
123   @boot_options = options
126 When /^I start the computer$/ do
127   next if @skip_steps_while_restoring_background
128   assert(!@vm.is_running?,
129          "Trying to start a VM that is already running")
130   @vm.start
131   post_vm_start_hook
134 When /^I power off the computer$/ do
135   next if @skip_steps_while_restoring_background
136   assert(@vm.is_running?,
137          "Trying to power off an already powered off VM")
138   @vm.power_off
141 When /^I cold reboot the computer$/ do
142   next if @skip_steps_while_restoring_background
143   step "I power off the computer"
144   step "I start the computer"
147 When /^I destroy the computer$/ do
148   next if @skip_steps_while_restoring_background
149   @vm.destroy
152 Given /^the computer boots Tails$/ do
153   next if @skip_steps_while_restoring_background
154   @screen.wait('TailsBootSplash.png', 30)
155   @screen.wait('TailsBootSplashTabMsg.png', 10)
156   @screen.type(Sikuli::Key.TAB)
157   @screen.waitVanish('TailsBootSplashTabMsg.png', 1)
158   @screen.type(" autotest_never_use_this_option #{@boot_options}" +
159                Sikuli::Key.ENTER)
160   @screen.wait('TailsGreeter.png', 30*60)
161   @vm.wait_until_remote_shell_is_up
162   activate_filesystem_shares
165 Given /^I log in to a new session$/ do
166   next if @skip_steps_while_restoring_background
167   @screen.wait_and_click('TailsGreeterLoginButton.png', 10)
170 Given /^I enable more Tails Greeter options$/ do
171   next if @skip_steps_while_restoring_background
172   match = @screen.find('TailsGreeterMoreOptions.png')
173   @screen.click(match.getCenter.offset(match.w/2, match.h*2))
174   @screen.wait_and_click('TailsGreeterForward.png', 10)
175   @screen.wait('TailsGreeterLoginButton.png', 20)
178 Given /^I set sudo password "([^"]*)"$/ do |password|
179   @sudo_password = password
180   next if @skip_steps_while_restoring_background
181   @screen.wait("TailsGreeterAdminPassword.png", 20)
182   @screen.type(@sudo_password)
183   @screen.type(Sikuli::Key.TAB)
184   @screen.type(@sudo_password)
187 Given /^Tails Greeter has dealt with the sudo password$/ do
188   next if @skip_steps_while_restoring_background
189   f1 = "/etc/sudoers.d/tails-greeter"
190   f2 = "#{f1}-no-password-lecture"
191   try_for(20) {
192     @vm.execute("test -e '#{f1}' -o -e '#{f2}'").success?
193   }
196 Given /^GNOME has started$/ do
197   next if @skip_steps_while_restoring_background
198   case @theme
199   when "windows"
200     desktop_started_picture = 'WindowsStartButton.png'
201   else
202     desktop_started_picture = 'GnomeApplicationsMenu.png'
203   end
204   @screen.wait(desktop_started_picture, 180)
207 Then /^Tails seems to have booted normally$/ do
208   next if @skip_steps_while_restoring_background
209   step "GNOME has started"
212 Given /^Tor is ready$/ do
213   next if @skip_steps_while_restoring_background
214   @screen.wait("GnomeTorIsReady.png", 300)
215   @screen.waitVanish("GnomeTorIsReady.png", 15)
217   # Having seen the "Tor is ready" notification implies that Tor has
218   # built a circuit, but let's check it directly to be on the safe side.
219   step "Tor has built a circuit"
221   step "the time has synced"
224 Given /^Tor has built a circuit$/ do
225   next if @skip_steps_while_restoring_background
226   wait_until_tor_is_working
229 Given /^the time has synced$/ do
230   next if @skip_steps_while_restoring_background
231   ["/var/run/tordate/done", "/var/run/htpdate/success"].each do |file|
232     try_for(300) { @vm.execute("test -e #{file}").success? }
233   end
236 Given /^available upgrades have been checked$/ do
237   next if @skip_steps_while_restoring_background
238   try_for(300) {
239     @vm.execute("test -e '/var/run/tails-upgrader/checked_upgrades'").success?
240   }
243 Given /^Iceweasel has started and is not loading a web page$/ do
244   next if @skip_steps_while_restoring_background
245   case @theme
246   when "windows"
247     iceweasel_picture = "WindowsIceweaselWindow.png"
248   else
249     iceweasel_picture = "IceweaselWindow.png"
250   end
252   # Stop iceweasel to load its home page. We do this to prevent Tor
253   # from getting confused in case we save and restore a snapshot in
254   # the middle of loading a page.
255   @screen.wait_and_click(iceweasel_picture, 120)
256   @screen.type("l", Sikuli::KeyModifier.CTRL)
257   @screen.type("about:blank" + Sikuli::Key.ENTER)
260 Given /^all notifications have disappeared$/ do
261   next if @skip_steps_while_restoring_background
262   case @theme
263   when "windows"
264     notification_picture = "WindowsNotificationX.png"
265   else
266     notification_picture = "GnomeNotificationX.png"
267   end
268   @screen.waitVanish(notification_picture, 60)
271 Given /^I save the state so the background can be restored next scenario$/ do
272   if @skip_steps_while_restoring_background
273     assert(File.size?($background_snapshot),
274            "We have been skipping steps but there is no snapshot to restore")
275   else
276     # To be sure we run the feature from scratch we remove any
277     # leftover snapshot that wasn't removed.
278     if File.exist?($background_snapshot)
279       File.delete($background_snapshot)
280     end
281     # Workaround: when libvirt takes ownership of the snapshot it may
282     # become unwritable for the user running this script so it cannot
283     # be removed during clean up.
284     FileUtils.touch($background_snapshot)
285     FileUtils.chmod(0666, $background_snapshot)
287     # Snapshots cannot be saved while filesystem shares are mounted
288     # XXX-9p: See XXX-9p above.
289     #deactivate_filesystem_shares
291     @vm.save_snapshot($background_snapshot)
292   end
293   restore_background
294   # Now we stop skipping steps from the snapshot restore.
295   @skip_steps_while_restoring_background = false
298 Then /^I see "([^"]*)" after at most (\d+) seconds$/ do |image, time|
299   next if @skip_steps_while_restoring_background
300   @screen.wait(image, time.to_i)
303 Then /^all Internet traffic has only flowed through Tor$/ do
304   next if @skip_steps_while_restoring_background
305   leaks = FirewallLeakCheck.new(@sniffer.pcap_file, get_tor_relays)
306   if !leaks.empty?
307     if !leaks.ipv4_tcp_leaks.empty?
308       puts "The following IPv4 TCP non-Tor Internet hosts were contacted:"
309       puts leaks.ipv4_tcp_leaks.join("\n")
310       puts
311     end
312     if !leaks.ipv4_nontcp_leaks.empty?
313       puts "The following IPv4 non-TCP Internet hosts were contacted:"
314       puts leaks.ipv4_nontcp_leaks.join("\n")
315       puts
316     end
317     if !leaks.ipv6_leaks.empty?
318       puts "The following IPv6 Internet hosts were contacted:"
319       puts leaks.ipv6_leaks.join("\n")
320       puts
321     end
322     if !leaks.nonip_leaks.empty?
323       puts "Some non-IP packets were sent\n"
324     end
325     save_pcap_file
326     raise "There were network leaks!"
327   end
330 Given /^I enter the sudo password in the gksu prompt$/ do
331   next if @skip_steps_while_restoring_background
332   @screen.wait('GksuAuthPrompt.png', 60)
333   sleep 1 # wait for weird fade-in to unblock the "Ok" button
334   @screen.type(@sudo_password)
335   @screen.type(Sikuli::Key.ENTER)
336   @screen.waitVanish('GksuAuthPrompt.png', 10)
339 Given /^I enter the sudo password in the pkexec prompt$/ do
340   next if @skip_steps_while_restoring_background
341   step "I enter the \"#{@sudo_password}\" password in the pkexec prompt"
344 def deal_with_polkit_prompt (image, password)
345   @screen.wait(image, 60)
346   sleep 1 # wait for weird fade-in to unblock the "Ok" button
347   @screen.type(password)
348   @screen.type(Sikuli::Key.ENTER)
349   @screen.waitVanish(image, 10)
352 Given /^I enter the "([^"]*)" password in the pkexec prompt$/ do |password|
353   next if @skip_steps_while_restoring_background
354   deal_with_polkit_prompt('PolicyKitAuthPrompt.png', password)
357 Given /^process "([^"]+)" is running$/ do |process|
358   next if @skip_steps_while_restoring_background
359   assert(@vm.has_process?(process),
360          "Process '#{process}' is not running")
363 Given /^process "([^"]+)" is running within (\d+) seconds$/ do |process, time|
364   next if @skip_steps_while_restoring_background
365   try_for(time.to_i, :msg => "Process '#{process}' is not running after " +
366                              "waiting for #{time} seconds") do
367     @vm.has_process?(process)
368   end
371 Given /^process "([^"]+)" is not running$/ do |process|
372   next if @skip_steps_while_restoring_background
373   assert(!@vm.has_process?(process),
374          "Process '#{process}' is running")
377 Given /^I kill the process "([^"]+)"$/ do |process|
378   next if @skip_steps_while_restoring_background
379   @vm.execute("killall #{process}")
380   try_for(10, :msg => "Process '#{process}' could not be killed") {
381     !@vm.has_process?(process)
382   }
385 Then /^Tails eventually shuts down$/ do
386   next if @skip_steps_while_restoring_background
387   nr_gibs_of_ram = (detected_ram_in_MiB.to_f/(2**10)).ceil
388   timeout = nr_gibs_of_ram*5*60
389   try_for(timeout, :msg => "VM is still running after #{timeout} seconds") do
390     ! @vm.is_running?
391   end
394 Then /^Tails eventually restarts$/ do
395   next if @skip_steps_while_restoring_background
396   nr_gibs_of_ram = (detected_ram_in_MiB.to_f/(2**10)).ceil
397   @screen.wait('TailsBootSplashPostReset.png', nr_gibs_of_ram*5*60)
400 Given /^I shutdown Tails and wait for the computer to power off$/ do
401   next if @skip_steps_while_restoring_background
402   @vm.execute("poweroff")
403   step 'Tails eventually shuts down'
406 When /^I request a shutdown using the emergency shutdown applet$/ do
407   next if @skip_steps_while_restoring_background
408   @screen.hide_cursor
409   @screen.wait_and_click('TailsEmergencyShutdownButton.png', 10)
410   @screen.hide_cursor
411   @screen.wait_and_click('TailsEmergencyShutdownHalt.png', 10)
414 When /^I request a reboot using the emergency shutdown applet$/ do
415   next if @skip_steps_while_restoring_background
416   @screen.hide_cursor
417   @screen.wait_and_click('TailsEmergencyShutdownButton.png', 10)
418   @screen.hide_cursor
419   @screen.wait_and_click('TailsEmergencyShutdownReboot.png', 10)
422 Given /^package "([^"]+)" is installed$/ do |package|
423   next if @skip_steps_while_restoring_background
424   assert(@vm.execute("dpkg -s '#{package}' 2>/dev/null | grep -qs '^Status:.*installed$'").success?,
425          "Package '#{package}' is not installed")
428 When /^I start Iceweasel$/ do
429   next if @skip_steps_while_restoring_background
430   case @theme
431   when "windows"
432     step 'I click the start menu'
433     @screen.wait_and_click("WindowsApplicationsInternet.png", 10)
434     @screen.wait_and_click("WindowsApplicationsIceweasel.png", 10)
435   else
436     @screen.wait_and_click("GnomeApplicationsMenu.png", 10)
437     @screen.wait_and_click("GnomeApplicationsInternet.png", 10)
438     @screen.wait_and_click("GnomeApplicationsIceweasel.png", 10)
439   end
442 Given /^I add a wired DHCP NetworkManager connection called "([^"]+)"$/ do |con_name|
443   next if @skip_steps_while_restoring_background
444   con_content = <<EOF
445 [802-3-ethernet]
446 duplex=full
448 [connection]
449 id=#{con_name}
450 uuid=bbc60668-1be0-11e4-a9c6-2f1ce0e75bf1
451 type=802-3-ethernet
452 timestamp=1395406011
454 [ipv6]
455 method=auto
457 [ipv4]
458 method=auto
460   con_content.split("\n").each do |line|
461     @vm.execute("echo '#{line}' >> /tmp/NM.#{con_name}")
462   end
463   @vm.execute("install -m 0600 '/tmp/NM.#{con_name}' '/etc/NetworkManager/system-connections/#{con_name}'")
464   try_for(10) {
465     nm_con_list = @vm.execute("nmcli --terse --fields NAME con list").stdout
466     nm_con_list.split("\n").include? "#{con_name}"
467   }
470 Given /^I switch to the "([^"]+)" NetworkManager connection$/ do |con_name|
471   next if @skip_steps_while_restoring_background
472   @vm.execute("nmcli con up id #{con_name}")
473   try_for(60) {
474     @vm.execute("nmcli --terse --fields NAME,STATE con status").stdout.chomp == "#{con_name}:activated"
475   }