Commits:
-
863fc44a
by Beatriz Rizental at 2026-03-24T14:25:12-03:00
BB 43243 [android]: Keep processing pending inits after failure
This is in the process of being uplifted.
Bug 2021618: bugzilla.mozilla.org/show_bug.cgi?id=2021618
Differential Revision: https://phabricator.services.mozilla.com/D286669
-
33b1e93e
by Beatriz Rizental at 2026-03-24T14:25:12-03:00
BB 43243 [android]: Start GeckoEngineSession early when Marionette enabled
This is in the process of being uplifted.
Bug 2021884: https://bugzilla.mozilla.org/show_bug.cgi?id=2021884
Differential Revision: https://phabricator.services.mozilla.com/D286676
-
24ce6582
by Beatriz Rizental at 2026-03-24T14:25:13-03:00
BB 43243: Modify mozharness scripts for Base Browser
7 changed files:
Changes:
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/FenixApplication.kt
| ... |
... |
@@ -507,6 +507,12 @@ open class FenixApplication : Application(), Provider, ThemeProvider { |
|
507
|
507
|
logElapsedTime(logger, "Starting Relay feature integration") {
|
|
508
|
508
|
components.relayFeatureIntegration.start()
|
|
509
|
509
|
}
|
|
|
510
|
+
|
|
|
511
|
+ // If running Marionette tests a GeckoEngineSession needs to be
|
|
|
512
|
+ // started and that must happen on the main thread.
|
|
|
513
|
+ logElapsedTime(logger, "Maybe setup Marionette") {
|
|
|
514
|
+ maybeSetupMarionette()
|
|
|
515
|
+ }
|
|
510
|
516
|
}
|
|
511
|
517
|
}
|
|
512
|
518
|
|
| ... |
... |
@@ -675,6 +681,13 @@ open class FenixApplication : Application(), Provider, ThemeProvider { |
|
675
|
681
|
FxNimbus.initialize { nimbus }
|
|
676
|
682
|
}
|
|
677
|
683
|
|
|
|
684
|
+ private fun maybeSetupMarionette() {
|
|
|
685
|
+ // If Marionette is enabled, start a GeckoEngineSession immediatelly.
|
|
|
686
|
+ if (System.getenv("MOZ_MARIONETTE") == "1") {
|
|
|
687
|
+ components.core.engine.speculativeCreateSession(components.appStore.state.mode.isPrivate)
|
|
|
688
|
+ }
|
|
|
689
|
+ }
|
|
|
690
|
+
|
|
678
|
691
|
/**
|
|
679
|
692
|
* Initiate Megazord sequence! Megazord Battle Mode!
|
|
680
|
693
|
*
|
mobile/shared/modules/geckoview/DelayedInit.sys.mjs
| ... |
... |
@@ -96,7 +96,11 @@ var Impl = { |
|
96
|
96
|
return false;
|
|
97
|
97
|
}
|
|
98
|
98
|
this.complete = true;
|
|
99
|
|
- this.fn.call();
|
|
|
99
|
+ try {
|
|
|
100
|
+ this.fn.call();
|
|
|
101
|
+ } catch (e) {
|
|
|
102
|
+ console.error("Error running init", e);
|
|
|
103
|
+ }
|
|
100
|
104
|
this.fn = null;
|
|
101
|
105
|
return true;
|
|
102
|
106
|
},
|
mobile/shared/modules/geckoview/test/xpcshell/test_DelayedInit.js
|
|
1
|
+/* Any copyright is dedicated to the Public Domain.
|
|
|
2
|
+http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
3
|
+"use strict";
|
|
|
4
|
+
|
|
|
5
|
+const { DelayedInit } = ChromeUtils.importESModule(
|
|
|
6
|
+ "resource://gre/modules/DelayedInit.sys.mjs"
|
|
|
7
|
+);
|
|
|
8
|
+
|
|
|
9
|
+add_task(async function test_delayed_init_continues_queue_on_failure() {
|
|
|
10
|
+ const results = [];
|
|
|
11
|
+ const waitMs = 0;
|
|
|
12
|
+
|
|
|
13
|
+ DelayedInit.schedule(
|
|
|
14
|
+ () => {
|
|
|
15
|
+ results.push("first");
|
|
|
16
|
+ },
|
|
|
17
|
+ null,
|
|
|
18
|
+ null,
|
|
|
19
|
+ waitMs
|
|
|
20
|
+ );
|
|
|
21
|
+
|
|
|
22
|
+ DelayedInit.schedule(
|
|
|
23
|
+ () => {
|
|
|
24
|
+ results.push("second");
|
|
|
25
|
+ throw new Error("Deliberate error for testing");
|
|
|
26
|
+ },
|
|
|
27
|
+ null,
|
|
|
28
|
+ null,
|
|
|
29
|
+ waitMs
|
|
|
30
|
+ );
|
|
|
31
|
+
|
|
|
32
|
+ DelayedInit.schedule(
|
|
|
33
|
+ () => {
|
|
|
34
|
+ results.push("third");
|
|
|
35
|
+ },
|
|
|
36
|
+ null,
|
|
|
37
|
+ null,
|
|
|
38
|
+ waitMs
|
|
|
39
|
+ );
|
|
|
40
|
+
|
|
|
41
|
+ await new Promise(resolve => ChromeUtils.idleDispatch(resolve));
|
|
|
42
|
+
|
|
|
43
|
+ Assert.deepEqual(
|
|
|
44
|
+ results,
|
|
|
45
|
+ ["first", "second", "third"],
|
|
|
46
|
+ "Queue processes all inits even when one fails"
|
|
|
47
|
+ );
|
|
|
48
|
+}); |
mobile/shared/modules/geckoview/test/xpcshell/xpcshell.toml
| ... |
... |
@@ -7,6 +7,8 @@ prefs = "browser.crashReports._onDemand_=true" |
|
7
|
7
|
|
|
8
|
8
|
["test_ChildCrashHandler.js"]
|
|
9
|
9
|
|
|
|
10
|
+["test_DelayedInit.js"]
|
|
|
11
|
+
|
|
10
|
12
|
["test_GeckoViewAppConstants.js"]
|
|
11
|
13
|
|
|
12
|
14
|
["test_RemoteSettingsCrashPull.js"] |
testing/mozharness/configs/android/android14-x86_64.py
| ... |
... |
@@ -11,8 +11,9 @@ config = { |
|
11
|
11
|
"emulator_avd_name": "mozemulator-android34-x86_64",
|
|
12
|
12
|
"emulator_process_name": "qemu-system-x86_64",
|
|
13
|
13
|
"emulator_extra_args": [
|
|
14
|
|
- "-gpu",
|
|
15
|
|
- "on",
|
|
|
14
|
+ "-no-window",
|
|
|
15
|
+ "-no-audio",
|
|
|
16
|
+ "-no-boot-anim",
|
|
16
|
17
|
"-skip-adb-auth",
|
|
17
|
18
|
"-verbose",
|
|
18
|
19
|
"-show-kernel",
|
testing/mozharness/configs/android/android_common.py
| ... |
... |
@@ -315,6 +315,19 @@ config = { |
|
315
|
315
|
"--deviceSerial=%(device_serial)s",
|
|
316
|
316
|
],
|
|
317
|
317
|
},
|
|
|
318
|
+ "marionette": {
|
|
|
319
|
+ "run_filename": "runtests.py",
|
|
|
320
|
+ "testsdir": "marionette/harness/marionette_harness",
|
|
|
321
|
+ "install": True,
|
|
|
322
|
+ "options": [
|
|
|
323
|
+ "-vv",
|
|
|
324
|
+ "--address=127.0.0.1:2828",
|
|
|
325
|
+ "--app=fennec",
|
|
|
326
|
+ ],
|
|
|
327
|
+ "tests": [
|
|
|
328
|
+ "%(abs_marionette_manifest_dir)s/unit-tests.toml",
|
|
|
329
|
+ ],
|
|
|
330
|
+ },
|
|
318
|
331
|
}, # end suite_definitions
|
|
319
|
332
|
"structured_suites": [
|
|
320
|
333
|
"mochitest-media",
|
testing/mozharness/scripts/android_emulator_unittest.py
| ... |
... |
@@ -7,15 +7,18 @@ import copy |
|
7
|
7
|
import datetime
|
|
8
|
8
|
import json
|
|
9
|
9
|
import os
|
|
|
10
|
+import socket
|
|
10
|
11
|
import subprocess
|
|
11
|
12
|
import sys
|
|
|
13
|
+import tempfile
|
|
|
14
|
+import time
|
|
12
|
15
|
|
|
13
|
16
|
# load modules from parent dir
|
|
14
|
17
|
here = os.path.abspath(os.path.dirname(__file__))
|
|
15
|
18
|
sys.path.insert(1, os.path.dirname(here))
|
|
16
|
19
|
|
|
17
|
20
|
from mozharness.base.log import WARNING
|
|
18
|
|
-from mozharness.base.script import BaseScript, PreScriptAction
|
|
|
21
|
+from mozharness.base.script import BaseScript, PostScriptAction, PreScriptAction
|
|
19
|
22
|
from mozharness.mozilla.automation import TBPL_RETRY
|
|
20
|
23
|
from mozharness.mozilla.mozbase import MozbaseMixin
|
|
21
|
24
|
from mozharness.mozilla.testing.android import AndroidMixin
|
| ... |
... |
@@ -26,7 +29,7 @@ from mozharness.mozilla.testing.codecoverage import ( |
|
26
|
29
|
from mozharness.mozilla.testing.testbase import TestingMixin, testing_config_options
|
|
27
|
30
|
|
|
28
|
31
|
SUITE_DEFAULT_E10S = ["geckoview-junit", "mochitest", "reftest"]
|
|
29
|
|
-SUITE_NO_E10S = ["cppunittest", "gtest", "jittest", "xpcshell"]
|
|
|
32
|
+SUITE_NO_E10S = ["cppunittest", "gtest", "jittest", "xpcshell", "marionette"]
|
|
30
|
33
|
SUITE_REPEATABLE = ["mochitest", "reftest", "xpcshell"]
|
|
31
|
34
|
|
|
32
|
35
|
|
| ... |
... |
@@ -178,6 +181,15 @@ class AndroidEmulatorTest( |
|
178
|
181
|
"times in which case the test must contain at least one of the given tags.",
|
|
179
|
182
|
},
|
|
180
|
183
|
],
|
|
|
184
|
+ [
|
|
|
185
|
+ ["--package-name"],
|
|
|
186
|
+ {
|
|
|
187
|
+ "action": "store",
|
|
|
188
|
+ "default": None,
|
|
|
189
|
+ "dest": "package_name",
|
|
|
190
|
+ "help": "The Android package name for the app being installed.",
|
|
|
191
|
+ },
|
|
|
192
|
+ ],
|
|
181
|
193
|
]
|
|
182
|
194
|
+ copy.deepcopy(testing_config_options)
|
|
183
|
195
|
+ copy.deepcopy(code_coverage_config_options)
|
| ... |
... |
@@ -228,6 +240,7 @@ class AndroidEmulatorTest( |
|
228
|
240
|
self.enable_isolated_zygote_process = c.get("enable_isolated_zygote_process")
|
|
229
|
241
|
self.extra_prefs = c.get("extra_prefs")
|
|
230
|
242
|
self.test_tags = c.get("test_tags")
|
|
|
243
|
+ self.package_name = c.get("package_name") or self.query_package_name()
|
|
231
|
244
|
|
|
232
|
245
|
def query_abs_dirs(self):
|
|
233
|
246
|
if self.abs_dirs:
|
| ... |
... |
@@ -329,6 +342,16 @@ class AndroidEmulatorTest( |
|
329
|
342
|
"error_summary_file": error_summary_file,
|
|
330
|
343
|
"xpcshell_extra": c.get("xpcshell_extra", ""),
|
|
331
|
344
|
"gtest_dir": os.path.join(dirs["abs_test_install_dir"], "gtest"),
|
|
|
345
|
+ "abs_marionette_manifest_dir": os.path.join(
|
|
|
346
|
+ dirs["abs_test_install_dir"],
|
|
|
347
|
+ "marionette",
|
|
|
348
|
+ "tests",
|
|
|
349
|
+ "testing",
|
|
|
350
|
+ "marionette",
|
|
|
351
|
+ "harness",
|
|
|
352
|
+ "marionette_harness",
|
|
|
353
|
+ "tests",
|
|
|
354
|
+ ),
|
|
332
|
355
|
}
|
|
333
|
356
|
|
|
334
|
357
|
user_paths = self._get_mozharness_test_paths(self.test_suite)
|
| ... |
... |
@@ -345,7 +368,7 @@ class AndroidEmulatorTest( |
|
345
|
368
|
|
|
346
|
369
|
if "%(app)" in option:
|
|
347
|
370
|
# only query package name if requested
|
|
348
|
|
- cmd.extend([option % {"app": self.query_package_name()}])
|
|
|
371
|
+ cmd.extend([option % {"app": self.package_name}])
|
|
349
|
372
|
else:
|
|
350
|
373
|
option = option % str_format_values
|
|
351
|
374
|
if option:
|
| ... |
... |
@@ -409,6 +432,7 @@ class AndroidEmulatorTest( |
|
409
|
432
|
self.config["suite_definitions"][self.test_suite].get("tests"),
|
|
410
|
433
|
None,
|
|
411
|
434
|
try_tests,
|
|
|
435
|
+ str_format_values=str_format_values,
|
|
412
|
436
|
)
|
|
413
|
437
|
)
|
|
414
|
438
|
|
| ... |
... |
@@ -449,6 +473,7 @@ class AndroidEmulatorTest( |
|
449
|
473
|
},
|
|
450
|
474
|
),
|
|
451
|
475
|
("xpcshell", {"xpcshell": "xpcshell"}),
|
|
|
476
|
+ ("marionette", {"marionette": "marionette"}),
|
|
452
|
477
|
]
|
|
453
|
478
|
suites = []
|
|
454
|
479
|
for category, all_suites in all:
|
| ... |
... |
@@ -473,6 +498,61 @@ class AndroidEmulatorTest( |
|
473
|
498
|
# in the base class, this checks for mozinstall, but we don't use it
|
|
474
|
499
|
pass
|
|
475
|
500
|
|
|
|
501
|
+ def _configure_marionette_virtualenv(self, action):
|
|
|
502
|
+ dirs = self.query_abs_dirs()
|
|
|
503
|
+ requirements = os.path.join(
|
|
|
504
|
+ dirs["abs_test_install_dir"], "config", "marionette_requirements.txt"
|
|
|
505
|
+ )
|
|
|
506
|
+ if not os.path.isfile(requirements):
|
|
|
507
|
+ self.fatal(f"Could not find marionette requirements file: {requirements}")
|
|
|
508
|
+
|
|
|
509
|
+ self.register_virtualenv_module(requirements=[requirements])
|
|
|
510
|
+
|
|
|
511
|
+ def _marionette_setup(self):
|
|
|
512
|
+ adb = self.query_exe("adb")
|
|
|
513
|
+
|
|
|
514
|
+ self.run_command([adb, "forward", "tcp:2828", "tcp:2828"])
|
|
|
515
|
+
|
|
|
516
|
+ with tempfile.NamedTemporaryFile(suffix=".yaml") as tmp_file:
|
|
|
517
|
+ tmp_file.write(
|
|
|
518
|
+ b"""args:
|
|
|
519
|
+- --marionette
|
|
|
520
|
+- --remote-allow-system-access
|
|
|
521
|
+"""
|
|
|
522
|
+ )
|
|
|
523
|
+ tmp_file.flush()
|
|
|
524
|
+
|
|
|
525
|
+ remote_path = f"/data/local/tmp/{self.package_name}-geckoview-config.yaml"
|
|
|
526
|
+ self.run_command([adb, "push", tmp_file.name, remote_path])
|
|
|
527
|
+
|
|
|
528
|
+ self.run_command([
|
|
|
529
|
+ adb,
|
|
|
530
|
+ "shell",
|
|
|
531
|
+ "am",
|
|
|
532
|
+ "start",
|
|
|
533
|
+ "-S",
|
|
|
534
|
+ "-W",
|
|
|
535
|
+ "-n",
|
|
|
536
|
+ f"{self.package_name}/org.mozilla.gecko.BrowserApp",
|
|
|
537
|
+ ])
|
|
|
538
|
+
|
|
|
539
|
+ # Wait for Marionette to be ready
|
|
|
540
|
+ for attempt in range(5):
|
|
|
541
|
+ try:
|
|
|
542
|
+ self.info(
|
|
|
543
|
+ f"Checking Marionette on 127.0.0.1:2828 (attempt {attempt + 1}/5)"
|
|
|
544
|
+ )
|
|
|
545
|
+ socket.create_connection(("127.0.0.1", 2828), 10).close()
|
|
|
546
|
+ self.info("Marionette is reachable")
|
|
|
547
|
+ break
|
|
|
548
|
+ except OSError:
|
|
|
549
|
+ if attempt == 4:
|
|
|
550
|
+ self.fatal(
|
|
|
551
|
+ "Timed out waiting for 127.0.0.1:2828 to become reachable"
|
|
|
552
|
+ )
|
|
|
553
|
+ self.info("Marionette not reachable yet, retrying in 10s")
|
|
|
554
|
+ time.sleep(10)
|
|
|
555
|
+
|
|
476
|
556
|
@PreScriptAction("create-virtualenv")
|
|
477
|
557
|
def pre_create_virtualenv(self, action):
|
|
478
|
558
|
dirs = self.query_abs_dirs()
|
| ... |
... |
@@ -488,6 +568,9 @@ class AndroidEmulatorTest( |
|
488
|
568
|
if requirements:
|
|
489
|
569
|
self.register_virtualenv_module(requirements=[requirements])
|
|
490
|
570
|
|
|
|
571
|
+ if ("marionette", "marionette") in suites:
|
|
|
572
|
+ self._configure_marionette_virtualenv(action)
|
|
|
573
|
+
|
|
491
|
574
|
def download_and_extract(self):
|
|
492
|
575
|
"""
|
|
493
|
576
|
Download and extract product APK, tests.zip, and host utils.
|
| ... |
... |
@@ -525,6 +608,9 @@ class AndroidEmulatorTest( |
|
525
|
608
|
for per_test_suite, suite in suites:
|
|
526
|
609
|
self.test_suite = suite
|
|
527
|
610
|
|
|
|
611
|
+ if self.test_suite == "marionette":
|
|
|
612
|
+ self._marionette_setup()
|
|
|
613
|
+
|
|
528
|
614
|
try:
|
|
529
|
615
|
cwd = self._query_tests_dir(self.test_suite)
|
|
530
|
616
|
except Exception:
|
| ... |
... |
@@ -594,6 +680,19 @@ class AndroidEmulatorTest( |
|
594
|
680
|
% (suite_category, suite, tbpl_status),
|
|
595
|
681
|
)
|
|
596
|
682
|
|
|
|
683
|
+ @PostScriptAction("run-tests")
|
|
|
684
|
+ def marionette_teardown(self, *args, **kwargs):
|
|
|
685
|
+ if ("marionette", "marionette") in self._query_suites():
|
|
|
686
|
+ adb = self.query_exe("adb")
|
|
|
687
|
+ self.run_command([adb, "shell", "am", "force-stop", self.package_name])
|
|
|
688
|
+ self.run_command([adb, "uninstall", self.package_name])
|
|
|
689
|
+ self.run_command([
|
|
|
690
|
+ adb,
|
|
|
691
|
+ "shell",
|
|
|
692
|
+ "rm",
|
|
|
693
|
+ f"/data/local/tmp/{self.package_name}-geckoview-config.yaml",
|
|
|
694
|
+ ])
|
|
|
695
|
+
|
|
597
|
696
|
|
|
598
|
697
|
if __name__ == "__main__":
|
|
599
|
698
|
test = AndroidEmulatorTest()
|
|