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)