common_steps.rb 35.7 KB
Newer Older
1
2
require 'fileutils'

3
4
5
6
7
def post_vm_start_hook
  # Sometimes the first click is lost (presumably it's used to give
  # focus to virt-viewer or similar) so we do that now rather than
  # having an important click lost. The point we click should be
  # somewhere where no clickable elements generally reside.
8
  @screen.click_point(@screen.w, @screen.h/2)
9
10
end

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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 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

40
41
42
43
44
45
46
47
48
49
50
def notification_helper(notification_image, time_to_wait)
  # notifiction-daemon may abort during start-up, causing the tests that look for
  # desktop notifications to fail (ticket #8686)
  begin
    @screen.wait(notification_image, time_to_wait)
  rescue FindFailed => e
    step 'process "notification-daemon" is running'
    raise e
  end
end

51
def restore_background
52
  @vm.restore_snapshot($background_snapshot)
53
  @vm.wait_until_remote_shell_is_up
54
  post_vm_start_hook
55
56
57
58

  # XXX-9p: See XXX-9p above
  #activate_filesystem_shares

59
60
61
  # 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.
62
  if @vm.has_network?
63
64
    if @vm.execute("service tor status").success?
      @vm.execute("service tor stop")
65
      @vm.execute("rm -f /var/log/tor/log")
66
67
      @vm.execute("killall vidalia")
      @vm.host_to_guest_time_sync
68
      @vm.execute("service tor start")
69
      wait_until_tor_is_working
70
      @vm.spawn("restart-vidalia")
71
    end
72
73
  else
    @vm.host_to_guest_time_sync
74
75
76
  end
end

77
Given /^a computer$/ do
78
  @vm.destroy_and_undefine if @vm
79
  @vm = VM.new($virt, VM_XML_PATH, $vmnet, $vmstorage, DISPLAY)
80
81
end

82
83
84
85
86
Given /^the computer has (\d+) ([[:alpha:]]+) of RAM$/ do |size, unit|
  next if @skip_steps_while_restoring_background
  @vm.set_ram_size(size, unit)
end

87
Given /^the computer is set to boot from the Tails DVD$/ do
88
  next if @skip_steps_while_restoring_background
89
  @vm.set_cdrom_boot(TAILS_ISO)
90
91
end

92
93
94
95
96
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)
end

97
98
99
100
101
102
Given /^I create a (\d+) ([[:alpha:]]+) disk named "([^"]+)"$/ do |size, unit, name|
  next if @skip_steps_while_restoring_background
  @vm.storage.create_new_disk(name, {:size => size, :unit => unit,
                                     :type => "qcow2"})
end

103
Given /^I plug (.+) drive "([^"]+)"$/ do |bus, name|
104
105
106
107
108
109
110
111
  next if @skip_steps_while_restoring_background
  @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|
112
  next if @skip_steps_while_restoring_background
113
114
115
116
117
118
119
120
121
122
  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"
  end
end

123
Given /^the network is plugged$/ do
124
125
126
  # We don't skip this step when restoring the background to ensure
  # that the network state is actually the same after restoring as
  # when the snapshot was made.
127
128
129
130
  @vm.plug_network
end

Given /^the network is unplugged$/ do
131
  # See comment in the step "the network is plugged".
132
133
134
  @vm.unplug_network
end

135
136
137
138
139
Given /^the hardware clock is set to "([^"]*)"$/ do |time|
  next if @skip_steps_while_restoring_background
  @vm.set_hardware_clock(DateTime.parse(time).to_time)
end

140
141
142
143
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.
144
  @sniffer = Sniffer.new("sniffer", $vmnet)
145
  @sniffer.capture
146
147
148
149
  add_after_scenario_hook do
    @sniffer.stop
    @sniffer.clear
  end
150
151
152
153
154
155
156
157
158
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
159
160
  assert(!@vm.is_running?,
         "Trying to start a VM that is already running")
161
  @vm.start
162
  post_vm_start_hook
163
164
end

165
Given /^I start Tails( from DVD)?( with network unplugged)? and I login$/ do |dvd_boot, network_unplugged|
166
167
  # 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
168
169
  step "the computer is set to boot from the Tails DVD" if dvd_boot
  if network_unplugged.nil?
170
171
172
173
    step "the network is plugged"
  else
    step "the network is unplugged"
  end
174
175
176
177
  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"
178
  if network_unplugged.nil?
179
180
181
182
183
184
    step "Tor is ready"
    step "all notifications have disappeared"
    step "available upgrades have been checked"
  else
    step "all notifications have disappeared"
  end
185
186
end

187
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|
188
189
190
  # 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 #{drive_type} drive \"#{drive_name}\""
191
192
193
194
195
  if network_unplugged.empty?
    step "the network is plugged"
  else
    step "the network is unplugged"
  end
196
197
  step "I start the computer"
  step "the computer boots Tails"
198
199
200
201
202
203
204
205
  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}\""
    else
      step "I enable read-only persistence with password \"#{persistence_pwd}\""
    end
  end
206
207
  step "I log in to a new session"
  step "Tails seems to have booted normally"
208
209
210
211
212
213
214
  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
215
216
end

217
218
When /^I power off the computer$/ do
  next if @skip_steps_while_restoring_background
219
220
  assert(@vm.is_running?,
         "Trying to power off an already powered off VM")
221
222
223
224
225
226
227
228
229
230
231
  @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
232
  @vm.destroy_and_undefine
233
234
end

235
Given /^the computer (re)?boots Tails$/ do |reboot|
236
  next if @skip_steps_while_restoring_background
237

238
239
240
241
  boot_timeout = 30
  # We need some extra time for memory wiping if rebooting
  boot_timeout += 90 if reboot

242
243
  case @os_loader
  when "UEFI"
244
245
    bootsplash = 'TailsBootSplashUEFI.png'
    bootsplash_tab_msg = 'TailsBootSplashTabMsgUEFI.png'
246
  else
247
248
    bootsplash = 'TailsBootSplash.png'
    bootsplash_tab_msg = 'TailsBootSplashTabMsg.png'
249
250
  end

251
252
  @screen.wait(bootsplash, boot_timeout)
  @screen.wait(bootsplash_tab_msg, 10)
253
  @screen.type(Sikuli::Key.TAB)
254
  @screen.waitVanish(bootsplash_tab_msg, 1)
255

anonym's avatar
anonym committed
256
  @screen.type(" autotest_never_use_this_option blacklist=psmouse #{@boot_options}" +
257
               Sikuli::Key.ENTER)
Tails developers's avatar
Tails developers committed
258
  @screen.wait('TailsGreeter.png', 30*60)
259
  @vm.wait_until_remote_shell_is_up
260
  activate_filesystem_shares
261
262
end

263
Given /^I log in to a new session(?: in )?(|German)$/ do |lang|
264
  next if @skip_steps_while_restoring_background
265
266
267
  case lang
  when 'German'
    @language = "German"
268
269
270
    @screen.wait_and_click('TailsGreeterLanguage.png', 10)
    @screen.wait_and_click("TailsGreeterLanguage#{@language}.png", 10)
    @screen.wait_and_click("TailsGreeterLoginButton#{@language}.png", 10)
271
272
273
274
275
  when ''
    @screen.wait_and_click('TailsGreeterLoginButton.png', 10)
  else
    raise "Unsupported language: #{lang}"
  end
276
277
end

278
Given /^I enable more Tails Greeter options$/ do
279
  next if @skip_steps_while_restoring_background
280
  match = @screen.find('TailsGreeterMoreOptions.png')
281
  @screen.click(match.getCenter.offset(match.w/2, match.h*2))
282
  @screen.wait_and_click('TailsGreeterForward.png', 10)
283
284
285
  @screen.wait('TailsGreeterLoginButton.png', 20)
end

286
287
288
289
290
Given /^I enable the specific Tor configuration option$/ do
  next if @skip_steps_while_restoring_background
  @screen.click('TailsGreeterTorConf.png')
