summaryrefslogtreecommitdiffstats
path: root/features
diff options
context:
space:
mode:
authoranonym <anonym@riseup.net>2017-01-20 12:19:28 +0100
committeranonym <anonym@riseup.net>2017-01-20 12:19:28 +0100
commit459b27518897f90f04fa069f9d7c8f42ecf108d6 (patch)
treea0deb341181d2a0b27235de16b3909215cd44c77 /features
parenta49b13bbdb5647c8e5d1d4a6dc6c77dda5394714 (diff)
parenta3e950662f983d280d3c63ae527dcaa5d17e9512 (diff)
Merge remote-tracking branch 'origin/testing' into wip/test/10381-fix-I-open-the-address-test-is-fragile
Diffstat (limited to 'features')
-rw-r--r--features/apt.feature14
-rw-r--r--features/build.feature40
-rw-r--r--features/checks.feature104
-rw-r--r--features/config/defaults.yml4
-rw-r--r--features/documentation.feature10
-rw-r--r--features/domains/cdrom.xml5
-rw-r--r--features/domains/default.xml17
-rw-r--r--features/domains/fs_share.xml6
-rw-r--r--features/electrum.feature4
-rw-r--r--features/emergency_shutdown.feature22
-rw-r--r--features/erase_memory.feature8
-rw-r--r--features/evince.feature10
-rw-r--r--features/gnome.feature8
-rw-r--r--features/hardening.feature14
-rw-r--r--features/i2p.feature10
-rw-r--r--features/icedove.feature43
-rw-r--r--features/images/CupsTestPage.pngbin2859 -> 1908 bytes
-rw-r--r--features/images/ElectrumConnectServer.pngbin3929 -> 0 bytes
-rw-r--r--features/images/ElectrumCreateNewSeed.pngbin0 -> 2496 bytes
-rw-r--r--features/images/ElectrumNoWallet.pngbin3434 -> 3872 bytes
-rw-r--r--features/images/GitCloneDone.pngbin0 -> 2974 bytes
-rw-r--r--features/images/GnomeCloseTopButton.pngbin0 -> 399 bytes
-rw-r--r--features/images/GpgAppletDecryptVerify.pngbin2349 -> 2537 bytes
-rw-r--r--features/images/GpgAppletEncryptPassphrase.pngbin2613 -> 2773 bytes
-rw-r--r--features/images/GpgAppletIconEncrypted.pngbin873 -> 875 bytes
-rw-r--r--features/images/GpgAppletIconNormal.pngbin585 -> 521 bytes
-rw-r--r--features/images/GpgAppletIconSigned.pngbin948 -> 1015 bytes
-rw-r--r--features/images/GpgAppletKeySelected.pngbin1164 -> 1181 bytes
-rw-r--r--features/images/IcedoveEnigmailAdvancedParameters.pngbin815 -> 0 bytes
-rw-r--r--features/images/IcedoveEnigmailAdvancedTab.pngbin1198 -> 0 bytes
-rw-r--r--features/images/IcedoveEnigmailExpertSettingsButton.pngbin3077 -> 0 bytes
-rw-r--r--features/images/IcedoveEnigmailKeyserver.pngbin2002 -> 0 bytes
-rw-r--r--features/images/IcedoveEnigmailKeyserverTab.pngbin1128 -> 0 bytes
-rw-r--r--features/images/IcedoveEnigmailPreferences.pngbin1151 -> 0 bytes
-rw-r--r--features/images/IcedoveEnigmailPreferencesWindow.pngbin2426 -> 0 bytes
-rw-r--r--features/images/IcedoveEnigmailProxy.pngbin2267 -> 0 bytes
-rw-r--r--features/images/IcedoveMailAccountSetup.pngbin2708 -> 0 bytes
-rw-r--r--features/images/IcedoveMainWindow.pngbin1905 -> 0 bytes
-rw-r--r--features/images/IcedoveMenuViewToolbars.pngbin998 -> 0 bytes
-rw-r--r--features/images/IcedoveMenuViewToolbarsStatusBar.pngbin1084 -> 0 bytes
-rw-r--r--features/images/IcedoveProtocolIMAP.pngbin1277 -> 0 bytes
-rw-r--r--features/images/IcedoveToolsMenuAddOns.pngbin939 -> 0 bytes
-rw-r--r--features/images/IcedoveTorbirdyEnabled.pngbin4037 -> 0 bytes
-rw-r--r--features/images/IcedoveTorbirdyPreferences.pngbin2177 -> 0 bytes
-rw-r--r--features/images/MozillaAddonsManagerExtensions.pngbin5192 -> 2045 bytes
-rw-r--r--features/images/MozillaExtensionsTorbirdy.pngbin4007 -> 2077 bytes
-rw-r--r--features/images/SSHError.pngbin0 -> 635 bytes
-rw-r--r--features/images/SampleLocalMp4VideoFrame.pngbin14887 -> 50014 bytes
-rw-r--r--features/images/SupportDocumentation.pngbin2099 -> 0 bytes
-rw-r--r--features/images/SupportDocumentationGerman.pngbin2629 -> 0 bytes
-rw-r--r--features/images/SynapticFailure.pngbin0 -> 2449 bytes
-rw-r--r--features/images/SynapticLoaded.pngbin0 -> 1833 bytes
-rw-r--r--features/images/SynapticReloadButton.pngbin1957 -> 0 bytes
-rw-r--r--features/images/SynapticSearchWindow.pngbin715 -> 671 bytes
-rw-r--r--features/images/TailsBootMenuKernelCmdline.pngbin0 -> 1629 bytes
-rw-r--r--features/images/TailsBootMenuKernelCmdlineUEFI.pngbin0 -> 2575 bytes
-rw-r--r--features/images/TailsBootSplash.pngbin5964 -> 0 bytes
-rw-r--r--features/images/TailsBootSplashUEFI.pngbin9188 -> 0 bytes
-rw-r--r--features/images/TailsBooting.pngbin0 -> 164 bytes
-rw-r--r--features/images/TailsBug11786a.pngbin0 -> 1971 bytes
-rw-r--r--features/images/TailsBug11786b.pngbin0 -> 1592 bytes
-rw-r--r--features/images/TailsHomepage.pngbin4743 -> 3678 bytes
-rw-r--r--features/images/TailsOfflineDocHomepage.pngbin4978 -> 0 bytes
-rw-r--r--features/images/TailsUpgraderDone.pngbin0 -> 3215 bytes
-rw-r--r--features/images/TailsUpgraderFailure.pngbin0 -> 2196 bytes
-rw-r--r--features/images/TailsUpgraderUpgradeNowButton.pngbin0 -> 2232 bytes
-rw-r--r--features/images/TailsUpgraderUpgradeTo1.1~test.pngbin0 -> 2512 bytes
-rw-r--r--features/images/TorBrowserSavedStartupPage.pngbin1700 -> 1250 bytes
-rw-r--r--features/images/UEFIFirmwareSetup.pngbin0 -> 172 bytes
-rw-r--r--features/keys.feature10
-rw-r--r--features/localization.feature2
-rw-r--r--features/mat.feature9
-rw-r--r--features/misc_files/sample.pdfbin22347 -> 0 bytes
-rw-r--r--features/misc_files/sample.pngbin0 -> 150 bytes
-rw-r--r--features/misc_files/sample.tex8
-rw-r--r--features/networking.feature27
-rw-r--r--features/persistence.feature10
-rw-r--r--features/pidgin.feature2
-rw-r--r--features/regression_tests.feature6
-rw-r--r--features/sane_defaults.feature15
-rw-r--r--features/ssh.feature5
-rw-r--r--features/step_definitions/apt.rb97
-rw-r--r--features/step_definitions/browser.rb11
-rw-r--r--features/step_definitions/checks.rb65
-rw-r--r--features/step_definitions/common_steps.rb216
-rw-r--r--features/step_definitions/electrum.rb13
-rw-r--r--features/step_definitions/erase_memory.rb37
-rw-r--r--features/step_definitions/firewall_leaks.rb4
-rw-r--r--features/step_definitions/git.rb24
-rw-r--r--features/step_definitions/icedove.rb264
-rw-r--r--features/step_definitions/ssh.rb38
-rw-r--r--features/step_definitions/tor.rb33
-rw-r--r--features/step_definitions/torified_gnupg.rb2
-rw-r--r--features/step_definitions/torified_misc.rb16
-rw-r--r--features/step_definitions/totem.rb21
-rw-r--r--features/step_definitions/usb.rb195
-rw-r--r--features/support/config.rb14
-rw-r--r--features/support/extra_hooks.rb43
-rw-r--r--features/support/helpers/dogtail.rb67
-rw-r--r--features/support/helpers/exec_helper.rb34
-rw-r--r--features/support/helpers/misc_helpers.rb16
-rw-r--r--features/support/helpers/sikuli_helper.rb33
-rw-r--r--features/support/helpers/storage_helper.rb35
-rw-r--r--features/support/helpers/vm_helper.rb84
-rw-r--r--features/support/hooks.rb4
-rw-r--r--features/time_syncing.feature2
-rw-r--r--features/tor_bridges.feature5
-rw-r--r--features/tor_enforcement.feature4
-rw-r--r--features/tor_stream_isolation.feature2
-rw-r--r--features/torified_browsing.feature35
-rw-r--r--features/torified_git.feature20
-rw-r--r--features/torified_misc.feature2
-rw-r--r--features/totem.feature31
-rw-r--r--features/untrusted_partitions.feature14
-rw-r--r--features/usb_install.feature12
-rw-r--r--features/usb_upgrade.feature43
-rw-r--r--features/virtualization.feature9
117 files changed, 1318 insertions, 640 deletions
diff --git a/features/apt.feature b/features/apt.feature
index b0ece1f..c835d27 100644
--- a/features/apt.feature
+++ b/features/apt.feature
@@ -9,17 +9,17 @@ Feature: Installing packages through APT
Given I have started Tails from DVD and logged in with an administration password and the network is connected
Scenario: APT sources are configured correctly
- Then the only hosts in APT sources are "ftp.us.debian.org,security.debian.org,deb.tails.boum.org,deb.torproject.org"
+ Then the only hosts in APT sources are "vwakviie2ienjx6t.onion,sgvtcaew4bxjd7ln.onion,jenw7xbd6tf7vfhp.onion,sdscoq7snqtznauu.onion"
- #10496: apt-get scenarios are fragile
- @check_tor_leaks @fragile
+ @check_tor_leaks
Scenario: Install packages using apt
- When I update APT using apt
+ When I configure APT to use non-onion sources
+ And I update APT using apt
Then I should be able to install a package using apt
- #10441: Synaptic test is fragile
- @check_tor_leaks @fragile
+ @check_tor_leaks
Scenario: Install packages using Synaptic
- When I start Synaptic
+ When I configure APT to use non-onion sources
+ And I start Synaptic
And I update APT using Synaptic
Then I should be able to install a package using Synaptic
diff --git a/features/build.feature b/features/build.feature
index 4241197..49b2d24 100644
--- a/features/build.feature
+++ b/features/build.feature
@@ -31,10 +31,10 @@ Feature: custom APT sources to build branches
And Tails 1.0 has not been released yet
And no frozen APT snapshot is encoded in config/APT_snapshots.d
When I successfully run "apt-snapshots-serials prepare-build"
- And I successfully run "apt-mirror debian"
- Then I should see the 0.10 tagged snapshot
- When I successfully run "apt-mirror torproject"
- Then I should see the 0.10 tagged snapshot
+ And I run "apt-mirror debian"
+ Then it should fail
+ When I run "apt-mirror torproject"
+ Then it should fail
When I successfully run "apt-mirror debian-security"
Then I should see a time-based snapshot
@@ -77,10 +77,10 @@ Feature: custom APT sources to build branches
And no frozen APT snapshot is encoded in config/APT_snapshots.d
And I checkout the 0.10 tag
When I successfully run "apt-snapshots-serials prepare-build"
- And I successfully run "apt-mirror debian"
- Then I should see the 0.10 tagged snapshot
- When I successfully run "apt-mirror torproject"
- Then I should see the 0.10 tagged snapshot
+ And I run "apt-mirror debian"
+ Then it should fail
+ When I run "apt-mirror torproject"
+ Then it should fail
When I successfully run "apt-mirror debian-security"
Then I should see the 0.10 tagged snapshot
@@ -127,10 +127,10 @@ Feature: custom APT sources to build branches
And I am working on the bugfix/disable_gdomap branch based on stable
And no frozen APT snapshot is encoded in config/APT_snapshots.d
When I successfully run "apt-snapshots-serials prepare-build"
- And I successfully run "apt-mirror debian"
- Then I should see the 0.10 tagged snapshot
- When I successfully run "apt-mirror torproject"
- Then I should see the 0.10 tagged snapshot
+ And I run "apt-mirror debian"
+ Then it should fail
+ When I run "apt-mirror torproject"
+ Then it should fail
When I successfully run "apt-mirror debian-security"
Then I should see a time-based snapshot
@@ -178,10 +178,10 @@ Feature: custom APT sources to build branches
And Tails 1.0 has not been released yet
And no frozen APT snapshot is encoded in config/APT_snapshots.d
When I successfully run "apt-snapshots-serials prepare-build"
- And I successfully run "apt-mirror debian"
- Then I should see a time-based snapshot
- When I successfully run "apt-mirror torproject"
- Then I should see a time-based snapshot
+ And I run "apt-mirror debian"
+ Then it should fail
+ When I run "apt-mirror torproject"
+ Then it should fail
When I successfully run "apt-mirror debian-security"
Then I should see a time-based snapshot
@@ -273,10 +273,10 @@ Feature: custom APT sources to build branches
And I am working on the bugfix/disable_gdomap branch based on testing
And no frozen APT snapshot is encoded in config/APT_snapshots.d
When I successfully run "apt-snapshots-serials prepare-build"
- And I successfully run "apt-mirror debian"
- Then I should see a time-based snapshot
- When I successfully run "apt-mirror torproject"
- Then I should see a time-based snapshot
+ And I run "apt-mirror debian"
+ Then it should fail
+ When I run "apt-mirror torproject"
+ Then it should fail
When I successfully run "apt-mirror debian-security"
Then I should see a time-based snapshot
diff --git a/features/checks.feature b/features/checks.feature
deleted file mode 100644
index bbda069..0000000
--- a/features/checks.feature
+++ /dev/null
@@ -1,104 +0,0 @@
-@product
-Feature: Various checks
-
- Scenario: AppArmor is enabled and has enforced profiles
- Given I have started Tails from DVD without network and logged in
- Then AppArmor is enabled
- And some AppArmor profiles are enforced
-
- Scenario: A screenshot is taken when the PRINTSCREEN key is pressed
- Given I have started Tails from DVD without network and logged in
- And there is no screenshot in the live user's Pictures directory
- When I press the "PRINTSCREEN" key
- Then a screenshot is saved to the live user's Pictures directory
-
- Scenario: VirtualBox guest modules are available
- Given I have started Tails from DVD without network and logged in
- When Tails has booted a 64-bit kernel
- Then the VirtualBox guest modules are available
-
- Scenario: The shipped Tails OpenPGP keys are up-to-date
- Given I have started Tails from DVD without network and logged in
- Then the OpenPGP keys shipped with Tails will be valid for the next 3 months
-
- Scenario: The Tails Debian repository key is up-to-date
- Given I have started Tails from DVD without network and logged in
- Then the shipped Debian repository key will be valid for the next 3 months
-
- @doc
- Scenario: The "Report an Error" launcher will open the support documentation
- Given I have started Tails from DVD without network and logged in
- And the network is plugged
- And Tor is ready
- And all notifications have disappeared
- When I double-click the Report an Error launcher on the desktop
- Then the support documentation page opens in Tor Browser
-
- Scenario: The live user is setup correctly
- Given I have started Tails from DVD without network and logged in
- Then the live user has been setup by live-boot
- And the live user is a member of only its own group and "audio cdrom dialout floppy video plugdev netdev scanner lp lpadmin vboxsf"
- And the live user owns its home dir and it has normal permissions
-
- Scenario: No initial network
- Given I have started Tails from DVD without network and logged in
- And I wait between 30 and 60 seconds
- Then the Tor Status icon tells me that Tor is not usable
- When the network is plugged
- Then Tor is ready
- And the Tor Status icon tells me that Tor is usable
- And all notifications have disappeared
- And the time has synced
-
- #11463
- @fragile
- Scenario: The 'Tor is ready' notification is shown when Tor has bootstrapped
- Given I have started Tails from DVD without network and logged in
- And the network is plugged
- When I see the 'Tor is ready' notification
- Then Tor is ready
-
- Scenario: The tor process should be confined with Seccomp
- Given I have started Tails from DVD without network and logged in
- And the network is plugged
- And Tor is ready
- Then the running process "tor" is confined with Seccomp in filter mode
-
- Scenario: No unexpected network services
- Given I have started Tails from DVD without network and logged in
- When the network is plugged
- And Tor is ready
- Then no unexpected services are listening for network connections
-
- Scenario: The emergency shutdown applet can shutdown Tails
- Given I have started Tails from DVD without network and logged in
- When I request a shutdown using the emergency shutdown applet
- Then Tails eventually shuts down
-
- Scenario: The emergency shutdown applet can reboot Tails
- Given I have started Tails from DVD without network and logged in
- When I request a reboot using the emergency shutdown applet
- Then Tails eventually restarts
-
- Scenario: tails-debugging-info does not leak information
- Given I have started Tails from DVD without network and logged in
- Then tails-debugging-info is not susceptible to symlink attacks
-
- Scenario: Tails shuts down on DVD boot medium removal
- Given I have started Tails from DVD without network and logged in
- When I eject the boot medium
- Then Tails eventually shuts down
-
- #10720
- @fragile
- Scenario: Tails shuts down on USB boot medium removal
- Given I have started Tails without network from a USB drive without a persistent partition and logged in
- When I eject the boot medium
- Then Tails eventually shuts down
-
- Scenario: The Tails Greeter "disable all networking" option disables networking within Tails
- Given I have started Tails from DVD without network and stopped at Tails Greeter's login screen
- And I enable more Tails Greeter options
- And I disable all networking in the Tails Greeter
- And I log in to a new session
- Then no network interfaces are enabled
diff --git a/features/config/defaults.yml b/features/config/defaults.yml
index bd2f130..1e7f8ea 100644
--- a/features/config/defaults.yml
+++ b/features/config/defaults.yml
@@ -1,7 +1,7 @@
CAPTURE: false
CAPTURE_ALL: false
+INTERACTIVE_DEBUGGING: false
MAX_NEW_TOR_CIRCUIT_RETRIES: 10
-PAUSE_ON_FAIL: false
SIKULI_RETRY_FINDFAILED: false
TMPDIR: "/tmp/TailsToaster"
@@ -33,4 +33,4 @@ Unsafe_SSH_private_key: |
NWema+bArbaF0rKVJpwvpkZWGcr6qRn94Ts0kJAzR+VIVTOjB9sVwdxjadwWHRs5
kKnpY0tnSF7hyVRwN7GOsNDJEaFjCW7k4+55D2ZNBy2iN3beW8CZ
-----END RSA PRIVATE KEY-----
-Unsafe_SSH_public_key: = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8xQ2BRQz+TK6jbqb5fDuKAbrOAYUwVtLe7yblK0awk6fvMuInw/kyaX9H7i105LqjBVThFplM+w1lkr8KViY4+GY28nTilUKGTYNnwABGD9MA2PeqMqzcP4x4puTVu3oSwDnmSAaxNSTLlOLxxzZadrbmOqNqAiLIzzbY8Yb2aYjr/MthHpAtSLM1pyJetEzdDhHixCQSt5WUd6ic8SIZSz3PHSzAKku08zlQhi17U9UeCTB4+xTq8zxSpVMr9XC0suAtqdeewwW7OrsNMBc+rj35dIU2rgmXFsQXr49Bdm9hnk15bTQars1Kk8/y6gevp/Un6YzHGczzmPNi1HH5 amnesia@amnesia"
+Unsafe_SSH_public_key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8xQ2BRQz+TK6jbqb5fDuKAbrOAYUwVtLe7yblK0awk6fvMuInw/kyaX9H7i105LqjBVThFplM+w1lkr8KViY4+GY28nTilUKGTYNnwABGD9MA2PeqMqzcP4x4puTVu3oSwDnmSAaxNSTLlOLxxzZadrbmOqNqAiLIzzbY8Yb2aYjr/MthHpAtSLM1pyJetEzdDhHixCQSt5WUd6ic8SIZSz3PHSzAKku08zlQhi17U9UeCTB4+xTq8zxSpVMr9XC0suAtqdeewwW7OrsNMBc+rj35dIU2rgmXFsQXr49Bdm9hnk15bTQars1Kk8/y6gevp/Un6YzHGczzmPNi1HH5 amnesia@amnesia"
diff --git a/features/documentation.feature b/features/documentation.feature
new file mode 100644
index 0000000..c636b19
--- /dev/null
+++ b/features/documentation.feature
@@ -0,0 +1,10 @@
+@product @doc
+Feature: Tails documentation
+
+ Scenario: The "Report an Error" launcher will open the support documentation
+ Given I have started Tails from DVD without network and logged in
+ And the network is plugged
+ And Tor is ready
+ And all notifications have disappeared
+ When I double-click the Report an Error launcher on the desktop
+ Then the support documentation page opens in Tor Browser
diff --git a/features/domains/cdrom.xml b/features/domains/cdrom.xml
new file mode 100644
index 0000000..8bc3be7
--- /dev/null
+++ b/features/domains/cdrom.xml
@@ -0,0 +1,5 @@
+<disk type='file' device='cdrom'>
+ <driver name='qemu' type='raw'/>
+ <target dev='hdc' bus='sata'/>
+ <readonly/>
+</disk>
diff --git a/features/domains/default.xml b/features/domains/default.xml
index 924bb85..431dcde 100644
--- a/features/domains/default.xml
+++ b/features/domains/default.xml
@@ -1,9 +1,9 @@
<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
<memory unit='KiB'>2097152</memory>
<currentMemory unit='KiB'>2097152</currentMemory>
- <vcpu>1</vcpu>
+ <vcpu>2</vcpu>
<os>
- <type arch='x86_64' machine='pc-0.15'>hvm</type>
+ <type arch='x86_64' machine='pc-i440fx-2.5'>hvm</type>
<boot dev='cdrom'/>
</os>
<features>
@@ -18,17 +18,8 @@
<on_crash>restart</on_crash>
<devices>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
- <disk type='file' device='cdrom'>
- <driver name='qemu' type='raw'/>
- <source file=''/>
- <target dev='hdc' bus='ide'/>
- <readonly/>
- </disk>
- <controller type='usb' index='0' model='ich9-ehci1'/>
- <controller type='usb' index='0' model='ich9-uhci1'>
- <master startport='0'/>
- </controller>
- <controller type='ide' index='0'/>
+ <controller type='usb' index='0' model='nec-xhci'/>
+ <controller type='sata' index='0'/>
<controller type='virtio-serial' index='0'/>
<interface type='network'>
<mac address='52:54:00:ac:dd:ee'/>
diff --git a/features/domains/fs_share.xml b/features/domains/fs_share.xml
deleted file mode 100644
index 718755e..0000000
--- a/features/domains/fs_share.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<filesystem type='mount' accessmode='passthrough'>
- <driver type='path' wrpolicy='immediate'/>
- <source dir=''/>
- <target dir=''/>
- <readonly/>
-</filesystem>
diff --git a/features/electrum.feature b/features/electrum.feature
index 9807fec..b611d23 100644
--- a/features/electrum.feature
+++ b/features/electrum.feature
@@ -10,7 +10,7 @@ Feature: Electrum Bitcoin client
But persistence for "electrum" is not enabled
Then I see a warning that Electrum is not persistent
- #10720: Tails Installer freezes on Jenkins
+ #11697
@fragile
Scenario: Using a persistent Electrum configuration
Given I have started Tails without network from a USB drive with a persistent partition enabled and logged in
@@ -21,7 +21,7 @@ Feature: Electrum Bitcoin client
Then persistence for "electrum" is enabled
When I start Electrum through the GNOME menu
But a bitcoin wallet is not present
- Then I am prompted to create a new wallet
+ Then I am prompted to configure Electrum
When I create a new bitcoin wallet
Then a bitcoin wallet is present
And I see the main Electrum client window
diff --git a/features/emergency_shutdown.feature b/features/emergency_shutdown.feature
new file mode 100644
index 0000000..3fac61e
--- /dev/null
+++ b/features/emergency_shutdown.feature
@@ -0,0 +1,22 @@
+@product
+Feature: Emergency shutdown
+
+ Scenario: The emergency shutdown applet can shutdown Tails
+ Given I have started Tails from DVD without network and logged in
+ When I request a shutdown using the emergency shutdown applet
+ Then Tails eventually shuts down
+
+ Scenario: The emergency shutdown applet can reboot Tails
+ Given I have started Tails from DVD without network and logged in
+ When I request a reboot using the emergency shutdown applet
+ Then Tails eventually restarts
+
+ Scenario: Tails shuts down on DVD boot medium removal
+ Given I have started Tails from DVD without network and logged in
+ When I eject the boot medium
+ Then Tails eventually shuts down
+
+ Scenario: Tails shuts down on USB boot medium removal
+ Given I have started Tails without network from a USB drive without a persistent partition and logged in
+ When I eject the boot medium
+ Then Tails eventually shuts down
diff --git a/features/erase_memory.feature b/features/erase_memory.feature
index d3425fd..28779f6 100644
--- a/features/erase_memory.feature
+++ b/features/erase_memory.feature
@@ -20,8 +20,6 @@ Feature: System memory erasure on shutdown
And I stop the boot at the bootloader menu
Then I find many patterns in the guest's memory
- #10776
- @fragile
Scenario: Memory erasure on a modern computer
Given a computer
And the computer is a modern 64-bit system
@@ -44,7 +42,7 @@ Feature: System memory erasure on shutdown
And I set Tails to boot with options "debug=wipemem"
And I start Tails from DVD with network unplugged and I login
Then the non-PAE kernel is running
- And at least 3500 MiB of RAM was detected
+ And at least 3 GiB of RAM was detected
And process "memlockd" is running
And process "udev-watchdog" is running
And udev-watchdog is monitoring the correct device
@@ -53,8 +51,6 @@ Feature: System memory erasure on shutdown
And I stop the boot at the bootloader menu
Then I find many patterns in the guest's memory
- #10776
- @fragile
Scenario: Memory erasure on an old computer
Given a computer
And the computer is an old pentium without the PAE extension
@@ -62,7 +58,7 @@ Feature: System memory erasure on shutdown
And I set Tails to boot with options "debug=wipemem"
And I start Tails from DVD with network unplugged and I login
And the non-PAE kernel is running
- And at least 3500 MiB of RAM was detected
+ And at least 3 GiB of RAM was detected
And process "memlockd" is running
And process "udev-watchdog" is running
And udev-watchdog is monitoring the correct device
diff --git a/features/evince.feature b/features/evince.feature
index 6fd27ec..c9501f9 100644
--- a/features/evince.feature
+++ b/features/evince.feature
@@ -9,7 +9,7 @@ Feature: Using Evince
Scenario: I can view and print a PDF file stored in /usr/share
Given I have started Tails from DVD without network and logged in
When I open "/usr/share/cups/data/default-testpage.pdf" with Evince
- Then I see "CupsTestPage.png" after at most 20 seconds
+ Then I see "CupsTestPage.png" after at most 40 seconds
And I can print the current document to "/home/amnesia/output.pdf"
#10994
@@ -18,7 +18,7 @@ Feature: Using Evince
Given I have started Tails from DVD without network and logged in
And I copy "/usr/share/cups/data/default-testpage.pdf" to "/home/amnesia" as user "amnesia"
When I open "/home/amnesia/default-testpage.pdf" with Evince
- Then I see "CupsTestPage.png" after at most 20 seconds
+ Then I see "CupsTestPage.png" after at most 40 seconds
And I can print the current document to "/home/amnesia/output.pdf"
Scenario: I cannot view a PDF file stored in non-persistent /home/amnesia/.gnupg
@@ -44,18 +44,16 @@ Feature: Using Evince
# as /lib/live/mount/overlay.
And AppArmor has denied "/usr/bin/evince" from opening "/lib/live/mount/overlay/home/amnesia/.gnupg/default-testpage.pdf"
- #10720: Tails Installer freezes on Jenkins
+ #10994
@fragile
Scenario: I can view and print a PDF file stored in persistent /home/amnesia/Persistent
Given I have started Tails without network from a USB drive with a persistent partition enabled and logged in
And I copy "/usr/share/cups/data/default-testpage.pdf" to "/home/amnesia/Persistent" as user "amnesia"
Then the file "/home/amnesia/Persistent/default-testpage.pdf" exists
When I open "/home/amnesia/Persistent/default-testpage.pdf" with Evince
- Then I see "CupsTestPage.png" after at most 20 seconds
+ Then I see "CupsTestPage.png" after at most 40 seconds
And I can print the current document to "/home/amnesia/Persistent/output.pdf"
- #10720: Tails Installer freezes on Jenkins
- @fragile
Scenario: I cannot view a PDF file stored in persistent /home/amnesia/.gnupg
Given I have started Tails without network from a USB drive with a persistent partition enabled and logged in
And I copy "/usr/share/cups/data/default-testpage.pdf" to "/home/amnesia/.gnupg" as user "amnesia"
diff --git a/features/gnome.feature b/features/gnome.feature
new file mode 100644
index 0000000..21b2672
--- /dev/null
+++ b/features/gnome.feature
@@ -0,0 +1,8 @@
+@product
+Feature: GNOME is well-integrated into Tails
+
+ Scenario: A screenshot is taken when the PRINTSCREEN key is pressed
+ Given I have started Tails from DVD without network and logged in
+ And there is no screenshot in the live user's Pictures directory
+ When I press the "PRINTSCREEN" key
+ Then a screenshot is saved to the live user's Pictures directory
diff --git a/features/hardening.feature b/features/hardening.feature
new file mode 100644
index 0000000..42600b8
--- /dev/null
+++ b/features/hardening.feature
@@ -0,0 +1,14 @@
+@product
+Feature: Hardening features
+
+ Scenario: AppArmor is enabled and has enforced profiles
+ Given I have started Tails from DVD without network and logged in
+ Then AppArmor is enabled
+ And some AppArmor profiles are enforced
+
+ Scenario: The tor process should be confined with Seccomp
+ Given I have started Tails from DVD without network and logged in
+ And the network is plugged
+ And Tor is ready
+ Then the running process "tor" is confined with Seccomp in filter mode
+
diff --git a/features/i2p.feature b/features/i2p.feature
index 616c5ff..c2d8b7c 100644
--- a/features/i2p.feature
+++ b/features/i2p.feature
@@ -15,11 +15,15 @@ Feature: I2P
And the I2P Browser sudo rules are present
And the I2P firewall rules are enabled
+ #11114
+ @fragile
Scenario: I2P's AppArmor profile is in enforce mode
Given I have started Tails from DVD with I2P enabled and logged in and the network is connected
When I2P is running
Then the running process "i2p" is confined with AppArmor in enforce mode
+ #11114
+ @fragile
Scenario: The I2P Browser works as it should
Given I have started Tails from DVD with I2P enabled and logged in and the network is connected
And the I2P router console is ready
@@ -27,7 +31,7 @@ Feature: I2P
Then the I2P router console is displayed in I2P Browser
And the I2P Browser uses all expected TBB shared libraries
- #11457, #11458
+ #11114, #11457, #11458
@fragile
Scenario: Closing the I2P Browser shows a stop notification and properly tears down the chroot.
Given I have started Tails from DVD with I2P enabled and logged in and the network is connected
@@ -48,6 +52,8 @@ Feature: I2P
When I open the address "http://i2p-projekt.i2p" in the I2P Browser
Then the I2P homepage loads in I2P Browser
+ #11114
+ @fragile
Scenario: I2P is configured to run in Hidden mode
Given I have started Tails from DVD with I2P enabled and logged in and the network is connected
And the I2P router console is ready
@@ -55,7 +61,7 @@ Feature: I2P
Then the I2P router console is displayed in I2P Browser
And I2P is running in hidden mode
- #10474
+ #10474, #11114
@fragile
Scenario: Connecting to the #i2p IRC channel with the pre-configured account
Given I have started Tails from DVD with I2P enabled and logged in and the network is connected
diff --git a/features/icedove.feature b/features/icedove.feature
index 8693a44..298be78 100644
--- a/features/icedove.feature
+++ b/features/icedove.feature
@@ -1,31 +1,58 @@
#11465
-@product @check_tor_leaks @fragile
+@product @check_tor_leaks
Feature: Icedove email client
As a Tails user
I may want to use an email client
Background:
Given I have started Tails from DVD and logged in and the network is connected
- When I start "Icedove" via the GNOME "Internet" applications menu
- And Icedove has started
And I have not configured an email account
+ When I start Icedove
Then I am prompted to setup an email account
- Scenario: Adblock is not enabled within Icedove
+ Scenario: Only the expected addons are installed
Given I cancel setting up an email account
When I open Icedove's Add-ons Manager
And I click the extensions tab
- Then I see that Adblock is not installed in Icedove
+ Then I see that only the Enigmail and TorBirdy addons are enabled in Icedove
Scenario: Enigmail is configured to use the correct keyserver
Given I cancel setting up an email account
And I go into Enigmail's preferences
- When I click Enigmail's keyserver tab
+ And I enable Enigmail's expert settings
+ When I click Enigmail's Keyserver tab
Then I see that Enigmail is configured to use the correct keyserver
- When I click Enigmail's advanced tab
+ When I click Enigmail's Advanced tab
Then I see that Enigmail is configured to use the correct SOCKS proxy
Scenario: Torbirdy is configured to use Tor
Given I cancel setting up an email account
- And I open Torbirdy's preferences
Then I see that Torbirdy is configured to use Tor
+
+ #11890
+ @fragile
+ Scenario: Icedove's autoconfiguration wizard defaults to IMAP and secure protocols
+ When I enter my email credentials into the autoconfiguration wizard
+ Then the autoconfiguration wizard's choice for the incoming server is secure IMAP
+ Then the autoconfiguration wizard's choice for the outgoing server is secure SMTP
+
+ #11890
+ @fragile
+ Scenario: Icedove can send emails, and receive emails over IMAP
+ When I enter my email credentials into the autoconfiguration wizard
+ Then the autoconfiguration wizard's choice for the incoming server is secure IMAP
+ When I accept the autoconfiguration wizard's configuration
+ And I send an email to myself
+ And I fetch my email
+ Then I can find the email I sent to myself in my inbox
+
+ #11890
+ @fragile
+ Scenario: Icedove can download the inbox with POP3
+ When I enter my email credentials into the autoconfiguration wizard
+ Then the autoconfiguration wizard's choice for the incoming server is secure IMAP
+ When I select the autoconfiguration wizard's POP3 choice
+ Then the autoconfiguration wizard's choice for the incoming server is secure POP3
+ When I accept the autoconfiguration wizard's configuration
+ And I fetch my email
+ Then my Icedove inbox is non-empty
diff --git a/features/images/CupsTestPage.png b/features/images/CupsTestPage.png
index 65b3cac..916486e 100644
--- a/features/images/CupsTestPage.png
+++ b/features/images/CupsTestPage.png
Binary files differ
diff --git a/features/images/ElectrumConnectServer.png b/features/images/ElectrumConnectServer.png
deleted file mode 100644
index 9e587ed..0000000
--- a/features/images/ElectrumConnectServer.png
+++ /dev/null
Binary files differ
diff --git a/features/images/ElectrumCreateNewSeed.png b/features/images/ElectrumCreateNewSeed.png
new file mode 100644
index 0000000..9d56724
--- /dev/null
+++ b/features/images/ElectrumCreateNewSeed.png
Binary files differ
diff --git a/features/images/ElectrumNoWallet.png b/features/images/ElectrumNoWallet.png
index a6213e6..784ec0d 100644
--- a/features/images/ElectrumNoWallet.png
+++ b/features/images/ElectrumNoWallet.png
Binary files differ
diff --git a/features/images/GitCloneDone.png b/features/images/GitCloneDone.png
new file mode 100644
index 0000000..f81dd59
--- /dev/null
+++ b/features/images/GitCloneDone.png
Binary files differ
diff --git a/features/images/GnomeCloseTopButton.png b/features/images/GnomeCloseTopButton.png
new file mode 100644
index 0000000..b5a2f7f
--- /dev/null
+++ b/features/images/GnomeCloseTopButton.png
Binary files differ
diff --git a/features/images/GpgAppletDecryptVerify.png b/features/images/GpgAppletDecryptVerify.png
index 3879f6b..c584792 100644
--- a/features/images/GpgAppletDecryptVerify.png
+++ b/features/images/GpgAppletDecryptVerify.png
Binary files differ
diff --git a/features/images/GpgAppletEncryptPassphrase.png b/features/images/GpgAppletEncryptPassphrase.png
index b2a9f99..6f07a29 100644
--- a/features/images/GpgAppletEncryptPassphrase.png
+++ b/features/images/GpgAppletEncryptPassphrase.png
Binary files differ
diff --git a/features/images/GpgAppletIconEncrypted.png b/features/images/GpgAppletIconEncrypted.png
index 940772a..479438e 100644
--- a/features/images/GpgAppletIconEncrypted.png
+++ b/features/images/GpgAppletIconEncrypted.png
Binary files differ
diff --git a/features/images/GpgAppletIconNormal.png b/features/images/GpgAppletIconNormal.png
index 5775c4f..102b903 100644
--- a/features/images/GpgAppletIconNormal.png
+++ b/features/images/GpgAppletIconNormal.png
Binary files differ
diff --git a/features/images/GpgAppletIconSigned.png b/features/images/GpgAppletIconSigned.png
index 44ec2e3..44978e6 100644
--- a/features/images/GpgAppletIconSigned.png
+++ b/features/images/GpgAppletIconSigned.png
Binary files differ
diff --git a/features/images/GpgAppletKeySelected.png b/features/images/GpgAppletKeySelected.png
index b67cdf9..761a832 100644
--- a/features/images/GpgAppletKeySelected.png
+++ b/features/images/GpgAppletKeySelected.png
Binary files differ
diff --git a/features/images/IcedoveEnigmailAdvancedParameters.png b/features/images/IcedoveEnigmailAdvancedParameters.png
deleted file mode 100644
index 9f7a325..0000000
--- a/features/images/IcedoveEnigmailAdvancedParameters.png
+++ /dev/null
Binary files differ
diff --git a/features/images/IcedoveEnigmailAdvancedTab.png b/features/images/IcedoveEnigmailAdvancedTab.png
deleted file mode 100644
index 808192a..0000000
--- a/features/images/IcedoveEnigmailAdvancedTab.png
+++ /dev/null
Binary files differ
diff --git a/features/images/IcedoveEnigmailExpertSettingsButton.png b/features/images/IcedoveEnigmailExpertSettingsButton.png
deleted file mode 100644
index d66767b..0000000
--- a/features/images/IcedoveEnigmailExpertSettingsButton.png
+++ /dev/null
Binary files differ
diff --git a/features/images/IcedoveEnigmailKeyserver.png b/features/images/IcedoveEnigmailKeyserver.png
deleted file mode 100644
index afe56ad..0000000
--- a/features/images/IcedoveEnigmailKeyserver.png
+++ /dev/null
Binary files differ
diff --git a/features/images/IcedoveEnigmailKeyserverTab.png b/features/images/IcedoveEnigmailKeyserverTab.png
deleted file mode 100644
index b9bdcae..0000000
--- a/features/images/IcedoveEnigmailKeyserverTab.png
+++ /dev/null
Binary files differ
diff --git a/features/images/IcedoveEnigmailPreferences.png b/features/images/IcedoveEnigmailPreferences.png
deleted file mode 100644
index 51153dc..0000000
--- a/features/images/IcedoveEnigmailPreferences.png
+++ /dev/null
Binary files differ
diff --git a/features/images/IcedoveEnigmailPreferencesWindow.png b/features/images/IcedoveEnigmailPreferencesWindow.png
deleted file mode 100644
index c840b04..0000000
--- a/features/images/IcedoveEnigmailPreferencesWindow.png
+++ /dev/null
Binary files differ
diff --git a/features/images/IcedoveEnigmailProxy.png b/features/images/IcedoveEnigmailProxy.png
deleted file mode 100644
index 10c034f..0000000
--- a/features/images/IcedoveEnigmailProxy.png
+++ /dev/null
Binary files differ
diff --git a/features/images/IcedoveMailAccountSetup.png b/features/images/IcedoveMailAccountSetup.png
deleted file mode 100644
index 4f8d1c8..0000000
--- a/features/images/IcedoveMailAccountSetup.png
+++ /dev/null
Binary files differ
diff --git a/features/images/IcedoveMainWindow.png b/features/images/IcedoveMainWindow.png
deleted file mode 100644
index 9ec3dc9..0000000
--- a/features/images/IcedoveMainWindow.png
+++ /dev/null
Binary files differ
diff --git a/features/images/IcedoveMenuViewToolbars.png b/features/images/IcedoveMenuViewToolbars.png
deleted file mode 100644
index 857320a..0000000
--- a/features/images/IcedoveMenuViewToolbars.png
+++ /dev/null
Binary files differ
diff --git a/features/images/IcedoveMenuViewToolbarsStatusBar.png b/features/images/IcedoveMenuViewToolbarsStatusBar.png
deleted file mode 100644
index e6d8313..0000000
--- a/features/images/IcedoveMenuViewToolbarsStatusBar.png
+++ /dev/null
Binary files differ
diff --git a/features/images/IcedoveProtocolIMAP.png b/features/images/IcedoveProtocolIMAP.png
deleted file mode 100644
index 1872487..0000000
--- a/features/images/IcedoveProtocolIMAP.png
+++ /dev/null
Binary files differ
diff --git a/features/images/IcedoveToolsMenuAddOns.png b/features/images/IcedoveToolsMenuAddOns.png
deleted file mode 100644
index 8b38ac2..0000000
--- a/features/images/IcedoveToolsMenuAddOns.png
+++ /dev/null
Binary files differ
diff --git a/features/images/IcedoveTorbirdyEnabled.png b/features/images/IcedoveTorbirdyEnabled.png
deleted file mode 100644
index 938b36b..0000000
--- a/features/images/IcedoveTorbirdyEnabled.png
+++ /dev/null
Binary files differ
diff --git a/features/images/IcedoveTorbirdyPreferences.png b/features/images/IcedoveTorbirdyPreferences.png
deleted file mode 100644
index 06d6d0d..0000000
--- a/features/images/IcedoveTorbirdyPreferences.png
+++ /dev/null
Binary files differ
diff --git a/features/images/MozillaAddonsManagerExtensions.png b/features/images/MozillaAddonsManagerExtensions.png
index 2241c1e..96d6d58 100644
--- a/features/images/MozillaAddonsManagerExtensions.png
+++ b/features/images/MozillaAddonsManagerExtensions.png
Binary files differ
diff --git a/features/images/MozillaExtensionsTorbirdy.png b/features/images/MozillaExtensionsTorbirdy.png
index 7560932..0beca90 100644
--- a/features/images/MozillaExtensionsTorbirdy.png
+++ b/features/images/MozillaExtensionsTorbirdy.png
Binary files differ
diff --git a/features/images/SSHError.png b/features/images/SSHError.png
new file mode 100644
index 0000000..b2cec40
--- /dev/null
+++ b/features/images/SSHError.png
Binary files differ
diff --git a/features/images/SampleLocalMp4VideoFrame.png b/features/images/SampleLocalMp4VideoFrame.png
index 336c955..df65c3a 100644
--- a/features/images/SampleLocalMp4VideoFrame.png
+++ b/features/images/SampleLocalMp4VideoFrame.png
Binary files differ
diff --git a/features/images/SupportDocumentation.png b/features/images/SupportDocumentation.png
deleted file mode 100644
index bccfb80..0000000
--- a/features/images/SupportDocumentation.png
+++ /dev/null
Binary files differ
diff --git a/features/images/SupportDocumentationGerman.png b/features/images/SupportDocumentationGerman.png
deleted file mode 100644
index 24c7708..0000000
--- a/features/images/SupportDocumentationGerman.png
+++ /dev/null
Binary files differ
diff --git a/features/images/SynapticFailure.png b/features/images/SynapticFailure.png
new file mode 100644
index 0000000..a0a702b
--- /dev/null
+++ b/features/images/SynapticFailure.png
Binary files differ
diff --git a/features/images/SynapticLoaded.png b/features/images/SynapticLoaded.png
new file mode 100644
index 0000000..91b9062
--- /dev/null
+++ b/features/images/SynapticLoaded.png
Binary files differ
diff --git a/features/images/SynapticReloadButton.png b/features/images/SynapticReloadButton.png
deleted file mode 100644
index 1af3f1d..0000000
--- a/features/images/SynapticReloadButton.png
+++ /dev/null
Binary files differ
diff --git a/features/images/SynapticSearchWindow.png b/features/images/SynapticSearchWindow.png
index 98574cf..2077789 100644
--- a/features/images/SynapticSearchWindow.png
+++ b/features/images/SynapticSearchWindow.png
Binary files differ
diff --git a/features/images/TailsBootMenuKernelCmdline.png b/features/images/TailsBootMenuKernelCmdline.png
new file mode 100644
index 0000000..efc5d89
--- /dev/null
+++ b/features/images/TailsBootMenuKernelCmdline.png
Binary files differ
diff --git a/features/images/TailsBootMenuKernelCmdlineUEFI.png b/features/images/TailsBootMenuKernelCmdlineUEFI.png
new file mode 100644
index 0000000..0a00fde
--- /dev/null
+++ b/features/images/TailsBootMenuKernelCmdlineUEFI.png
Binary files differ
diff --git a/features/images/TailsBootSplash.png b/features/images/TailsBootSplash.png
deleted file mode 100644
index 2bed76d..0000000
--- a/features/images/TailsBootSplash.png
+++ /dev/null
Binary files differ
diff --git a/features/images/TailsBootSplashUEFI.png b/features/images/TailsBootSplashUEFI.png
deleted file mode 100644
index 445f6dc..0000000
--- a/features/images/TailsBootSplashUEFI.png
+++ /dev/null
Binary files differ
diff --git a/features/images/TailsBooting.png b/features/images/TailsBooting.png
new file mode 100644
index 0000000..8a7a77b
--- /dev/null
+++ b/features/images/TailsBooting.png
Binary files differ
diff --git a/features/images/TailsBug11786a.png b/features/images/TailsBug11786a.png
new file mode 100644
index 0000000..2e6428b
--- /dev/null
+++ b/features/images/TailsBug11786a.png
Binary files differ
diff --git a/features/images/TailsBug11786b.png b/features/images/TailsBug11786b.png
new file mode 100644
index 0000000..49ad928
--- /dev/null
+++ b/features/images/TailsBug11786b.png
Binary files differ
diff --git a/features/images/TailsHomepage.png b/features/images/TailsHomepage.png
index e21c0f7..66ded74 100644
--- a/features/images/TailsHomepage.png
+++ b/features/images/TailsHomepage.png
Binary files differ
diff --git a/features/images/TailsOfflineDocHomepage.png b/features/images/TailsOfflineDocHomepage.png
deleted file mode 100644
index 95d5ea6..0000000
--- a/features/images/TailsOfflineDocHomepage.png
+++ /dev/null
Binary files differ
diff --git a/features/images/TailsUpgraderDone.png b/features/images/TailsUpgraderDone.png
new file mode 100644
index 0000000..4bbb10c
--- /dev/null
+++ b/features/images/TailsUpgraderDone.png
Binary files differ
diff --git a/features/images/TailsUpgraderFailure.png b/features/images/TailsUpgraderFailure.png
new file mode 100644
index 0000000..34cdf9f
--- /dev/null
+++ b/features/images/TailsUpgraderFailure.png
Binary files differ
diff --git a/features/images/TailsUpgraderUpgradeNowButton.png b/features/images/TailsUpgraderUpgradeNowButton.png
new file mode 100644
index 0000000..8cf0cc9
--- /dev/null
+++ b/features/images/TailsUpgraderUpgradeNowButton.png
Binary files differ
diff --git a/features/images/TailsUpgraderUpgradeTo1.1~test.png b/features/images/TailsUpgraderUpgradeTo1.1~test.png
new file mode 100644
index 0000000..2790588
--- /dev/null
+++ b/features/images/TailsUpgraderUpgradeTo1.1~test.png
Binary files differ
diff --git a/features/images/TorBrowserSavedStartupPage.png b/features/images/TorBrowserSavedStartupPage.png
index f32a0f9..0d43813 100644
--- a/features/images/TorBrowserSavedStartupPage.png
+++ b/features/images/TorBrowserSavedStartupPage.png
Binary files differ
diff --git a/features/images/UEFIFirmwareSetup.png b/features/images/UEFIFirmwareSetup.png
new file mode 100644
index 0000000..368857d
--- /dev/null
+++ b/features/images/UEFIFirmwareSetup.png
Binary files differ
diff --git a/features/keys.feature b/features/keys.feature
new file mode 100644
index 0000000..e1db95c
--- /dev/null
+++ b/features/keys.feature
@@ -0,0 +1,10 @@
+@product
+Feature: Tails-related cryptographic keys are up-to-date
+
+ Scenario: The shipped Tails OpenPGP keys are up-to-date
+ Given I have started Tails from DVD without network and logged in
+ Then the OpenPGP keys shipped with Tails will be valid for the next 3 months
+
+ Scenario: The Tails Debian repository key is up-to-date
+ Given I have started Tails from DVD without network and logged in
+ Then the shipped Debian repository key will be valid for the next 3 months
diff --git a/features/localization.feature b/features/localization.feature
index 4ec4a05..2f36aa2 100644
--- a/features/localization.feature
+++ b/features/localization.feature
@@ -13,6 +13,8 @@ Feature: Localization
When I double-click the Report an Error launcher on the desktop
Then the support documentation page opens in Tor Browser
+ #11711
+ @fragile
Scenario: The Unsafe Browser can be used in all languages supported in Tails
Given I have started Tails from DVD and logged in and the network is connected
Then the Unsafe Browser works in all supported languages
diff --git a/features/mat.feature b/features/mat.feature
index e492b0f..1b6345f 100644
--- a/features/mat.feature
+++ b/features/mat.feature
@@ -3,11 +3,8 @@ Feature: Metadata Anonymization Toolkit
As a Tails user
I want to be able to remove leaky metadata from documents and media files
- # In this feature we cannot restore from snapshots since it's
- # incompatible with filesystem shares.
-
- Scenario: MAT can clean a PDF file
+ Scenario: MAT can clean a PNG file
Given a computer
- And I setup a filesystem share containing a sample PDF
And I start Tails from DVD with network unplugged and I login
- Then MAT can clean some sample PDF file
+ And I plug and mount a USB drive containing a sample PNG
+ Then MAT can clean some sample PNG file
diff --git a/features/misc_files/sample.pdf b/features/misc_files/sample.pdf
deleted file mode 100644
index d0cc950..0000000
--- a/features/misc_files/sample.pdf
+++ /dev/null
Binary files differ
diff --git a/features/misc_files/sample.png b/features/misc_files/sample.png
new file mode 100644
index 0000000..facf4269
--- /dev/null
+++ b/features/misc_files/sample.png
Binary files differ
diff --git a/features/misc_files/sample.tex b/features/misc_files/sample.tex
deleted file mode 100644
index 043faae..0000000
--- a/features/misc_files/sample.tex
+++ /dev/null
@@ -1,8 +0,0 @@
-\documentclass[12pt]{article}
-\title{Sample PDF document}
-\author{Tails developers}
-\date{March 12, 2013}
-\begin{document}
-\maketitle
-Does this PDF still have metadata?
-\end{document}
diff --git a/features/networking.feature b/features/networking.feature
new file mode 100644
index 0000000..a5e15b2
--- /dev/null
+++ b/features/networking.feature
@@ -0,0 +1,27 @@
+@product
+Feature: Networking
+
+ Scenario: No initial network
+ Given I have started Tails from DVD without network and logged in
+ And I wait between 30 and 60 seconds
+ Then the Tor Status icon tells me that Tor is not usable
+ When the network is plugged
+ Then Tor is ready
+ And the Tor Status icon tells me that Tor is usable
+ And all notifications have disappeared
+ And the time has synced
+
+ Scenario: The Tails Greeter "disable all networking" option disables networking within Tails
+ Given I have started Tails from DVD without network and stopped at Tails Greeter's login screen
+ And I enable more Tails Greeter options
+ And I disable all networking in the Tails Greeter
+ And I log in to a new session
+ Then no network interfaces are enabled
+
+ #11463
+ @fragile
+ Scenario: The 'Tor is ready' notification is shown when Tor has bootstrapped
+ Given I have started Tails from DVD without network and logged in
+ And the network is plugged
+ When I see the 'Tor is ready' notification
+ Then Tor is ready
diff --git a/features/persistence.feature b/features/persistence.feature
index 42f91de..c7416e4 100644
--- a/features/persistence.feature
+++ b/features/persistence.feature
@@ -1,5 +1,4 @@
-#10720: Tails Installer freezes on Jenkins
-@product @fragile
+@product
Feature: Tails persistence
As a Tails user
I want to use Tails persistence feature
@@ -55,3 +54,10 @@ Feature: Tails persistence
And all notifications have disappeared
When I delete the persistent partition
Then there is no persistence partition on USB drive "__internal"
+
+ Scenario: Dotfiles persistence
+ Given I have started Tails without network from a USB drive with a persistent partition enabled and logged in
+ When I write some dotfile expected to persist
+ And I shutdown Tails and wait for the computer to power off
+ And I start Tails from USB drive "__internal" with network unplugged and I login with persistence enabled
+ Then the expected persistent dotfile is present in the filesystem
diff --git a/features/pidgin.feature b/features/pidgin.feature
index aabe9b7..84ec5dd 100644
--- a/features/pidgin.feature
+++ b/features/pidgin.feature
@@ -91,7 +91,7 @@ Feature: Chatting anonymously using Pidgin
And I close Pidgin's certificate manager
Then I cannot add a certificate from the "/live/overlay/home/amnesia/.gnupg" directory to Pidgin
- #10720: Tails Installer freezes on Jenkins
+ #11584
@check_tor_leaks @fragile
Scenario: Using a persistent Pidgin configuration
Given I have started Tails without network from a USB drive with a persistent partition enabled and logged in
diff --git a/features/regression_tests.feature b/features/regression_tests.feature
new file mode 100644
index 0000000..a1925ff
--- /dev/null
+++ b/features/regression_tests.feature
@@ -0,0 +1,6 @@
+@product
+Feature: Regressions tests
+
+ Scenario: tails-debugging-info does not leak information
+ Given I have started Tails from DVD without network and logged in
+ Then tails-debugging-info is not susceptible to symlink attacks
diff --git a/features/sane_defaults.feature b/features/sane_defaults.feature
new file mode 100644
index 0000000..65c10f9
--- /dev/null
+++ b/features/sane_defaults.feature
@@ -0,0 +1,15 @@
+@product
+Feature: Tails has a sane default configuration
+
+ Scenario: The live user is setup correctly
+ Given I have started Tails from DVD without network and logged in
+ Then the live user has been setup by live-boot
+ And the live user is a member of only its own group and "audio cdrom dialout floppy video plugdev netdev scanner lp lpadmin vboxsf"
+ And the live user owns its home dir and it has normal permissions
+
+ Scenario: No unexpected network services
+ Given I have started Tails from DVD without network and logged in
+ When the network is plugged
+ And Tor is ready
+ Then no unexpected services are listening for network connections
+
diff --git a/features/ssh.feature b/features/ssh.feature
index 0e64e86..695f1b6 100644
--- a/features/ssh.feature
+++ b/features/ssh.feature
@@ -1,5 +1,4 @@
-#10498: SSH tests are fragile
-@product @fragile
+@product
Feature: Logging in via SSH
As a Tails user
When I connect to SSH servers on the Internet
@@ -12,7 +11,6 @@ Feature: Logging in via SSH
Scenario: Connecting to an SSH server on the Internet
Given I have the SSH key pair for an SSH server
When I connect to an SSH server on the Internet
- And I verify the SSH fingerprint for the SSH server
Then I have sucessfully logged into the SSH server
@check_tor_leaks
@@ -26,5 +24,4 @@ Feature: Logging in via SSH
Scenario: Connecting to an SFTP server on the Internet using the GNOME "Connect to Server" feature
Given I have the SSH key pair for an SFTP server
When I connect to an SFTP server on the Internet
- And I verify the SSH fingerprint for the SFTP server
Then I successfully connect to the SFTP server
diff --git a/features/step_definitions/apt.rb b/features/step_definitions/apt.rb
index 8756803..86f878d 100644
--- a/features/step_definitions/apt.rb
+++ b/features/step_definitions/apt.rb
@@ -11,46 +11,99 @@ Given /^the only hosts in APT sources are "([^"]*)"$/ do |hosts_str|
}
end
+When /^I configure APT to use non-onion sources$/ do
+ script = <<-EOF
+ use strict;
+ use warnings FATAL => "all";
+ s{vwakviie2ienjx6t[.]onion}{ftp.us.debian.org};
+ s{sgvtcaew4bxjd7ln[.]onion}{security.debian.org};
+ s{sdscoq7snqtznauu[.]onion}{deb.torproject.org};
+ s{jenw7xbd6tf7vfhp[.]onion}{deb.tails.boum.org};
+EOF
+ # VMCommand:s cannot handle newlines, and they're irrelevant in the
+ # above perl script any way
+ script.delete!("\n")
+ $vm.execute_successfully(
+ "perl -pi -E '#{script}' /etc/apt/sources.list /etc/apt/sources.list.d/*"
+ )
+end
+
When /^I update APT using apt$/ do
- Timeout::timeout(30*60) do
- $vm.execute_successfully("echo #{@sudo_password} | " +
- "sudo -S apt update", :user => LIVE_USER)
+ recovery_proc = Proc.new do
+ step 'I kill the process "apt"'
+ $vm.execute('rm -rf /var/lib/apt/lists/*')
+ end
+ retry_tor(recovery_proc) do
+ Timeout::timeout(15*60) do
+ $vm.execute_successfully("echo #{@sudo_password} | " +
+ "sudo -S apt update", :user => LIVE_USER)
+ end
end
end
Then /^I should be able to install a package using apt$/ do
package = "cowsay"
- Timeout::timeout(120) do
- $vm.execute_successfully("echo #{@sudo_password} | " +
- "sudo -S apt install #{package}",
- :user => LIVE_USER)
+ recovery_proc = Proc.new do
+ step 'I kill the process "apt"'
+ $vm.execute("apt purge #{package}")
+ end
+ retry_tor(recovery_proc) do
+ Timeout::timeout(2*60) do
+ $vm.execute_successfully("echo #{@sudo_password} | " +
+ "sudo -S apt install #{package}",
+ :user => LIVE_USER)
+ end
end
step "package \"#{package}\" is installed"
end
When /^I update APT using Synaptic$/ do
- @screen.click('SynapticReloadButton.png')
- @screen.wait('SynapticReloadPrompt.png', 20)
- @screen.waitVanish('SynapticReloadPrompt.png', 30*60)
+ recovery_proc = Proc.new do
+ step 'I kill the process "synaptic"'
+ step "I start Synaptic"
+ end
+ retry_tor(recovery_proc) do
+ try_for(60, :msg => "Failed to trigger the reload of the package list") {
+ # here using the Synaptic keyboard shortcut is more effective on retries.
+ @screen.type("r", Sikuli::KeyModifier.CTRL)
+ @screen.wait('SynapticReloadPrompt.png', 10)
+ }
+ try_for(15*60, :msg => "Took too much time to download the APT data") {
+ !$vm.has_process?("/usr/lib/apt/methods/tor+http")
+ }
+ if @screen.exists('SynapticFailure.png')
+ raise "Updating APT with Synaptic failed."
+ end
+ if !$vm.has_process?("synaptic")
+ raise "Synaptic process vanished, did it segfault again?"
+ end
+ end
end
Then /^I should be able to install a package using Synaptic$/ do
package = "cowsay"
- try_for(60) do
- @screen.wait_and_click('SynapticSearchButton.png', 10)
- @screen.wait_and_click('SynapticSearchWindow.png', 10)
- end
- @screen.type(package + Sikuli::Key.ENTER)
- @screen.wait_and_double_click('SynapticCowsaySearchResult.png', 20)
- @screen.wait_and_click('SynapticApplyButton.png', 10)
- @screen.wait('SynapticApplyPrompt.png', 60)
- @screen.type(Sikuli::Key.ENTER)
- @screen.wait('SynapticChangesAppliedPrompt.png', 240)
- step "package \"#{package}\" is installed"
+ recovery_proc = Proc.new do
+ step 'I kill the process "synaptic"'
+ $vm.execute("apt -y purge #{package}")
+ step "I start Synaptic"
+ end
+ retry_tor(recovery_proc) do
+ try_for(60) do
+ @screen.wait_and_click('SynapticSearchButton.png', 10)
+ @screen.wait_and_click('SynapticSearchWindow.png', 10)
+ end
+ @screen.type(package + Sikuli::Key.ENTER)
+ @screen.wait_and_double_click('SynapticCowsaySearchResult.png', 20)
+ @screen.wait_and_click('SynapticApplyButton.png', 10)
+ @screen.wait('SynapticApplyPrompt.png', 60)
+ @screen.type(Sikuli::Key.ENTER)
+ @screen.wait('SynapticChangesAppliedPrompt.png', 4*60)
+ step "package \"#{package}\" is installed"
+ end
end
When /^I start Synaptic$/ do
step 'I start "Synaptic Package Manager" via the GNOME "System Tools" applications menu'
deal_with_polkit_prompt('PolicyKitAuthPrompt.png', @sudo_password)
- @screen.wait('SynapticReloadButton.png', 30)
+ @screen.wait('SynapticLoaded.png', 30)
end
diff --git a/features/step_definitions/browser.rb b/features/step_definitions/browser.rb
index 896906b..fac23b7 100644
--- a/features/step_definitions/browser.rb
+++ b/features/step_definitions/browser.rb
@@ -116,14 +116,21 @@ end
# This step is limited to the Tor Browser due to #7502 since dogtail
# uses the same interface.
Then /^"([^"]+)" has loaded in the Tor Browser$/ do |title|
- expected_title = "#{title} - Tor Browser"
+ if @language == 'German'
+ browser_name = 'Tor-Browser'
+ reload_action = 'Aktuelle Seite neu laden'
+ else
+ browser_name = 'Tor Browser'
+ reload_action = 'Reload current page'
+ end
+ expected_title = "#{title} - #{browser_name}"
app = Dogtail::Application.new('Firefox')
app.child(expected_title, roleName: 'frame').wait(60)
# The 'Reload current page' button (graphically shown as a looping
# arrow) is only shown when a page has loaded, so once we see the
# expected title *and* this button has appeared, then we can be sure
# that the page has fully loaded.
- app.child('Reload current page', roleName: 'push button').wait(60)
+ app.child(reload_action, roleName: 'push button').wait(60)
end
Then /^the (.*) has no plugins installed$/ do |browser|
diff --git a/features/step_definitions/checks.rb b/features/step_definitions/checks.rb
index 423b839..d61c89f 100644
--- a/features/step_definitions/checks.rb
+++ b/features/step_definitions/checks.rb
@@ -96,6 +96,11 @@ Then /^no unexpected services are listening for network connections$/ do
end
end
+When /^Tails has booted a 32-bit kernel$/ do
+ assert(! $vm.execute("uname -r | grep -qs 'amd64$'").success?,
+ "Tails has not booted a 32-bit kernel.")
+end
+
When /^Tails has booted a 64-bit kernel$/ do
assert($vm.execute("uname -r | grep -qs 'amd64$'").success?,
"Tails has not booted a 64-bit kernel.")
@@ -123,39 +128,53 @@ Then /^the VirtualBox guest modules are available$/ do
"The vboxguest module is not available.")
end
-Given /^I setup a filesystem share containing a sample PDF$/ do
- shared_pdf_dir_on_host = "#{$config["TMPDIR"]}/shared_pdf_dir"
- @shared_pdf_dir_on_guest = "/tmp/shared_pdf_dir"
- FileUtils.mkdir_p(shared_pdf_dir_on_host)
- Dir.glob("#{MISC_FILES_DIR}/*.pdf") do |pdf_file|
- FileUtils.cp(pdf_file, shared_pdf_dir_on_host)
+Then /^the support documentation page opens in Tor Browser$/ do
+ if @language == 'German'
+ expected_title = 'Tails - Hilfe & Support'
+ expected_heading = 'Die Dokumentation durchsuchen'
+ else
+ expected_title = 'Tails - Support'
+ expected_heading = 'Search the documentation'
end
- add_after_scenario_hook { FileUtils.rm_r(shared_pdf_dir_on_host) }
- $vm.add_share(shared_pdf_dir_on_host, @shared_pdf_dir_on_guest)
+ step "\"#{expected_title}\" has loaded in the Tor Browser"
+ headings = Dogtail::Application.new('Firefox')
+ .child(expected_title, roleName: 'document frame')
+ .children(roleName: 'heading')
+ assert(
+ headings.any? { |heading| heading.text == expected_heading }
+ )
end
-Then /^the support documentation page opens in Tor Browser$/ do
- @screen.wait("SupportDocumentation#{@language}.png", 120)
+Given /^I plug and mount a USB drive containing a sample PNG$/ do
+ @png_dir = share_host_files(Dir.glob("#{MISC_FILES_DIR}/*.png"))
end
-Then /^MAT can clean some sample PDF file$/ do
- for pdf_on_host in Dir.glob("#{MISC_FILES_DIR}/*.pdf") do
- pdf_name = File.basename(pdf_on_host)
- pdf_on_guest = "/home/#{LIVE_USER}/#{pdf_name}"
- step "I copy \"#{@shared_pdf_dir_on_guest}/#{pdf_name}\" to \"#{pdf_on_guest}\" as user \"#{LIVE_USER}\""
- check_before = $vm.execute_successfully("mat --check '#{pdf_on_guest}'",
+Then /^MAT can clean some sample PNG file$/ do
+ for png_on_host in Dir.glob("#{MISC_FILES_DIR}/*.png") do
+ png_name = File.basename(png_on_host)
+ png_on_guest = "/home/#{LIVE_USER}/#{png_name}"
+ step "I copy \"#{@png_dir}/#{png_name}\" to \"#{png_on_guest}\" as user \"#{LIVE_USER}\""
+ raw_check_cmd = "grep --quiet --fixed-strings --text " +
+ "'Created with GIMP' '#{png_on_guest}'"
+ assert($vm.execute(raw_check_cmd, user: LIVE_USER).success?,
+ 'The comment is not present in the PNG')
+ check_before = $vm.execute_successfully("mat --check '#{png_on_guest}'",
:user => LIVE_USER).stdout
- assert(check_before.include?("#{pdf_on_guest} is not clean"),
- "MAT failed to see that '#{pdf_on_host}' is dirty")
- $vm.execute_successfully("mat '#{pdf_on_guest}'", :user => LIVE_USER)
- check_after = $vm.execute_successfully("mat --check '#{pdf_on_guest}'",
+ assert(check_before.include?("#{png_on_guest} is not clean"),
+ "MAT failed to see that '#{png_on_host}' is dirty")
+ $vm.execute_successfully("mat '#{png_on_guest}'", :user => LIVE_USER)
+ check_after = $vm.execute_successfully("mat --check '#{png_on_guest}'",
:user => LIVE_USER).stdout
- assert(check_after.include?("#{pdf_on_guest} is clean"),
- "MAT failed to clean '#{pdf_on_host}'")
- $vm.execute_successfully("rm '#{pdf_on_guest}'")
+ assert(check_after.include?("#{png_on_guest} is clean"),
+ "MAT failed to clean '#{png_on_host}'")
+ assert($vm.execute(raw_check_cmd, user: LIVE_USER).failure?,
+ 'The comment is still present in the PNG')
+ $vm.execute_successfully("rm '#{png_on_guest}'")
end
end
+
+
Then /^AppArmor is enabled$/ do
assert($vm.execute("aa-status").success?, "AppArmor is not enabled")
end
diff --git a/features/step_definitions/common_steps.rb b/features/step_definitions/common_steps.rb
index 250700f..1353fc7 100644
--- a/features/step_definitions/common_steps.rb
+++ b/features/step_definitions/common_steps.rb
@@ -5,25 +5,7 @@ def post_vm_start_hook
# 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.
- @screen.click_point(@screen.w, @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
+ @screen.click_point(@screen.w - 1, @screen.h/2)
end
def context_menu_helper(top, bottom, menu_item)
@@ -41,17 +23,6 @@ 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)
@@ -92,9 +63,6 @@ def post_snapshot_restore_hook
$vm.wait_until_remote_shell_is_up
post_vm_start_hook
- # XXX-9p: See XXX-9p above
- #activate_filesystem_shares
-
# 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.
@@ -136,7 +104,7 @@ Given /^the computer is set to boot from (.+?) drive "(.+?)"$/ do |type, name|
$vm.set_disk_boot(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
@@ -164,6 +132,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
@@ -252,16 +225,16 @@ When /^I destroy the computer$/ do
$vm.destroy_and_undefine
end
-def bootsplash
+def boot_menu_cmdline_image
case @os_loader
when "UEFI"
- 'TailsBootSplashUEFI.png'
+ 'TailsBootMenuKernelCmdlineUEFI.png'
else
- 'TailsBootSplash.png'
+ 'TailsBootMenuKernelCmdline.png'
end
end
-def bootsplash_tab_msg
+def boot_menu_tab_msg_image
case @os_loader
when "UEFI"
'TailsBootSplashTabMsgUEFI.png'
@@ -270,22 +243,74 @@ def bootsplash_tab_msg
end
end
-Given /^the computer (re)?boots Tails$/ do |reboot|
+def memory_wipe_timeout
+ nr_gigs_of_ram = convert_from_bytes($vm.get_ram_size_in_bytes, 'GiB').ceil
+ nr_gigs_of_ram*30
+end
- boot_timeout = 30
+Given /^Tails is at the boot menu's cmdline( after rebooting)?$/ do |reboot|
+ boot_timeout = 3*60
# 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)
+ boot_timeout += memory_wipe_timeout if reboot
+ # Simply looking for the boot splash image is not robust; sometimes
+ # sikuli is not fast enough to see it. Here we hope that spamming
+ # TAB, which will halt the boot process by showing the prompt for
+ # the kernel cmdline, will make this a bit more robust. We want this
+ # spamming to happen in parallel with Sikuli waiting for the image,
+ # but multi-threading etc is working extremely poor in our Ruby +
+ # jrb environment when Sikuli is involved. Hence we run the spamming
+ # from a separate process.
+ tab_spammer_code = <<-EOF
+ require 'libvirt'
+ tab_key_code = 0xf
+ virt = Libvirt::open("qemu:///system")
+ begin
+ domain = virt.lookup_domain_by_name('#{$vm.domain_name}')
+ loop do
+ domain.send_key(Libvirt::Domain::KEYCODE_SET_LINUX, 0, [tab_key_code])
+ sleep 0.1
+ end
+ ensure
+ virt.close
+ end
+ EOF
+ # Our UEFI firmware (OVMF) has the interesting "feature" that pressing
+ # any button will open its setup menu, so we have to exit the setup,
+ # and to not have the TAB spammer potentially interfering we pause
+ # it meanwhile.
+ dealt_with_uefi_setup = false
+ # The below code is not completely reliable, so we might have to
+ # retry by rebooting.
+ try_for(boot_timeout) do
+ begin
+ tab_spammer = IO.popen(['ruby', '-e', tab_spammer_code])
+ if not(dealt_with_uefi_setup) && @os_loader == 'UEFI'
+ @screen.wait('UEFIFirmwareSetup.png', 30)
+ Process.kill("TSTP", tab_spammer.pid)
+ @screen.type(Sikuli::Key.ENTER)
+ Process.kill("CONT", tab_spammer.pid)
+ dealt_with_uefi_setup = true
+ end
+ @screen.wait(boot_menu_cmdline_image, 15)
+ rescue FindFailed => e
+ debug_log('We missed the boot menu before we could deal with it, ' +
+ 'resetting...')
+ dealt_with_uefi_setup = false
+ $vm.reset
+ retry
+ ensure
+ Process.kill("TERM", tab_spammer.pid)
+ tab_spammer.close
+ end
+ end
+end
+Given /^the computer (re)?boots Tails$/ do |reboot|
+ 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('TailsGreeter.png', 30*60)
+ @screen.wait('TailsGreeter.png', 5*60)
$vm.wait_until_remote_shell_is_up
- activate_filesystem_shares
step 'I configure Tails to use a simulated Tor network'
end
@@ -301,14 +326,14 @@ Given /^I log in to a new session(?: in )?(|German)$/ do |lang|
else
raise "Unsupported language: #{lang}"
end
- step 'Tails Greeter has dealt with the sudo password'
+ step 'Tails Greeter has applied all settings'
step 'the Tails desktop is ready'
end
Given /^I enable more Tails Greeter options$/ do
match = @screen.find('TailsGreeterMoreOptions.png')
@screen.click(match.getCenter.offset(match.w/2, match.h*2))
- @screen.wait_and_click('TailsGreeterForward.png', 10)
+ @screen.wait_and_click('TailsGreeterForward.png', 20)
@screen.wait('TailsGreeterLoginButton.png', 20)
end
@@ -323,11 +348,12 @@ Given /^I set an administration password$/ do
@screen.type(@sudo_password)
end
-Given /^Tails Greeter has dealt with the sudo password$/ do
- f1 = "/etc/sudoers.d/tails-greeter"
- f2 = "#{f1}-no-password-lecture"
- try_for(30) {
- $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*$/) != nil
}
end
@@ -516,19 +542,31 @@ Given /^I kill the process "([^"]+)"$/ do |process|
}
end
-Then /^Tails eventually shuts down$/ do
+Then /^Tails eventually (shuts down|restarts)$/ do |mode|
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?
+ # Work around Tails bug #11786, where something goes wrong when we
+ # kexec to the new kernel for memory wiping and gets dropped to a
+ # BusyBox shell instead.
+ try_for(timeout) do
+ if @screen.existsAny(['TailsBug11786a.png', 'TailsBug11786b.png'])
+ puts "We were hit by bug #11786: memory wiping got stuck"
+ if mode == 'restarts'
+ $vm.reset
+ else
+ $vm.power_off
+ end
+ else
+ if mode == 'restarts'
+ @screen.find('TailsBootSplash.png')
+ true
+ else
+ ! $vm.is_running?
+ end
+ 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'
@@ -537,6 +575,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
@@ -547,6 +590,9 @@ 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
@@ -613,7 +659,7 @@ end
When /^I start and focus GNOME Terminal$/ do
step 'I start "Terminal" via the GNOME "Utilities" applications menu'
- @screen.wait('GnomeTerminalWindow.png', 20)
+ @screen.wait('GnomeTerminalWindow.png', 40)
end
When /^I run "([^"]+)" in GNOME Terminal$/ do |command|
@@ -905,7 +951,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)
@@ -913,3 +959,43 @@ 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
diff --git a/features/step_definitions/electrum.rb b/features/step_definitions/electrum.rb
index 85bcf0f..712b3f0 100644
--- a/features/step_definitions/electrum.rb
+++ b/features/step_definitions/electrum.rb
@@ -6,7 +6,7 @@ When /^a bitcoin wallet is (|not )present$/ do |existing|
wallet = "/home/#{LIVE_USER}/.electrum/wallets/default_wallet"
case existing
when ""
- step "the file \"#{wallet}\" exists after at most 10 seconds"
+ step "the file \"#{wallet}\" exists after at most 30 seconds"
when "not "
step "the file \"#{wallet}\" does not exist"
else
@@ -17,6 +17,8 @@ end
When /^I create a new bitcoin wallet$/ do
@screen.wait("ElectrumNoWallet.png", 10)
@screen.wait_and_click("ElectrumNextButton.png", 10)
+ @screen.wait("ElectrumCreateNewSeed.png", 10)
+ @screen.wait_and_click("ElectrumNextButton.png", 10)
@screen.wait("ElectrumWalletGenerationSeed.png", 15)
@screen.wait_and_click("ElectrumWalletSeedTextbox.png", 15)
@screen.type('a', Sikuli::KeyModifier.CTRL) # select wallet seed
@@ -27,11 +29,10 @@ When /^I create a new bitcoin wallet$/ do
@screen.wait_and_click("ElectrumWalletSeedTextbox.png", 15)
@screen.type(seed) # Confirm seed
@screen.wait_and_click("ElectrumNextButton.png", 10)
- @screen.wait_and_click("ElectrumEncryptWallet.png", 10)
+ @screen.wait("ElectrumEncryptWallet.png", 10)
+ @screen.type(Sikuli::Key.TAB) # focus first password field
@screen.type("asdf" + Sikuli::Key.TAB) # set password
@screen.type("asdf" + Sikuli::Key.TAB) # confirm password
- @screen.type(Sikuli::Key.ENTER)
- @screen.wait("ElectrumConnectServer.png", 20)
@screen.wait_and_click("ElectrumNextButton.png", 10)
@screen.wait("ElectrumPreferencesButton.png", 30)
end
@@ -40,8 +41,8 @@ Then /^I see a warning that Electrum is not persistent$/ do
@screen.wait('GnomeQuestionDialogIcon.png', 30)
end
-Then /^I am prompted to create a new wallet$/ do
- @screen.wait('ElectrumNoWallet.png', 60)
+Then /^I am prompted to configure Electrum$/ do
+ @screen.wait("ElectrumNoWallet.png", 60)
end
Then /^I see the main Electrum client window$/ do
diff --git a/features/step_definitions/erase_memory.rb b/features/step_definitions/erase_memory.rb
index 24b21e0..3625360 100644
--- a/features/step_definitions/erase_memory.rb
+++ b/features/step_definitions/erase_memory.rb
@@ -64,7 +64,7 @@ Given /^at least (\d+) ([[:alpha:]]+) of RAM was detected$/ do |min_ram, unit|
puts "Detected #{@detected_ram_m} MiB of RAM"
min_ram_m = convert_to_MiB(min_ram.to_i, unit)
# All RAM will not be reported by `free`, so we allow a 196 MB gap
- gap = convert_to_MiB(196, "MiB")
+ gap = convert_to_MiB(256, "MiB")
assert(@detected_ram_m + gap >= min_ram_m, "Didn't detect enough RAM")
end
@@ -105,9 +105,9 @@ Given /^I fill the guest's memory with a known pattern(| without verifying)$/ do
# The (guest) kernel may freeze when approaching full memory without
# adjusting the OOM killer and memory overcommitment limitations.
- kernel_mem_reserved_k = 64*1024
+ kernel_mem_reserved_k = 64*1024 # Duplicated in /usr/share/initramfs-tools/scripts/init-premount/memory_wipe
kernel_mem_reserved_m = convert_to_MiB(kernel_mem_reserved_k, 'k')
- admin_mem_reserved_k = 128*1024
+ admin_mem_reserved_k = 128*1024 # Duplicated in /usr/share/initramfs-tools/scripts/init-premount/memory_wipe
admin_mem_reserved_m = convert_to_MiB(admin_mem_reserved_k, 'k')
kernel_mem_settings = [
# Let's avoid killing other random processes, and instead focus on
@@ -181,7 +181,7 @@ end
Then /^I find very few patterns in the guest's memory$/ do
coverage = pattern_coverage_in_guest_ram()
- max_coverage = 0.005
+ max_coverage = 0.008
assert(coverage < max_coverage,
"#{"%.3f" % (coverage*100)}% of the free memory still has the " +
"pattern, but less than #{"%.3f" % (max_coverage*100)}% was expected")
@@ -200,19 +200,28 @@ When /^I reboot without wiping the memory$/ do
end
When /^I stop the boot at the bootloader menu$/ do
- @screen.wait(bootsplash, 90)
- @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"
end
When /^I shutdown and wait for Tails to finish wiping the memory$/ do
$vm.spawn("halt")
- nr_gibs_of_ram = convert_from_bytes($vm.get_ram_size_in_bytes, 'GiB').ceil
- try_for(nr_gibs_of_ram*5*60, { :msg => "memory wipe didn't finish, probably the VM crashed" }) do
- # We spam keypresses to prevent console blanking from hiding the
- # image we're waiting for
- @screen.type(" ")
- @screen.find('MemoryWipeCompleted.png')
+ match = nil
+ begin
+ try_for(memory_wipe_timeout, msg: "memory wipe didn't finish, probably the VM crashed") do
+ # We spam keypresses to prevent console blanking from hiding the
+ # image we're waiting for
+ @screen.type(" ")
+ match, _ = @screen.findAny(
+ ['MemoryWipeCompleted.png', 'TailsBug11786a.png', 'TailsBug11786b.png']
+ )
+ match != nil
+ end
+ # Just throw the same exception as a if the try_for would fail
+ raise Timeout::Error if match != 'MemoryWipeCompleted.png'
+ rescue Timeout::Error
+ puts "Cannot tell if memory wipe completed. " +
+ "One possible reason for this is #11786, " +
+ "so let's go on and rely on the next steps to check " +
+ "how well memory was wiped."
end
end
diff --git a/features/step_definitions/firewall_leaks.rb b/features/step_definitions/firewall_leaks.rb
index 5d07a6e..ca814a6 100644
--- a/features/step_definitions/firewall_leaks.rb
+++ b/features/step_definitions/firewall_leaks.rb
@@ -17,12 +17,12 @@ Given(/^I disable Tails' firewall$/) do
end
When(/^I do a TCP DNS lookup of "(.*?)"$/) do |host|
- lookup = $vm.execute("host -T #{host} #{SOME_DNS_SERVER}", :user => LIVE_USER)
+ lookup = $vm.execute("host -T -t A #{host} #{SOME_DNS_SERVER}", :user => LIVE_USER)
assert(lookup.success?, "Failed to resolve #{host}:\n#{lookup.stdout}")
end
When(/^I do a UDP DNS lookup of "(.*?)"$/) do |host|
- lookup = $vm.execute("host #{host} #{SOME_DNS_SERVER}", :user => LIVE_USER)
+ lookup = $vm.execute("host -t A #{host} #{SOME_DNS_SERVER}", :user => LIVE_USER)
assert(lookup.success?, "Failed to resolve #{host}:\n#{lookup.stdout}")
end
diff --git a/features/step_definitions/git.rb b/features/step_definitions/git.rb
index bf6f869..579c5c7 100644
--- a/features/step_definitions/git.rb
+++ b/features/step_definitions/git.rb
@@ -1,3 +1,27 @@
+When /^I clone the Git repository "([\S]+)" in GNOME Terminal$/ do |repo|
+ repo_directory = /[\S]+\/([\S]+)(\.git)?$/.match(repo)[1]
+ assert(!$vm.directory_exist?("/home/#{LIVE_USER}/#{repo_directory}"))
+
+ recovery_proc = Proc.new do
+ $vm.execute("rm -rf /home/#{LIVE_USER}/#{repo_directory}",
+ :user => LIVE_USER)
+ step 'I kill the process "git"'
+ @screen.type('clear' + Sikuli::Key.ENTER)
+ end
+
+ retry_tor(recovery_proc) do
+ step "I run \"git clone #{repo}\" in GNOME Terminal"
+ m = /^(https?|git):\/\//.match(repo)
+ unless m
+ step 'I verify the SSH fingerprint for the Git repository'
+ end
+ try_for(180, :msg => 'Git process took too long') {
+ !$vm.has_process?('/usr/bin/git')
+ }
+ @screen.wait('GitCloneDone.png', 10)
+ end
+end
+
Then /^the Git repository "([\S]+)" has been cloned successfully$/ do |repo|
assert($vm.directory_exist?("/home/#{LIVE_USER}/#{repo}/.git"))
assert($vm.file_exist?("/home/#{LIVE_USER}/#{repo}/.git/config"))
diff --git a/features/step_definitions/icedove.rb b/features/step_definitions/icedove.rb
index dd3a0a0..07625c0 100644
--- a/features/step_definitions/icedove.rb
+++ b/features/step_definitions/icedove.rb
@@ -1,84 +1,252 @@
-Then /^Icedove has started$/ do
- step 'process "icedove" is running within 30 seconds'
- @screen.wait('IcedoveMainWindow.png', 60)
+def icedove_app
+ Dogtail::Application.new('Icedove')
end
-When /^I have not configured an email account$/ do
- icedove_prefs = $vm.file_content("/home/#{LIVE_USER}/.icedove/profile.default/prefs.js").chomp
- assert(!icedove_prefs.include?('mail.accountmanager.accounts'))
+def icedove_main
+ # The main window title depends on context so without regexes it
+ # will be hard to find it, but it so happens that it is always the
+ # first frame of Icedove, so we do not have to be specific.
+ icedove_app.child(roleName: 'frame')
end
-Then /^I am prompted to setup an email account$/ do
- $vm.focus_window('Mail Account Setup')
- @screen.wait('IcedoveMailAccountSetup.png', 30)
+def icedove_wizard
+ icedove_app.child('Mail Account Setup', roleName: 'frame')
+end
+
+def icedove_inbox
+ folder_view = icedove_main.child($config['Icedove']['address'],
+ roleName: 'table row').parent
+ folder_view.children(roleName: 'table row', recursive: false).find do |e|
+ e.name.match(/^Inbox( .*)?$/)
+ end
end
-Then /^IMAP is the default protocol$/ do
- $vm.focus_window('Mail Account Setup')
- @screen.wait('IcedoveProtocolIMAP.png', 10)
+When /^I start Icedove$/ do
+ workaround_pref_lines = [
+ # When we generate a random subject line it may contain one of the
+ # keywords that will make Icedove show an extra prompt when trying
+ # to send an email. Let's disable this feature.
+ 'pref("mail.compose.attachment_reminder", false);'
+ ]
+ workaround_pref_lines.each do |line|
+ $vm.file_append('/etc/icedove/pref/icedove.js ', line)
+ end
+ step 'I start "Icedove" via the GNOME "Internet" applications menu'
+ icedove_main.wait(60)
+end
+
+When /^I have not configured an email account$/ do
+ conf_path = "/home/#{LIVE_USER}/.icedove/profile.default/prefs.js"
+ if $vm.file_exist?(conf_path)
+ icedove_prefs = $vm.file_content(conf_path).chomp
+ assert(!icedove_prefs.include?('mail.accountmanager.accounts'))
+ end
+end
+
+Then /^I am prompted to setup an email account$/ do
+ icedove_wizard.wait(30)
end
Then /^I cancel setting up an email account$/ do
- $vm.focus_window('Mail Account Setup')
- @screen.type(Sikuli::Key.ESC)
- @screen.waitVanish('IcedoveMailAccountSetup.png', 10)
+ icedove_wizard.button('Cancel').click
end
Then /^I open Icedove's Add-ons Manager$/ do
- $vm.focus_window('Icedove')
- @screen.wait_and_click('MozillaMenuButton.png', 10)
- @screen.wait_and_click('IcedoveToolsMenuAddOns.png', 10)
- @screen.wait('MozillaAddonsManagerExtensions.png', 30)
+ icedove_main.button('AppMenu').click
+ icedove_main.child('Add-ons', roleName: 'menu item').click
+ @icedove_addons = icedove_app.child(
+ 'Add-ons Manager - Icedove Mail/News', roleName: 'frame'
+ )
+ @icedove_addons.wait
end
Then /^I click the extensions tab$/ do
- @screen.wait_and_click('MozillaAddonsManagerExtensions.png', 10)
+ @icedove_addons.child('Extensions', roleName: 'list item').click
end
-Then /^I see that Adblock is not installed in Icedove$/ do
- if @screen.exists('MozillaExtensionsAdblockPlus.png')
- raise 'Adblock should not be enabled within Icedove'
+Then /^I see that only the (.+) addons are enabled in Icedove$/ do |addons|
+ expected_addons = addons.split(/, | and /)
+ actual_addons =
+ @icedove_addons.child('TorBirdy', roleName: 'label')
+ .parent.parent.children(roleName: 'list item', recursive: false)
+ .map { |item| item.name }
+ expected_addons.each do |addon|
+ result = actual_addons.find { |e| e.start_with?(addon) }
+ assert_not_nil(result)
+ actual_addons.delete(result)
end
+ assert_equal(0, actual_addons.size)
end
When /^I go into Enigmail's preferences$/ do
$vm.focus_window('Icedove')
@screen.type("a", Sikuli::KeyModifier.ALT)
- @screen.wait_and_click('IcedoveEnigmailPreferences.png', 10)
- @screen.wait('IcedoveEnigmailPreferencesWindow.png', 10)
- @screen.click('IcedoveEnigmailExpertSettingsButton.png')
- @screen.wait('IcedoveEnigmailKeyserverTab.png', 10)
+ icedove_main.child('Preferences', roleName: 'menu item').click
+ @enigmail_prefs = icedove_app.dialog('Enigmail Preferences')
end
-When /^I click Enigmail's keyserver tab$/ do
- @screen.wait_and_click('IcedoveEnigmailKeyserverTab.png', 10)
+When /^I enable Enigmail's expert settings$/ do
+ @enigmail_prefs.button('Display Expert Settings and Menus').click
end
-Then /^I see that Enigmail is configured to use the correct keyserver$/ do
- @screen.wait('IcedoveEnigmailKeyserver.png', 10)
+Then /^I click Enigmail's (.+) tab$/ do |tab_name|
+ @enigmail_prefs.child(tab_name, roleName: 'page tab').click
end
-Then /^I click Enigmail's advanced tab$/ do
- @screen.wait_and_click('IcedoveEnigmailAdvancedTab.png', 10)
+Then /^I see that Enigmail is configured to use the correct keyserver$/ do
+ keyservers = @enigmail_prefs.child(
+ 'Specify your keyserver(s):', roleName: 'entry'
+ ).text
+ assert_equal('hkps://hkps.pool.sks-keyservers.net', keyservers)
end
Then /^I see that Enigmail is configured to use the correct SOCKS proxy$/ do
- @screen.click('IcedoveEnigmailAdvancedParameters.png')
- @screen.type(Sikuli::Key.END)
- @screen.wait('IcedoveEnigmailProxy.png', 10)
+ gnupg_parameters = @enigmail_prefs.child(
+ 'Additional parameters for GnuPG', roleName: 'entry'
+ ).text
+ assert_not_nil(
+ gnupg_parameters['--keyserver-options http-proxy=socks5h://127.0.0.1:9050']
+ )
end
Then /^I see that Torbirdy is configured to use Tor$/ do
- @screen.wait('IcedoveTorbirdyEnabled.png', 10)
-end
-
-When /^I open Torbirdy's preferences$/ do
- step "I open Icedove's Add-ons Manager"
- step 'I click the extensions tab'
- @screen.wait_and_click('MozillaExtensionsTorbirdy.png', 10)
- @screen.type(Sikuli::Key.TAB) # Select 'More' link
- @screen.type(Sikuli::Key.TAB) # Select 'Preferences' button
- @screen.type(Sikuli::Key.SPACE) # Press 'Preferences' button
- @screen.wait('GnomeQuestionDialogIcon.png', 10)
- @screen.type(Sikuli::Key.ENTER)
+ icedove_main.child(roleName: 'status bar')
+ .child('TorBirdy Enabled: Tor', roleName: 'label').wait
+end
+
+When /^I enter my email credentials into the autoconfiguration wizard$/ do
+ icedove_wizard.child('Email address:', roleName: 'entry')
+ .typeText($config['Icedove']['address'])
+ icedove_wizard.child('Password:', roleName: 'entry')
+ .typeText($config['Icedove']['password'])
+ icedove_wizard.button('Continue').click
+ # This button is shown if and only if a configuration has been found
+ icedove_wizard.button('Done').wait(120)
+end
+
+Then /^the autoconfiguration wizard's choice for the (incoming|outgoing) server is secure (.+)$/ do |type, protocol|
+ type = type.capitalize + ':'
+ assert_not_nil(
+ icedove_wizard.child(type, roleName: 'entry').text
+ .match(/^#{protocol},[^,]+, (SSL|STARTTLS)$/)
+ )
+end
+
+When /^I fetch my email$/ do
+ account = icedove_main.child($config['Icedove']['address'],
+ roleName: 'table row')
+ account.click
+ icedove_main = icedove_app.child("#{$config['Icedove']['address']} - Icedove Mail/News", roleName: 'frame')
+
+ icedove_main.child('Mail Toolbar', roleName: 'tool bar')
+ .button('Get Messages').click
+ fetch_progress = icedove_main.child(roleName: 'status bar')
+ .child(roleName: 'progress bar')
+ fetch_progress.wait_vanish(120)
+end
+
+When /^I accept the (?:autoconfiguration wizard's|manual) configuration$/ do
+ # The password check can fail due to bad Tor circuits.
+ retry_tor do
+ try_for(120) do
+ if icedove_wizard.exist?
+ # Spam the button, even if it is disabled (while it is still
+ # testing the password).
+ icedove_wizard.button('Done').click
+ false
+ else
+ true
+ end
+ end
+ end
+ # The account isn't fully created before we fetch our mail. For
+ # instance, if we'd try to send an email before this, yet another
+ # wizard will start, indicating (incorrectly) that we do not have an
+ # account set up yet.
+ step 'I fetch my email'
+end
+
+When /^I select the autoconfiguration wizard's (IMAP|POP3) choice$/ do |protocol|
+ if protocol == 'IMAP'
+ choice = 'IMAP (remote folders)'
+ else
+ choice = 'POP3 (keep mail on your computer)'
+ end
+ icedove_wizard.child(choice, roleName: 'radio button').click
+end
+
+When /^I select manual configuration$/ do
+ icedove_wizard.button('Manual config').click
+end
+
+When /^I alter the email configuration to use (.*) over a hidden services$/ do |protocol|
+ case protocol.upcase
+ when 'IMAP', 'POP3'
+ entry_name = 'Incoming:'
+ when 'SMTP'
+ entry_name = 'Outgoing:'
+ else
+ raise "Unknown mail protocol '#{protocol}'"
+ end
+ entry = icedove_wizard.child(entry_name, roleName: 'entry')
+ entry.text = ''
+ entry.typeText($config['Icedove']["#{protocol.downcase}_hidden_service"])
+end
+
+When /^I send an email to myself$/ do
+ icedove_main.child('Mail Toolbar', roleName: 'tool bar').button('Write').click
+ compose_window = icedove_app.child('Write: (no subject)')
+ compose_window.wait(10)
+ compose_window.child('To:', roleName: 'autocomplete').child(roleName: 'entry')
+ .typeText($config['Icedove']['address'])
+ # The randomness of the subject will make it easier for us to later
+ # find *exactly* this email. This makes it safe to run several tests
+ # in parallel.
+ @subject = "Automated test suite: #{random_alnum_string(32)}"
+ compose_window.child('Subject:', roleName: 'entry')
+ .typeText(@subject)
+ compose_window = icedove_app.child("Write: #{@subject}")
+ compose_window.child('about:blank', roleName: 'document frame')
+ .typeText('test')
+ compose_window.child('Composition Toolbar', roleName: 'tool bar')
+ .button('Send').click
+ compose_window.wait_vanish(120)
+end
+
+Then /^I can find the email I sent to myself in my inbox$/ do
+ recovery_proc = Proc.new { step 'I fetch my email' }
+ retry_tor(recovery_proc) do
+ icedove_inbox.click
+ filter = icedove_main.child('Filter these messages <Ctrl+Shift+K>',
+ roleName: 'entry')
+ filter.typeText(@subject)
+ hit_counter = icedove_main.child('1 message')
+ hit_counter.wait
+ inbox_view = hit_counter.parent
+ message_list = inbox_view.child(roleName: 'table')
+ the_message = message_list.children(roleName: 'table row').find do |message|
+ # The message will be cropped in the list, so we cannot search
+ # for the full message.
+ message.name.start_with?("Automated test suite:")
+ end
+ assert_not_nil(the_message)
+ # Let's clean up
+ the_message.click
+ inbox_view.button('Delete').click
+ end
+end
+
+Then /^my Icedove inbox is non-empty$/ do
+ icedove_inbox.click
+ # The button is located on the first row in the message list, the
+ # one that shows the column labels (Subject, From, ...).
+ message_list = icedove_main.child('Select columns to display',
+ roleName: 'push button')
+ .parent.parent
+ visible_messages = message_list.children(recursive: false,
+ roleName: 'table row')
+ # The first element is the column label row, which is not a message,
+ # so let's remove it.
+ visible_messages.shift
+ assert(visible_messages.size > 0)
end
diff --git a/features/step_definitions/ssh.rb b/features/step_definitions/ssh.rb
index 64c6841..e998101 100644
--- a/features/step_definitions/ssh.rb
+++ b/features/step_definitions/ssh.rb
@@ -60,6 +60,7 @@ end
Given /^I (?:am prompted to )?verify the SSH fingerprint for the (?:Git|SSH) (?:repository|server)$/ do
@screen.wait("SSHFingerprint.png", 60)
+ sleep 1 # brief pause to ensure that the following keystrokes do not get lost
@screen.type('yes' + Sikuli::Key.ENTER)
end
@@ -95,8 +96,17 @@ When /^I connect to an SSH server on the (Internet|LAN)$/ do |location|
cmd = "ssh #{@ssh_username}@#{@ssh_host} #{ssh_port_suffix}"
step 'process "ssh" is not running'
- step "I run \"#{cmd}\" in GNOME Terminal"
- step 'process "ssh" is running within 10 seconds'
+
+ recovery_proc = Proc.new do
+ step 'I kill the process "ssh"' if $vm.has_process?("ssh")
+ step 'I run "clear" in GNOME Terminal'
+ end
+
+ retry_tor(recovery_proc) do
+ step "I run \"#{cmd}\" in GNOME Terminal"
+ step 'process "ssh" is running within 10 seconds'
+ step 'I verify the SSH fingerprint for the SSH server'
+ end
end
Then /^I have sucessfully logged into the SSH server$/ do
@@ -105,17 +115,29 @@ end
Then /^I connect to an SFTP server on the Internet$/ do
read_and_validate_ssh_config "SFTP"
+
@sftp_port ||= 22
@sftp_port = @sftp_port.to_s
- step 'I start "Files" via the GNOME "Accessories" applications menu'
- @screen.wait_and_click("GnomeFilesConnectToServer.png", 10)
- @screen.wait("GnomeConnectToServerWindow.png", 10)
- @screen.type("sftp://" + @sftp_username + "@" + @sftp_host + ":" + @sftp_port)
- @screen.wait_and_click("GnomeConnectToServerConnectButton.png", 10)
+
+ recovery_proc = Proc.new do
+ step 'I kill the process "ssh"'
+ @screen.type(Sikuli::Key.ESC)
+ @screen.click("GnomeCloseTopButton.png")
+ @screen.waitVanish("GnomeCloseTopButton.png", 10)
+ end
+
+ retry_tor(recovery_proc) do
+ step 'I start "Files" via the GNOME "Accessories" applications menu'
+ @screen.wait_and_click("GnomeFilesConnectToServer.png", 10)
+ @screen.wait("GnomeConnectToServerWindow.png", 10)
+ @screen.type("sftp://" + @sftp_username + "@" + @sftp_host + ":" + @sftp_port)
+ @screen.wait_and_click("GnomeConnectToServerConnectButton.png", 10)
+ step "I verify the SSH fingerprint for the SFTP server"
+ end
end
Then /^I verify the SSH fingerprint for the SFTP server$/ do
- @screen.wait_and_click("GnomeSSHVerificationConfirm.png", 60)
+ @screen.wait_and_click("GnomeSSHVerificationConfirm.png", 2*60)
end
Then /^I successfully connect to the SFTP server$/ do
diff --git a/features/step_definitions/tor.rb b/features/step_definitions/tor.rb
index babde27..73b3abb 100644
--- a/features/step_definitions/tor.rb
+++ b/features/step_definitions/tor.rb
@@ -256,7 +256,8 @@ def stream_isolation_info(application)
when "Tor Browser"
{
:grep_monitor_expr => '/firefox\>',
- :socksport => 9150
+ :socksport => 9150,
+ :controller => true,
}
when "Gobby"
{
@@ -288,17 +289,19 @@ When /^I monitor the network connections of (.*)$/ do |application|
end
Then /^I see that (.+) is properly stream isolated$/ do |application|
- expected_port = stream_isolation_info(application)[:socksport]
+ info = stream_isolation_info(application)
+ expected_ports = [info[:socksport]]
+ expected_ports << 9051 if info[:controller]
assert_not_nil(@process_monitor_log)
log_lines = $vm.file_content(@process_monitor_log).split("\n")
assert(log_lines.size > 0,
"Couldn't see any connection made by #{application} so " \
"something is wrong")
log_lines.each do |line|
- addr_port = line.split(/\s+/)[4]
- assert_equal("127.0.0.1:#{expected_port}", addr_port,
- "#{application} should use SocksPort #{expected_port} but " \
- "was seen connecting to #{addr_port}")
+ ip_port = line.split(/\s+/)[4]
+ assert(expected_ports.map { |port| "127.0.0.1:#{port}" }.include?(ip_port),
+ "#{application} should only connect to #{expected_ports} but " \
+ "was seen connecting to #{ip_port}")
end
end
@@ -394,21 +397,3 @@ When /^all Internet traffic has only flowed through the configured pluggable tra
@bridge_hosts.include?({ address: c.daddr, port: c.dport })
end
end
-
-Then /^the Tor binary is configured to use the expected Tor authorities$/ do
- tor_auths = Set.new
- tor_binary_orport_strings = $vm.execute_successfully(
- "strings /usr/bin/tor | grep -E 'orport=[0-9]+'").stdout.chomp.split("\n")
- tor_binary_orport_strings.each do |potential_auth_string|
- auth_regex = /^\S+ orport=\d+( bridge)?( no-v2)?( v3ident=[A-Z0-9]{40})? ([0-9\.]+):\d+( [A-Z0-9]{4}){10}$/
- m = auth_regex.match(potential_auth_string)
- if m
- auth_ipv4_addr = m[4]
- tor_auths << auth_ipv4_addr
- end
- end
- expected_tor_auths = Set.new(TOR_AUTHORITIES)
- assert_equal(expected_tor_auths, tor_auths,
- "The Tor binary does not have the expected Tor authorities " +
- "configured")
-end
diff --git a/features/step_definitions/torified_gnupg.rb b/features/step_definitions/torified_gnupg.rb
index 282db48..cb0a9db 100644
--- a/features/step_definitions/torified_gnupg.rb
+++ b/features/step_definitions/torified_gnupg.rb
@@ -108,7 +108,7 @@ end
Then /^I synchronize keys in Seahorse$/ do
recovery_proc = Proc.new do
- # The versions of Seahorse in Wheezy and Jessie will abort with a
+ # The version of Seahorse in Jessie will abort with a
# segmentation fault whenever there's any sort of network error while
# syncing keys. This will usually happens after clicking away the error
# message. This does not appear to be a problem in Stretch.
diff --git a/features/step_definitions/torified_misc.rb b/features/step_definitions/torified_misc.rb
index 7112776..7ccdb22 100644
--- a/features/step_definitions/torified_misc.rb
+++ b/features/step_definitions/torified_misc.rb
@@ -1,3 +1,5 @@
+require 'resolv'
+
When /^I query the whois directory service for "([^"]+)"$/ do |domain|
retry_tor do
@vm_execute_res = $vm.execute("whois '#{domain}'", :user => LIVE_USER)
@@ -9,10 +11,18 @@ When /^I query the whois directory service for "([^"]+)"$/ do |domain|
end
end
-When /^I wget "([^"]+)" to stdout(?:| with the '([^']+)' options)$/ do |url, options|
- arguments = "-O - '#{url}'"
- arguments = "#{options} #{arguments}" if options
+When /^I wget "([^"]+)" to stdout(?:| with the '([^']+)' options)$/ do |target, options|
retry_tor do
+ if target == "some Tails mirror"
+ host = 'dl.amnesia.boum.org'
+ address = Resolv.new.getaddresses(host).sample
+ puts "Resolved #{host} to #{address}"
+ url = "http://#{address}/tails/stable/"
+ else
+ url = target
+ end
+ arguments = "-O - '#{url}'"
+ arguments = "#{options} #{arguments}" if options
@vm_execute_res = $vm.execute("wget #{arguments}", :user => LIVE_USER)
if @vm_execute_res.failure?
raise "wget:ing #{url} with options #{options} failed with:\n" +
diff --git a/features/step_definitions/totem.rb b/features/step_definitions/totem.rb
index 7b45b2e..a5b88d1 100644
--- a/features/step_definitions/totem.rb
+++ b/features/step_definitions/totem.rb
@@ -1,23 +1,24 @@
Given /^I create sample videos$/ do
- @shared_video_dir_on_host = "#{$config["TMPDIR"]}/shared_video_dir"
- @shared_video_dir_on_guest = "/tmp/shared_video_dir"
- FileUtils.mkdir_p(@shared_video_dir_on_host)
- add_after_scenario_hook { FileUtils.rm_r(@shared_video_dir_on_host) }
+ @video_dir_on_host = "#{$config["TMPDIR"]}/video_dir"
+ FileUtils.mkdir_p(@video_dir_on_host)
+ add_after_scenario_hook { FileUtils.rm_r(@video_dir_on_host) }
fatal_system("avconv -loop 1 -t 30 -f image2 " +
- "-i 'features/images/TailsBootSplash.png' " +
+ "-i 'features/images/USBTailsLogo.png' " +
"-an -vcodec libx264 -y " +
'-filter:v "crop=in_w-mod(in_w\,2):in_h-mod(in_h\,2)" ' +
- "'#{@shared_video_dir_on_host}/video.mp4' >/dev/null 2>&1")
+ "'#{@video_dir_on_host}/video.mp4' >/dev/null 2>&1")
end
-Given /^I setup a filesystem share containing sample videos$/ do
- $vm.add_share(@shared_video_dir_on_host, @shared_video_dir_on_guest)
+Given /^I plug and mount a USB drive containing sample videos$/ do
+ @video_dir_on_guest = share_host_files(
+ Dir.glob("#{@video_dir_on_host}/*")
+ )
end
Given /^I copy the sample videos to "([^"]+)" as user "([^"]+)"$/ do |destination, user|
- for video_on_host in Dir.glob("#{@shared_video_dir_on_host}/*.mp4") do
+ for video_on_host in Dir.glob("#{@video_dir_on_host}/*.mp4") do
video_name = File.basename(video_on_host)
- src_on_guest = "#{@shared_video_dir_on_guest}/#{video_name}"
+ src_on_guest = "#{@video_dir_on_guest}/#{video_name}"
dst_on_guest = "#{destination}/#{video_name}"
step "I copy \"#{src_on_guest}\" to \"#{dst_on_guest}\" as user \"amnesia\""
end
diff --git a/features/step_definitions/usb.rb b/features/step_definitions/usb.rb
index a1d333a..0791c6a 100644
--- a/features/step_definitions/usb.rb
+++ b/features/step_definitions/usb.rb
@@ -48,6 +48,14 @@ def persistent_volumes_mountpoints
$vm.execute("ls -1 -d /live/persistence/*_unlocked/").stdout.chomp.split
end
+def recover_from_upgrader_failure
+ $vm.execute('killall tails-upgrade-frontend tails-upgrade-frontend-wrapper zenity')
+ # Remove unnecessary sleep for retry
+ $vm.execute_successfully('sed -i "/^sleep 30$/d" ' +
+ '/usr/local/bin/tails-upgrade-frontend-wrapper')
+ $vm.spawn('tails-upgrade-frontend-wrapper', user: LIVE_USER)
+end
+
Given /^I clone USB drive "([^"]+)" to a new USB drive "([^"]+)"$/ do |from, to|
$vm.storage.clone_to_new_disk(from, to)
end
@@ -70,17 +78,25 @@ end
def usb_install_helper(name)
@screen.wait('USBTailsLogo.png', 10)
- if @screen.exists("USBCannotUpgrade.png")
+ text = Dogtail::Application.new('tails-installer')
+ .child('', roleName: 'text').text
+ dev = $vm.disk_dev(name)
+ if text.match(/It is impossible to upgrade the device .+ #{dev}\d* /)
raise UpgradeNotSupported
end
- @screen.wait_and_click('USBCreateLiveUSB.png', 10)
- @screen.wait('USBCreateLiveUSBConfirmWindow.png', 10)
- @screen.wait_and_click('USBCreateLiveUSBConfirmYes.png', 10)
- @screen.wait('USBInstallationComplete.png', 30*60)
+ begin
+ @screen.wait_and_click('USBCreateLiveUSB.png', 10)
+ @screen.wait('USBCreateLiveUSBConfirmWindow.png', 10)
+ @screen.wait_and_click('USBCreateLiveUSBConfirmYes.png', 10)
+ @screen.wait('USBInstallationComplete.png', 30*60)
+ rescue FindFailed => e
+ debug_log("Tails Installer debug log:\n" + $vm.file_content('/tmp/tails-installer-*'))
+ raise e
+ end
end
When /^I start Tails Installer$/ do
- step 'I start "Tails Installer" via the GNOME "Tails" applications menu'
+ step 'I run "export DEBUG=1 ; tails-installer-launcher" in GNOME Terminal'
@screen.wait('USBCloneAndInstall.png', 30)
end
@@ -140,13 +156,9 @@ When /^I am told that the destination device cannot be upgraded$/ do
@screen.find("USBCannotUpgrade.png")
end
-Given /^I setup a filesystem share containing the Tails ISO$/ do
- shared_iso_dir_on_host = "#{$config["TMPDIR"]}/shared_iso_dir"
- @shared_iso_dir_on_guest = "/tmp/shared_iso_dir"
- FileUtils.mkdir_p(shared_iso_dir_on_host)
- FileUtils.cp(TAILS_ISO, shared_iso_dir_on_host)
- add_after_scenario_hook { FileUtils.rm_r(shared_iso_dir_on_host) }
- $vm.add_share(shared_iso_dir_on_host, @shared_iso_dir_on_guest)
+Given /^I plug and mount a USB drive containing the Tails ISO$/ do
+ iso_dir = share_host_files(TAILS_ISO)
+ @iso_path = "#{iso_dir}/#{File.basename(TAILS_ISO)}"
end
When /^I do a "Upgrade from ISO" on USB drive "([^"]+)"$/ do |name|
@@ -158,8 +170,7 @@ When /^I do a "Upgrade from ISO" on USB drive "([^"]+)"$/ do |name|
@screen.wait_and_click('GnomeFileDiagHome.png', 10)
@screen.type("l", Sikuli::KeyModifier.CTRL)
@screen.wait('GnomeFileDiagTypeFilename.png', 10)
- iso = "#{@shared_iso_dir_on_guest}/#{File.basename(TAILS_ISO)}"
- @screen.type(iso)
+ @screen.type(@iso_path)
@screen.wait_and_click('GnomeFileDiagOpenButton.png', 10)
usb_install_helper(name)
end
@@ -174,7 +185,7 @@ Given /^I enable all persistence presets$/ do
@screen.type(Sikuli::Key.TAB + Sikuli::Key.SPACE)
end
@screen.wait_and_click('PersistenceWizardSave.png', 10)
- @screen.wait('PersistenceWizardDone.png', 30)
+ @screen.wait('PersistenceWizardDone.png', 60)
@screen.type(Sikuli::Key.F4, Sikuli::KeyModifier.ALT)
end
@@ -189,7 +200,7 @@ end
Given /^I create a persistent partition$/ do
step 'I start "Configure persistent volume" via the GNOME "Tails" applications menu'
- @screen.wait('PersistenceWizardStart.png', 20)
+ @screen.wait('PersistenceWizardStart.png', 60)
@screen.type(@persistence_password + "\t" + @persistence_password + Sikuli::Key.ENTER)
@screen.wait('PersistenceWizardPresets.png', 300)
step "I enable all persistence presets"
@@ -263,10 +274,9 @@ Then /^the running Tails is installed on USB drive "([^"]+)"$/ do |target_name|
end
Then /^the ISO's Tails is installed on USB drive "([^"]+)"$/ do |target_name|
- iso = "#{@shared_iso_dir_on_guest}/#{File.basename(TAILS_ISO)}"
iso_root = "/mnt/iso"
$vm.execute("mkdir -p #{iso_root}")
- $vm.execute("mount -o loop #{iso} #{iso_root}")
+ $vm.execute("mount -o loop #{@iso_path} #{iso_root}")
tails_is_installed_helper(target_name, iso_root, "isolinux")
$vm.execute("umount #{iso_root}")
end
@@ -283,10 +293,10 @@ Then /^a Tails persistence partition exists on USB drive "([^"]+)"$/ do |name|
# The LUKS container may already be opened, e.g. by udisks after
# we've run tails-persistence-setup.
- c = $vm.execute("ls -1 /dev/mapper/")
+ c = $vm.execute("ls -1 --hide 'control' /dev/mapper/")
if c.success?
for candidate in c.stdout.split("\n")
- luks_info = $vm.execute("cryptsetup status #{candidate}")
+ luks_info = $vm.execute("cryptsetup status '#{candidate}'")
if luks_info.success? and luks_info.stdout.match("^\s+device:\s+#{dev}$")
luks_dev = "/dev/mapper/#{candidate}"
break
@@ -309,7 +319,7 @@ Then /^a Tails persistence partition exists on USB drive "([^"]+)"$/ do |name|
mount_dir = "/mnt/#{name}"
$vm.execute("mkdir -p #{mount_dir}")
- c = $vm.execute("mount #{luks_dev} #{mount_dir}")
+ c = $vm.execute("mount '#{luks_dev}' #{mount_dir}")
assert(c.success?,
"Couldn't mount opened LUKS device '#{dev}' on drive '#{name}'")
@@ -395,23 +405,21 @@ end
Then /^Tails is running from (.*) drive "([^"]+)"$/ do |bus, name|
bus = bus.downcase
case bus
- when "ide"
+ when "sata"
expected_bus = "ata"
else
expected_bus = bus
end
assert_equal(expected_bus, boot_device_type)
actual_dev = boot_device
- # The boot partition differs between a "normal" install using the
- # USB installer and isohybrid installations
- expected_dev_normal = $vm.disk_dev(name) + "1"
- expected_dev_isohybrid = $vm.disk_dev(name) + "4"
- assert(actual_dev == expected_dev_normal ||
- actual_dev == expected_dev_isohybrid,
+ # The boot partition differs between an using Tails installer and
+ # isohybrids. There's also a strange case isohybrids are thought to
+ # be booting from the "raw" device, and not a partition of it
+ # (#10504).
+ expected_devs = ['', '1', '4'].map { |e| $vm.disk_dev(name) + e }
+ assert(expected_devs.include?(actual_dev),
"We are running from device #{actual_dev}, but for #{bus} drive " +
- "'#{name}' we expected to run from either device " +
- "#{expected_dev_normal} (when installed via the USB installer) " +
- "or #{expected_dev_isohybrid} (when installed from an isohybrid)")
+ "'#{name}' we expected to run from one of #{expected_devs}")
end
Then /^the boot device has safe access rights$/ do
@@ -514,6 +522,12 @@ When /^I write some files expected to persist$/ do
end
end
+When /^I write some dotfile expected to persist$/ do
+ assert($vm.execute("touch /live/persistence/TailsData_unlocked/dotfiles/.XXX_persist",
+ :user => LIVE_USER).success?,
+ "Could not create a file in the dotfiles persistence.")
+end
+
When /^I remove some files expected to persist$/ do
persistent_mounts.each do |_, dir|
owner = $vm.execute("stat -c %U #{dir}").stdout.chomp
@@ -550,6 +564,14 @@ Then /^the expected persistent files(| created with the old Tails version) are p
end
end
+Then /^the expected persistent dotfile is present in the filesystem$/ do
+ expected_dirs = persistent_dirs
+ assert($vm.execute("test -L #{expected_dirs['dotfiles']}/.XXX_persist").success?,
+ "Could not find expected persistent dotfile link.")
+ assert($vm.execute("test -e $(readlink -f #{expected_dirs['dotfiles']}/.XXX_persist)").success?,
+ "Could not find expected persistent dotfile link target.")
+end
+
Then /^only the expected files are present on the persistence partition on USB drive "([^"]+)"$/ do |name|
assert(!$vm.is_running?)
disk = {
@@ -590,7 +612,7 @@ end
When /^I delete the persistent partition$/ do
step 'I start "Delete persistent volume" via the GNOME "Tails" applications menu'
- @screen.wait("PersistenceWizardDeletionStart.png", 20)
+ @screen.wait("PersistenceWizardDeletionStart.png", 120)
@screen.type(" ")
@screen.wait("PersistenceWizardDone.png", 120)
end
@@ -615,3 +637,110 @@ end
Then /^no USB drive is selected$/ do
@screen.wait("TailsInstallerNoQEMUHardDisk.png", 30)
end
+
+Given /^the file system changes introduced in version (.+) are (not )?present(?: in the (\S+) Browser's chroot)?$/ do |version, not_present, chroot_browser|
+ assert_equal('1.1~test', version)
+ upgrade_applied = not_present.nil?
+ chroot_browser = "#{chroot_browser.downcase}-browser" if chroot_browser
+ changes = [
+ {
+ filesystem: :rootfs,
+ path: 'some_new_file',
+ status: :added,
+ new_content: <<-EOF
+Some content
+ EOF
+ },
+ {
+ filesystem: :rootfs,
+ path: 'etc/amnesia/version',
+ status: :modified,
+ new_content: <<-EOF
+#{version} - 20380119
+ffffffffffffffffffffffffffffffffffffffff
+live-build: 3.0.5+really+is+2.0.12-0.tails2
+live-boot: 4.0.2-1
+live-config: 4.0.4-1
+ EOF
+ },
+ {
+ filesystem: :rootfs,
+ path: 'etc/os-release',
+ status: :modified,
+ new_content: <<-EOF
+TAILS_PRODUCT_NAME="Tails"
+TAILS_VERSION_ID="#{version}"
+ EOF
+ },
+ {
+ filesystem: :rootfs,
+ path: 'usr/share/common-licenses/BSD',
+ status: :removed
+ },
+ {
+ filesystem: :medium,
+ path: 'utils/linux/syslinux',
+ status: :removed
+ },
+ ]
+ changes.each do |change|
+ case change[:filesystem]
+ when :rootfs
+ path = '/'
+ path += "var/lib/#{chroot_browser}/chroot/" if chroot_browser
+ path += change[:path]
+ when :medium
+ path = '/lib/live/mount/medium/' + change[:path]
+ else
+ raise "Unknown filesysten '#{change[:filesystem]}'"
+ end
+ case change[:status]
+ when :removed
+ assert_equal(!upgrade_applied, $vm.file_exist?(path))
+ when :added
+ assert_equal(upgrade_applied, $vm.file_exist?(path))
+ if upgrade_applied && change[:new_content]
+ assert_equal(change[:new_content], $vm.file_content(path))
+ end
+ when :modified
+ assert($vm.file_exist?(path))
+ if upgrade_applied
+ assert_not_nil(change[:new_content])
+ assert_equal(change[:new_content], $vm.file_content(path))
+ end
+ else
+ raise "Unknown status '#{change[:status]}'"
+ end
+ end
+end
+
+Then /^I am proposed to install an incremental upgrade to version (.+)$/ do |version|
+ recovery_proc = Proc.new do
+ recover_from_upgrader_failure
+ end
+ failure_pic = 'TailsUpgraderFailure.png'
+ success_pic = "TailsUpgraderUpgradeTo#{version}.png"
+ retry_tor(recovery_proc) do
+ match, _ = @screen.waitAny([success_pic, failure_pic], 2*60)
+ assert_equal(success_pic, match)
+ end
+end
+
+When /^I agree to install the incremental upgrade$/ do
+ @screen.click('TailsUpgraderUpgradeNowButton.png')
+end
+
+Then /^I can successfully install the incremental upgrade to version (.+)$/ do |version|
+ step 'I agree to install the incremental upgrade'
+ recovery_proc = Proc.new do
+ recover_from_upgrader_failure
+ step "I am proposed to install an incremental upgrade to version #{version}"
+ step 'I agree to install the incremental upgrade'
+ end
+ failure_pic = 'TailsUpgraderFailure.png'
+ success_pic = "TailsUpgraderDone.png"
+ retry_tor(recovery_proc) do
+ match, _ = @screen.waitAny([success_pic, failure_pic], 2*60)
+ assert_equal(success_pic, match)
+ end
+end
diff --git a/features/support/config.rb b/features/support/config.rb
index ce3c048..89fa1ba 100644
--- a/features/support/config.rb
+++ b/features/support/config.rb
@@ -74,20 +74,6 @@ SERVICES_EXPECTED_ON_ALL_IFACES =
]
# OpenDNS
SOME_DNS_SERVER = "208.67.222.222"
-TOR_AUTHORITIES =
- # List grabbed from Tor's sources, src/or/config.c:~750.
- [
- "86.59.21.38",
- "128.31.0.39",
- "194.109.206.212",
- "82.94.251.203",
- "199.254.238.52",
- "131.188.40.189",
- "193.23.244.244",
- "208.83.223.34",
- "171.25.193.9",
- "154.35.175.225",
- ]
VM_XML_PATH = "#{Dir.pwd}/features/domains"
TAILS_SIGNING_KEY = cmd_helper(". #{Dir.pwd}/config/amnesia; echo ${AMNESIA_DEV_KEYID}").tr(' ', '').chomp
diff --git a/features/support/extra_hooks.rb b/features/support/extra_hooks.rb
index 16196a5..b990b9b 100644
--- a/features/support/extra_hooks.rb
+++ b/features/support/extra_hooks.rb
@@ -1,18 +1,21 @@
# Make the code below work with cucumber >= 2.0. Once we stop
# supporting <2.0 we should probably do this differently, but this way
# we can easily support both at the same time.
+
begin
if not(Cucumber::Core::Ast::Feature.instance_methods.include?(:accept_hook?))
- require 'gherkin/tag_expression'
+ if Gem::Version.new(Cucumber::VERSION) >= Gem::Version.new('2.4.0')
+ require 'cucumber/core/gherkin/tag_expression'
+ else
+ require 'gherkin/tag_expression'
+ Cucumber::Core::Gherkin = Gherkin
+ end
class Cucumber::Core::Ast::Feature
# Code inspired by Cucumber::Core::Test::Case.match_tags?() in
# cucumber-ruby-core 1.1.3, lib/cucumber/core/test/case.rb:~59.
def accept_hook?(hook)
- tag_expr = Gherkin::TagExpression.new(hook.tag_expressions.flatten)
- tags = @tags.map do |t|
- Gherkin::Formatter::Model::Tag.new(t.name, t.line)
- end
- tag_expr.evaluate(tags)
+ tag_expr = Cucumber::Core::Gherkin::TagExpression.new(hook.tag_expressions.flatten)
+ tag_expr.evaluate(@tags)
end
end
end
@@ -53,10 +56,10 @@ if not($at_exit_print_artifacts_dir_patching_done)
alias old_print_stats print_stats
end
def print_stats(*args)
- if Dir.exists?(ARTIFACTS_DIR) and Dir.entries(ARTIFACTS_DIR).size > 2
- @io.puts "Artifacts directory: #{ARTIFACTS_DIR}"
- @io.puts
- end
+ @io.puts "Artifacts directory: #{ARTIFACTS_DIR}"
+ @io.puts
+ @io.puts "Debug log: #{ARTIFACTS_DIR}/debug.log"
+ @io.puts
if self.class.method_defined?(:old_print_stats)
old_print_stats(*args)
end
@@ -104,8 +107,11 @@ module ExtraFormatters
# anything. We only use it do hook into the correct events so we can
# add our extra hooks.
class ExtraHooks
- def initialize(*args)
+ def initialize(runtime, io, options)
# We do not care about any of the arguments.
+ # XXX: We should be able to just have `*args` for the arguments
+ # in the prototype, but since moving to cucumber 2.4 that breaks
+ # this formatter for some unknown reason.
end
def before_feature(feature)
@@ -127,8 +133,8 @@ module ExtraFormatters
# The pretty formatter with debug logging mixed into its output.
class PrettyDebug < Cucumber::Formatter::Pretty
- def initialize(*args)
- super(*args)
+ def initialize(runtime, io, options)
+ super(runtime, io, options)
$debug_log_fns ||= []
$debug_log_fns << self.method(:debug_log)
end
@@ -160,6 +166,13 @@ AfterConfiguration do |config|
# AfterConfiguration hook multiple times. We only want our
# ExtraHooks formatter to be loaded once, otherwise the hooks would
# be run miltiple times.
- extra_hooks = ['ExtraFormatters::ExtraHooks', '/dev/null']
- config.formats << extra_hooks if not(config.formats.include?(extra_hooks))
+ extra_hooks = [
+ ['ExtraFormatters::ExtraHooks', '/dev/null'],
+ ['Cucumber::Formatter::Pretty', "#{ARTIFACTS_DIR}/pretty.log"],
+ ['Cucumber::Formatter::Json', "#{ARTIFACTS_DIR}/cucumber.json"],
+ ['ExtraFormatters::PrettyDebug', "#{ARTIFACTS_DIR}/debug.log"],
+ ]
+ extra_hooks.each do |hook|
+ config.formats << hook if not(config.formats.include?(hook))
+ end
end
diff --git a/features/support/helpers/dogtail.rb b/features/support/helpers/dogtail.rb
index c9cf79d..59fac3c 100644
--- a/features/support/helpers/dogtail.rb
+++ b/features/support/helpers/dogtail.rb
@@ -16,6 +16,10 @@ module Dogtail
:textentry,
]
+ TREE_API_NODE_SEARCH_FIELDS = [
+ :parent,
+ ]
+
TREE_API_NODE_ACTIONS = [
:click,
:doubleClick,
@@ -54,6 +58,9 @@ module Dogtail
@init_lines = @opts[:init_lines] || [
"from dogtail import tree",
"from dogtail.config import config",
+ "config.logDebugToFile = False",
+ "config.logDebugToStdOut = False",
+ "config.blinkOnActions = True",
"config.searchShowingOnly = True",
"application = tree.root.application('#{@app_name}')",
]
@@ -81,12 +88,12 @@ module Dogtail
$vm.file_overwrite(script_path, script, @opts[:user])
args = ["/usr/bin/python '#{script_path}'", @opts]
if @opts[:allow_failure]
- $vm.execute(*args)
+ ret = $vm.execute(*args)
else
- $vm.execute_successfully(*args)
+ ret = $vm.execute_successfully(*args)
end
- ensure
$vm.execute("rm -f '#{script_path}'")
+ ret
end
def self.value_to_s(v)
@@ -109,6 +116,7 @@ module Dogtail
# into the parentheses of a Python function call.
# Example: [42, {:foo => 'bar'}] => "42, foo = 'bar'"
def self.args_to_s(args)
+ return "" if args.size == 0
args_list = args
args_hash = nil
if args_list.class == Array && args_list.last.class == Hash
@@ -128,9 +136,39 @@ module Dogtail
end
end
+ def exist?
+ @opts[:allow_failure] = true
+ # We do not want any retries since this method should return the
+ # result for the immediate situation, not for the situation up
+ # to 20 retries in the future.
+ optimization = "config.searchCutoffCount = 1"
+ @init_lines << optimization unless @init_lines.include?(optimization)
+ run.success?
+ end
+
+ def wait_vanish(timeout)
+ try_for(timeout) { not(exist?) }
+ end
+
# Equivalent to the Tree API's Node.findChildren(), with the
# arguments constructing a GenericPredicate to use as parameter.
def children(*args)
+ non_predicates = [:recursive, :showingOnly]
+ findChildren_opts = []
+ findChildren_opts_hash = Hash.new
+ if args.last.class == Hash
+ args_hash = args.last
+ non_predicates.each do |opt|
+ if args_hash.has_key?(opt)
+ findChildren_opts_hash[opt] = args_hash[opt]
+ args_hash.delete(opt)
+ end
+ end
+ end
+ findChildren_opts = ""
+ if findChildren_opts_hash.size > 0
+ findChildren_opts = ", " + self.class.args_to_s([findChildren_opts_hash])
+ end
# A fundamental assumption of ScriptProxy is that we will only
# act on *one* object at a time. If we were to allow more, we'd
# have to port looping, conditionals and much more into our
@@ -140,9 +178,10 @@ module Dogtail
# internal a11y AT-SPI "path" to uniquely identify a Dogtail
# node, so we can give handles to each of them that can be used
# later to re-find them.
+ predicate_opts = self.class.args_to_s(args)
find_paths_script_lines = [
"from dogtail import predicate",
- "for n in #{build_line}.findChildren(predicate.GenericPredicate(#{self.class.args_to_s(args)})):",
+ "for n in #{build_line}.findChildren(predicate.GenericPredicate(#{predicate_opts})#{findChildren_opts}):",
" print(n.path)",
]
a11y_at_spi_paths = run(find_paths_script_lines).stdout.chomp.split("\n")
@@ -180,6 +219,14 @@ module Dogtail
get_field('text')
end
+ def text=(value)
+ set_field('text', value)
+ end
+
+ def name
+ get_field('name')
+ end
+
def proxy_call(method, args)
args_str = self.class.args_to_s(args)
method_call = "#{method.to_s}(#{args_str})"
@@ -198,6 +245,18 @@ module Dogtail
end
end
+ TREE_API_NODE_SEARCH_FIELDS.each do |field|
+ define_method(field) do
+ Node.new(
+ @app_name,
+ @opts.merge(
+ init_lines: @init_lines,
+ components: @components + [field]
+ )
+ )
+ end
+ end
+
end
class Node < Application
diff --git a/features/support/helpers/exec_helper.rb b/features/support/helpers/exec_helper.rb
index 98fba8c..7ab486f 100644
--- a/features/support/helpers/exec_helper.rb
+++ b/features/support/helpers/exec_helper.rb
@@ -3,6 +3,8 @@ require 'socket'
class VMCommand
+ @@request_id ||= 0
+
attr_reader :cmd, :returncode, :stdout, :stderr
def initialize(vm, cmd, options = {})
@@ -23,31 +25,29 @@ class VMCommand
# response will always be [0, "", ""] (only used as an
# ACK). execute() will always block until a response is received,
# though. Spawning is useful when starting processes in the
- # background (or running scripts that does the same) like our
- # onioncircuits wrapper, or any application we want to interact with.
+ # background (or running scripts that does the same) or any
+ # application we want to interact with.
def VMCommand.execute(vm, cmd, options = {})
options[:user] ||= "root"
options[:spawn] ||= false
type = options[:spawn] ? "spawn" : "call"
+ id = (@@request_id += 1)
socket = TCPSocket.new("127.0.0.1", vm.get_remote_shell_port)
debug_log("#{type}ing as #{options[:user]}: #{cmd}")
- begin
- socket.puts(JSON.dump([type, options[:user], cmd]))
+ socket.puts(JSON.dump([id, type, options[:user], cmd]))
+ loop do
s = socket.readline(sep = "\0").chomp("\0")
- ensure
- socket.close
- end
- debug_log("#{type} returned: #{s}") if not(options[:spawn])
- begin
- return JSON.load(s)
- rescue JSON::ParserError
- # The server often returns something unparsable for the very
- # first execute() command issued after a VM start/restore
- # (generally from wait_until_remote_shell_is_up()) presumably
- # because the TCP -> serial link isn't properly setup yet. All
- # will be well after that initial hickup, so we just retry.
- return VMCommand.execute(vm, cmd, options)
+ response_id, *rest = JSON.load(s)
+ if response_id == id
+ debug_log("#{type} returned: #{s}") if not(options[:spawn])
+ return rest
+ else
+ debug_log("Dropped out-of-order remote shell response: " +
+ "got id #{response_id} but expected id #{id}")
+ end
end
+ ensure
+ socket.close if defined?(socket) && socket
end
def success?
diff --git a/features/support/helpers/misc_helpers.rb b/features/support/helpers/misc_helpers.rb
index 8f4bcd8..db907e5 100644
--- a/features/support/helpers/misc_helpers.rb
+++ b/features/support/helpers/misc_helpers.rb
@@ -1,4 +1,6 @@
require 'date'
+require 'io/console'
+require 'pry'
require 'timeout'
require 'test/unit'
@@ -266,6 +268,16 @@ end
def pause(message = "Paused")
STDERR.puts
- STDERR.puts "#{message} (Press ENTER to continue!)"
- STDIN.gets
+ STDERR.puts message
+ STDERR.puts
+ loop do
+ STDERR.puts "Return: Continue; d: Debugging REPL"
+ c = STDIN.getch
+ case c
+ when "\r"
+ return
+ when "d"
+ binding.pry(quiet: true)
+ end
+ end
end
diff --git a/features/support/helpers/sikuli_helper.rb b/features/support/helpers/sikuli_helper.rb
index 2eb0667..4455ecc 100644
--- a/features/support/helpers/sikuli_helper.rb
+++ b/features/support/helpers/sikuli_helper.rb
@@ -1,9 +1,19 @@
require 'rjb'
require 'rjbextension'
$LOAD_PATH << ENV['SIKULI_HOME']
-require 'sikuli-script.jar'
+begin
+ require 'sikulixapi.jar'
+ USING_SIKULIX = true
+rescue LoadError
+ require 'sikuli-script.jar'
+ USING_SIKULIX = false
+end
Rjb::load
+def using_sikulix?
+ USING_SIKULIX
+end
+
package_members = [
"java.io.FileOutputStream",
"java.io.PrintStream",
@@ -16,11 +26,18 @@ package_members = [
"org.sikuli.script.Pattern",
"org.sikuli.script.Region",
"org.sikuli.script.Screen",
- "org.sikuli.script.Settings",
]
+if using_sikulix?
+ package_members << "org.sikuli.basics.Settings"
+ package_members << "org.sikuli.script.ImagePath"
+else
+ package_members << "org.sikuli.script.Settings"
+end
+
translations = Hash[
"org.sikuli.script", "Sikuli",
+ "org.sikuli.basics", "Sikuli",
"java.lang", "Java::Lang",
"java.io", "Java::Io",
]
@@ -186,14 +203,20 @@ def sikuli_script_proxy.new(*args)
end
def s.hide_cursor
- self.hover_point(self.w, self.h/2)
+ self.hover_point(self.w - 1, self.h/2)
end
s
end
# Configure sikuli
-java.lang.System.setProperty("SIKULI_IMAGE_PATH", "#{Dir.pwd}/features/images/")
+if using_sikulix?
+ Sikuli::ImagePath.add("#{Dir.pwd}/features/images/")
+else
+ java.lang.System.setProperty("SIKULI_IMAGE_PATH",
+ "#{Dir.pwd}/features/images/")
+ ENV["SIKULI_IMAGE_PATH"] = "#{Dir.pwd}/features/images/"
+end
# ruby and rjb doesn't play well together when it comes to static
# fields (and possibly methods) so we instantiate and access the field
@@ -211,4 +234,4 @@ sikuli_settings.MinSimilarity = 0.9
sikuli_settings.ActionLogs = true
sikuli_settings.DebugLogs = true
sikuli_settings.InfoLogs = true
-sikuli_settings.ProfileLogs = true
+sikuli_settings.ProfileLogs = false
diff --git a/features/support/helpers/storage_helper.rb b/features/support/helpers/storage_helper.rb
index 21537a9..9cf0db2 100644
--- a/features/support/helpers/storage_helper.rb
+++ b/features/support/helpers/storage_helper.rb
@@ -24,7 +24,8 @@ class VMStorage
rescue Libvirt::RetrieveError
@pool = nil
end
- if @pool and not(KEEP_SNAPSHOTS)
+ if @pool and (not(KEEP_SNAPSHOTS) or
+ (KEEP_SNAPSHOTS and not(Dir.exists?(@pool_path))))
VMStorage.clear_storage_pool(@pool)
@pool = nil
end
@@ -143,13 +144,7 @@ class VMStorage
end
def disk_mklabel(name, parttype)
- disk = {
- :path => disk_path(name),
- :opts => {
- :format => disk_format(name)
- }
- }
- guestfs_disk_helper(disk) do |g, disk_handle|
+ guestfs_disk_helper(name) do |g, disk_handle|
g.part_init(disk_handle, parttype)
end
end
@@ -157,13 +152,7 @@ class VMStorage
def disk_mkpartfs(name, parttype, fstype, opts = {})
opts[:label] ||= nil
opts[:luks_password] ||= nil
- disk = {
- :path => disk_path(name),
- :opts => {
- :format => disk_format(name)
- }
- }
- guestfs_disk_helper(disk) do |g, disk_handle|
+ guestfs_disk_helper(name) do |g, disk_handle|
g.part_disk(disk_handle, parttype)
g.part_set_name(disk_handle, 1, opts[:label]) if opts[:label]
primary_partition = g.list_partitions()[0]
@@ -181,13 +170,7 @@ class VMStorage
end
def disk_mkswap(name, parttype)
- disk = {
- :path => disk_path(name),
- :opts => {
- :format => disk_format(name)
- }
- }
- guestfs_disk_helper(disk) do |g, disk_handle|
+ guestfs_disk_helper(name) do |g, disk_handle|
g.part_disk(disk_handle, parttype)
primary_partition = g.list_partitions()[0]
g.mkswap(primary_partition)
@@ -205,7 +188,13 @@ class VMStorage
Guestfs::EVENT_TRACE)
g.set_autosync(1)
disks.each do |disk|
- g.add_drive_opts(disk[:path], disk[:opts])
+ if disk.class == String
+ g.add_drive_opts(disk_path(disk), format: disk_format(disk))
+ elsif disk.class == Hash
+ g.add_drive_opts(disk[:path], disk[:opts])
+ else
+ raise "cannot handle type '#{disk.class}'"
+ end
end
g.launch()
yield(g, *g.list_devices())
diff --git a/features/support/helpers/vm_helper.rb b/features/support/helpers/vm_helper.rb
index 72448f3..04cf7bc 100644
--- a/features/support/helpers/vm_helper.rb
+++ b/features/support/helpers/vm_helper.rb
@@ -63,7 +63,7 @@ end
class VM
- attr_reader :domain, :display, :vmnet, :storage
+ attr_reader :domain, :domain_name, :display, :vmnet, :storage
def initialize(virt, xml_path, vmnet, storage, x_display)
@virt = virt
@@ -147,30 +147,40 @@ class VM
update(domain_xml.to_s)
end
- def set_cdrom_image(image)
- image = nil if image == ''
- domain_xml = REXML::Document.new(@domain.xml_desc)
- domain_xml.elements.each('domain/devices/disk') do |e|
- if e.attribute('device').to_s == "cdrom"
- if image.nil?
- e.elements.delete('source')
- else
- if ! e.elements['source']
- e.add_element('source')
- end
- e.elements['source'].attributes['file'] = image
- end
- if is_running?
- @domain.update_device(e.to_s)
- else
- update(domain_xml.to_s)
- end
- end
+ def add_cdrom_device
+ if is_running?
+ raise "Can't attach a CDROM device to a running domain"
+ end
+ domain_rexml = REXML::Document.new(@domain.xml_desc)
+ if domain_rexml.elements["domain/devices/disk[@device='cdrom']"]
+ raise "A CDROM device already exists"
+ end
+ cdrom_rexml = REXML::Document.new(File.read("#{@xml_path}/cdrom.xml")).root
+ domain_rexml.elements['domain/devices'].add_element(cdrom_rexml)
+ update(domain_rexml.to_s)
+ end
+
+ def remove_cdrom_device
+ if is_running?
+ raise "Can't detach a CDROM device to a running domain"
+ end
+ domain_rexml = REXML::Document.new(@domain.xml_desc)
+ cdrom_el = domain_rexml.elements["domain/devices/disk[@device='cdrom']"]
+ if cdrom_el.nil?
+ raise "No CDROM device is present"
end
+ domain_rexml.elements["domain/devices"].delete_element(cdrom_el)
+ update(domain_rexml.to_s)
end
- def remove_cdrom
- set_cdrom_image(nil)
+ def eject_cdrom
+ domain_rexml = REXML::Document.new(@domain.xml_desc)
+ cdrom_el = domain_rexml.elements["domain/devices/disk[@device='cdrom']"]
+ if cdrom_el.nil?
+ raise "No CDROM device is present"
+ end
+ cdrom_el.delete_element('source')
+ update(domain_rexml.to_s)
rescue Libvirt::Error => e
# While the CD-ROM is removed successfully we still get this
# error, so let's ignore it.
@@ -181,12 +191,27 @@ class VM
raise e if not(Regexp.new(acceptable_error).match(e.to_s))
end
+ def set_cdrom_image(image)
+ if image.nil? or image == ''
+ raise "Can't set cdrom image to an empty string"
+ end
+ eject_cdrom
+ domain_rexml = REXML::Document.new(@domain.xml_desc)
+ cdrom_el = domain_rexml.elements["domain/devices/disk[@device='cdrom']"]
+ cdrom_el.add_element('source', { 'file' => image })
+ update(domain_rexml.to_s)
+ end
+
def set_cdrom_boot(image)
if is_running?
raise "boot settings can only be set for inactive vms"
end
- set_boot_device('cdrom')
+ domain_rexml = REXML::Document.new(@domain.xml_desc)
+ if not domain_rexml.elements["domain/devices/disk[@device='cdrom']"]
+ add_cdrom_device
+ end
set_cdrom_image(image)
+ set_boot_device('cdrom')
end
def list_disk_devs
@@ -309,9 +334,16 @@ class VM
end
plug_drive(name, type) if not(disk_plugged?(name))
set_boot_device('hd')
- # For some reason setting the boot device doesn't prevent cdrom
- # boot unless it's empty
- remove_cdrom
+ # XXX:Stretch: since our isotesters upgraded QEMU from
+ # 2.5+dfsg-4~bpo8+1 to 2.6+dfsg-3.1~bpo8+1 it seems we must remove
+ # the CDROM device to allow disk boot. This is not the case with the same
+ # version on Debian Sid. Let's hope we can remove this ugly
+ # workaround when we only support running the automated test suite
+ # on Stretch.
+ domain_rexml = REXML::Document.new(@domain.xml_desc)
+ if domain_rexml.elements["domain/devices/disk[@device='cdrom']"]
+ remove_cdrom_device
+ end
end
# XXX-9p: Shares don't work together with snapshot save+restore. See
diff --git a/features/support/hooks.rb b/features/support/hooks.rb
index 6589d9b..6693120 100644
--- a/features/support/hooks.rb
+++ b/features/support/hooks.rb
@@ -26,6 +26,8 @@ AfterConfiguration do |config|
# excluding persistence) and will create yet another disk and
# install Tails on it. This should be the peak of disk usage.
'features/usb_install.feature',
+ # This feature needs a copy of the ISO and creates a new disk.
+ 'features/usb_upgrade.feature',
]
feature_files = config.feature_files
# The &-intersection is specified to keep the element ordering of
@@ -251,7 +253,7 @@ After('@product') do |scenario|
info_log
info_log_artifact_location(type, artifact_path)
end
- pause("Scenario failed") if $config["PAUSE_ON_FAIL"]
+ pause("Scenario failed") if $config["INTERACTIVE_DEBUGGING"]
else
if @video_path && File.exist?(@video_path) && not($config['CAPTURE_ALL'])
FileUtils.rm(@video_path)
diff --git a/features/time_syncing.feature b/features/time_syncing.feature
index cda75f6..cd835bc 100644
--- a/features/time_syncing.feature
+++ b/features/time_syncing.feature
@@ -10,6 +10,8 @@ Feature: Time syncing
And Tor is ready
Then Tails clock is less than 5 minutes incorrect
+ #11589
+ @fragile
Scenario: Clock with host's time in bridge mode
Given I have started Tails from DVD without network and logged in with bridge mode enabled
When the network is plugged
diff --git a/features/tor_bridges.feature b/features/tor_bridges.feature
index bda0304..1355f2b 100644
--- a/features/tor_bridges.feature
+++ b/features/tor_bridges.feature
@@ -1,4 +1,5 @@
-@product
+#11606
+@product @fragile
Feature: Using Tails with Tor pluggable transports
As a Tails user
I want to circumvent censorship of Tor by using Tor pluggable transports
@@ -11,6 +12,8 @@ Feature: Using Tails with Tor pluggable transports
Then the Tor Launcher autostarts
And the Tor Launcher uses all expected TBB shared libraries
+ #11589
+ @fragile
Scenario: Using bridges
When I configure some bridge pluggable transports in Tor Launcher
Then Tor is ready
diff --git a/features/tor_enforcement.feature b/features/tor_enforcement.feature
index 7cd6d20..0095f77 100644
--- a/features/tor_enforcement.feature
+++ b/features/tor_enforcement.feature
@@ -5,10 +5,6 @@ Feature: The Tor enforcement is effective
And as a Tails developer
I want to ensure that the automated test suite detects firewall leaks reliably
- Scenario: Tails' Tor binary is configured to use the expected Tor authorities
- Given I have started Tails from DVD and logged in and the network is connected
- Then the Tor binary is configured to use the expected Tor authorities
-
Scenario: The firewall configuration is very restrictive
Given I have started Tails from DVD and logged in and the network is connected
Then the firewall's policy is to drop all IPv4 traffic
diff --git a/features/tor_stream_isolation.feature b/features/tor_stream_isolation.feature
index 59aa34d..129707e 100644
--- a/features/tor_stream_isolation.feature
+++ b/features/tor_stream_isolation.feature
@@ -21,6 +21,8 @@ Feature: Tor stream isolation is effective
And I re-run tails-upgrade-frontend-wrapper
Then I see that tails-upgrade-frontend-wrapper is properly stream isolated
+ #11592
+ @fragile
Scenario: The Tor Browser is using the web browser-specific SocksPort
When I monitor the network connections of Tor Browser
And I start the Tor Browser
diff --git a/features/torified_browsing.feature b/features/torified_browsing.feature
index 45017a7..878340f 100644
--- a/features/torified_browsing.feature
+++ b/features/torified_browsing.feature
@@ -4,6 +4,8 @@ Feature: Browsing the web using the Tor Browser
when I browse the web using the Tor Browser
all Internet traffic should flow only through Tor
+ #11591, #11592
+ @fragile
Scenario: The Tor Browser cannot access the LAN
Given I have started Tails from DVD and logged in and the network is connected
And a web server is running on the LAN
@@ -14,7 +16,8 @@ Feature: Browsing the web using the Tor Browser
Then the Tor Browser shows the "Unable to connect" error
And no traffic was sent to the web server on the LAN
- @check_tor_leaks
+ #11592
+ @check_tor_leaks @fragile
Scenario: The Tor Browser directory is usable
Given I have started Tails from DVD and logged in and the network is connected
Then the amnesiac Tor Browser directory exists
@@ -25,7 +28,8 @@ Feature: Browsing the web using the Tor Browser
Then I can save the current page as "index.html" to the default downloads directory
And I can print the current page as "output.pdf" to the default downloads directory
- @check_tor_leaks
+ #11592
+ @check_tor_leaks @fragile
Scenario: Downloading files with the Tor Browser
Given I have started Tails from DVD and logged in and the network is connected
When I start the Tor Browser
@@ -35,7 +39,8 @@ Feature: Browsing the web using the Tor Browser
When I save the file to the default Tor Browser download directory
Then the file is saved to the default Tor Browser download directory
- @check_tor_leaks
+ #11592
+ @check_tor_leaks @fragile
Scenario: Playing HTML5 audio
Given I have started Tails from DVD and logged in and the network is connected
When I start the Tor Browser
@@ -45,6 +50,19 @@ Feature: Browsing the web using the Tor Browser
And I click the HTML5 play button
And 1 application is playing audio after 10 seconds
+ @check_tor_leaks @fragile
+ Scenario: Watching a WebM video
+ Given I have started Tails from DVD and logged in and the network is connected
+ When I start the Tor Browser
+ And the Tor Browser has started and loaded the startup page
+ And I open the address "https://tails.boum.org/lib/test_suite/test.webm" in the Tor Browser
+ And I click the blocked video icon
+ And I see "TorBrowserNoScriptTemporarilyAllowDialog.png" after at most 30 seconds
+ And I accept to temporarily allow playing this video
+ Then I see "TorBrowserSampleRemoteWebMVideoFrame.png" after at most 180 seconds
+
+ #11592
+ @fragile
Scenario: I can view a file stored in "~/Tor Browser" but not in ~/.gnupg
Given I have started Tails from DVD and logged in and the network is connected
And I copy "/usr/share/synaptic/html/index.html" to "/home/amnesia/Tor Browser/synaptic.html" as user "amnesia"
@@ -88,7 +106,7 @@ Feature: Browsing the web using the Tor Browser
Given I have started Tails from DVD and logged in and the network is connected
When I double-click on the "Tails documentation" link on the Desktop
Then the Tor Browser has started
- And I see "TailsOfflineDocHomepage.png" after at most 10 seconds
+ And "Tails - Getting started..." has loaded in the Tor Browser
Scenario: The Tor Browser uses TBB's shared libraries
Given I have started Tails from DVD and logged in and the network is connected
@@ -96,7 +114,8 @@ Feature: Browsing the web using the Tor Browser
And the Tor Browser has started
Then the Tor Browser uses all expected TBB shared libraries
- @check_tor_leaks
+ #11592
+ @check_tor_leaks @fragile
Scenario: The Tor Browser's "New identity" feature works as expected
Given I have started Tails from DVD and logged in and the network is connected
When I start the Tor Browser
@@ -107,13 +126,15 @@ Feature: Browsing the web using the Tor Browser
And I acknowledge Torbutton's New Identity confirmation prompt
Then the Tor Browser loads the startup page
+ #11592
+ @fragile
Scenario: The Tor Browser should not have any plugins enabled
Given I have started Tails from DVD and logged in and the network is connected
When I start the Tor Browser
And the Tor Browser has started and loaded the startup page
Then the Tor Browser has no plugins installed
- #10720
+ #11592
@fragile
Scenario: The persistent Tor Browser directory is usable
Given I have started Tails without network from a USB drive with a persistent partition enabled and logged in
@@ -130,7 +151,7 @@ Feature: Browsing the web using the Tor Browser
Then I see "TorBrowserSavedStartupPage.png" after at most 10 seconds
And I can print the current page as "output.pdf" to the persistent Tor Browser directory
- #10720
+ #11585
@fragile
Scenario: Persistent browser bookmarks
Given I have started Tails without network from a USB drive with a persistent partition enabled and logged in
diff --git a/features/torified_git.feature b/features/torified_git.feature
index 64f93a6..cbb3963 100644
--- a/features/torified_git.feature
+++ b/features/torified_git.feature
@@ -1,4 +1,4 @@
-@product @check_tor_leaks @fragile
+@product @check_tor_leaks
Feature: Cloning a Git repository
As a Tails user
when I clone a Git repository
@@ -7,23 +7,17 @@ Feature: Cloning a Git repository
Background:
Given I have started Tails from DVD and logged in and the network is connected
+ #11563
@fragile
Scenario: Cloning a Git repository anonymously over HTTPS
- When I run "git clone https://git-tails.immerda.ch/myprivatekeyispublic/testing" in GNOME Terminal
- Then process "git" is running within 10 seconds
- And process "git" has stopped running after at most 180 seconds
- And the Git repository "testing" has been cloned successfully
+ When I clone the Git repository "https://git-tails.immerda.ch/myprivatekeyispublic/testing" in GNOME Terminal
+ Then the Git repository "testing" has been cloned successfully
Scenario: Cloning a Git repository anonymously over the Git protocol
- When I run "git clone git://git.tails.boum.org/myprivatekeyispublic/testing" in GNOME Terminal
- Then process "git" is running within 10 seconds
- And process "git" has stopped running after at most 180 seconds
- And the Git repository "testing" has been cloned successfully
+ When I clone the Git repository "git://git.tails.boum.org/myprivatekeyispublic/testing" in GNOME Terminal
+ Then the Git repository "testing" has been cloned successfully
Scenario: Cloning git repository over SSH
Given I have the SSH key pair for a Git repository
- When I run "git clone tails@git.tails.boum.org:myprivatekeyispublic/testing" in GNOME Terminal
- Then process "git" is running within 10 seconds
- When I verify the SSH fingerprint for the Git repository
- And process "git" has stopped running after at most 180 seconds
+ When I clone the Git repository "tails@git.tails.boum.org:myprivatekeyispublic/testing" in GNOME Terminal
Then the Git repository "testing" has been cloned successfully
diff --git a/features/torified_misc.feature b/features/torified_misc.feature
index 5bb83c8..aab694a 100644
--- a/features/torified_misc.feature
+++ b/features/torified_misc.feature
@@ -15,7 +15,7 @@ Feature: Various checks for torified software
And the wget standard output contains "Example Domain"
Scenario: wget(1) with tricky options should work for HTTP and go through Tor.
- When I wget "http://195.154.14.189/tails/stable/" to stdout with the '--spider --header="Host: dl.amnesia.boum.org"' options
+ When I wget "some Tails mirror" to stdout with the '--spider --header="Host: dl.amnesia.boum.org"' options
Then the wget command is successful
Scenario: whois(1) should work and go through Tor.
diff --git a/features/totem.feature b/features/totem.feature
index c5fb37a..2944739 100644
--- a/features/totem.feature
+++ b/features/totem.feature
@@ -9,14 +9,13 @@ Feature: Using Totem
Given I create sample videos
Scenario: Watching a MP4 video stored on the non-persistent filesystem
- Given a computer
- And I setup a filesystem share containing sample videos
- And I start Tails from DVD with network unplugged and I login
+ Given I have started Tails from DVD without network and logged in
+ And I plug and mount a USB drive containing sample videos
And I copy the sample videos to "/home/amnesia" as user "amnesia"
And the file "/home/amnesia/video.mp4" exists
Given I start monitoring the AppArmor log of "/usr/bin/totem"
When I open "/home/amnesia/video.mp4" with Totem
- Then I see "SampleLocalMp4VideoFrame.png" after at most 20 seconds
+ Then I see "SampleLocalMp4VideoFrame.png" after at most 40 seconds
And AppArmor has not denied "/usr/bin/totem" from opening "/home/amnesia/video.mp4"
Given I close Totem
And I copy the sample videos to "/home/amnesia/.gnupg" as user "amnesia"
@@ -39,31 +38,27 @@ Feature: Using Totem
# Due to our AppArmor aliases, /live/overlay will be treated
# as /lib/live/mount/overlay.
And AppArmor has denied "/usr/bin/totem" from opening "/lib/live/mount/overlay/home/amnesia/.gnupg/video.mp4"
+ Given I close Totem
+ And I copy "/home/amnesia/video.mp4" to "/home/amnesia/.purple/otr.private_key" as user "amnesia"
+ And I restart monitoring the AppArmor log of "/usr/bin/totem"
+ When I try to open "/home/amnesia/.purple/otr.private_key" with Totem
+ Then I see "TotemUnableToOpen.png" after at most 10 seconds
+ And AppArmor has denied "/usr/bin/totem" from opening "/home/amnesia/.purple/otr.private_key"
@check_tor_leaks @fragile
Scenario: Watching a WebM video over HTTPS
Given I have started Tails from DVD and logged in and the network is connected
Then I can watch a WebM video over HTTPs
- #10720: Tails Installer freezes on Jenkins
- @fragile
Scenario: Watching MP4 videos stored on the persistent volume should work as expected given our AppArmor confinement
- Given I have started Tails without network from a USB drive with a persistent partition and stopped at Tails Greeter's login screen
- # Due to bug #5571 we have to reboot to be able to use
- # filesystem shares.
- And I shutdown Tails and wait for the computer to power off
- And I setup a filesystem share containing sample videos
- And I start Tails from USB drive "__internal" with network unplugged and I login with persistence enabled
+ Given I have started Tails without network from a USB drive with a persistent partition enabled and logged in
+ And I plug and mount a USB drive containing sample videos
And I copy the sample videos to "/home/amnesia/Persistent" as user "amnesia"
- And I copy the sample videos to "/home/amnesia/.gnupg" as user "amnesia"
- And I shutdown Tails and wait for the computer to power off
- And I start Tails from USB drive "__internal" with network unplugged and I login with persistence enabled
- And the file "/home/amnesia/Persistent/video.mp4" exists
When I open "/home/amnesia/Persistent/video.mp4" with Totem
- Then I see "SampleLocalMp4VideoFrame.png" after at most 10 seconds
+ Then I see "SampleLocalMp4VideoFrame.png" after at most 40 seconds
Given I close Totem
- And the file "/home/amnesia/.gnupg/video.mp4" exists
And I start monitoring the AppArmor log of "/usr/bin/totem"
+ And I copy the sample videos to "/home/amnesia/.gnupg" as user "amnesia"
When I try to open "/home/amnesia/.gnupg/video.mp4" with Totem
Then I see "TotemUnableToOpen.png" after at most 10 seconds
And AppArmor has denied "/usr/bin/totem" from opening "/home/amnesia/.gnupg/video.mp4"
diff --git a/features/untrusted_partitions.feature b/features/untrusted_partitions.feature
index b1045c4..ff94cd5 100644
--- a/features/untrusted_partitions.feature
+++ b/features/untrusted_partitions.feature
@@ -7,7 +7,7 @@ Feature: Untrusted partitions
Given a computer
And I temporarily create a 100 MiB disk named "swap"
And I create a gpt swap partition on disk "swap"
- And I plug ide drive "swap"
+ And I plug sata drive "swap"
When I start Tails with network unplugged and I login
Then a "swap" partition was detected by Tails on drive "swap"
But Tails has no disk swap enabled
@@ -36,7 +36,7 @@ Feature: Untrusted partitions
Given a computer
And I temporarily create a 100 MiB disk named "fake_TailsData"
And I create a gpt partition labeled "TailsData" with an ext4 filesystem encrypted with password "asdf" on disk "fake_TailsData"
- And I plug ide drive "fake_TailsData"
+ And I plug sata drive "fake_TailsData"
When I start the computer
And the computer boots Tails
Then drive "fake_TailsData" is detected by Tails
@@ -46,16 +46,16 @@ Feature: Untrusted partitions
Given a computer
And I temporarily create a 2 GiB disk named "live_hd"
And I cat an ISO of the Tails image to disk "live_hd"
- And the computer is set to boot from ide drive "live_hd"
+ And the computer is set to boot from sata drive "live_hd"
And I set Tails to boot with options "live-media="
When I start Tails with network unplugged and I login
- Then Tails is running from ide drive "live_hd"
+ Then Tails is running from sata drive "live_hd"
Scenario: Tails booting from a DVD does not use live systems stored on hard drives
Given a computer
And I temporarily create a 2 GiB disk named "live_hd"
And I cat an ISO of the Tails image to disk "live_hd"
- And I plug ide drive "live_hd"
+ And I plug sata drive "live_hd"
And I start Tails from DVD with network unplugged and I login
Then drive "live_hd" is detected by Tails
And drive "live_hd" is not mounted
@@ -64,7 +64,7 @@ Feature: Untrusted partitions
Given a computer
And I temporarily create a 100 MiB disk named "gpt_ext2"
And I create a gpt partition with an ext2 filesystem on disk "gpt_ext2"
- And I plug ide drive "gpt_ext2"
+ And I plug sata drive "gpt_ext2"
And I start Tails from DVD with network unplugged and I login
Then drive "gpt_ext2" is detected by Tails
And drive "gpt_ext2" is not mounted
@@ -73,7 +73,7 @@ Feature: Untrusted partitions
Given a computer
And I temporarily create a 100 MiB disk named "msdos_fat32"
And I create an msdos partition with a vfat filesystem on disk "msdos_fat32"
- And I plug ide drive "msdos_fat32"
+ And I plug sata drive "msdos_fat32"
And I start Tails from DVD with network unplugged and I login
Then drive "msdos_fat32" is detected by Tails
And drive "msdos_fat32" is not mounted
diff --git a/features/usb_install.feature b/features/usb_install.feature
index e8ce2fe..64da8ee 100644
--- a/features/usb_install.feature
+++ b/features/usb_install.feature
@@ -27,8 +27,6 @@ Feature: Installing Tails to a USB drive
Then no USB drive is selected
And a suitable USB device is not found
- #10720: Tails Installer freezes on Jenkins
- @fragile
Scenario: Installing Tails to a pristine USB drive
Given I have started Tails from DVD without network and logged in
And I temporarily create a 4 GiB disk named "install"
@@ -37,16 +35,12 @@ Feature: Installing Tails to a USB drive
Then the running Tails is installed on USB drive "install"
But there is no persistence partition on USB drive "install"
- #10720: Tails Installer freezes on Jenkins
- @fragile
Scenario: Booting Tails from a USB drive without a persistent partition and creating one
Given I have started Tails without network from a USB drive without a persistent partition and stopped at Tails Greeter's login screen
And I log in to a new session
When I create a persistent partition
Then a Tails persistence partition exists on USB drive "__internal"
- #10720: Tails Installer freezes on Jenkins
- @fragile
Scenario: Booting Tails from a USB drive without a persistent partition
Given I have started Tails without network from a USB drive without a persistent partition and stopped at Tails Greeter's login screen
When I log in to a new session
@@ -54,7 +48,7 @@ Feature: Installing Tails to a USB drive
And the persistent Tor Browser directory does not exist
And there is no persistence partition on USB drive "__internal"
- #10720: Tails Installer freezes on Jenkins
+ #11583
@fragile
Scenario: Booting Tails from a USB drive in UEFI mode
Given I have started Tails without network from a USB drive without a persistent partition and stopped at Tails Greeter's login screen
@@ -66,8 +60,6 @@ Feature: Installing Tails to a USB drive
And the boot device has safe access rights
And Tails has started in UEFI mode
- #10720: Tails Installer freezes on Jenkins
- @fragile
Scenario: Installing Tails to a USB drive with an MBR partition table but no partitions, and making sure that it boots
Given I have started Tails from DVD without network and logged in
And I temporarily create a 4 GiB disk named "mbr"
@@ -82,8 +74,6 @@ Feature: Installing Tails to a USB drive
And the boot device has safe access rights
And there is no persistence partition on USB drive "mbr"
- #10720: Tails Installer freezes on Jenkins
- @fragile
Scenario: Cat:ing a Tails isohybrid to a USB drive and booting it, then trying to upgrading it but ending up having to do a fresh installation, which boots
Given a computer
And I temporarily create a 4 GiB disk named "isohybrid"
diff --git a/features/usb_upgrade.feature b/features/usb_upgrade.feature
index fc61308..33cc0da 100644
--- a/features/usb_upgrade.feature
+++ b/features/usb_upgrade.feature
@@ -1,5 +1,4 @@
-#10720: Tails Installer freezes on Jenkins
-@product @fragile
+@product
Feature: Upgrading an old Tails USB installation
As a Tails user
If I have an old versoin of Tails installed on a USB device
@@ -12,9 +11,8 @@ Feature: Upgrading an old Tails USB installation
# dependencies (which are documented below).
Scenario: Try to "Upgrade from ISO" Tails to a pristine USB drive
- Given a computer
- And I setup a filesystem share containing the Tails ISO
- And I start Tails from DVD with network unplugged and I login
+ Given I have started Tails from DVD without network and logged in
+ And I plug and mount a USB drive containing the Tails ISO
And I temporarily create a 4 GiB disk named "pristine"
And I plug USB drive "pristine"
And I start Tails Installer in "Upgrade from ISO" mode
@@ -30,9 +28,8 @@ Feature: Upgrading an old Tails USB installation
And I am told that the destination device cannot be upgraded
Scenario: Try to "Upgrade from ISO" Tails to a USB drive with GPT and a FAT partition
- Given a computer
- And I setup a filesystem share containing the Tails ISO
- And I start Tails from DVD with network unplugged and I login
+ Given I have started Tails from DVD without network and logged in
+ And I plug and mount a USB drive containing the Tails ISO
And I temporarily create a 4 GiB disk named "gptfat"
And I create a gpt partition with a vfat filesystem on disk "gptfat"
And I plug USB drive "gptfat"
@@ -45,7 +42,7 @@ Feature: Upgrading an old Tails USB installation
And I temporarily create a 4 GiB disk named "gptfat"
And I create a gpt partition with a vfat filesystem on disk "gptfat"
And I plug USB drive "gptfat"
- And I start Tails Installer in "Upgrade from ISO" mode
+ And I start Tails Installer in "Clone & Upgrade" mode
Then a suitable USB device is not found
And I am told that the destination device cannot be upgraded
@@ -133,8 +130,8 @@ Feature: Upgrading an old Tails USB installation
Scenario: Upgrading an old Tails USB installation from an ISO image, running on the old version
Given a computer
And I clone USB drive "old" to a new USB drive "to_upgrade"
- And I setup a filesystem share containing the Tails ISO
When I start Tails from USB drive "old" with network unplugged and I login
+ And I plug and mount a USB drive containing the Tails ISO
And I plug USB drive "to_upgrade"
And I do a "Upgrade from ISO" on USB drive "to_upgrade"
Then the ISO's Tails is installed on USB drive "to_upgrade"
@@ -142,10 +139,9 @@ Feature: Upgrading an old Tails USB installation
# Depends on scenario: Writing files to a read/write-enabled persistent partition with the old Tails USB installation
Scenario: Upgrading an old Tails USB installation from an ISO image, running on the new version
- Given a computer
+ Given I have started Tails from DVD without network and logged in
+ And I plug and mount a USB drive containing the Tails ISO
And I clone USB drive "old" to a new USB drive "to_upgrade"
- And I setup a filesystem share containing the Tails ISO
- And I start Tails from DVD with network unplugged and I login
And I plug USB drive "to_upgrade"
And I do a "Upgrade from ISO" on USB drive "to_upgrade"
Then the ISO's Tails is installed on USB drive "to_upgrade"
@@ -160,3 +156,24 @@ Feature: Upgrading an old Tails USB installation
And the boot device has safe access rights
And the expected persistent files created with the old Tails version are present in the filesystem
And all persistent directories from the old Tails version have safe access rights
+
+ Scenario: Upgrading Tails with Tails Upgrader through an incremental upgrade
+ Given I have started Tails without network from a USB drive with a persistent partition enabled and logged in
+ And Tails is fooled to think it is running version 1.0~test
+ And the file system changes introduced in version 1.1~test are not present
+ When the network is plugged
+ And Tor is ready
+ And all notifications have disappeared
+ Then I am proposed to install an incremental upgrade to version 1.1~test
+ And I can successfully install the incremental upgrade to version 1.1~test
+ Given I shutdown Tails and wait for the computer to power off
+ When I start Tails from USB drive "__internal" with network unplugged and I login with persistence enabled
+ Then Tails is running version 1.1~test
+ And all persistence presets are enabled
+ And the file system changes introduced in version 1.1~test are present
+ When the network is plugged
+ And the network connection is ready within 30 seconds
+ And all notifications have disappeared
+ # Regression test on #8158 (i.e. the IUK's filesystem is not part of the Unsafe Browser's chroot)
+ And I successfully start the Unsafe Browser
+ Then the file system changes introduced in version 1.1~test are present in the Unsafe Browser's chroot
diff --git a/features/virtualization.feature b/features/virtualization.feature
new file mode 100644
index 0000000..38a4e80
--- /dev/null
+++ b/features/virtualization.feature
@@ -0,0 +1,9 @@
+@product
+Feature: Virtualization support
+
+ Scenario: VirtualBox guest modules are available
+ Given a computer
+ And the computer is an old pentium without the PAE extension
+ And I start Tails from DVD with network unplugged and I login
+ When Tails has booted a 32-bit kernel
+ Then the VirtualBox guest modules are available