Skip to content

Fix running emrun tests on Linux against the Linux system browser#26810

Merged
juj merged 14 commits into
emscripten-core:mainfrom
juj:fix_emrun_on_linux
Apr 28, 2026
Merged

Fix running emrun tests on Linux against the Linux system browser#26810
juj merged 14 commits into
emscripten-core:mainfrom
juj:fix_emrun_on_linux

Conversation

@juj

@juj juj commented Apr 28, 2026

Copy link
Copy Markdown
Collaborator

Fix running emrun tests on Linux against the Linux system browser. This has a few fixes in one:

  1. Fix emrun to work around Firefox https://bugzil.la/1996614 by moving -url to last on cmdline.
  2. Move emrun suite to its own file test_emrun, so that it can be migrated to run single-threaded. This is because emrun may tear down the test browser in a non-parallel-friendly manner:

    emscripten/emrun.py

    Lines 423 to 443 in 92ef17c

    def detect_browser_processes():
    if not browser_exe:
    return # Running with --no-browser, we are not binding to a spawned browser.
    global current_browser_processes
    logv('First navigation occurred. Identifying currently running browser processes')
    running_browser_processes = list_processes_by_name(browser_exe)
    def pid_existed(pid):
    for proc in previous_browser_processes:
    if proc['pid'] == pid:
    return True
    return False
    for p in running_browser_processes:
    logv(f'Detected running browser process id: {p["pid"]}, existed already at emrun startup? {pid_existed(p["pid"])}')
    current_browser_processes = [p for p in running_browser_processes if not pid_existed(p['pid'])]
    if len(current_browser_processes) == 0:
    logv('Was unable to detect the browser process that was spawned by emrun. This may occur if the target page was opened in a tab on a browser process that already existed before emrun started up.')
  3. Adjust the platform.ini lookup of system Firefox on Linux to find it in /usr/lib/, or fall back to assuming oldest supported browser if not found.

Comment thread emrun.py Outdated
if 'firefox' in browser_exe:
browser += ['-url', url]
else:
browser += [url]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the old code the url was only added in the non-android case.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, good catch..

Comment thread test/browser_common.py Outdated
if WINDOWS:
exe_file += '.exe'
if os.path.isfile(exe_file):
return exe_file

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you just use shutil.which here?

If not, can you move this utility out the top level?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I wasn't aware of that - that's much better.

Comment thread test/test_emrun.py

from tools.shared import EMCC, PIPE


Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The emrun class below is moved without changes.

Comment thread test/browser_common.py Outdated

if not EMTEST_BROWSER:
EMTEST_BROWSER = 'google-chrome'
EMTEST_BROWSER = shutil.which('google-chrome')

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed? IIRC this works as expected on my desktop without this change.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, because if I don't do that search, I don't know if I should search for firefox. I imagine we do want to favor google-chrome as the first default, if both are installed on the system?

@sbc100 sbc100 Apr 28, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think just having single default is fine. We have --browser=xxxx in addition to EMTEST_BROWSER=xxx that folks can use to specifiy non-default values.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be convenient to have this automatically look up like this. This way I can run e.g. test/runner emrun on Raspberry Pi, finding Chrome as default, and the same command on Debian Linux, finding firefox as default.

This makes it simpler for me to not need to encode on the CI runner master script side the knowledge of "what browser do you have". I'd imagine this can help other users as well, who have a certain browser on their system. (and not have a FTUE that errors)

@juj

juj commented Apr 28, 2026

Copy link
Copy Markdown
Collaborator Author

This is now ready to land, though there is an odd error