end

291
292
293
294
Given /^I set sudo password "([^"]*)"$/ do |password|
  @sudo_password = password
  next if @skip_steps_while_restoring_background
  @screen.wait("TailsGreeterAdminPassword.png", 20)
295
296
297
  @screen.type(@sudo_password)
  @screen.type(Sikuli::Key.TAB)
  @screen.type(@sudo_password)
298
299
300
end

Given /^Tails Greeter has dealt with the sudo password$/ do
301
  next if @skip_steps_while_restoring_background
302
303
304
305
306
  f1 = "/etc/sudoers.d/tails-greeter"
  f2 = "#{f1}-no-password-lecture"
  try_for(20) {
    @vm.execute("test -e '#{f1}' -o -e '#{f2}'").success?
  }
307
308
end

309
Given /^the Tails desktop is ready$/ do
310
  next if @skip_steps_while_restoring_background
311
  case @theme
312
313
  when "windows"
    desktop_started_picture = 'WindowsStartButton.png'
314
  else
315
    desktop_started_picture = "GnomeApplicationsMenu#{@language}.png"
316
317
318
    # We wait for the Florence icon to be displayed to ensure reliable systray icon clicking.
    # By this point the only icon left is Vidalia and it will not cause the other systray
    # icons to shift positions.
kytv's avatar
kytv committed
319
    @screen.wait("GnomeSystrayFlorence.png", 180)
320
  end
Tails developers's avatar
Tails developers committed
321
  @screen.wait(desktop_started_picture, 180)
322
323
end

324
325
Then /^Tails seems to have booted normally$/ do
  next if @skip_steps_while_restoring_background
326
  step "the Tails desktop is ready"
327
328
end

329
When /^I see the 'Tor is ready' notification$/ do
330
  next if @skip_steps_while_restoring_background
331
  notification_helper('GnomeTorIsReady.png', 300)
332
  @screen.waitVanish("GnomeTorIsReady.png", 15)
333
end
334

335
336
Given /^Tor is ready$/ do
  next if @skip_steps_while_restoring_background
337
338
  step "Tor has built a circuit"
  step "the time has synced"
339
340
341
end

Given /^Tor has built a circuit$/ do
342
  next if @skip_steps_while_restoring_background
343
344
345
346
  wait_until_tor_is_working
end

Given /^the time has synced$/ do
347
  next if @skip_steps_while_restoring_background
348
349
350
351
352
  ["/var/run/tordate/done", "/var/run/htpdate/success"].each do |file|
    try_for(300) { @vm.execute("test -e #{file}").success? }
  end
end

353
354
355
356
357
358
359
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?
  }
end

360
Given /^the Tor Browser has started$/ do
361
  next if @skip_steps_while_restoring_background
362
  case @theme
363
  when "windows"
364
    tor_browser_picture = "WindowsTorBrowserWindow.png"
365
  else
366
    tor_browser_picture = "TorBrowserWindow.png"
367
  end
368

369
  @screen.wait(tor_browser_picture, 60)
370
371
end

372
Given /^the Tor Browser (?:has started and )?load(?:ed|s) the (startup page|Tails roadmap)$/ do |page|
373
  next if @skip_steps_while_restoring_background
374
375
376
377
378
379
380
381
  case page
  when "startup page"
    picture = "TorBrowserStartupPage.png"
  when "Tails roadmap"
    picture = "TorBrowserTailsRoadmap.png"
  else
    raise "Unsupported page: #{page}"
  end
382
  step "the Tor Browser has started"
383
  @screen.wait(picture, 120)
384
385
end

386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
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)
  @screen.type("d", Sikuli::KeyModifier.CTRL)
  @screen.wait("TorBrowserBookmarkPrompt.png", 10)
  @screen.type(url + Sikuli::Key.ENTER)
end

Given /^the Tor Browser has a bookmark to eff.org$/ do
  next if @skip_steps_while_restoring_background
  @screen.type("b", Sikuli::KeyModifier.ALT)
  @screen.wait("TorBrowserEFFBookmark.png", 10)
