brizental pushed to branch tor-browser-149.0a1-16.0-2 at The Tor Project / Applications / Tor Browser
Commits:
-
70bab416
by Beatriz Rizental at 2026-03-24T18:49:21+01:00
6 changed files:
- testing/marionette/harness/marionette_harness/__init__.py
- testing/marionette/harness/marionette_harness/runner/__init__.py
- testing/marionette/harness/marionette_harness/runner/mixins/__init__.py
- + testing/marionette/harness/marionette_harness/runner/mixins/tor_browser.py
- testing/tor/test_circuit_isolation.py
- testing/tor/test_network_check.py
Changes:
| ... | ... | @@ -28,5 +28,6 @@ from .runner import ( |
| 28 | 28 | TestManifest,
|
| 29 | 29 | TestResult,
|
| 30 | 30 | TestResultCollection,
|
| 31 | + TorBrowserMixin,
|
|
| 31 | 32 | WindowManagerMixin,
|
| 32 | 33 | ) |
| ... | ... | @@ -13,4 +13,7 @@ from .base import ( |
| 13 | 13 | TestResult,
|
| 14 | 14 | TestResultCollection,
|
| 15 | 15 | )
|
| 16 | -from .mixins import WindowManagerMixin |
|
| 16 | +from .mixins import (
|
|
| 17 | + TorBrowserMixin,
|
|
| 18 | + WindowManagerMixin,
|
|
| 19 | +) |
| ... | ... | @@ -3,3 +3,4 @@ |
| 3 | 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
| 4 | 4 | |
| 5 | 5 | from .window_manager import WindowManagerMixin
|
| 6 | +from .tor_browser import TorBrowserMixin |
| 1 | +from marionette_driver.errors import ScriptTimeoutException
|
|
| 2 | + |
|
| 3 | +DEFAULT_BOOTSTRAP_TIMEOUT_MS = 60 * 1000
|
|
| 4 | +DEFAULT_BOOTSTRAP_MAX_RETRIES = 3
|
|
| 5 | + |
|
| 6 | +class TorBrowserMixin:
|
|
| 7 | + def bootstrap(
|
|
| 8 | + self,
|
|
| 9 | + max_retries=DEFAULT_BOOTSTRAP_MAX_RETRIES,
|
|
| 10 | + ):
|
|
| 11 | + """Bootstrap the Tor connection.
|
|
| 12 | + |
|
| 13 | + This doesn't fail if already bootstrapped, but will retry a few times if
|
|
| 14 | + a script timeout is hit.
|
|
| 15 | + |
|
| 16 | + This function is UI-agnostic, meaning it can be used both on Desktop and Android.
|
|
| 17 | + """
|
|
| 18 | + |
|
| 19 | + attempt = 0
|
|
| 20 | + while attempt < max_retries:
|
|
| 21 | + try:
|
|
| 22 | + with self.marionette.using_context("chrome"):
|
|
| 23 | + self.marionette.execute_async_script(
|
|
| 24 | + """
|
|
| 25 | + const { TorConnect, TorConnectStage, TorConnectTopics } = ChromeUtils.importESModule(
|
|
| 26 | + "resource://gre/modules/TorConnect.sys.mjs"
|
|
| 27 | + );
|
|
| 28 | + const [resolve] = arguments;
|
|
| 29 | + |
|
| 30 | + // Only the first test of a suite will need to bootstrap.
|
|
| 31 | + if (TorConnect.stage.name === TorConnectStage.Bootstrapped) {
|
|
| 32 | + resolve();
|
|
| 33 | + return;
|
|
| 34 | + }
|
|
| 35 | + |
|
| 36 | + function waitForBootstrap() {
|
|
| 37 | + const topic = TorConnectTopics.BootstrapComplete;
|
|
| 38 | + Services.obs.addObserver(function observer() {
|
|
| 39 | + Services.obs.removeObserver(observer, topic);
|
|
| 40 | + resolve();
|
|
| 41 | + }, topic);
|
|
| 42 | + TorConnect.beginBootstrapping();
|
|
| 43 | + }
|
|
| 44 | + |
|
| 45 | + const stageTopic = TorConnectTopics.StageChange;
|
|
| 46 | + function stageObserver() {
|
|
| 47 | + if (TorConnect.canBeginNormalBootstrap) {
|
|
| 48 | + Services.obs.removeObserver(stageObserver, stageTopic);
|
|
| 49 | + waitForBootstrap();
|
|
| 50 | + }
|
|
| 51 | + }
|
|
| 52 | + Services.obs.addObserver(stageObserver, stageTopic);
|
|
| 53 | + stageObserver();
|
|
| 54 | + """,
|
|
| 55 | + script_timeout=DEFAULT_BOOTSTRAP_TIMEOUT_MS,
|
|
| 56 | + )
|
|
| 57 | + |
|
| 58 | + return
|
|
| 59 | + except ScriptTimeoutException:
|
|
| 60 | + attempt += 1
|
|
| 61 | + with self.marionette.using_context("chrome"):
|
|
| 62 | + self.marionette.execute_script(
|
|
| 63 | + """
|
|
| 64 | + const { TorConnect } = ChromeUtils.importESModule(
|
|
| 65 | + "resource://gre/modules/TorConnect.sys.mjs"
|
|
| 66 | + );
|
|
| 67 | + |
|
| 68 | + TorConnect._makeStageRequest(TorConnectStage.Start, true);
|
|
| 69 | + """
|
|
| 70 | + )
|
|
| 71 | + |
|
| 72 | + |
|
| 73 | + raise RuntimeError("Unable to connect to Tor Network") |
| ... | ... | @@ -2,47 +2,13 @@ from ipaddress import ip_address |
| 2 | 2 | |
| 3 | 3 | from marionette_driver import By
|
| 4 | 4 | from marionette_driver.errors import NoSuchElementException
|
| 5 | -from marionette_harness import MarionetteTestCase
|
|
| 5 | +from marionette_harness import MarionetteTestCase, TorBrowserMixin
|
|
| 6 | 6 | |
| 7 | -TOR_BOOTSTRAP_TIMEOUT = 30000 # 30s
|
|
| 8 | 7 | |
| 9 | - |
|
| 10 | -class TestCircuitIsolation(MarionetteTestCase):
|
|
| 8 | +class TestCircuitIsolation(MarionetteTestCase, TorBrowserMixin):
|
|
| 11 | 9 | def tearDown(self):
|
| 12 | - self.marionette.restart(in_app=False, clean=True)
|
|
| 13 | 10 | super().tearDown()
|
| 14 | 11 | |
| 15 | - def bootstrap(self):
|
|
| 16 | - with self.marionette.using_context("chrome"):
|
|
| 17 | - self.marionette.execute_async_script(
|
|
| 18 | - """
|
|
| 19 | - const { TorConnect, TorConnectTopics } = ChromeUtils.importESModule(
|
|
| 20 | - "resource://gre/modules/TorConnect.sys.mjs"
|
|
| 21 | - );
|
|
| 22 | - const [resolve] = arguments;
|
|
| 23 | - |
|
| 24 | - function waitForBootstrap() {
|
|
| 25 | - const topic = TorConnectTopics.BootstrapComplete;
|
|
| 26 | - Services.obs.addObserver(function observer() {
|
|
| 27 | - Services.obs.removeObserver(observer, topic);
|
|
| 28 | - resolve();
|
|
| 29 | - }, topic);
|
|
| 30 | - TorConnect.beginBootstrapping();
|
|
| 31 | - }
|
|
| 32 | - |
|
| 33 | - const stageTopic = TorConnectTopics.StageChange;
|
|
| 34 | - function stageObserver() {
|
|
| 35 | - if (TorConnect.canBeginNormalBootstrap) {
|
|
| 36 | - Services.obs.removeObserver(stageObserver, stageTopic);
|
|
| 37 | - waitForBootstrap();
|
|
| 38 | - }
|
|
| 39 | - }
|
|
| 40 | - Services.obs.addObserver(stageObserver, stageTopic);
|
|
| 41 | - stageObserver();
|
|
| 42 | - """,
|
|
| 43 | - script_timeout=TOR_BOOTSTRAP_TIMEOUT,
|
|
| 44 | - )
|
|
| 45 | - |
|
| 46 | 12 | def extract_from_check_tpo(self):
|
| 47 | 13 | # Fetch the IP from check.torproject.org.
|
| 48 | 14 | # In addition to that, since we are loading this page, we
|
| 1 | -from marionette_driver import By, Wait, errors
|
|
| 2 | -from marionette_driver.localization import L10n
|
|
| 3 | -from marionette_harness import MarionetteTestCase
|
|
| 1 | +from marionette_harness import MarionetteTestCase, TorBrowserMixin
|
|
| 4 | 2 | |
| 5 | 3 | NETWORK_CHECK_URL = "https://check.torproject.org/"
|
| 6 | -TOR_BOOTSTRAP_TIMEOUT = 30 # 30s
|
|
| 7 | 4 | |
| 8 | -STRINGS_LOCATION = "chrome://torbutton/locale/torConnect.properties"
|
|
| 9 | - |
|
| 10 | - |
|
| 11 | -class TestNetworkCheck(MarionetteTestCase):
|
|
| 12 | - def setUp(self):
|
|
| 13 | - MarionetteTestCase.setUp(self)
|
|
| 14 | - |
|
| 15 | - self.l10n = L10n(self.marionette)
|
|
| 16 | - |
|
| 17 | - def tearDown(self):
|
|
| 18 | - self.marionette.restart(in_app=False, clean=True)
|
|
| 19 | - super().tearDown()
|
|
| 20 | - |
|
| 21 | - def attemptConnection(self, tries=1):
|
|
| 22 | - if tries > 3:
|
|
| 23 | - self.assertTrue(False, "Failed to connect to Tor after 3 attempts")
|
|
| 24 | - |
|
| 25 | - connectBtn = self.marionette.find_element(By.ID, "connectButton")
|
|
| 26 | - Wait(self.marionette, timeout=10).until(
|
|
| 27 | - lambda _: connectBtn.is_displayed(),
|
|
| 28 | - message="Timed out waiting for tor connect button to show up.",
|
|
| 29 | - )
|
|
| 30 | - connectBtn.click()
|
|
| 31 | - |
|
| 32 | - try:
|
|
| 33 | - |
|
| 34 | - def check(m):
|
|
| 35 | - if not m.get_url().startswith("about:torconnect"):
|
|
| 36 | - # We have finished connecting and have been redirected.
|
|
| 37 | - return True
|
|
| 38 | - |
|
| 39 | - try:
|
|
| 40 | - heading = self.marionette.find_element(By.ID, "tor-connect-heading")
|
|
| 41 | - except errors.NoSuchElementException:
|
|
| 42 | - # Page is probably redirecting.
|
|
| 43 | - return False
|
|
| 44 | - |
|
| 45 | - if heading.text not in [
|
|
| 46 | - self.l10n.localize_property(
|
|
| 47 | - [STRINGS_LOCATION], "torConnect.torConnecting"
|
|
| 48 | - ),
|
|
| 49 | - self.l10n.localize_property(
|
|
| 50 | - [STRINGS_LOCATION], "torConnect.torConnected"
|
|
| 51 | - ),
|
|
| 52 | - ]:
|
|
| 53 | - raise ValueError("Tor connect page is not connecting or connected")
|
|
| 54 | - |
|
| 55 | - return False
|
|
| 56 | - |
|
| 57 | - Wait(self.marionette, timeout=TOR_BOOTSTRAP_TIMEOUT).until(check)
|
|
| 58 | - except (errors.TimeoutException, ValueError):
|
|
| 59 | - cancelBtn = self.marionette.find_element(By.ID, "cancelButton")
|
|
| 60 | - if cancelBtn.is_displayed():
|
|
| 61 | - cancelBtn.click()
|
|
| 62 | - |
|
| 63 | - self.attemptConnection(tries + 1)
|
|
| 64 | 5 | |
| 6 | +class TestNetworkCheck(MarionetteTestCase, TorBrowserMixin):
|
|
| 65 | 7 | def test_network_check(self):
|
| 66 | - self.attemptConnection()
|
|
| 8 | + self.bootstrap()
|
|
| 67 | 9 | self.marionette.navigate(NETWORK_CHECK_URL)
|
| 68 | 10 | self.assertRegex(
|
| 69 | 11 | self.marionette.title,
|