Mercurial
diff third_party/emsdk/test/test.py @ 186:8cf4ec5e2191 hg-web
Fixed merge conflict.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Fri, 23 Jan 2026 22:38:59 -0800 |
| parents | 8d17f6e6e290 |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/emsdk/test/test.py Fri Jan 23 22:38:59 2026 -0800 @@ -0,0 +1,281 @@ +#!/usr/bin/env python3 +import json +import os +import platform +import shutil +import subprocess +import sys +import tempfile +import unittest + +WINDOWS = sys.platform.startswith('win') +MACOS = sys.platform == 'darwin' +MACOS_ARM64 = MACOS and platform.machine() == 'arm64' + +emconfig = os.path.abspath('.emscripten') +assert os.path.exists(emconfig) + +upstream_emcc = os.path.join('upstream', 'emscripten', 'emcc') +emsdk = './emsdk' +if WINDOWS: + upstream_emcc += '.bat' + emsdk = 'emsdk.bat' +else: + emsdk = './emsdk' + +# Utilities + + +def listify(x): + if type(x) in {list, tuple}: + return x + return [x] + + +def check_call(cmd, **kwargs): + if type(cmd) is not list: + cmd = cmd.split() + print('running: %s' % cmd) + subprocess.run(cmd, check=True, text=True, **kwargs) + + +def checked_call_with_output(cmd, expected=None, unexpected=None, stderr=None, env=None): + cmd = cmd.split(' ') + print('running: %s' % cmd) + try: + stdout = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=stderr, check=True, text=True, env=env).stdout + except subprocess.CalledProcessError as e: + print(e.stderr) + print(e.stdout) + raise e + + if expected: + for x in listify(expected): + assert x in stdout, 'expected output missing: ' + stdout + '\n[[[' + x + ']]]' + if unexpected: + for x in listify(unexpected): + assert x not in stdout, 'unexpected output present: ' + stdout + '\n[[[' + x + ']]]' + + +def failing_call_with_output(cmd, expected, env=None): + proc = subprocess.run(cmd.split(' '), capture_output=True, text=True, env=env) + stdout = proc.stdout + stderr = proc.stderr + if WINDOWS: + print('warning: skipping part of failing_call_with_output() due to error codes not being propagated (see #592)') + else: + assert proc.returncode, 'call must have failed: ' + str([stdout, '\n========\n', stderr]) + assert expected in stdout or expected in stderr, 'call did not have the expected output: %s: %s' % (expected, str([stdout, '\n========\n', stderr])) + + +def hack_emsdk(marker, replacement): + with open('emsdk.py') as f: + src = f.read() + assert marker in src + src = src.replace(marker, replacement) + name = '__test_emsdk' + with open(name, 'w') as f: + f.write(src) + return name + + +# Set up + +TAGS = json.loads(open('emscripten-releases-tags.json').read()) + +# Tests + + +def do_lib_building(emcc): + cache_building_messages = ['generating system library: '] + + def do_build(args, is_expected=None): + unexpected = None + expected = None + if is_expected is True: + expected = cache_building_messages + elif is_expected is False: + unexpected = cache_building_messages + checked_call_with_output(emcc + ' hello_world.c' + args, + expected=expected, + unexpected=unexpected, + stderr=subprocess.STDOUT) + + # The emsdk ships all system libraries so we don't expect to see any + # cache population unless we explicly --clear-cache. + do_build('', is_expected=False) + check_call(emcc + ' --clear-cache') + do_build(' -O2', is_expected=True) + # Do another build at -O0. In nwers SDK versions this generates + # different libs, but not in older ones so don't assert here. + do_build('') + # Now verify that libs are *not* build + do_build(' -s WASM=0', is_expected=False) + do_build(' -O2 -s WASM=0', is_expected=False) + + +def run_emsdk(cmd): + if type(cmd) is not list: + cmd = cmd.split() + check_call([emsdk] + cmd) + + +class Emsdk(unittest.TestCase): + @classmethod + def setUpClass(cls): + with open('hello_world.c', 'w') as f: + f.write('''\ +#include <stdio.h> + +int main() { + printf("Hello, world!\\n"); + return 0; +} +''') + + def setUp(self): + run_emsdk('install latest') + run_emsdk('activate latest') + + def test_unknown_arch(self): + env = os.environ.copy() + env['EMSDK_ARCH'] = 'mips' + failing_call_with_output(emsdk + ' install latest', + expected='unknown machine architecture: mips', + env=env) + + def test_wrong_bitness(self): + env = os.environ.copy() + env['EMSDK_ARCH'] = 'x86' + failing_call_with_output(emsdk + ' install sdk-latest-64bit', + expected='is only provided for 64-bit OSe', + env=env) + + def test_already_installed(self): + # Test we don't re-download unnecessarily + checked_call_with_output(emsdk + ' install latest', expected='already installed', unexpected='Downloading:') + + def test_list(self): + # Test we report installed tools properly. The latest version should be + # installed, but not some random old one. + checked_call_with_output(emsdk + ' list', expected=TAGS['aliases']['latest'] + ' INSTALLED', unexpected='1.39.15 INSTALLED:') + + def test_config_contents(self): + print('test .emscripten contents') + with open(emconfig) as f: + config = f.read() + assert 'upstream' in config + + def test_lib_building(self): + print('building proper system libraries') + do_lib_building(upstream_emcc) + + def test_redownload(self): + print('go back to using upstream') + run_emsdk('activate latest') + + # Test the normal tools like node don't re-download on re-install + print('another install must re-download') + checked_call_with_output(emsdk + ' uninstall node-22.16.0-64bit') + checked_call_with_output(emsdk + ' install node-22.16.0-64bit', expected='Downloading:', unexpected='already installed') + checked_call_with_output(emsdk + ' install node-22.16.0-64bit', unexpected='Downloading:', expected='already installed') + + def test_tot_upstream(self): + print('test update-tags') + run_emsdk('update-tags') + print('test tot-upstream') + run_emsdk('install tot-upstream') + with open(emconfig) as f: + config = f.read() + run_emsdk('activate tot-upstream') + with open(emconfig + '.old') as f: + old_config = f.read() + self.assertEqual(config, old_config) + # TODO; test on latest as well + check_call(upstream_emcc + ' hello_world.c') + + def test_closure(self): + # Specifically test with `--closure` so we know that node_modules is working + check_call(upstream_emcc + ' hello_world.c --closure=1') + + def test_specific_version(self): + if MACOS_ARM64: + self.skipTest('Old sdk versions do not have ARM64 binaries') + print('test specific release (new, short name)') + run_emsdk('install 1.38.33') + print('another install, but no need for re-download') + checked_call_with_output(emsdk + ' install 1.38.33', expected='Skipped', unexpected='Downloading:') + run_emsdk('activate 1.38.33') + + def test_specific_version_full(self): + if MACOS_ARM64: + self.skipTest('Old sdk versions do not have ARM64 binaries') + print('test specific release (new, full name)') + run_emsdk('install sdk-1.38.33-64bit') + run_emsdk('activate sdk-1.38.33-64bit') + print('test specific release (new, tag name)') + run_emsdk('install sdk-tag-1.38.33-64bit') + run_emsdk('activate sdk-tag-1.38.33-64bit') + + def test_binaryen_from_source(self): + if MACOS: + self.skipTest("https://github.com/WebAssembly/binaryen/issues/4299") + if WINDOWS: + self.skipTest("https://github.com/emscripten-core/emsdk/issues/1624") + print('test binaryen source build') + run_emsdk(['install', '--build=Release', '--generator=Unix Makefiles', 'binaryen-main-64bit']) + + def test_no_32bit(self): + print('test 32-bit error') + emsdk_hacked = hack_emsdk('not is_os_64bit()', 'True') + failing_call_with_output('%s %s install latest' % (sys.executable, emsdk_hacked), + 'this tool is only provided for 64-bit OSes') + os.remove(emsdk_hacked) + + def test_update_no_git(self): + print('test non-git update') + + temp_dir = tempfile.mkdtemp() + for filename in os.listdir('.'): + if not filename.startswith('.') and not os.path.isdir(filename): + shutil.copy2(filename, os.path.join(temp_dir, filename)) + + olddir = os.getcwd() + try: + os.chdir(temp_dir) + run_emsdk('update') + + print('second time') + run_emsdk('update') + finally: + os.chdir(olddir) + + def test_install_arbitrary(self): + # Test that its possible to install arbrary emscripten-releases SDKs + run_emsdk('install 1b7f7bc6002a3ca73647f41fc10e1fac7f06f804') + + # Check that its not re-downloaded + checked_call_with_output(emsdk + ' install 1b7f7bc6002a3ca73647f41fc10e1fac7f06f804', expected='Skipped', unexpected='Downloading:') + + def test_install_tool(self): + # Test that its possible to install emscripten as tool instead of SDK + checked_call_with_output(emsdk + ' install releases-77b065ace39e6ab21446e13f92897f956c80476a', unexpected='Installing SDK') + + def test_activate_missing(self): + run_emsdk('install latest') + failing_call_with_output(emsdk + ' activate 2.0.1', expected="error: tool is not installed and therefore cannot be activated: 'releases-13e29bd55185e3c12802bc090b4507901856b2ba-64bit'") + + def test_keep_downloads(self): + env = os.environ.copy() + env['EMSDK_KEEP_DOWNLOADS'] = '1' + # With EMSDK_KEEP_DOWNLOADS the downloading should happen on the first + # install of 2.0.28, and again when we install 2.0.29, but not on the + # second install of 2.0.28 because the zip should already be local. + shutil.rmtree('downloads') + checked_call_with_output(emsdk + ' install 3.1.54', expected='Downloading:', env=env) + checked_call_with_output(emsdk + ' install 3.1.55', expected='Downloading:', env=env) + checked_call_with_output(emsdk + ' install 3.1.54', expected='already downloaded, skipping', unexpected='Downloading:', env=env) + + +if __name__ == '__main__': + unittest.main(verbosity=2)