405
406
end

407
Given /^all notifications have disappeared$/ do
408
  next if @skip_steps_while_restoring_background
409
  case @theme
410
411
  when "windows"
    notification_picture = "WindowsNotificationX.png"
412
413
414
  else
    notification_picture = "GnomeNotificationX.png"
  end
415
  @screen.waitVanish(notification_picture, 60)
Tails developers's avatar
Tails developers committed
416
417
end

418
419
420
421
422
423
424
425
426
427
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)
    end
Tails developers's avatar
Tails developers committed
428
429
430
    # 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.
431
432
    FileUtils.touch($background_snapshot)
    FileUtils.chmod(0666, $background_snapshot)
433
434
435
436
437

    # Snapshots cannot be saved while filesystem shares are mounted
    # XXX-9p: See XXX-9p above.
    #deactivate_filesystem_shares

438
    @vm.save_snapshot($background_snapshot)
439
  end
440
  restore_background
441
442
  # Now we stop skipping steps from the snapshot restore.
  @skip_steps_while_restoring_background = false
443
444
445
end

Then /^I see "([^"]*)" after at most (\d+) seconds$/ do |image, time|
446
  next if @skip_steps_while_restoring_background
447
448
449
450
  @screen.wait(image, time.to_i)
end

Then /^all Internet traffic has only flowed through Tor$/ do
451
  next if @skip_steps_while_restoring_background
452
  leaks = FirewallLeakCheck.new(@sniffer.pcap_file, get_all_tor_nodes)
453
  leaks.assert_no_leaks
454
end
455

456
457
458
459
460
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)
461
  @screen.type(Sikuli::Key.ENTER)
462
463
464
  @screen.waitVanish('GksuAuthPrompt.png', 10)
end

465
Given /^I enter the sudo password in the pkexec prompt$/ do
466
  next if @skip_steps_while_restoring_background
467
  step "I enter the \"#{@sudo_password}\" password in the pkexec prompt"
468
469
end

470
471
def deal_with_polkit_prompt (image, password)
  @screen.wait(image, 60)
472
  @screen.type(password)
473
  @screen.type(Sikuli::Key.ENTER)
474
475
476
477
478
479
  @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)
480
481
482
483
end

Given /^process "([^"]+)" is running$/ do |process|
  next if @skip_steps_while_restoring_background
484
485
  assert(@vm.has_process?(process),
         "Process '#{process}' is not running")
486
487
end

488
489
Given /^process "([^"]+)" is running within (\d+) seconds$/ do |process, time|
  next if @skip_steps_while_restoring_background
Tails developers's avatar
Tails developers committed
490
491
  try_for(time.to_i, :msg => "Process '#{process}' is not running after " +
                             "waiting for #{time} seconds") do
492
493
494
495
    @vm.has_process?(process)
  end
end

496
497
498
499
500
501
502
503
Given /^process "([^"]+)" has stopped running after at most (\d+) seconds$/ do |process, time|
  next if @skip_steps_while_restoring_background
  try_for(time.to_i, :msg => "Process '#{process}' is still running after " +
                             "waiting for #{time} seconds") do
    not @vm.has_process?(process)
  end
end

504
505
506
507
508
509
Given /^process "([^"]+)" is not running$/ do |process|
  next if @skip_steps_while_restoring_background
  assert(!@vm.has_process?(process),
         "Process '#{process}' is running")
end

510
Given /^I kill the process "([^"]+)"$/ do |process|
511
  next if @skip_steps_while_restoring_background
512
  @vm.execute("killall #{process}")
513
  try_for(10, :msg => "Process '#{process}' could not be killed") {
514
515
    !@vm.has_process?(process)
  }
516
517
end

518
519
520
521
522
523
524
525
526
527
528
529
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
  timeout = nr_gibs_of_ram*5*60
  try_for(timeout, :msg => "VM is still running after #{timeout} seconds") do
    ! @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
530
  @screen.wait('TailsBootSplash.png', nr_gibs_of_ram*5*60)