======================================================================
FAIL [0.001s]: test_fp16 (test_other.other)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/root/project/test/common.py", line 381, in resulting_test
    return func(self, *args)
  File "/root/project/test/decorators.py", line 183, in decorated
    return func(self, *args, **kwargs)
  File "/root/project/test/test_other.py", line 14948, in test_fp16
    self.do_runf('test_fp16.c', cflags=['-msimd128', '-mfp16', '-mrelaxed-simd', '-sENVIRONMENT=shell'] + args)
  File "/root/project/test/common.py", line 1393, in do_runf
    return self._build_and_run(filename, expected_output, **kwargs)
  File "/root/project/test/common.py", line 1441, in _build_and_run
    js_output = self.run_js(js_file, engine, args,
  File "/root/project/test/common.py", line 1025, in run_js
    self.fail('JS subprocess failed (%s): %s (expected=%s).  Output:\n%s' % (error.cmd, error.returncode, assert_returncode, ret))
AssertionError: JS subprocess failed (/root/.jsvu/bin/v8 --wasm-staging --experimental-wasm-fp16 /tmp/emtest_cmsh_y2k/emscripten_test_other__ga4wtwi/test_fp16.js --): 1 (expected=0).  Output:
V8 is running with experimental features enabled. Stability and security will suffer.
console.error: failed to asynchronously prepare wasm: CompileError: WebAssembly.instantiate(): Compiling function #12 failed: invalid simd opcode @+8623
console.error: Aborted(CompileError: WebAssembly.instantiate(): Compiling function #12 failed: invalid simd opcode @+8623)
/tmp/emtest_cmsh_y2k/emscripten_test_other__ga4wtwi/test_fp16.js:520: RuntimeError: Aborted(CompileError: WebAssembly.instantiate(): Compiling function #12 failed: invalid simd opcode @+8623)
  var e = new WebAssembly.RuntimeError(what);
          ^
RuntimeError: Aborted(CompileError: WebAssembly.instantiate(): Compiling function #12 failed: invalid simd opcode @+8623)
    at abort (/tmp/emtest_cmsh_y2k/emscripten_test_other__ga4wtwi/test_fp16.js:520:11)
    at instantiateArrayBuffer (/tmp/emtest_cmsh_y2k/emscripten_test_other__ga4wtwi/test_fp16.js:602:5)
    at async createWasm (/tmp/emtest_cmsh_y2k/emscripten_test_other__ga4wtwi/test_fp16.js:691:16)

1 pending unhandled Promise rejection(s) detected.

from a fp16 test, that should not be related to this PR. I wonder how that failed.

Comment thread test/browser_common.py Outdated
if not EMTEST_BROWSER:
EMTEST_BROWSER = shutil.which('firefox')
if not EMTEST_BROWSER:
EMTEST_BROWSER = 'default-browser-not-found'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about making this into a hard error?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is run to detect a browser in the general test runner config, even if a browser is not actually run. So it was more straightforward to keep it as-is.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should fix that at some point. We shouldn't need to go through browser config unless we are running browser tests. I know there are some subtle issues with multiprocessing and startup here, so its maybe non-trival though.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, about about:

EMTEST_BROWSER = 'google-chrome'
if not shutil.which(EMTEST_BROWSER):
   EMTEST_BROWSER = 'firefox'
if not shutil.which(EMTEST_BROWSER):
   # FIXME: This should really be and error, but this code currently also runs for non-browser tests.
   EMTEST_BROWSER = 'default-browser-not-found'

This way the value of EMTEST_BROWSER doesn't become and absolute path as part of this change.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that works

Comment thread test/test_emrun.py
self.assertContained('Hello, world!', stdout)
self.assertContained('Testing ASCII characters: !"$%&\'()*+,-./:;<=>?@[\\]^_`{|}~', stdout)
self.assertContained('Testing char sequences: %20%21 &auml;', stdout)
self.assertContained('hello, error stream!', stderr)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you make the "move emrun into its own file" into separate PR?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Seems reasonable to combine with adding it to list of non-parall suites, since that is the motivation for moving it).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer not, that would cause more churn at this point.

Comment thread test/browser_common.py
ini_path = find_system_firefox_platform_ini()
if not ini_path:
logger.warning(f'Firefox browser detected in {EMTEST_BROWSER}, but could not find Firefox platform.ini to detect Firefox version. Assuming OLDEST_SUPPORTED_FIREFOX={OLDEST_SUPPORTED_FIREFOX}')
return OLDEST_SUPPORTED_FIREFOX

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you ever run into this issue?

Maybe we should just assert os.path.exists(init_path), 'unable to find firefox ini file' below before reading it?

@juj juj Apr 28, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the CircleCI build-linux bot just ran into it.. that is why I added that.

It has /usr/bin/firefox but no platform.ini anywhere in sight.

On my own systems I do have platform.ini in /usr/lib/firefox-esr/platform.ini or /usr/lib/firefox/platform.ini

@juj juj Apr 28, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I debugged CircleCI with a search from /usr/ to find if there's a platform.ini somewhere, but that turned up nothing.

8b43aae#diff-eb5e6400ad6f92fec8556d07ec1d8d14915c79814e87f354c5ab65e6163f6ad1R176-R177

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The firefox we use in circleci installed from tar archive in EMTEST_BROWSER="$HOME/firefox/firefox"? Is that the one you mean?

@juj juj Apr 28, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This step: https://app.circleci.com/pipelines/github/emscripten-core/emscripten/50907/workflows/6696a39d-0750-4b09-9165-349d61aa099b/jobs/1184191

has /usr/bin/firefox (shutil.which('firefox') returned /usr/bin/firefox), but does not have /usr/.../platform.ini anywhere.

EMTEST_BROWSER: /usr/bin/firefox -new-instance -wait-for-browser
Searching for platform.ini
/usr/local/lib/python3.10/dist-packages/numpy/typing/tests/data/mypy.ini
/usr/local/lib/python3.10/dist-packages/numpy/_core/lib/npy-pkg-config/npymath.ini
/usr/local/lib/python3.10/dist-packages/numpy/_core/lib/npy-pkg-config/mlib.ini
Searching for platform.ini over
Traceback (most recent call last):
  File "/root/project/./test/runner.py", line 775, in <module>
    sys.exit(main())
  File "/root/project/./test/runner.py", line 732, in main
    log_test_environment()
  File "/root/project/./test/runner.py", line 655, in log_test_environment
    print(f'Firefox version: {browser_common.get_firefox_version()}')
  File "/root/project/test/browser_common.py", line 189, in get_firefox_version
    m = re.search(r"^Milestone=(.*)$", read_file(ini_path), re.MULTILINE)
  File "/root/project/tools/utils.py", line 157, in read_file
    with open(file_path, encoding='utf-8') as fh:
FileNotFoundError: [Errno 2] No such file or directory: '/usr/bin/platform.ini'

@juj juj Apr 28, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now I see.. this task gives a clue: https://app.circleci.com/pipelines/github/emscripten-core/emscripten/50916/workflows/c9a01534-7402-4de2-aac7-028abe35de8e/jobs/1184421

It prints

Command '/usr/bin/firefox' requires the firefox snap to be installed.
Please install it with:

snap install firefox

so I wonder if there's a stub executable in /usr/bin/firefox present that prints out that message. So it is expected that there wouldn't exist any platform.ini on the system.

But now I'm puzzled a bit as to why this is detecting /usr/bin/firefox in the first place...

Oh that's because I wrote a silly typo.. fixed now.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That CI job should not have EMTEST_BROWSER set at all.. its not trying to run any browser tests.

I guess your new code is causing that error because its now finding the /usr/bin/firefox even though it doesn't needed to for this CI step.

My guess is that /usr/bin/firefox is a symlink to somewhere like /snap/..., so maybe following the symlinks would fix it.

Ideally we could fix that TODO since then it would be moot point, but I know it wasn't easy last time I tried to move that code around.

I'm happy to iterate on this stuff in followups if you want to land this change now.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, yeah hopefully after this lands, the instability from emrun suite will be gone.

@juj juj enabled auto-merge (squash) April 28, 2026 21:14
@juj juj disabled auto-merge April 28, 2026 21:47
@juj juj merged commit a046aaf into emscripten-core:main Apr 28, 2026
28 of 30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants