Skip to content

Commit 93ce932

Browse files
authored
Add test runner script for cobalt_browsertests on Linux starboard (#8582)
Test runner script to manage the test process life cycle. Starboard cannot launch a new process as cobalt runs in single process mode. In the browser test, each test case must own its process, so we have to run one test case each time. The python test wrapper helps manage the test processes. ``` python3 cobalt/testing/browser_tests/run_browser_tests.py out/linux-x64x11_devel/cobalt_browsertests "ContentMainRunnerImplBrowserTest*" ... [ OK ] ContentMainRunnerImplBrowserTest.StartupSequence (122 ms) [----------] 1 test from ContentMainRunnerImplBrowserTest (122 ms total) [----------] Global test environment tear-down [==========] 1 test from 1 test suite ran. (122 ms total) [ PASSED ] 1 test. ======================================== Total: 1, Passed: 1, Failed: 0 All tests PASSED. ``` Issue: 433354983
1 parent bd28605 commit 93ce932

1 file changed

Lines changed: 144 additions & 0 deletions

File tree

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
#!/usr/bin/env python3
2+
"""Helper script to run Cobalt browser tests.
3+
4+
Cobalt is designed to run in single process mode on TV devices. Its
5+
browsertests is following it with --single-process-test as well.
6+
To avoid regressions crashed the process, the test process management
7+
is left in this python script, which list each single test case
8+
and iterate them.
9+
"""
10+
import subprocess
11+
import sys
12+
import os
13+
14+
15+
def main():
16+
if len(sys.argv) < 2:
17+
print("Usage: python3 run_browser_tests.py <path_to_executable> "
18+
"[gtest_filter]")
19+
sys.exit(1)
20+
21+
binary_path = sys.argv[1]
22+
extra_filter = sys.argv[2] if len(sys.argv) > 2 else "*"
23+
24+
if not os.path.isfile(binary_path):
25+
print(f"Error: Binary not found at {binary_path}", file=sys.stderr)
26+
sys.exit(1)
27+
28+
# 1. List all tests
29+
print(f"Listing tests from {binary_path}...")
30+
try:
31+
output = subprocess.check_output(
32+
[binary_path, "--gtest_list_tests", f"--gtest_filter={extra_filter}"],
33+
text=True)
34+
except subprocess.CalledProcessError as e:
35+
print("Failed to list tests.", file=sys.stderr)
36+
sys.exit(e.returncode)
37+
38+
tests = []
39+
current_suite = None
40+
for line in output.splitlines():
41+
# Clean up the line
42+
line = line.strip()
43+
44+
# Skip empty lines or logs (logs usually start with [pid:...)
45+
if not line or line.startswith("["):
46+
continue
47+
48+
# Remove comments (starting with #)
49+
line = line.split("#")[0].strip()
50+
51+
if not line:
52+
continue
53+
54+
if line.endswith("."):
55+
current_suite = line
56+
else:
57+
# It's a test name
58+
if current_suite:
59+
full_test_name = f"{current_suite}{line}"
60+
if "DISABLED_" not in full_test_name:
61+
tests.append(full_test_name)
62+
else:
63+
print(
64+
f"Warning: Found test '{line}' without a suite. Skipping.",
65+
file=sys.stderr)
66+
67+
if not tests:
68+
print("No tests found matching the filter.")
69+
sys.exit(0)
70+
71+
# Sort tests to ensure PRE_ tests run before their main tests.
72+
# Logic: PRE_PRE_Test -> PRE_Test -> Test
73+
def get_sort_key(test_name):
74+
# test_name is Suite.Test
75+
parts = test_name.split(".", 1)
76+
if len(parts) != 2:
77+
return (test_name, 0)
78+
suite, case = parts
79+
80+
pre_count = 0
81+
while case.startswith("PRE_"):
82+
case = case[4:]
83+
pre_count += 1
84+
85+
# Sort by base name (Suite.TestBase), then by pre_count DESCENDING
86+
return (f"{suite}.{case}", -pre_count)
87+
88+
tests.sort(key=get_sort_key)
89+
90+
print(f"Found {len(tests)} tests. Running them one by one...\n")
91+
92+
# 2. Run each test in a fresh process
93+
failed_tests = []
94+
passed_count = 0
95+
96+
for i, test in enumerate(tests):
97+
print(f"[{i+1}/{len(tests)}] Running {test}...")
98+
try:
99+
# We pass the filter to run EXACTLY this test case.
100+
# Using --gtest_filter=ExactTestName
101+
# Add standard Cobalt flags:
102+
# --no-sandbox --single-process --no-zygote --ozone-platform=starboard
103+
cmd = [
104+
binary_path,
105+
f"--gtest_filter={test}",
106+
"--single-process-tests",
107+
"--no-sandbox",
108+
"--single-process",
109+
"--no-zygote",
110+
"--ozone-platform=starboard",
111+
]
112+
113+
# Run the test and let it print to stdout/stderr
114+
retcode = subprocess.run(cmd, check=False).returncode
115+
116+
if retcode != 0:
117+
print(f"FAILED: {test} (Exit code: {retcode})")
118+
failed_tests.append(test)
119+
else:
120+
passed_count += 1
121+
122+
except KeyboardInterrupt:
123+
print("\nAborted by user.")
124+
sys.exit(130)
125+
except Exception as e: # pylint: disable=broad-exception-caught
126+
print(f"Error running {test}: {e}")
127+
failed_tests.append(test)
128+
129+
print("\n" + "=" * 40)
130+
print(f"Total: {len(tests)}, Passed: {passed_count}, "
131+
f"Failed: {len(failed_tests)}")
132+
133+
if failed_tests:
134+
print("\nFailed Tests:")
135+
for t in failed_tests:
136+
print(f" {t}")
137+
sys.exit(1)
138+
else:
139+
print("\nAll tests PASSED.")
140+
sys.exit(0)
141+
142+
143+
if __name__ == "__main__":
144+
main()

0 commit comments

Comments
 (0)