531
532
533
534
end

Given /^I shutdown Tails and wait for the computer to power off$/ do
  next if @skip_steps_while_restoring_background
535
  @vm.execute("poweroff")
536
537
538
539
  step 'Tails eventually shuts down'
end

When /^I request a shutdown using the emergency shutdown applet$/ do
540
  next if @skip_steps_while_restoring_background
541
  @screen.hide_cursor
542
  @screen.wait_and_click('TailsEmergencyShutdownButton.png', 10)
543
  @screen.hide_cursor
544
  @screen.wait_and_click('TailsEmergencyShutdownHalt.png', 10)
545
546
end

547
548
549
550
551
When /^I warm reboot the computer$/ do
  next if @skip_steps_while_restoring_background
  @vm.execute("reboot")
end

552
553
554
555
556
557
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)
558
559
560
561
end

Given /^package "([^"]+)" is installed$/ do |package|
  next if @skip_steps_while_restoring_background
562
  assert(@vm.execute("dpkg -s '#{package}' 2>/dev/null | grep -qs '^Status:.*installed$'").success?,
563
         "Package '#{package}' is not installed")
564
end
565

566
When /^I start the Tor Browser$/ do
567
  next if @skip_steps_while_restoring_background
568
  step 'I start "TorBrowser" via the GNOME "Internet" applications menu'
569
end
570

571
572
573
574
575
576
577
578
When /^I request a new identity using Torbutton$/ do
  next if @skip_steps_while_restoring_background
  @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
  next if @skip_steps_while_restoring_background
kytv's avatar
kytv committed
579
  @screen.wait('GnomeQuestionDialogIcon.png', 30)
580
581
582
  step 'I type "y"'
end

583
584
585
586
587
588
589
590
591
592
593
594
595
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

Tails developers's avatar
Tails developers committed
596
597
598
599
600
601
602
def xul_application_info(application)
  binary = @vm.execute_successfully(
                '. /usr/local/lib/tails-shell-library/tor-browser.sh; ' +
                'echo ${TBB_INSTALL}/firefox'
                                    ).stdout.chomp
  case application
  when "Tor Browser"
603
    user = LIVE_USER
Tails developers's avatar
Tails developers committed
604
605
    cmd_regex = "#{binary} .* -profile /home/#{user}/\.tor-browser/profile\.default"
    chroot = ""
606
607
    new_tab_button_image = "TorBrowserNewTabButton.png"
    address_bar_image = "TorBrowserAddressBar.png"
Tails developers's avatar
Tails developers committed
608
609
610
611
  when "Unsafe Browser"
    user = "clearnet"
    cmd_regex = "#{binary} .* -profile /home/#{user}/\.unsafe-browser/profile\.default"
    chroot = "/var/lib/unsafe-browser/chroot"
612
613
    new_tab_button_image = "UnsafeBrowserNewTabButton.png"
    address_bar_image = "UnsafeBrowserAddressBar.png"
Tails developers's avatar
Tails developers committed
614
615
616
617
  when "I2P Browser"
    user = "i2pbrowser"
    cmd_regex = "#{binary} .* -profile /home/#{user}/\.i2p-browser/profile\.default"
    chroot = "/var/lib/i2p-browser/chroot"
618
619
    new_tab_button_image = nil
    address_bar_image = nil
Tails developers's avatar
Tails developers committed
620
621
622
623
  when "Tor Launcher"
    user = "tor-launcher"
    cmd_regex = "#{binary} -app /home/#{user}/\.tor-launcher/tor-launcher-standalone/application\.ini"
    chroot = ""
624
625
    new_tab_button_image = nil
    address_bar_image = nil
Tails developers's avatar
Tails developers committed
626
627
628
629
630
631
632
  else
    raise "Invalid browser or XUL application: #{application}"
  end
  return {
    :user => user,
    :cmd_regex => cmd_regex,
    :chroot => chroot,
633
634
    :new_tab_button_image => new_tab_button_image,
    :address_bar_image => address_bar_image,
Tails developers's avatar
Tails developers committed
635
636
637
  }
end

638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
When /^I open a new tab in the (.*)$/ do |browser|
  next if @skip_steps_while_restoring_background
  info = xul_application_info(browser)
  @screen.click(info[:new_tab_button_image])
  @screen.wait(info[:address_bar_image], 10)
end

When /^I open the address "([^"]*)" in the (.*)$/ do |address, browser|
  next if @skip_steps_while_restoring_background
  step "I open a new tab in the #{browser}"
  info = xul_application_info(browser)
  @screen.click(info[:address_bar_image])
  sleep 0.5
  @screen.type(address + Sikuli::Key.ENTER)
end

Then /^the (.*) has no plugins installed$/ do |browser|
  next if @skip_steps_while_restoring_background
  step "I open the address \"about:plugins\" in the #{browser}"
  step "I see \"TorBrowserNoPlugins.png\" after at most 30 seconds"
end

660
661
662
663
664
665
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; " +
666
                 "ls -1 #{chroot}${TBB_INSTALL}/*.so"
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
                                      ).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}")
end

Tails developers's avatar
Tails developers committed
691
Then /^the (.*) uses all expected TBB shared libraries$/ do |application|
692
  next if @skip_steps_while_restoring_background
Tails developers's avatar
Tails developers committed
693
694
  info = xul_application_info(application)
  pid = @vm.execute_successfully("pgrep --uid #{info[:user]} --full --exact '#{info[:cmd_regex]}'").stdout.chomp
695
  assert(/\A\d+\z/.match(pid), "It seems like #{application} is not running")
Tails developers's avatar
Tails developers committed
696
  xul_app_shared_lib_check(pid, info[:chroot])
697
end
698

699
700
Then /^the (.*) chroot is torn down$/ do |browser|
  next if @skip_steps_while_restoring_background
Tails developers's avatar
Tails developers committed
701
702
703
704
  info = xul_application_info(browser)
  try_for(30, :msg => "The #{browser} chroot '#{info[:chroot]}' was " \
                      "not removed") do
    !@vm.execute("test -d '#{info[:chroot]}'").success?
705
706
707
  end
end

708
709
710
711
712
713
714
715
716
Then /^the (.*) runs as the expected user$/ do |browser|
  next if @skip_steps_while_restoring_background
  info = xul_application_info(browser)
  assert_vmcommand_success(@vm.execute(
    "pgrep --full --exact '#{info[:cmd_regex]}'"),
    "The #{browser} is not running")
  assert_vmcommand_success(@vm.execute(
    "pgrep --uid #{info[:user]} --full --exact '#{info[:cmd_regex]}'"),
    "The #{browser} is not running as the #{info[:user]} user")
717
end
718

719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
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

[connection]
id=#{con_name}
uuid=bbc60668-1be0-11e4-a9c6-2f1ce0e75bf1
type=802-3-ethernet
timestamp=1395406011

[ipv6]
method=auto

[ipv4]
method=auto
EOF
  con_content.split("\n").each do |line|
    @vm.execute("echo '#{line}' >> /tmp/NM.#{con_name}")
  end
  @vm.execute("install -m 0600 '/tmp/NM.#{con_name}' '/etc/NetworkManager/system-connections/#{con_name}'")
  try_for(10) {
    nm_con_list = @vm.execute("nmcli --terse --fields NAME con list").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"
  }
end
754
755
756

When /^I start and focus GNOME Terminal$/ do
  next if @skip_steps_while_restoring_background
757
  step 'I start "Terminal" via the GNOME "Accessories" applications menu'
758
759
760
761
762
  @screen.wait_and_click('GnomeTerminalWindow.png', 20)
end

When /^I run "([^"]+)" in GNOME Terminal$/ do |command|
  next if @skip_steps_while_restoring_background
763
764
765
766
767
  if !@vm.has_process?("gnome-terminal")
    step "I start and focus GNOME Terminal"
  else
    @screen.wait_and_click('GnomeTerminalWindow.png', 20)
  end
