Skip to content

Commit 69f92f0

Browse files
brendandahlkripken
andauthored
[testing] Improve running browser tests locally. (#25013)
This changes how the browser test runner works by default. It will now automatically detect the browser type and: - Use the same options used in CircleCI (for chrome and firefox) - Create and delete a temporary profile directory - Terminate the browser Adds two new options: - `EMTEST_BROWSER_AUTO_CONFIG` or `--browser-auto-config`: disables the automatic configuration, - `EMTEST_HEADLESS` or `--headless`: run the test in headless mode Last, two flags were removed from chrome: - `--disk-cache-dir` this was causing errors (on CI and locally) - `--incognito` not allowed in corporate environments and not needed since the profile directory is automatically deleted. These changes make it much easier to reproduce the circle ci environment locally and make running a test multiple times more reliable. --------- Co-authored-by: Alon Zakai <[email protected]>
1 parent 04bf0e2 commit 69f92f0

File tree

5 files changed

+109
-57
lines changed

5 files changed

+109
-57
lines changed

.circleci/config.yml

Lines changed: 6 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -314,18 +314,9 @@ commands:
314314
name: run tests (<< parameters.title >>)
315315
environment:
316316
EMTEST_DETECT_TEMPFILE_LEAKS: "0"
317-
# --no-sandbox because we are running as root and chrome requires
318-
# this flag for now: https://crbug.com/638180
319-
CHROME_FLAGS_BASE: "--no-first-run -start-maximized --no-sandbox --enable-unsafe-swiftshader --use-gl=swiftshader --user-data-dir=/tmp/chrome-emscripten-profile --enable-experimental-web-platform-features --enable-features=JavaScriptSourcePhaseImports"
320-
# Increase the window size to avoid flaky sdl tests see #24236.
321-
CHROME_FLAGS_HEADLESS: "--headless=new --window-size=1024,768 --remote-debugging-port=1234"
322-
CHROME_FLAGS_WASM: "--enable-experimental-webassembly-features --js-flags=\"--experimental-wasm-stack-switching --experimental-wasm-type-reflection --experimental-wasm-rab-integration\""
323-
CHROME_FLAGS_NOCACHE: "--disk-cache-dir=/dev/null --disk-cache-size=1 --media-cache-size=1 --disable-application-cache --incognito"
324-
# The runners lack sound hardware so fallback to a dummy device (and
325-
# bypass the user gesture so audio tests work without interaction)
326-
CHROME_FLAGS_AUDIO: " --use-fake-device-for-media-stream --autoplay-policy=no-user-gesture-required"
317+
EMTEST_HEADLESS: "1"
318+
EMTEST_BROWSER: "/usr/bin/google-chrome"
327319
command: |
328-
export EMTEST_BROWSER="/usr/bin/google-chrome $CHROME_FLAGS_BASE $CHROME_FLAGS_HEADLESS $CHROME_FLAGS_WASM $CHROME_FLAGS_NOCACHE $CHROME_FLAGS_AUDIO"
329320
# There are tests in the browser test suite that using libraries
330321
# that are not included by "./embuilder build ALL". For example the
331322
# PIC version of libSDL which is used by test_sdl2_misc_main_module
@@ -373,18 +364,6 @@ commands:
373364
apt-get update -y
374365
apt-get install -q -y pulseaudio
375366
pulseaudio --start
376-
- run:
377-
name: configure firefox
378-
command: |
379-
# Note: the autoplay pref allows playback without user interaction
380-
mkdir ~/tmp-firefox-profile/
381-
cat > ~/tmp-firefox-profile/user.js \<<EOF
382-
user_pref("gfx.offscreencanvas.enabled", true);
383-
user_pref("javascript.options.shared_memory", true);
384-
user_pref("javascript.options.wasm_memory64", true);
385-
user_pref("dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled", true);
386-
user_pref("media.autoplay.default", 0);
387-
EOF
388367
- run:
389368
name: run tests (<< parameters.title >>)
390369
environment:
@@ -398,9 +377,10 @@ commands:
398377
# OffscreenCanvas support is not yet done in Firefox.
399378
EMTEST_LACKS_OFFSCREEN_CANVAS: "1"
400379
EMTEST_DETECT_TEMPFILE_LEAKS: "0"
380+
EMTEST_HEADLESS: "1"
401381
DISPLAY: ":0"
402382
command: |
403-
export EMTEST_BROWSER="$HOME/firefox/firefox -headless -profile $HOME/tmp-firefox-profile/"
383+
export EMTEST_BROWSER="$HOME/firefox/firefox"
404384
# There are tests in the browser test suite that using libraries
405385
# that are not included by "./embuilder build ALL". For example the
406386
# PIC version of libSDL which is used by test_sdl2_misc_main_module
@@ -423,14 +403,9 @@ commands:
423403
environment:
424404
EMTEST_LACKS_SOUND_HARDWARE: "1"
425405
EMTEST_DETECT_TEMPFILE_LEAKS: "0"
426-
# --no-sandbox becasue we are running as root and chrome requires
427-
# this flag for now: https://crbug.com/638180
428-
CHROME_FLAGS_BASE: "--no-first-run -start-maximized --no-sandbox --use-gl=swiftshader --user-data-dir=/tmp/chrome-emscripten-profile"
429-
CHROME_FLAGS_HEADLESS: "--headless=new --remote-debugging-port=1234"
430-
CHROME_FLAGS_WASM: "--enable-experimental-webassembly-features"
431-
CHROME_FLAGS_NOCACHE: "--disk-cache-dir=/dev/null --disk-cache-size=1 --media-cache-size=1 --disable-application-cache --incognito"
406+
EMTEST_HEADLESS: "1"
407+
EMTEST_BROWSER: "/usr/bin/google-chrome"
432408
command: |
433-
export EMTEST_BROWSER="/usr/bin/google-chrome $CHROME_FLAGS_BASE $CHROME_FLAGS_HEADLESS $CHROME_FLAGS_WASM $CHROME_FLAGS_NOCACHE"
434409
test/runner sockets
435410
- upload-test-results
436411

test/common.py

Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,21 @@
4545

4646
# User can specify an environment variable EMTEST_BROWSER to force the browser
4747
# test suite to run using another browser command line than the default system
48-
# browser.
49-
# There are two special value that can be used here if running in an actual
48+
# browser. If only the path to the browser executable is given, the tests
49+
# will run in headless mode with a temporary profile with the same options
50+
# used in CI. To use a custom start command specify the executable and command
51+
# line flags.
52+
#
53+
# There are two special values that can be used here if running in an actual
5054
# browser is not desired:
5155
# EMTEST_BROWSER=0 : This will disable the actual running of the test and simply
5256
# verify that it compiles and links.
5357
# EMTEST_BROWSER=node : This will attempt to run the browser test under node.
5458
# For most browser tests this does not work, but it can
5559
# be useful for running pthread tests under node.
5660
EMTEST_BROWSER = None
61+
EMTEST_BROWSER_AUTO_CONFIG = None
62+
EMTEST_HEADLESS = None
5763
EMTEST_DETECT_TEMPFILE_LEAKS = None
5864
EMTEST_SAVE_DIR = None
5965
# generally js engines are equivalent, testing 1 is enough. set this
@@ -76,13 +82,47 @@
7682
if 'EM_BUILD_VERBOSE' in os.environ:
7783
exit_with_error('EM_BUILD_VERBOSE has been renamed to EMTEST_BUILD_VERBOSE')
7884

85+
86+
# Default flags used to run browsers in CI testing:
87+
class ChromeConfig:
88+
data_dir_flag = '--user-data-dir='
89+
default_flags = (
90+
# --no-sandbox because we are running as root and chrome requires
91+
# this flag for now: https://crbug.com/638180
92+
'--no-first-run -start-maximized --no-sandbox --enable-unsafe-swiftshader --use-gl=swiftshader --enable-experimental-web-platform-features --enable-features=JavaScriptSourcePhaseImports',
93+
'--enable-experimental-webassembly-features --js-flags="--experimental-wasm-stack-switching --experimental-wasm-type-reflection --experimental-wasm-rab-integration"',
94+
# The runners lack sound hardware so fallback to a dummy device (and
95+
# bypass the user gesture so audio tests work without interaction)
96+
'--use-fake-device-for-media-stream --autoplay-policy=no-user-gesture-required',
97+
# Cache options.
98+
'--disk-cache-size=1 --media-cache-size=1 --disable-application-cache',
99+
)
100+
headless_flags = '--headless=new --window-size=1024,768 --remote-debugging-port=1234'
101+
102+
@staticmethod
103+
def configure(data_dir):
104+
"""Chrome has no special configuration step."""
105+
106+
107+
class FirefoxConfig:
108+
data_dir_flag = '-profile '
109+
default_flags = ()
110+
headless_flags = '-headless'
111+
112+
@staticmethod
113+
def configure(data_dir):
114+
shutil.copy(test_file('firefox_user.js'), os.path.join(data_dir, 'user.js'))
115+
116+
79117
# Special value for passing to assert_returncode which means we expect that program
80118
# to fail with non-zero return code, but we don't care about specifically which one.
81119
NON_ZERO = -1
82120

83121
TEST_ROOT = path_from_root('test')
84122
LAST_TEST = path_from_root('out/last_test.txt')
85123

124+
DEFAULT_BROWSER_DATA_DIR = path_from_root('out/browser-profile')
125+
86126
WEBIDL_BINDER = shared.bat_suffix(path_from_root('tools/webidl_binder'))
87127

88128
EMBUILDER = shared.bat_suffix(path_from_root('embuilder'))
@@ -114,6 +154,17 @@ def has_browser():
114154
return EMTEST_BROWSER != '0'
115155

116156

157+
CHROMIUM_BASED_BROWSERS = ['chrom', 'edge', 'opera']
158+
159+
160+
def is_chrome():
161+
return EMTEST_BROWSER and any(pattern in EMTEST_BROWSER.lower() for pattern in CHROMIUM_BASED_BROWSERS)
162+
163+
164+
def is_firefox():
165+
return EMTEST_BROWSER and 'firefox' in EMTEST_BROWSER.lower()
166+
167+
117168
def compiler_for(filename, force_c=False):
118169
if shared.suffix(filename) in ('.cc', '.cxx', '.cpp') and not force_c:
119170
return EMXX
@@ -2323,9 +2374,7 @@ def __init__(self, *args, **kwargs):
23232374
super().__init__(*args, **kwargs)
23242375

23252376
@classmethod
2326-
def browser_restart(cls):
2327-
# Kill existing browser
2328-
logger.info('Restarting browser process')
2377+
def browser_terminate(cls):
23292378
cls.browser_proc.terminate()
23302379
# If the browser doesn't shut down gracefully (in response to SIGTERM)
23312380
# after 2 seconds kill it with force (SIGKILL).
@@ -2335,6 +2384,13 @@ def browser_restart(cls):
23352384
logger.info('Browser did not respond to `terminate`. Using `kill`')
23362385
cls.browser_proc.kill()
23372386
cls.browser_proc.wait()
2387+
cls.browser_data_dir = None
2388+
2389+
@classmethod
2390+
def browser_restart(cls):
2391+
# Kill existing browser
2392+
logger.info('Restarting browser process')
2393+
cls.browser_terminate()
23382394
cls.browser_open(cls.HARNESS_URL)
23392395

23402396
@classmethod
@@ -2343,6 +2399,24 @@ def browser_open(cls, url):
23432399
if not EMTEST_BROWSER:
23442400
logger.info('No EMTEST_BROWSER set. Defaulting to `google-chrome`')
23452401
EMTEST_BROWSER = 'google-chrome'
2402+
2403+
if EMTEST_BROWSER_AUTO_CONFIG:
2404+
logger.info('Using default CI configuration.')
2405+
cls.browser_data_dir = DEFAULT_BROWSER_DATA_DIR
2406+
if os.path.exists(cls.browser_data_dir):
2407+
utils.delete_dir(cls.browser_data_dir)
2408+
os.mkdir(cls.browser_data_dir)
2409+
if is_chrome():
2410+
config = ChromeConfig()
2411+
elif is_firefox():
2412+
config = FirefoxConfig()
2413+
else:
2414+
exit_with_error("EMTEST_BROWSER_AUTO_CONFIG only currently works with firefox or chrome.")
2415+
EMTEST_BROWSER += f" {config.data_dir_flag}{cls.browser_data_dir} {' '.join(config.default_flags)}"
2416+
if EMTEST_HEADLESS == 1:
2417+
EMTEST_BROWSER += f" {config.headless_flags}"
2418+
config.configure(cls.browser_data_dir)
2419+
23462420
if WINDOWS:
23472421
# On Windows env. vars canonically use backslashes as directory delimiters, e.g.
23482422
# set EMTEST_BROWSER=C:\Program Files\Mozilla Firefox\firefox.exe
@@ -2373,6 +2447,7 @@ def tearDownClass(cls):
23732447
return
23742448
cls.harness_server.terminate()
23752449
print('[Browser harness server terminated]')
2450+
cls.browser_terminate()
23762451
if WINDOWS:
23772452
# On Windows, shutil.rmtree() in tearDown() raises this exception if we do not wait a bit:
23782453
# WindowsError: [Error 32] The process cannot access the file because it is being used by another process.

test/firefox_user.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
user_pref("gfx.offscreencanvas.enabled", true);
2+
user_pref("javascript.options.shared_memory", true);
3+
user_pref("javascript.options.wasm_memory64", true);
4+
user_pref("dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled", true);
5+
user_pref("media.autoplay.default", 0);

test/runner.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,10 @@ def parse_args():
392392
help='Automatically update test expectations for tests that support it.')
393393
parser.add_argument('--browser',
394394
help='Command to launch web browser in which to run browser tests.')
395+
parser.add_argument('--headless', action='store_true',
396+
help='Run browser tests in headless mode.', default=None)
397+
parser.add_argument('--browser-auto-config', type=bool, default=True,
398+
help='Use the default CI browser configuration.')
395399
parser.add_argument('tests', nargs='*')
396400
parser.add_argument('--failfast', action='store_true')
397401
parser.add_argument('--start-at', metavar='NAME', help='Skip all tests up until <NAME>')
@@ -407,6 +411,8 @@ def parse_args():
407411

408412
def configure():
409413
common.EMTEST_BROWSER = os.getenv('EMTEST_BROWSER')
414+
common.EMTEST_BROWSER_AUTO_CONFIG = os.getenv('EMTEST_BROWSER_AUTO_CONFIG')
415+
common.EMTEST_HEADLESS = int(os.getenv('EMTEST_HEADLESS', '0'))
410416
common.EMTEST_DETECT_TEMPFILE_LEAKS = int(os.getenv('EMTEST_DETECT_TEMPFILE_LEAKS', '0'))
411417
common.EMTEST_ALL_ENGINES = int(os.getenv('EMTEST_ALL_ENGINES', '0'))
412418
common.EMTEST_SKIP_SLOW = int(os.getenv('EMTEST_SKIP_SLOW', '0'))
@@ -451,6 +457,8 @@ def set_env(name, option_value):
451457
os.environ[name] = value
452458

453459
set_env('EMTEST_BROWSER', options.browser)
460+
set_env('EMTEST_BROWSER_AUTO_CONFIG', options.browser_auto_config)
461+
set_env('EMTEST_HEADLESS', options.headless)
454462
set_env('EMTEST_DETECT_TEMPFILE_LEAKS', options.detect_leaks)
455463
if options.save_dir:
456464
common.EMTEST_SAVE_DIR = 1

test/test_browser.py

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from urllib.request import urlopen
2121

2222
import common
23-
from common import BrowserCore, RunnerCore, path_from_root, has_browser, EMTEST_BROWSER, Reporting
23+
from common import BrowserCore, RunnerCore, path_from_root, has_browser, Reporting, is_chrome, is_firefox, CHROMIUM_BASED_BROWSERS
2424
from common import create_file, parameterized, ensure_dir, disabled, test_file, WEBIDL_BINDER
2525
from common import read_file, EMRUN, no_wasm64, no_2gb, no_4gb, copytree
2626
from common import requires_wasm2js, parameterize, find_browser_test_file, with_all_sjlj
@@ -130,23 +130,12 @@ def shell_with_script(shell_file, output_file, replacement):
130130
create_file(output_file, shell.replace('{{{ SCRIPT }}}', replacement))
131131

132132

133-
CHROMIUM_BASED_BROWSERS = ['chrom', 'edge', 'opera']
134-
135-
136-
def is_chrome():
137-
return EMTEST_BROWSER and any(pattern in EMTEST_BROWSER.lower() for pattern in CHROMIUM_BASED_BROWSERS)
138-
139-
140133
def no_chrome(note='chrome is not supported'):
141134
if is_chrome():
142135
return unittest.skip(note)
143136
return lambda f: f
144137

145138

146-
def is_firefox():
147-
return EMTEST_BROWSER and 'firefox' in EMTEST_BROWSER.lower()
148-
149-
150139
def no_firefox(note='firefox is not supported'):
151140
if is_firefox():
152141
return unittest.skip(note)
@@ -162,7 +151,7 @@ def no_swiftshader(f):
162151

163152
@wraps(f)
164153
def decorated(self, *args, **kwargs):
165-
if is_chrome() and '--use-gl=swiftshader' in EMTEST_BROWSER:
154+
if is_chrome() and '--use-gl=swiftshader' in common.EMTEST_BROWSER:
166155
self.skipTest('not compatible with swiftshader')
167156
return f(self, *args, **kwargs)
168157

@@ -210,7 +199,7 @@ class browser(BrowserCore):
210199
def setUpClass(cls):
211200
super().setUpClass()
212201
cls.browser_timeout = 60
213-
if EMTEST_BROWSER != 'node':
202+
if common.EMTEST_BROWSER != 'node':
214203
print()
215204
print('Running the browser tests. Make sure the browser allows popups from localhost.')
216205
print()
@@ -220,7 +209,7 @@ def proxy_to_worker(self):
220209

221210
def require_jspi(self):
222211
if not is_chrome():
223-
self.skipTest(f'Current browser ({EMTEST_BROWSER}) does not support JSPI. Only chromium-based browsers ({CHROMIUM_BASED_BROWSERS}) support JSPI today.')
212+
self.skipTest(f'Current browser ({common.EMTEST_BROWSER}) does not support JSPI. Only chromium-based browsers ({CHROMIUM_BASED_BROWSERS}) support JSPI today.')
224213
super().require_jspi()
225214

226215
def post_manual_reftest(self):
@@ -3305,7 +3294,7 @@ def test_cocos2d_hello(self):
33053294
})
33063295
def test_async(self, args):
33073296
if is_jspi(args) and not is_chrome():
3308-
self.skipTest(f'Current browser ({EMTEST_BROWSER}) does not support JSPI. Only chromium-based browsers ({CHROMIUM_BASED_BROWSERS}) support JSPI today.')
3297+
self.skipTest(f'Current browser ({common.EMTEST_BROWSER}) does not support JSPI. Only chromium-based browsers ({CHROMIUM_BASED_BROWSERS}) support JSPI today.')
33093298

33103299
for opts in (0, 1, 2, 3):
33113300
print(opts)
@@ -4977,7 +4966,7 @@ def test_embind_with_pthreads(self):
49774966
})
49784967
def test_embind(self, args):
49794968
if is_jspi(args) and not is_chrome():
4980-
self.skipTest(f'Current browser ({EMTEST_BROWSER}) does not support JSPI. Only chromium-based browsers ({CHROMIUM_BASED_BROWSERS}) support JSPI today.')
4969+
self.skipTest(f'Current browser ({common.EMTEST_BROWSER}) does not support JSPI. Only chromium-based browsers ({CHROMIUM_BASED_BROWSERS}) support JSPI today.')
49814970
if is_jspi(args) and self.is_wasm64():
49824971
self.skipTest('_emval_await fails')
49834972

@@ -5597,9 +5586,9 @@ def test_no_browser(self):
55975586
self.run_process([EMCC, test_file('test_emrun.c'), '--emrun', '-o', 'hello_world.html'])
55985587
proc = subprocess.Popen([EMRUN, '--no-browser', '.', '--port=3333'], stdout=PIPE)
55995588
try:
5600-
if EMTEST_BROWSER:
5589+
if common.EMTEST_BROWSER:
56015590
print('Starting browser')
5602-
browser_cmd = shlex.split(EMTEST_BROWSER)
5591+
browser_cmd = shlex.split(common.EMTEST_BROWSER)
56035592
browser = subprocess.Popen(browser_cmd + ['http://localhost:3333/hello_world.html'])
56045593
try:
56055594
while True:
@@ -5638,11 +5627,11 @@ def test_emrun(self):
56385627
'--log-stdout', self.in_dir('stdout.txt'),
56395628
'--log-stderr', self.in_dir('stderr.txt')]
56405629

5641-
if EMTEST_BROWSER is not None:
5630+
if common.EMTEST_BROWSER is not None:
56425631
# If EMTEST_BROWSER carried command line arguments to pass to the browser,
56435632
# (e.g. "firefox -profile /path/to/foo") those can't be passed via emrun,
56445633
# so strip them out.
5645-
browser_cmd = shlex.split(EMTEST_BROWSER)
5634+
browser_cmd = shlex.split(common.EMTEST_BROWSER)
56465635
browser_path = browser_cmd[0]
56475636
args_base += ['--browser', browser_path]
56485637
if len(browser_cmd) > 1:

0 commit comments

Comments
 (0)