768
769
  @screen.type(command + Sikuli::Key.ENTER)
end
770

771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
When /^the file "([^"]+)" exists(?:| after at most (\d+) seconds)$/ do |file, timeout|
  next if @skip_steps_while_restoring_background
  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|
  next if @skip_steps_while_restoring_background
  assert(! (@vm.file_exist?(file)))
end

When /^the directory "([^"]+)" exists$/ do |directory|
788
  next if @skip_steps_while_restoring_background
789
790
791
792
  assert(@vm.directory_exist?(directory))
end

When /^the directory "([^"]+)" does not exist$/ do |directory|
793
  next if @skip_steps_while_restoring_background
794
  assert(! (@vm.directory_exist?(directory)))
795
796
797
798
end

When /^I copy "([^"]+)" to "([^"]+)" as user "([^"]+)"$/ do |source, destination, user|
  next if @skip_steps_while_restoring_background
799
  c = @vm.execute("cp \"#{source}\" \"#{destination}\"", LIVE_USER)
800
  assert(c.success?, "Failed to copy file:\n#{c.stdout}\n#{c.stderr}")
801
end
802

803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
def is_persistent?(app)
  conf = get_persistence_presets(true)["#{app}"]
  @vm.execute("findmnt --noheadings --output SOURCE --target '#{conf}'").success?
end

Then /^persistence for "([^"]+)" is (|not )enabled$/ do |app, enabled|
  next if @skip_steps_while_restoring_background
  case enabled
  when ''
    assert(is_persistent?(app), "Persistence should be enabled.")
  when 'not '
    assert(!is_persistent?(app), "Persistence should not be enabled.")
  end
end

818
819
820
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"
821
    step "I create a 4 GiB disk named \"#{drive}\""
822
823
824
825
826
827
828
829
830
831
    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"
end
832

833
834
835
836
837
838
839
840
841
def gnome_app_menu_click_helper(click_me, verify_me = nil)
  try_for(60) do
    @screen.hide_cursor
    @screen.wait_and_click(click_me, 10)
    @screen.wait(verify_me, 10) if verify_me
    return
  end
end

842
843
844
845
846
847
848
849
Given /^I start "([^"]+)" via the GNOME "([^"]+)" applications menu$/ do |app, submenu|
  next if @skip_steps_while_restoring_background
  case @theme
  when "windows"
    prefix = 'Windows'
  else
    prefix = 'Gnome'
  end
850
851
852
853
854
855
  menu_button = prefix + "ApplicationsMenu.png"
  sub_menu_entry = prefix + "Applications" + submenu + ".png"
  application_entry = prefix + "Applications" + app + ".png"
  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)
856
end
857
858
859
860
861
862
863
864
865

Given /^I start "([^"]+)" via the GNOME "([^"]+)"\/"([^"]+)" applications menu$/ do |app, submenu, subsubmenu|
  next if @skip_steps_while_restoring_background
  case @theme
  when "windows"
    prefix = 'Windows'
  else
    prefix = 'Gnome'
  end
866
867
868
869
870
871
872
873
  menu_button = prefix + "ApplicationsMenu.png"
  sub_menu_entry = prefix + "Applications" + submenu + ".png"
  sub_sub_menu_entry = prefix + "Applications" + subsubmenu + ".png"
  application_entry = prefix + "Applications" + app + ".png"
  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)
874
end
875
876
877
878
879
880
881
882

When /^I type "([^"]+)"$/ do |string|
  next if @skip_steps_while_restoring_background
  @screen.type(string)
end

When /^I press the "([^"]+)" key$/ do |key|
  next if @skip_steps_while_restoring_background
883
884
885
  begin
    @screen.type(eval("Sikuli::Key.#{key}"))
  rescue RuntimeError
Tails developers's avatar
Tails developers committed
886
    raise "unsupported key #{key}"
887
888
  end
end
889
890

Then /^the (amnesiac|persistent) Tor Browser directory (exists|does not exist)$/ do |persistent_or_not, mode|
891
  next if @skip_steps_while_restoring_background
892
893
  case persistent_or_not
  when "amnesiac"
894
    dir = "/home/#{LIVE_USER}/Tor Browser"
895
  when "persistent"
896
    dir = "/home/#{LIVE_USER}/Persistent/Tor Browser"
897
898
899
900
901
  end
  step "the directory \"#{dir}\" #{mode}"
end

Then /^there is a GNOME bookmark for the (amnesiac|persistent) Tor Browser directory$/ do |persistent_or_not|
902
  next if @skip_steps_while_restoring_background
903
904
905
906
907
908
909
910
911
912
913
  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

914
915
916
917
918
919
920
Then /^there is no GNOME bookmark for the persistent Tor Browser directory$/ do
  next if @skip_steps_while_restoring_background
  @screen.wait_and_click('GnomePlaces.png', 10)
  @screen.wait("GnomePlacesWithoutTorBrowserPersistent.png", 40)
  @screen.type(Sikuli::Key.ESC)
end

921
def pulseaudio_sink_inputs
922
  pa_info = @vm.execute_successfully('pacmd info', LIVE_USER).stdout
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
  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|
  next if @skip_steps_while_restoring_background
  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
  next if @skip_steps_while_restoring_background
  @screen.wait_and_double_click("DesktopTailsDocumentationIcon.png", 10)
end

When /^I click the blocked video icon$/ do
  next if @skip_steps_while_restoring_background
  @screen.wait_and_click("TorBrowserBlockedVideo.png", 30)
end

When /^I accept to temporarily allow playing this video$/ do
  next if @skip_steps_while_restoring_background
  @screen.wait_and_click("TorBrowserOkButton.png", 10)
end

When /^I click the HTML5 play button$/ do
  next if @skip_steps_while_restoring_background
  @screen.wait_and_click("TorBrowserHtml5PlayButton.png", 30)
end

When /^I can save the current page as "([^"]+[.]html)" to the (default downloads|persistent Tor Browser) directory$/ do |output_file, output_dir|
  next if @skip_steps_while_restoring_background
  @screen.type("s", Sikuli::KeyModifier.CTRL)
  if output_dir == "persistent Tor Browser"
958
    output_dir = "/home/#{LIVE_USER}/Persistent/Tor Browser"
959
960
961
962
963
964
965
    @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)
  else
966
    output_dir = "/home/#{LIVE_USER}/Tor Browser"
967
968
969
970
971
972
973
974
975
976
977
978
979
  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)
  try_for(10, :msg => "The page was not saved to #{output_dir}/#{output_file}") {
    @vm.file_exist?("#{output_dir}/#{output_file}")
  }
end

When /^I can print the current page as "([^"]+[.]pdf)" to the (default downloads|persistent Tor Browser) directory$/ do |output_file, output_dir|
  next if @skip_steps_while_restoring_background
  if output_dir == "persistent Tor Browser"
980
    output_dir = "/home/#{LIVE_USER}/Persistent/Tor Browser"
981
  else
982
    output_dir = "/home/#{LIVE_USER}/Tor Browser"
983
984
985
986
  end
  @screen.type("p", Sikuli::KeyModifier.CTRL)
  @screen.wait("TorBrowserPrintDialog.png", 10)
  @screen.wait_and_click("PrintToFile.png", 10)
987
  # Tor Browser is not allowed to read /home/#{LIVE_USER}, and I found no way
988
989
990
  # to change the default destination directory for "Print to File",
  # so let's click through the warning
  @screen.wait("TorBrowserCouldNotReadTheContentsOfWarning.png", 10)
991
  @screen.wait_and_click("TorBrowserWarningDialogOkButton.png", 10)
992
993
994
995
996
  @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
997
  @screen.type(output_dir + '/' + output_file.sub(/[.]pdf$/, '') + Sikuli::Key.ENTER)
998
999
1000
  try_for(30, :msg => "The page was not printed to #{output_dir}/#{output_file}") {
    @vm.file_exist?("#{output_dir}/#{output_file}")
  }
For faster browsing, not all history is shown. View entire blame