text: # coding=utf-8
  # Copyright 2013 The Emscripten Authors.  All rights reserved.
  # Emscripten is available under two separate licenses, the MIT license and the
  # University of Illinois/NCSA Open Source License.  Both these licenses can be
  # found in the LICENSE file.
  
  # noqa: E241
  
  from functools import wraps
  import glob
  import gzip
  import itertools
  import json
  import os
  import re
  import select
  import shlex
  import shutil
  import subprocess
  import sys
  import time
  import tempfile
  import unittest
  import uuid
  from pathlib import Path
  from subprocess import PIPE, STDOUT
  
  if __name__ == '__main__':
    raise Exception('do not run this file directly; do something like: tests/runner other')
  
  from tools.shared import try_delete, config
  from tools.shared import EMCC, EMXX, EMAR, EMRANLIB, PYTHON, FILE_PACKAGER, WINDOWS, EM_BUILD_VERBOSE
  from tools.shared import CLANG_CC, CLANG_CXX, LLVM_AR, LLVM_DWARFDUMP, EMCMAKE, EMCONFIGURE
  from common import RunnerCore, path_from_root, is_slow_test, ensure_dir, disabled, make_executable
  from common import env_modify, no_mac, no_windows, requires_native_clang, with_env_modify
  from common import create_file, parameterized, NON_ZERO, node_pthreads, TEST_ROOT, test_file
  from common import compiler_for, read_file, read_binary, EMBUILDER, require_v8, require_node
  from tools import shared, building, utils, deps_info, response_file
  import common
  import jsrun
  import clang_native
  from tools import line_endings
  from tools import webassembly
  
  scons_path = utils.which('scons')
  emmake = shared.bat_suffix(path_from_root('emmake'))
  emconfig = shared.bat_suffix(path_from_root('em-config'))
  emsize = shared.bat_suffix(path_from_root('emsize'))
  emprofile = shared.bat_suffix(path_from_root('emprofile'))
  wasm_dis = Path(building.get_binaryen_bin(), 'wasm-dis')
  wasm_opt = Path(building.get_binaryen_bin(), 'wasm-opt')
  
  
  class temp_directory():
    def __init__(self, dirname):
      self.dir = dirname
  
    def __enter__(self):
      self.directory = tempfile.mkdtemp(prefix='emtest_temp_', dir=self.dir)
      self.prev_cwd = os.getcwd()
      os.chdir(self.directory)
      print('temp_directory: ' + self.directory)
      return self.directory
  
    def __exit__(self, type, value, traceback):
      os.chdir(self.prev_cwd)
  
  
  def uses_canonical_tmp(func):
    """Decorator that signals the use of the canonical temp by a test method.
  
    This decorator takes care of cleaning the directory after the
    test to satisfy the leak detector.
    """
    @wraps(func)
    def decorated(self):
      # Before running the test completely remove the canonical_tmp
      if os.path.exists(self.canonical_temp_dir):
        shutil.rmtree(self.canonical_temp_dir)
      try:
        func(self)
      finally:
        # Make sure the test isn't lying about the fact that it uses
        # canonical_tmp
        self.assertTrue(os.path.exists(self.canonical_temp_dir))
        # Remove the temp dir in a try-finally, as otherwise if the
        # test fails we would not clean it up, and if leak detection
        # is set we will show that error instead of the actual one.
        shutil.rmtree(self.canonical_temp_dir)
  
    return decorated
  
  
  def parse_wasm(filename):
    wat = shared.run_process([wasm_dis, filename], stdout=PIPE).stdout
    imports = []
    exports = []
    funcs = []
    for line in wat.splitlines():
      line = line.strip()
      if line.startswith('(import '):
        line = line.strip('()')
        parts = line.split()
        module = parts[1].strip('"')
        name = parts[2].strip('"')
        imports.append('%s.%s' % (module, name))
      if line.startswith('(export '):
        line = line.strip('()')
        name = line.split()[1].strip('"')
        exports.append(name)
      if line.startswith('(func '):
        line = line.strip('()')
        name = line.split()[1].strip('"')
        funcs.append(name)
    return imports, exports, funcs
  
  
  def also_with_wasmfs(f):
    def metafunc(self, wasmfs):
      if wasmfs:
        self.set_setting('WASMFS')
        self.emcc_args.append('-DWASMFS')
        f(self)
      else:
        f(self)
  
    metafunc._parameterize = {'': (False,),
                              'wasmfs': (True,)}
  
    return metafunc
  
  
  class other(RunnerCore):
    def assertIsObjectFile(self, filename):
      self.assertTrue(building.is_wasm(filename))
  
    def assertIsWasmDylib(self, filename):
      self.assertTrue(building.is_wasm_dylib(filename))
  
    def do_other_test(self, testname, emcc_args=[], **kwargs):
      self.do_run_in_out_file_test('other', testname, emcc_args=emcc_args, **kwargs)
  
    # Another utility to run a test in this suite. This receives a source file
    # to compile, with optional compiler and execution flags.
    # Output can be checked by seeing if literals are contained, and that a list
    # of regexes match. The return code can also be checked.
    def do_smart_test(self, source, literals=[], engine=None, regexes=[],
                      emcc_args=[], run_args=[], assert_returncode=0):
      self.run_process([compiler_for(source), source] + emcc_args)
      seen = self.run_js('a.out.js', engine=engine, args=run_args, assert_returncode=assert_returncode) + '\n'
  
      for literal in literals:
        self.assertContained([literal], seen)
  
      for regex in regexes:
        self.assertTrue(re.search(regex, seen), 'Expected regex "%s" to match on:\n%s' % (regex, seen))
  
    def run_on_pty(self, cmd):
      master, slave = os.openpty()
      output = []
  
      try:
        with env_modify({'TERM': 'xterm-color'}):
          proc = subprocess.Popen(cmd, stdout=slave, stderr=slave)
          while proc.poll() is None:
            r, w, x = select.select([master], [], [], 1)
            if r:
              output.append(os.read(master, 1024))
          return (proc.returncode, b''.join(output))
      finally:
        os.close(master)
        os.close(slave)
  
    # Test that running `emcc -v` always works even in the presence of `EMCC_CFLAGS`.
    # This needs to work because many tools run `emcc -v` internally and it should
    # always work even if the user has `EMCC_CFLAGS` set.
    @with_env_modify({'EMCC_CFLAGS': '-should -be -ignored'})
    def test_emcc_v(self):
      for compiler in [EMCC, EMXX]:
        # -v, without input files
        proc = self.run_process([compiler, '-v'], stdout=PIPE, stderr=PIPE)
        self.assertEqual(proc.stdout, '')
        # assert that the emcc message comes first.  We had a bug where the sub-process output
        # from clang would be flushed to stderr first.
        self.assertContained('emcc (Emscripten gcc/clang-like replacement', proc.stderr)
        self.assertTrue(proc.stderr.startswith('emcc (Emscripten gcc/clang-like replacement'))
        self.assertContained('clang version %s' % shared.EXPECTED_LLVM_VERSION, proc.stderr)
        self.assertContained('GNU', proc.stderr)
        self.assertContained('Target: wasm32-unknown-emscripten', proc.stderr)
        self.assertNotContained('this is dangerous', proc.stderr)
  
    def test_emcc_check(self):
      proc = self.run_process([EMCC, '--check'], stdout=PIPE, stderr=PIPE)
      self.assertEqual(proc.stdout, '')
      self.assertContained('emcc (Emscripten gcc/clang-like replacement', proc.stderr)
      self.assertContained('Running sanity checks', proc.stderr)
      proc = self.run_process([EMCC, '--check'], stdout=PIPE, stderr=PIPE)
      self.assertContained('Running sanity checks', proc.stderr)
  
    def test_emcc_generate_config(self):
      for compiler in [EMCC, EMXX]:
        config_path = './emscripten_config'
        self.run_process([compiler, '--generate-config', config_path])
        self.assertExists(config_path, 'A config file should have been created at %s' % config_path)
        config_contents = read_file(config_path)
        self.assertContained('EMSCRIPTEN_ROOT', config_contents)
        self.assertContained('LLVM_ROOT', config_contents)
        os.remove(config_path)
  
    def test_emcc_output_mjs(self):
      self.run_process([EMCC, '-o', 'hello_world.mjs', test_file('hello_world.c')])
      output = read_file('hello_world.mjs')
      self.assertContained('export default Module;', output)
      # TODO(sbc): Test that this is actually runnable.  We currently don't have
      # any tests for EXPORT_ES6 but once we do this should be enabled.
      # self.assertContained('hello, world!', self.run_js('hello_world.mjs'))
  
    @parameterized({
      '': (True, [],),
      'no_import_meta': (False, ['-sUSE_ES6_IMPORT_META=0'],),
    })
    def test_emcc_output_worker_mjs(self, has_import_meta, args):
      os.mkdir('subdir')
      self.run_process([EMCC, '-o', 'subdir/hello_world.mjs', '-pthread', '-O1',
                        test_file('hello_world.c')] + args)
      src = read_file('subdir/hello_world.mjs')
      self.assertContainedIf("new URL('hello_world.wasm', import.meta.url)", src, condition=has_import_meta)
      self.assertContainedIf("new Worker(new URL('hello_world.worker.js', import.meta.url))", src, condition=has_import_meta)
      self.assertContained('export default Module;', src)
      src = read_file('subdir/hello_world.worker.js')
      self.assertContained('import("./hello_world.mjs")', src)
  
    def test_emcc_output_worker_mjs_single_file(self):
      self.run_process([EMCC, '-o', 'hello_world.mjs', '-pthread',
                        test_file('hello_world.c'), '-sSINGLE_FILE=1'])
      src = read_file('hello_world.mjs')
      self.assertNotContained("new URL('data:", src)
      self.assertContained("new Worker(new URL('hello_world.worker.js', import.meta.url))", src)
  
    def test_emcc_output_mjs_closure(self):
      self.run_process([EMCC, '-o', 'hello_world.mjs',
                        test_file('hello_world.c'), '--closure=1'])
      src = read_file('hello_world.mjs')
      self.assertContained('new URL("hello_world.wasm", import.meta.url)', src)
  
    def test_export_es6_implies_modularize(self):
      self.run_process([EMCC, test_file('hello_world.c'), '-sEXPORT_ES6=1'])
      src = read_file('a.out.js')
      self.assertContained('export default Module;', src)
  
    def test_export_es6_requires_modularize(self):
      err = self.expect_fail([EMCC, test_file('hello_world.c'), '-sEXPORT_ES6=1', '-sMODULARIZE=0'])
      self.assertContained('EXPORT_ES6 requires MODULARIZE to be set', err)
  
    def test_export_es6_allows_export_in_post_js(self):
      self.run_process([EMCC, test_file('hello_world.c'), '-O3', '-sEXPORT_ES6=1', '--post-js', test_file('export_module.js')])
      src = read_file('a.out.js')
      self.assertContained('export{doNothing};', src)
  
    def test_emcc_out_file(self):
      # Verify that "-ofile" works in addition to "-o" "file"
      self.run_process([EMCC, '-c', '-ofoo.o', test_file('hello_world.c')])
      self.assertExists('foo.o')
      self.run_process([EMCC, '-ofoo.js', 'foo.o'])
      self.assertExists('foo.js')
  
    @parameterized({
      'c': [EMCC, '.c'],
      'cxx': [EMXX, '.cpp']})
    def test_emcc_basics(self, compiler, suffix):
      # emcc src.cpp ==> writes a.out.js and a.out.wasm
      self.run_process([compiler, test_file('hello_world' + suffix)])
      self.assertExists('a.out.js')
      self.assertExists('a.out.wasm')
      self.assertContained('hello, world!', self.run_js('a.out.js'))
  
      # --version
      output = self.run_process([compiler, '--version'], stdout=PIPE, stderr=PIPE)
      output = output.stdout.replace('\r', '')
      self.assertContained('emcc (Emscripten gcc/clang-like replacement', output)
      self.assertContained('''Copyright (C) 2014 the Emscripten authors (see AUTHORS.txt)
  This is free and open source software under the MIT license.
  There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  ''', output)
  
      # --help
      output = self.run_process([compiler, '--help'], stdout=PIPE, stderr=PIPE)
      self.assertContained('Display this information', output.stdout)
      self.assertContained('Most clang options will work', output.stdout)
  
      # -dumpmachine
      output = self.run_process([compiler, '-dumpmachine'], stdout=PIPE, stderr=PIPE)
      self.assertContained('wasm32-unknown-emscripten', output.stdout)
  
      # -dumpversion
      output = self.run_process([compiler, '-dumpversion'], stdout=PIPE, stderr=PIPE)
      self.assertEqual(shared.EMSCRIPTEN_VERSION, output.stdout.strip())
  
      # properly report source code errors, and stop there
      self.clear()
      stderr = self.expect_fail([compiler, test_file('hello_world_error' + suffix)])
      self.assertNotContained('IOError', stderr) # no python stack
      self.assertNotContained('Traceback', stderr) # no python stack
      self.assertContained('error: invalid preprocessing directive', stderr)
      self.assertContained(["error: use of undeclared identifier 'cheez", "error: unknown type name 'cheez'"], stderr)
      self.assertContained('errors generated.', stderr.splitlines()[-2])
  
    @parameterized({
      'c': [EMCC, '.c'],
      'cxx': [EMXX, '.cpp']})
    def test_emcc_2(self, compiler, suffix):
      # emcc src.cpp -c    and   emcc src.cpp -o src.[o|bc] ==> should give a .bc file
      for args in [[], ['-o', 'src.o'], ['-o', 'src.bc'], ['-o', 'src.so']]:
        print('args:', args)
        target = args[1] if len(args) == 2 else 'hello_world.o'
        self.clear()
        self.run_process([compiler, '-c', test_file('hello_world' + suffix)] + args)
        syms = building.llvm_nm(target)
        self.assertIn('main', syms['defs'])
        # wasm backend will also have '__original_main' or such
        self.assertEqual(len(syms['defs']), 2)
        if target == 'js': # make sure emcc can recognize the target as a bitcode file
          shutil.move(target, target + '.bc')
          target += '.bc'
        self.run_process([compiler, target, '-o', target + '.js'])
        self.assertContained('hello, world!', self.run_js(target + '.js'))
  
    @parameterized({
      'c': [EMCC, '.c'],
      'cxx': [EMXX, '.cpp']})
    def test_emcc_3(self, compiler, suffix):
      # handle singleton archives
      self.run_process([compiler, '-c', test_file('hello_world' + suffix), '-o', 'a.o'])
      self.run_process([LLVM_AR, 'r', 'a.a', 'a.o'], stdout=PIPE, stderr=PIPE)
      self.run_process([compiler, 'a.a'])
      self.assertContained('hello, world!', self.run_js('a.out.js'))
  
      # emcc [..] -o [path] ==> should work with absolute paths
      for path in [os.path.abspath(Path('../file1.js')), Path('b_dir/file2.js')]:
        print(path)
        os.chdir(self.get_dir())
        self.clear()
        print(os.listdir(os.getcwd()))
        ensure_dir(Path('a_dir/b_dir'))
        os.chdir('a_dir')
        # use single file so we don't have more files to clean up
        self.run_process([compiler, test_file('hello_world' + suffix), '-o', path, '-sSINGLE_FILE'])
        last = os.getcwd()
        os.chdir(os.path.dirname(path))
        self.assertContained('hello, world!', self.run_js(os.path.basename(path)))
        os.chdir(last)
        try_delete(path)
  
    @is_slow_test
    @parameterized({
      'c': [EMCC],
      'cxx': [EMXX]})
    def test_emcc_4(self, compiler):
      # Optimization: emcc src.cpp -o something.js [-Ox]. -O0 is the same as not specifying any optimization setting
      # link_param are used after compiling first
      for params, opt_level, link_params, closure, has_malloc in [
        (['-o', 'something.js'],                          0, None, 0, 1),
        (['-o', 'something.js', '-O0', '-g'],             0, None, 0, 0),
        (['-o', 'something.js', '-O1'],                   1, None, 0, 0),
        (['-o', 'something.js', '-O1', '-g'],             1, None, 0, 0), # no closure since debug
        (['-o', 'something.js', '-O2'],                   2, None, 0, 1),
        (['-o', 'something.js', '-O2', '-g'],             2, None, 0, 0),
        (['-o', 'something.js', '-Os'],                   2, None, 0, 1),
        (['-o', 'something.js', '-O3'],                   3, None, 0, 1),
        # and, test compiling first
        (['-c', '-o', 'something.o'],        0, [],      0, 0),
        (['-c', '-o', 'something.o', '-O0'], 0, [],      0, 0),
        (['-c', '-o', 'something.o', '-O1'], 1, ['-O1'], 0, 0),
        (['-c', '-o', 'something.o', '-O2'], 2, ['-O2'], 0, 0),
        (['-c', '-o', 'something.o', '-O3'], 3, ['-O3'], 0, 0),
        (['-O1', '-c', '-o', 'something.o'], 1, [],      0, 0),
        # non-wasm
        (['-sWASM=0', '-o', 'something.js'],                          0, None, 0, 1),
        (['-sWASM=0', '-o', 'something.js', '-O0', '-g'],             0, None, 0, 0),
        (['-sWASM=0', '-o', 'something.js', '-O1'],                   1, None, 0, 0),
        (['-sWASM=0', '-o', 'something.js', '-O1', '-g'],             1, None, 0, 0), # no closure since debug
        (['-sWASM=0', '-o', 'something.js', '-O2'],                   2, None, 0, 1),
        (['-sWASM=0', '-o', 'something.js', '-O2', '-g'],             2, None, 0, 0),
        (['-sWASM=0', '-o', 'something.js', '-Os'],                   2, None, 0, 1),
        (['-sWASM=0', '-o', 'something.js', '-O3'],                   3, None, 0, 1),
        # and, test compiling to bitcode first
        (['-flto', '-c', '-o', 'something.o'],        0, [],      0, 0),
        (['-flto', '-c', '-o', 'something.o', '-O0'], 0, [],      0, 0),
        (['-flto', '-c', '-o', 'something.o', '-O1'], 1, ['-O1'], 0, 0),
        (['-flto', '-c', '-o', 'something.o', '-O2'], 2, ['-O2'], 0, 0),
        (['-flto', '-c', '-o', 'something.o', '-O3'], 3, ['-O3'], 0, 0),
        (['-flto', '-O1', '-c', '-o', 'something.o'], 1, [],      0, 0),
      ]:
        print(params, opt_level, link_params, closure, has_malloc)
        self.clear()
        keep_debug = '-g' in params
        if has_malloc:
          filename = test_file('hello_world_loop_malloc.c')
        else:
          filename = test_file('hello_world_loop.c')
        args = [compiler, filename] + params
        print('..', args)
        output = self.run_process(args, stdout=PIPE, stderr=PIPE)
        assert len(output.stdout) == 0, output.stdout
        if link_params is not None:
          self.assertExists('something.o', output.stderr)
          obj_args = [compiler, 'something.o', '-o', 'something.js'] + link_params
          print('....', obj_args)
          output = self.run_process(obj_args, stdout=PIPE, stderr=PIPE)
        self.assertExists('something.js', output.stderr)
        self.assertContained('hello, world!', self.run_js('something.js'))
  
        # Verify optimization level etc. in the generated code
        # XXX these are quite sensitive, and will need updating when code generation changes
        generated = read_file('something.js')
        main = self.get_func(generated, '_main') if 'function _main' in generated else generated
        assert 'new Uint16Array' in generated and 'new Uint32Array' in generated, 'typed arrays 2 should be used by default'
        assert 'SAFE_HEAP' not in generated, 'safe heap should not be used by default'
        assert ': while(' not in main, 'when relooping we also js-optimize, so there should be no labelled whiles'
        if closure:
          if opt_level == 0:
            assert '._main =' in generated, 'closure compiler should have been run'
          elif opt_level >= 1:
            assert '._main=' in generated, 'closure compiler should have been run (and output should be minified)'
        else:
          # closure has not been run, we can do some additional checks. TODO: figure out how to do these even with closure
          assert '._main = ' not in generated, 'closure compiler should not have been run'
          if keep_debug:
            assert ('assert(INITIAL_MEMORY >= TOTAL_STACK' in generated) == (opt_level == 0), 'assertions should be in opt == 0'
          if 'WASM=0' in params:
            looks_unminified = ' = {}' in generated and ' = []' in generated
            looks_minified = '={}' in generated and '=[]' and ';var' in generated
            assert not (looks_minified and looks_unminified)
            if opt_level == 0 or '-g' in params:
              assert looks_unminified
            elif opt_level >= 2:
              assert looks_minified
  
    def test_multiple_sources(self):
      # Compiling two sources at a time should work.
      cmd = [EMCC, '-c', test_file('twopart_main.cpp'), test_file('twopart_side.c')]
      self.run_process(cmd)
  
      # Object files should be generated by default in the current working
      # directory, and not alongside the sources.
      self.assertExists('twopart_main.o')
      self.assertExists('twopart_side.o')
      self.assertNotExists(test_file('twopart_main.o'))
      self.assertNotExists(test_file('twopart_side.o'))
  
      # But it is an error if '-o' is also specified.
      self.clear()
      err = self.expect_fail(cmd + ['-o', 'out.o'])
  
      self.assertContained('cannot specify -o with -c/-S/-E/-M and multiple source files', err)
      self.assertNotExists('twopart_main.o')
      self.assertNotExists('twopart_side.o')
      self.assertNotExists(test_file('twopart_main.o'))
      self.assertNotExists(test_file('twopart_side.o'))
  
    def test_tsearch(self):
      self.do_other_test('test_tsearch.c')
  
    def test_libc_progname(self):
      self.do_other_test('test_libc_progname.c')
  
    def test_combining_object_files(self):
      # Compiling two files with -c will generate separate object files
      self.run_process([EMCC, test_file('twopart_main.cpp'), test_file('twopart_side.c'), '-c'])
      self.assertExists('twopart_main.o')
      self.assertExists('twopart_side.o')
  
      # Linking with just one of them is expected to fail
      err = self.expect_fail([EMCC, 'twopart_main.o'])
      self.assertContained('undefined symbol: theFunc', err)
  
      # Linking with both should work
      self.run_process([EMCC, 'twopart_main.o', 'twopart_side.o'])
      self.assertContained('side got: hello from main, over', self.run_js('a.out.js'))
  
      # Combining object files into another object should also work, using the `-r` flag
      err = self.run_process([EMCC, '-r', 'twopart_main.o', 'twopart_side.o', '-o', 'combined.o'], stderr=PIPE).stderr
      self.assertNotContained('warning:', err)
      # Warn about legecy support for outputing object file without `-r`, `-c` or `-shared`
      err = self.run_process([EMCC, 'twopart_main.o', 'twopart_side.o', '-o', 'combined2.o'], stderr=PIPE).stderr
      self.assertContained('warning: object file output extension (.o) used for non-object output', err)
  
      # Should be two symbols (and in the wasm backend, also __original_main)
      syms = building.llvm_nm('combined.o')
      self.assertIn('main', syms['defs'])
      self.assertEqual(len(syms['defs']), 3)
  
      self.run_process([EMCC, 'combined.o', '-o', 'combined.o.js'])
      self.assertContained('side got: hello from main, over', self.run_js('combined.o.js'))
  
    def test_combining_object_files_from_archive(self):
      # Compiling two files with -c will generate separate object files
      self.run_process([EMCC, test_file('twopart_main.cpp'), test_file('twopart_side.c'), '-c'])
      self.assertExists('twopart_main.o')
      self.assertExists('twopart_side.o')
  
      # Combining object files into a library archive should work
      self.run_process([EMAR, 'crs', 'combined.a', 'twopart_main.o', 'twopart_side.o'])
      self.assertExists('combined.a')
  
      # Combining library archive into an object should yield a valid object, using the `-r` flag
      self.run_process([EMXX, '-r', '-o', 'combined.o', '-Wl,--whole-archive', 'combined.a'])
      self.assertIsObjectFile('combined.o')
  
      # Should be two symbols (and in the wasm backend, also __original_main)
      syms = building.llvm_nm('combined.o')
      self.assertIn('main', syms['defs'])
      self.assertEqual(len(syms['defs']), 3)
  
      self.run_process([EMXX, 'combined.o', '-o', 'combined.o.js'])
      self.assertContained('side got: hello from main, over', self.run_js('combined.o.js'))
  
    def test_js_transform(self):
      with open('t.py', 'w') as f:
        f.write('''
  import sys
  f = open(sys.argv[1], 'a')
  f.write('transformed!')
  f.close()
  ''')
  
      err = self.run_process([EMCC, test_file('hello_world.c'), '-gsource-map', '--js-transform', '%s t.py' % (PYTHON)], stderr=PIPE).stderr
      self.assertContained('disabling source maps because a js transform is being done', err)
      self.assertIn('transformed!', read_file('a.out.js'))
  
    def test_js_mem_file(self):
      for opts in [0, 1, 2, 3]:
        print('mem init in', opts)
        self.clear()
        self.run_process([EMCC, test_file('hello_world.c'), '-sWASM=0', '-O' + str(opts)])
        if opts >= 2:
          self.assertExists('a.out.js.mem')
        else:
          self.assertNotExists('a.out.js.mem')
  
    def test_emcc_asm_v_wasm(self):
      for opts in ([], ['-O1'], ['-O2'], ['-O3']):
        print('opts', opts)
        for mode in ([], ['-sWASM=0']):
          self.clear()
          wasm = '=0' not in str(mode)
          print('  mode', mode, 'wasm?', wasm)
          self.run_process([EMCC, test_file('hello_world.c'), '-sENVIRONMENT=node,shell'] + opts + mode)
          self.assertExists('a.out.js')
          if wasm:
            self.assertExists('a.out.wasm')
          for engine in config.JS_ENGINES:
            print('    engine', engine)
            out = self.run_js('a.out.js', engine=engine)
            self.assertContained('hello, world!', out)
  
    def test_emcc_cflags(self):
      output = self.run_process([EMCC, '--cflags'], stdout=PIPE)
      flags = output.stdout.strip()
      self.assertContained('-target wasm32-unknown-emscripten', flags)
      self.assertContained('--sysroot=', flags)
      output = self.run_process([EMXX, '--cflags'], stdout=PIPE)
      flags = output.stdout.strip()
      self.assertContained('-target wasm32-unknown-emscripten', flags)
      self.assertContained('--sysroot=', flags)
      # check they work
      cmd = [CLANG_CXX, test_file('hello_world.cpp')] + shlex.split(flags.replace('\\', '\\\\')) + ['-c', '-o', 'out.o']
      self.run_process(cmd)
      self.run_process([EMCC, 'out.o'])
      self.assertContained('hello, world!', self.run_js('a.out.js'))
  
    def test_emcc_print_search_dirs(self):
      result = self.run_process([EMCC, '-print-search-dirs'], stdout=PIPE, stderr=PIPE)
      self.assertContained('programs: =', result.stdout)
      self.assertContained('libraries: =', result.stdout)
  
    def test_emar_em_config_flag(self):
      # Test that the --em-config flag is accepted but not passed down do llvm-ar.
      # We expand this in case the EM_CONFIG is ~/.emscripten (default)
      conf = os.path.expanduser(config.EM_CONFIG)
      proc = self.run_process([EMAR, '--em-config', conf, '--version'], stdout=PIPE, stderr=PIPE)
      self.assertEqual(proc.stderr, "")
      self.assertContained('LLVM', proc.stdout)
  
    def test_em_config_missing_arg(self):
      out = self.expect_fail([EMCC, '--em-config'])
      self.assertContained('error: --em-config must be followed by a filename', out)
  
    def test_emsize(self):
      # test binaryen generated by running:
      #   emcc tests/hello_world.c -Oz --closure 1 -o tests/other/test_emsize.js
      expected = read_file(test_file('other/test_emsize.out'))
      cmd = [emsize, test_file('other/test_emsize.js')]
      for command in [cmd, cmd + ['-format=sysv']]:
        output = self.run_process(cmd, stdout=PIPE).stdout
        self.assertContained(expected, output)
  
    @is_slow_test
    @parameterized({
      # ('directory to the test', 'output filename', ['extra args to pass to
      # CMake']) Testing all combinations would be too much work and the test
      # would take 10 minutes+ to finish (CMake feature detection is slow), so
      # combine multiple features into one to try to cover as much as possible
      # while still keeping this test in sensible time limit.
      'js':          ('target_js',      'test_cmake.js',         ['-DCMAKE_BUILD_TYPE=Debug']),
      'html':        ('target_html',    'hello_world_gles.html', ['-DCMAKE_BUILD_TYPE=Release']),
      'library':     ('target_library', 'libtest_cmake.a',       ['-DCMAKE_BUILD_TYPE=MinSizeRel']),
      'static_cpp':  ('target_library', 'libtest_cmake.a',       ['-DCMAKE_BUILD_TYPE=RelWithDebInfo', '-DCPP_LIBRARY_TYPE=STATIC']),
      'stdproperty': ('stdproperty',    'helloworld.js',         []),
      'post_build':  ('post_build',     'hello.js',              []),
    })
    def test_cmake(self, test_dir, output_file, cmake_args):
      # Test all supported generators.
      if WINDOWS:
        generators = ['MinGW Makefiles', 'NMake Makefiles']
      else:
        generators = ['Unix Makefiles', 'Ninja', 'Eclipse CDT4 - Ninja']
  
      configurations = {
        'MinGW Makefiles'     : {'build'   : ['mingw32-make'] }, # noqa
        'NMake Makefiles'     : {'build'   : ['nmake', '/NOLOGO']}, # noqa
        'Unix Makefiles'      : {'build'   : ['make']}, # noqa
        'Ninja'               : {'build'   : ['ninja']}, # noqa
        'Eclipse CDT4 - Ninja': {'build'   : ['ninja']}, # noqa
      }
      for generator in generators:
        conf = configurations[generator]
  
        if not utils.which(conf['build'][0]):
          # Use simple test if applicable
          print('Skipping %s test for CMake support; build tool found found: %s.' % (generator, conf['build'][0]))
          continue
  
        cmakelistsdir = test_file('cmake', test_dir)
        with temp_directory(self.get_dir()) as tempdirname:
          # Run Cmake
          cmd = [EMCMAKE, 'cmake'] + cmake_args + ['-G', generator, cmakelistsdir]
  
          env = os.environ.copy()
          # https://github.com/emscripten-core/emscripten/pull/5145: Check that CMake works even if EMCC_SKIP_SANITY_CHECK=1 is passed.
          if test_dir == 'target_html':
            env['EMCC_SKIP_SANITY_CHECK'] = '1'
          print(str(cmd))
          self.run_process(cmd, env=env, stdout=None if EM_BUILD_VERBOSE >= 2 else PIPE, stderr=None if EM_BUILD_VERBOSE >= 1 else PIPE)
  
          # Build
          cmd = conf['build']
          if EM_BUILD_VERBOSE >= 3 and 'Ninja' not in generator:
            cmd += ['VERBOSE=1']
          self.run_process(cmd, stdout=None if EM_BUILD_VERBOSE >= 2 else PIPE)
          self.assertExists(tempdirname + '/' + output_file, 'building a cmake-generated Makefile failed to produce an output file %s!' % tempdirname + '/' + output_file)
  
          # Run through node, if CMake produced a .js file.
          if output_file.endswith('.js'):
            ret = self.run_process(config.NODE_JS + [tempdirname + '/' + output_file], stdout=PIPE).stdout
            self.assertTextDataIdentical(read_file(cmakelistsdir + '/out.txt').strip(), ret.strip())
  
          if test_dir == 'post_build':
            ret = self.run_process(['ctest'], env=env)
  
    # Test that the various CMAKE_xxx_COMPILE_FEATURES that are advertised for the Emscripten toolchain match with the actual language features that Clang supports.
    # If we update LLVM version and this test fails, copy over the new advertised features from Clang and place them to cmake/Modules/Platform/Emscripten.cmake.
    @no_windows('Skipped on Windows because CMake does not configure native Clang builds well on Windows.')
    def test_cmake_compile_features(self):
      with temp_directory(self.get_dir()):
        cmd = ['cmake', '-DCMAKE_C_COMPILER=' + CLANG_CC, '-DCMAKE_CXX_COMPILER=' + CLANG_CXX, test_file('cmake/stdproperty')]
        print(str(cmd))
        native_features = self.run_process(cmd, stdout=PIPE).stdout
  
      with temp_directory(self.get_dir()):
        cmd = [EMCMAKE, 'cmake', test_file('cmake/stdproperty')]
        print(str(cmd))
        emscripten_features = self.run_process(cmd, stdout=PIPE).stdout
  
      native_features = '\n'.join([x for x in native_features.split('\n') if '***' in x])
      emscripten_features = '\n'.join([x for x in emscripten_features.split('\n') if '***' in x])
      self.assertTextDataIdentical(native_features, emscripten_features)
  
    # Test that the user's explicitly specified generator is always honored
    # Internally we override the generator on windows, unles the user specifies one
    # Test require Ninja to be installed
    def test_cmake_explicit_generator(self):
      if not utils.which('ninja'):
        self.skipTest('Skipped concat generator argument test since ninja not found')
      # use -Wno-dev to suppress an irrelevant warning about the test files only.
      cmd = [EMCMAKE, 'cmake', '-GNinja', '-Wno-dev', test_file('cmake/cpp_lib')]
      self.run_process(cmd)
      self.assertExists(self.get_dir() + '/build.ninja')
  
    # Tests that it's possible to pass C++11 or GNU++11 build modes to CMake by building code that
    # needs C++11 (embind)
    def test_cmake_with_embind_cpp11_mode(self):
      if WINDOWS and not utils.which('ninja'):
        self.skipTest('Skipping cmake test on windows since ninja not found')
      for args in [[], ['-DNO_GNU_EXTENSIONS=1']]:
        self.clear()
        # Use ninja generator here since we assume its always installed on our build/test machines.
        configure = [EMCMAKE, 'cmake', test_file('cmake/cmake_with_emval')] + args
        if WINDOWS:
          configure += ['-G', 'Ninja']
        print(str(configure))
        self.run_process(configure)
        build = ['cmake', '--build', '.']
        print(str(build))
        self.run_process(build)
  
        out = self.run_process(config.NODE_JS + ['cmake_with_emval.js'], stdout=PIPE).stdout
        if '-DNO_GNU_EXTENSIONS=1' in args:
          self.assertContained('Hello! __STRICT_ANSI__: 1, __cplusplus: 201103', out)
        else:
          self.assertContained('Hello! __STRICT_ANSI__: 0, __cplusplus: 201103', out)
  
    # Tests that the Emscripten CMake toolchain option
    def test_cmake_bitcode_static_libraries(self):
      # Test that this option produces an error
      err = self.expect_fail([EMCMAKE, 'cmake', test_file('cmake/static_lib'), '-DEMSCRIPTEN_GENERATE_BITCODE_STATIC_LIBRARIES=ON'])
      self.assertContained('EMSCRIPTEN_GENERATE_BITCODE_STATIC_LIBRARIES is not compatible with the', err)
  
    @parameterized({
      '': ['0'],
      '_suffix': ['1'],
    })
    def test_cmake_static_lib(self, custom):
      # Test that one is able to use custom suffixes for static libraries.
      # (sometimes projects want to emulate stuff, and do weird things like files
      # with ".so" suffix which are in fact either ar archives or bitcode files)
      self.run_process([EMCMAKE, 'cmake', f'-DSET_CUSTOM_SUFFIX_IN_PROJECT={custom}', test_file('cmake/static_lib')])
      self.run_process(['cmake', '--build', '.'])
      if custom == '1':
        self.assertTrue(building.is_ar('myprefix_static_lib.somecustomsuffix'))
      else:
        self.assertTrue(building.is_ar('libstatic_lib.a'))
  
    # Tests that cmake functions which require evaluation via the node runtime run properly with pthreads
    def test_cmake_pthreads(self):
      self.run_process([EMCMAKE, 'cmake', '-DCMAKE_C_FLAGS=-pthread', test_file('cmake/target_js')])
  
    # Tests that the CMake variable EMSCRIPTEN_VERSION is properly provided to user CMake scripts
    def test_cmake_emscripten_version(self):
      self.run_process([EMCMAKE, 'cmake', test_file('cmake/emscripten_version')])
  
    def test_cmake_emscripten_system_processor(self):
      cmake_dir = test_file('cmake/emscripten_system_processor')
      # The default CMAKE_SYSTEM_PROCESSOR is x86.
      out = self.run_process([EMCMAKE, 'cmake', cmake_dir], stdout=PIPE).stdout
      self.assertContained('CMAKE_SYSTEM_PROCESSOR is x86', out)
  
      # It can be overridden by setting EMSCRIPTEN_SYSTEM_PROCESSOR.
      out = self.run_process(
        [EMCMAKE, 'cmake', cmake_dir, '-DEMSCRIPTEN_SYSTEM_PROCESSOR=arm'], stdout=PIPE).stdout
      self.assertContained('CMAKE_SYSTEM_PROCESSOR is arm', out)
  
    def test_cmake_find_stuff(self):
      # Ensure that zlib exists in the sysroot
      self.run_process([EMCC, test_file('hello_world.c'), '-sUSE_ZLIB'])
      self.run_process([EMCMAKE, 'cmake', test_file('cmake/find_stuff')])
  
    def test_cmake_install(self):
      # Build and install a library `foo`
      os.mkdir('build1')
      self.run_process([EMCMAKE, 'cmake', test_file('cmake/install_lib')], cwd='build1')
      self.run_process(['cmake', '--build', 'build1'])
      # newer versions of cmake support --install but we currently have 3.10.2 in CI
      # so we using `--build --target install` instead.
      self.run_process(['cmake', '--build', 'build1', '--target', 'install'])
      # Build an application that uses `find_package` to locate and use the above library.
      os.mkdir('build2')
      self.run_process([EMCMAKE, 'cmake', test_file('cmake/find_package')], cwd='build2')
      self.run_process(['cmake', '--build', 'build2'])
      self.assertContained('foo: 42\n', self.run_js('build2/Bar.js'))
  
    def test_cmake_find_sdl2(self):
      os.mkdir('build')
      self.run_process([EMCMAKE, 'cmake', test_file('cmake/find_sdl2')], cwd='build')
      self.run_process(['cmake', '--build', 'build'])
      self.assertContained('SDL version: 2.0.', self.run_js('build/sdl2.js'))
  
    def test_system_include_paths(self):
      # Verify that all default include paths are within `emscripten/system`
  
      def verify_includes(stderr):
        self.assertContained('<...> search starts here:', stderr)
        assert stderr.count('End of search list.') == 1, stderr
        start = stderr.index('<...> search starts here:')
        end = stderr.index('End of search list.')
        includes = stderr[start:end]
        includes = [i.strip() for i in includes.splitlines()[1:]]
        cachedir = os.path.normpath(shared.Cache.dirname)
        llvmroot = os.path.normpath(os.path.dirname(config.LLVM_ROOT))
        for i in includes:
          i = os.path.normpath(i)
          # we also allow for the cache include directory and llvm's own builtin includes.
          # all other include paths should be inside the sysroot.
          if i.startswith(cachedir) or i.startswith(llvmroot):
            continue
          self.assertContained(path_from_root('system'), i)
  
      err = self.run_process([EMCC, test_file('hello_world.c'), '-v'], stderr=PIPE).stderr
      verify_includes(err)
      err = self.run_process([EMXX, test_file('hello_world.cpp'), '-v'], stderr=PIPE).stderr
      verify_includes(err)
  
    def test_failure_error_code(self):
      for compiler in [EMCC, EMXX]:
        # Test that if one file is missing from the build, then emcc shouldn't succeed, and shouldn't produce an output file.
        self.expect_fail([compiler, test_file('hello_world.c'), 'this_file_is_missing.c', '-o', 'out.js'])
        self.assertFalse(os.path.exists('out.js'))
  
    def test_use_cxx(self):
      create_file('empty_file', ' ')
      dash_xc = self.run_process([EMCC, '-v', '-xc', 'empty_file'], stderr=PIPE).stderr
      self.assertNotContained('-x c++', dash_xc)
      dash_xcpp = self.run_process([EMCC, '-v', '-xc++', 'empty_file'], stderr=PIPE).stderr
      self.assertContained('-x c++', dash_xcpp)
  
    def test_cxx11(self):
      for std in ['-std=c++11', '--std=c++11']:
        for compiler in [EMCC, EMXX]:
          self.run_process([compiler, std, test_file('hello_cxx11.cpp')])
  
    # Regression test for issue #4522: Incorrect CC vs CXX detection
    def test_incorrect_c_detection(self):
      # This auto-detection only works for the compile phase.
      # For linking you need to use `em++` or pass `-x c++`
      create_file('test.c', 'foo\n')
      for compiler in [EMCC, EMXX]:
        self.run_process([compiler, '-c', '-lembind', '--embed-file', 'test.c', test_file('hello_world.cpp')])
  
    def test_odd_suffixes(self):
      for suffix in ['CPP', 'c++', 'C++', 'cxx', 'CXX', 'cc', 'CC']:
        self.clear()
        print(suffix)
        shutil.copyfile(test_file('hello_world.c'), 'test.' + suffix)
        self.run_process([EMCC, self.in_dir('test.' + suffix)])
        self.assertContained('hello, world!', self.run_js('a.out.js'))
  
      for suffix in ['lo']:
        self.clear()
        print(suffix)
        self.run_process([EMCC, test_file('hello_world.c'), '-shared', '-o', 'binary.' + suffix])
        self.run_process([EMCC, 'binary.' + suffix])
        self.assertContained('hello, world!', self.run_js('a.out.js'))
  
    def test_preprocessed_input(self):
      # .i and .ii files are assumed to be the output the pre-processor so clang doesn't add include
      # paths.  This means we can only compile and run things that don't contain includes.
      for suffix in ['.i', '.ii']:
        create_file('simple' + suffix, '''
          #ifdef __cplusplus
          extern "C" {
          #endif
          int puts(const char *s);
          #ifdef __cplusplus
          }
          #endif
          int main() { puts("hello"); }
          ''')
        self.do_runf('simple' + suffix, 'hello')
  
        create_file('with_include' + suffix, '#include <stdio.h>\nint main() { puts("hello"); }')
        err = self.expect_fail([EMCC, 'with_include' + suffix])
        self.assertContained('fatal error: \'stdio.h\' file not found', err)
  
    def test_wl_linkflags(self):
      # Test path -L and -l via -Wl, arguments and -Wl, response files
      create_file('main.cpp', '''
        extern "C" void printey();
        int main() {
          printey();
          return 0;
        }
      ''')
      create_file('libfile.cpp', '''
        #include <stdio.h>
        extern "C" void printey() {
          printf("hello from lib\\n");
        }
      ''')
      create_file('linkflags.txt', '''
      -L.
      -lfoo
      ''')
      self.run_process([EMXX, '-o', 'libfile.o', '-c', 'libfile.cpp'])
      self.run_process([EMAR, 'cr', 'libfoo.a', 'libfile.o'])
      self.run_process([EMXX, 'main.cpp', '-L.', '-lfoo'])
      self.run_process([EMXX, 'main.cpp', '-Wl,-L.', '-Wl,-lfoo'])
      self.run_process([EMXX, 'main.cpp', '-Wl,@linkflags.txt'])
  
    def test_l_link(self):
      # Linking with -lLIBNAME and -L/DIRNAME should work, also should work with spaces
      create_file('main.cpp', '''
        extern void printey();
        int main() {
          printey();
          return 0;
        }
      ''')
      create_file('libfile.cpp', '''
        #include <stdio.h>
        void printey() {
          printf("hello from lib\\n");
        }
      ''')
  
      ensure_dir('libdir')
      libfile = self.in_dir('libdir', 'libfile.so')
      aout = 'a.out.js'
  
      def build(path, args):
        self.run_process([EMCC, path] + args)
  
      # Test linking the library built here by emcc
      build('libfile.cpp', ['-c'])
      shutil.move('libfile.o', libfile)
      build('main.cpp', ['-L' + 'libdir', '-lfile'])
  
      self.assertContained('hello from lib', self.run_js(aout))
  
      # Also test execution with `-l c` and space-separated library linking syntax
      os.remove(aout)
      build('libfile.cpp', ['-c', '-l', 'c'])
      shutil.move('libfile.o', libfile)
      build('main.cpp', ['-L', 'libdir', '-l', 'file'])
  
      self.assertContained('hello from lib', self.run_js(aout))
  
      # Must not leave unneeded linker stubs
      self.assertNotExists('a.out')
      self.assertNotExists('a.exe')
  
    def test_commons_link(self):
      create_file('a.h', r'''
  #if !defined(A_H)
  #define A_H
  extern int foo[8];
  #endif
  ''')
      create_file('a.c', r'''
  #include "a.h"
  int foo[8];
  ''')
      create_file('main.c', r'''
  #include <stdio.h>
  #include "a.h"
  
  int main() {
      printf("|%d|\n", foo[0]);
      return 0;
  }
  ''')
  
      self.run_process([EMCC, '-o', 'a.o', '-c', 'a.c'])
      self.run_process([EMAR, 'rv', 'library.a', 'a.o'])
      self.run_process([EMCC, '-o', 'main.o', '-c', 'main.c'])
      self.run_process([EMCC, '-o', 'a.js', 'main.o', 'library.a'])
      self.assertContained('|0|', self.run_js('a.js'))
  
    @parameterized({
      'expand_symlinks': [[]],
      'no_canonical_prefixes': [['-no-canonical-prefixes']],
    })
    @no_windows('Windows does not support symlinks')
    def test_symlink_points_to_bad_suffix(self, flags):
      """Tests compiling a symlink where foobar.c points to foobar.xxx.
  
      In this case, we should always successfully compile the code."""
      create_file('foobar.xxx', 'int main(){ return 0; }')
      os.symlink('foobar.xxx', 'foobar.c')
      self.run_process([EMCC, 'foobar.c', '-c', '-o', 'foobar.o'] + flags)
  
    @no_windows('Windows does not support symlinks')
    def test_symlink_has_bad_suffix(self):
      """Tests that compiling foobar.xxx fails even if it points to foobar.c.
      """
      create_file('foobar.c', 'int main(){ return 0; }')
      os.symlink('foobar.c', 'foobar.xxx')
      err = self.expect_fail([EMCC, 'foobar.xxx', '-o', 'foobar.js'])
      self.assertContained('unknown file type: foobar.xxx', err)
  
    def test_multiply_defined_libsymbols(self):
      lib_name = 'libA.c'
      a2_name = 'a2.c'
      b2_name = 'b2.c'
      main_name = 'main.c'
      create_file(lib_name, 'int mult() { return 1; }')
      create_file(a2_name, 'void x() {}')
      create_file(b2_name, 'void y() {}')
      create_file(main_name, r'''
        #include <stdio.h>
        int mult();
        int main() {
          printf("result: %d\n", mult());
          return 0;
        }
      ''')
  
      self.emcc(lib_name, ['-shared'], output_filename='libA.so')
  
      self.emcc(a2_name, ['-r', '-L.', '-lA'])
      self.emcc(b2_name, ['-r', '-L.', '-lA'])
  
      self.emcc(main_name, ['-L.', '-lA', a2_name + '.o', b2_name + '.o'], output_filename='a.out.js')
  
      self.assertContained('result: 1', self.run_js('a.out.js'))
  
    def test_multiply_defined_libsymbols_2(self):
      a = "int x() { return 55; }"
      a_name = 'a.c'
      create_file(a_name, a)
      b = "int y() { return 2; }"
      b_name = 'b.c'
      create_file(b_name, b)
      c = "int z() { return 5; }"
      c_name = 'c.c'
      create_file(c_name, c)
      main = r'''
        #include <stdio.h>
        int x();
        int y();
        int z();
        int main() {
          printf("result: %d\n", x() + y() + z());
          return 0;
        }
      '''
      main_name = 'main.c'
      create_file(main_name, main)
  
      self.emcc(a_name, ['-c']) # a.c.o
      self.emcc(b_name, ['-c']) # b.c.o
      self.emcc(c_name, ['-c']) # c.c.o
      lib_name = 'libLIB.a'
      building.emar('cr', lib_name, [a_name + '.o', b_name + '.o']) # libLIB.a with a and b
  
      # a is in the lib AND in an .o, so should be ignored in the lib. We do still need b from the lib though
      self.emcc(main_name, [a_name + '.o', c_name + '.o', '-L.', '-lLIB'], output_filename='a.out.js')
  
      self.assertContained('result: 62', self.run_js('a.out.js'))
  
    def test_link_group(self):
      create_file('lib.c', 'int x() { return 42; }')
  
      create_file('main.c', r'''
        #include <stdio.h>
        int x();
        int main() {
          printf("result: %d\n", x());
          return 0;
        }
      ''')
  
      self.emcc('lib.c', ['-c']) # lib.c.o
      lib_name = 'libLIB.a'
      building.emar('cr', lib_name, ['lib.c.o']) # libLIB.a with lib.c.o
  
      def test(compiler, main_name, lib_args, err_expected):
        print(err_expected)
        output = self.run_process([compiler, main_name, '-o', 'a.out.js'] + lib_args, stderr=PIPE, check=not err_expected)
        if err_expected:
          self.assertContained(err_expected, output.stderr)
        else:
          self.assertNotContained('undefined symbol', output.stderr)
          out_js = 'a.out.js'
          self.assertExists(out_js)
          self.assertContained('result: 42', self.run_js(out_js))
  
      test(EMCC, 'main.c', ['-Wl,--start-group', lib_name, '-Wl,--end-group'], None)
      test(EMCC, 'main.c', ['-Wl,--start-group', lib_name], None)
  
      print('embind test with groups')
  
      create_file('main.cpp', r'''
        #include <stdio.h>
        #include <emscripten/val.h>
        using namespace emscripten;
        extern "C" int x();
        int main() {
          int y = -x();
          y = val::global("Math").call<int>("abs", y);
          printf("result: %d\n", y);
          return 0;
        }
      ''')
      test(EMXX, 'main.cpp', ['-Wl,--start-group', lib_name, '-Wl,--end-group', '-lembind'], None)
  
    def test_whole_archive(self):
      # Verify that -Wl,--whole-archive includes the static constructor from the
      # otherwise unreferenced library.
      self.run_process([EMCC, '-c', '-o', 'main.o', test_file('test_whole_archive/main.c')])
      self.run_process([EMCC, '-c', '-o', 'testlib.o', test_file('test_whole_archive/testlib.c')])
      self.run_process([EMAR, 'crs', 'libtest.a', 'testlib.o'])
  
      self.run_process([EMCC, '-Wl,--whole-archive', 'libtest.a', '-Wl,--no-whole-archive', 'main.o'])
      self.assertContained('foo is: 42\n', self.run_js('a.out.js'))
  
      self.run_process([EMCC, '-Wl,-whole-archive', 'libtest.a', '-Wl,-no-whole-archive', 'main.o'])
      self.assertContained('foo is: 42\n', self.run_js('a.out.js'))
  
      # Verify the --no-whole-archive prevents the inclusion of the ctor
      self.run_process([EMCC, '-Wl,-whole-archive', '-Wl,--no-whole-archive', 'libtest.a', 'main.o'])
      self.assertContained('foo is: 0\n', self.run_js('a.out.js'))
  
    def test_whole_archive_48156(self):
      # Regression test for http://llvm.org/PR48156
      # TODO: distill this test further and move to lld
      self.run_process([EMXX, '-c', '-o', 'foo.o', '-O1',
                        test_file('test_whole_archive_foo.cpp')])
      self.run_process([EMXX, '-c', '-o', 'main.o', '-O1',
                        test_file('test_whole_archive_main.cpp')])
      self.run_process([EMAR, 'rc', 'libfoo.a', 'foo.o'])
      self.run_process([EMAR, 'rc', 'libmain.a', 'main.o'])
      self.run_process([
          EMXX, test_file('test_whole_archive_init.cpp'),
          '-O1', 'libfoo.a', '-Wl,--whole-archive', 'libmain.a', '-Wl,--no-whole-archive'])
      self.assertContained('Result: 11', self.run_js('a.out.js'))
  
    def test_link_group_bitcode(self):
      create_file('1.c', r'''
  int f(void);
  int main() {
    f();
    return 0;
  }
  ''')
      create_file('2.c', r'''
  #include <stdio.h>
  int f() {
    printf("Hello\n");
    return 0;
  }
  ''')
  
      self.run_process([EMCC, '-flto', '-o', '1.o', '-c', '1.c'])
      self.run_process([EMCC, '-flto', '-o', '2.o', '-c', '2.c'])
      self.run_process([EMAR, 'crs', '2.a', '2.o'])
      self.run_process([EMCC, '-r', '-flto', '-o', 'out.o', '-Wl,--start-group', '2.a', '1.o', '-Wl,--end-group'])
      self.run_process([EMCC, 'out.o'])
      self.assertContained('Hello', self.run_js('a.out.js'))
  
    # We deliberately ignore duplicate input files in order to allow
    # "libA.so" on the command line twice. This is not really .so support
    # and the .so files are really object files.
    def test_redundant_link(self):
      create_file('libA.c', 'int mult() { return 1; }')
      create_file('main.c', r'''
        #include <stdio.h>
        int mult();
        int main() {
          printf("result: %d\n", mult());
          return 0;
        }
      ''')
      self.emcc('libA.c', ['-shared'], output_filename='libA.so')
      self.emcc('main.c', ['libA.so', 'libA.so'], output_filename='a.out.js')
      self.assertContained('result: 1', self.run_js('a.out.js'))
  
    def test_dot_a_all_contents_invalid(self):
      # check that we error if an object file in a .a is not valid bitcode.
      # do not silently ignore native object files, which may have been
      # built by mistake
      create_file('native.c', 'int native() { return 5; }')
      create_file('main.c', 'extern int native(); int main() { return native(); }')
      self.run_process([CLANG_CC, 'native.c', '-target', 'x86_64-linux', '-c', '-o', 'native.o'])
      self.run_process([EMAR, 'crs', 'libfoo.a', 'native.o'])
      stderr = self.expect_fail([EMCC, 'main.c', 'libfoo.a'])
      self.assertContained('unknown file type', stderr)
  
    def test_export_all(self):
      lib = r'''
        #include <stdio.h>
        void libf1() { printf("libf1\n"); }
        void libf2() { printf("libf2\n"); }
      '''
      create_file('lib.c', lib)
  
      create_file('main.js', '''
        var Module = {
          onRuntimeInitialized: function() {
            _libf1();
            _libf2();
          }
        };
      ''')
  
      self.emcc('lib.c', ['-sEXPORT_ALL', '-sLINKABLE', '--pre-js', 'main.js'], output_filename='a.out.js')
      self.assertContained('libf1\nlibf2\n', self.run_js('a.out.js'))
  
    def test_export_all_and_exported_functions(self):
      # EXPORT_ALL should not export library functions by default.
      # This means that to export library function you also need to explicitly
      # list them in EXPORTED_FUNCTIONS.
      lib = r'''
        #include <stdio.h>
        #include <emscripten.h>
        EMSCRIPTEN_KEEPALIVE void libfunc() { puts("libfunc\n"); }
        void libfunc2() { puts("libfunc2\n"); }
      '''
      create_file('lib.c', lib)
      create_file('main.js', '''
        var Module = {
          onRuntimeInitialized: function() {
            _libfunc();
            _libfunc2();
          }
        };
      ''')
  
      # libfunc2 should not be linked by default, even with EXPORT_ALL
      self.emcc('lib.c', ['-sEXPORT_ALL', '--pre-js', 'main.js'], output_filename='a.out.js')
      err = self.run_js('a.out.js', assert_returncode=NON_ZERO)
      self.assertContained('_libfunc2 is not defined', err)
  
      self.emcc('lib.c', ['-sEXPORTED_FUNCTIONS=_libfunc2', '-sEXPORT_ALL', '--pre-js', 'main.js'], output_filename='a.out.js')
      self.assertContained('libfunc\n', self.run_js('a.out.js'))
  
    def test_stdin(self):
      def run_test():
        for engine in config.JS_ENGINES:
          if engine == config.V8_ENGINE:
            continue # no stdin support in v8 shell
          engine[0] = os.path.normpath(engine[0])
          print(engine, file=sys.stderr)
          # work around a bug in python's subprocess module
          # (we'd use self.run_js() normally)
          try_delete('out.txt')
          cmd = jsrun.make_command(os.path.normpath('out.js'), engine)
          cmd = shared.shlex_join(cmd)
          if WINDOWS:
            os.system(f'type "in.txt" | {cmd} >out.txt')
          else: # posix
            os.system(f'cat in.txt | {cmd} > out.txt')
          self.assertContained('abcdef\nghijkl\neof', read_file('out.txt'))
  
      self.emcc(test_file('module/test_stdin.c'), output_filename='out.js')
      create_file('in.txt', 'abcdef\nghijkl')
      run_test()
      self.emcc(test_file('module/test_stdin.c'),
                ['-O2', '--closure=1'], output_filename='out.js')
      run_test()
  
    def test_ungetc_fscanf(self):
      create_file('main.cpp', r'''
        #include <stdio.h>
        int main(int argc, char const *argv[])
        {
            char str[4] = {0};
            FILE* f = fopen("my_test.input", "r");
            if (f == NULL) {
                printf("cannot open file\n");
                return -1;
            }
            ungetc('x', f);
            ungetc('y', f);
            ungetc('z', f);
            fscanf(f, "%3s", str);
            printf("%s\n", str);
            return 0;
        }
      ''')
      create_file('my_test.input', 'abc')
      self.emcc('main.cpp', ['--embed-file', 'my_test.input'], output_filename='a.out.js')
      self.assertContained('zyx', self.run_process(config.JS_ENGINES[0] + ['a.out.js'], stdout=PIPE, stderr=PIPE).stdout)
  
    def test_abspaths(self):
      # Includes with absolute paths are generally dangerous, things like -I/usr/.. will get to system local headers, not our portable ones.
  
      shutil.copyfile(test_file('hello_world.c'), 'main.c')
  
      for args, expected in [(['-I/usr/something', '-Wwarn-absolute-paths'], True),
                             (['-L/usr/something', '-Wwarn-absolute-paths'], True),
                             (['-I/usr/something'], False),
                             (['-L/usr/something'], False),
                             (['-I/usr/something', '-Wno-warn-absolute-paths'], False),
                             (['-L/usr/something', '-Wno-warn-absolute-paths'], False),
                             (['-Isubdir/something', '-Wwarn-absolute-paths'], False),
                             (['-Lsubdir/something', '-Wwarn-absolute-paths'], False),
                             ([], False)]:
        print(args, expected)
        proc = self.run_process([EMCC, 'main.c'] + args, stderr=PIPE)
        WARNING = 'encountered. If this is to a local system header/library, it may cause problems (local system files make sense for compiling natively on your system, but not necessarily to JavaScript)'
        self.assertContainedIf(WARNING, proc.stderr, expected)
  
    def test_local_link(self):
      # Linking a local library directly, like /usr/lib/libsomething.so, cannot work of course since it
      # doesn't contain bitcode. However, when we see that we should look for a bitcode file for that
      # library in the -L paths and system/lib
      create_file('main.cpp', '''
        extern void printey();
        int main() {
          printey();
          return 0;
        }
      ''')
  
      ensure_dir('subdir')
      create_file('subdir/libfile.so.1.2.3', 'this is not llvm bitcode!')
  
      create_file('libfile.cpp', '''
        #include <stdio.h>
        void printey() {
          printf("hello from lib\\n");
        }
      ''')
  
      self.run_process([EMXX, 'libfile.cpp', '-shared', '-o', 'libfile.so'], stderr=PIPE)
      err = self.run_process([EMXX, 'main.cpp', Path('subdir/libfile.so.1.2.3'), '-L.'], stderr=PIPE).stderr
      self.assertContained('Mapping to `-lfile` and hoping for the best [-Wmap-unrecognized-libraries]', err)
      self.assertContained('hello from lib', self.run_js('a.out.js'))
  
    def test_identical_basenames(self):
      # Issue 287: files in different dirs but with the same basename get confused as the same,
      # causing multiply defined symbol errors
      ensure_dir('foo')
      ensure_dir('bar')
      create_file('foo/main.cpp', '''
        extern void printey();
        int main() {
          printey();
          return 0;
        }
      ''')
      create_file('bar/main.cpp', '''
        #include <stdio.h>
        void printey() { printf("hello there\\n"); }
      ''')
  
      self.run_process([EMXX, Path('foo/main.cpp'), Path('bar/main.cpp')])
      self.assertContained('hello there', self.run_js('a.out.js'))
  
      # ditto with first creating .o files
      try_delete('a.out.js')
      self.run_process([EMXX, '-c', Path('foo/main.cpp'), '-o', Path('foo/main.o')])
      self.run_process([EMXX, '-c', Path('bar/main.cpp'), '-o', Path('bar/main.o')])
      self.run_process([EMCC, Path('foo/main.o'), Path('bar/main.o')])
      self.assertContained('hello there', self.run_js('a.out.js'))
  
    def test_main_a(self):
      # if main() is in a .a, we need to pull in that .a
  
      main_name = 'main.c'
      create_file(main_name, r'''
        #include <stdio.h>
        extern int f();
        int main() {
          printf("result: %d.\n", f());
          return 0;
        }
      ''')
  
      other_name = 'other.c'
      create_file(other_name, r'''
        #include <stdio.h>
        int f() { return 12346; }
      ''')
  
      self.run_process([EMCC, main_name, '-c', '-o', main_name + '.o'])
      self.run_process([EMCC, other_name, '-c', '-o', other_name + '.o'])
  
      self.run_process([EMAR, 'cr', main_name + '.a', main_name + '.o'])
  
      self.run_process([EMCC, other_name + '.o', main_name + '.a'])
  
      self.assertContained('result: 12346.', self.run_js('a.out.js'))
  
    def test_multiple_archives_duplicate_basenames(self):
      create_file('common.c', r'''
        #include <stdio.h>
        void a(void) {
          printf("a\n");
        }
      ''')
      self.run_process([EMCC, 'common.c', '-c', '-o', 'common.o'])
      try_delete('liba.a')
      self.run_process([EMAR, 'rc', 'liba.a', 'common.o'])
  
      create_file('common.c', r'''
        #include <stdio.h>
        void b(void) {
          printf("b\n");
        }
      ''')
      self.run_process([EMCC, 'common.c', '-c', '-o', 'common.o'])
      try_delete('libb.a')
      self.run_process([EMAR, 'rc', 'libb.a', 'common.o'])
  
      create_file('main.c', r'''
        void a(void);
        void b(void);
        int main() {
          a();
          b();
        }
      ''')
  
      self.run_process([EMCC, 'main.c', '-L.', '-la', '-lb'])
      self.assertContained('a\nb\n', self.run_js('a.out.js'))
  
    def test_archive_duplicate_basenames(self):
      ensure_dir('a')
      create_file('a/common.c', r'''
        #include <stdio.h>
        void a(void) {
          printf("a\n");
        }
      ''')
      self.run_process([EMCC, Path('a/common.c'), '-c', '-o', Path('a/common.o')])
  
      ensure_dir('b')
      create_file('b/common.c', r'''
        #include <stdio.h>
        void b(void) {
          printf("b...\n");
        }
      ''')
      self.run_process([EMCC, Path('b/common.c'), '-c', '-o', Path('b/common.o')])
  
      try_delete('liba.a')
      self.run_process([EMAR, 'rc', 'liba.a', Path('a/common.o'), Path('b/common.o')])
  
      # Verify that archive contains basenames with hashes to avoid duplication
      text = self.run_process([EMAR, 't', 'liba.a'], stdout=PIPE).stdout
      self.assertEqual(text.count('common'), 2)
      for line in text.split('\n'):
        # should not have huge hash names
        self.assertLess(len(line), 20, line)
  
      create_file('main.c', r'''
        void a(void);
        void b(void);
        int main() {
          a();
          b();
        }
      ''')
      err = self.run_process([EMCC, 'main.c', '-L.', '-la'], stderr=PIPE).stderr
      self.assertNotIn('archive file contains duplicate entries', err)
      self.assertContained('a\nb...\n', self.run_js('a.out.js'))
  
      # Using llvm-ar directly should cause duplicate basenames
      try_delete('libdup.a')
      self.run_process([LLVM_AR, 'rc', 'libdup.a', Path('a/common.o'), Path('b/common.o')])
      text = self.run_process([EMAR, 't', 'libdup.a'], stdout=PIPE).stdout
      self.assertEqual(text.count('common.o'), 2)
  
      # With fastcomp we don't support duplicate members so this should generate
      # a warning.  With the wasm backend (lld) this is fully supported.
      cmd = [EMCC, 'main.c', '-L.', '-ldup']
      self.run_process(cmd)
      self.assertContained('a\nb...\n', self.run_js('a.out.js'))
  
    def test_export_from_archive(self):
      export_name = 'this_is_an_entry_point'
      full_export_name = '_this_is_an_entry_point'
  
      create_file('export.c', r'''
        #include <stdio.h>
        void this_is_an_entry_point(void) {
          printf("Hello, world!\n");
        }
      ''')
      self.run_process([EMCC, 'export.c', '-c', '-o', 'export.o'])
      self.run_process([EMAR, 'rc', 'libexport.a', 'export.o'])
  
      create_file('main.c', r'''
        int main() {
          return 0;
        }
      ''')
  
      # Sanity check: the symbol should not be linked in if not requested.
      self.run_process([EMCC, 'main.c', '-L.', '-lexport'])
      self.assertFalse(self.is_exported_in_wasm(export_name, 'a.out.wasm'))
  
      # Exporting it causes it to appear in the output.
      self.run_process([EMCC, 'main.c', '-L.', '-lexport', '-sEXPORTED_FUNCTIONS=%s' % full_export_name])
      self.assertTrue(self.is_exported_in_wasm(export_name, 'a.out.wasm'))
  
    @parameterized({
      'embed': (['--embed-file', 'somefile.txt'],),
      'embed-twice': (['--embed-file', 'somefile.txt', '--embed-file', 'somefile.txt'],),
      'preload': (['--preload-file', 'somefile.txt'],)
    })
    def test_include_file(self, args):
      create_file('somefile.txt', 'hello from a file with lots of data and stuff in it thank you very much')
      create_file('main.c', r'''
        #include <assert.h>
        #include <stdio.h>
        int main() {
          FILE *f = fopen("somefile.txt", "r");
          assert(f);
          char buf[100];
          int rtn = fread(buf, 1, 20, f);
          assert(rtn == 20);
          buf[20] = 0;
          fclose(f);
          printf("|%s|\n", buf);
          return 0;
        }
      ''')
  
      self.run_process([EMCC, 'main.c'] + args)
      # run in node.js to ensure we verify that file preloading works there
      result = self.run_js('a.out.js', engine=config.NODE_JS)
      self.assertContained('|hello from a file wi|', result)
  
    @parameterized({
      '': ([],),
      'wasmfs': (['-sWASMFS'],),
    })
    def test_embed_file_dup(self, args):
      ensure_dir(self.in_dir('tst', 'test1'))
      ensure_dir(self.in_dir('tst', 'test2'))
  
      open(self.in_dir('tst', 'aa.txt'), 'w').write('frist')
      open(self.in_dir('tst', 'test1', 'aa.txt'), 'w').write('sacond')
      open(self.in_dir('tst', 'test2', 'aa.txt'), 'w').write('thard')
      create_file('main.cpp', r'''
        #include <stdio.h>
        #include <string.h>
        void print_file(const char *name) {
          FILE *f = fopen(name, "r");
          char buf[100];
          memset(buf, 0, 100);
          fread(buf, 1, 20, f);
          buf[20] = 0;
          fclose(f);
          printf("|%s|\n", buf);
        }
        int main() {
          print_file("tst/aa.txt");
          print_file("tst/test1/aa.txt");
          print_file("tst/test2/aa.txt");
          return 0;
        }
      ''')
  
      self.run_process([EMXX, 'main.cpp', '--embed-file', 'tst'] + args)
      self.assertContained('|frist|\n|sacond|\n|thard|\n', self.run_js('a.out.js'))
  
    def test_exclude_file(self):
      ensure_dir(self.in_dir('tst', 'abc.exe'))
      ensure_dir(self.in_dir('tst', 'abc.txt'))
  
      open(self.in_dir('tst', 'hello.exe'), 'w').write('hello')
      open(self.in_dir('tst', 'hello.txt'), 'w').write('world')
      open(self.in_dir('tst', 'abc.exe', 'foo'), 'w').write('emscripten')
      open(self.in_dir('tst', 'abc.txt', 'bar'), 'w').write('!!!')
      create_file('main.cpp', r'''
        #include <stdio.h>
        int main() {
          if(fopen("tst/hello.exe", "rb")) printf("Failed\n");
          if(!fopen("tst/hello.txt", "rb")) printf("Failed\n");
          if(fopen("tst/abc.exe/foo", "rb")) printf("Failed\n");
          if(!fopen("tst/abc.txt/bar", "rb")) printf("Failed\n");
  
          return 0;
        }
      ''')
  
      self.run_process([EMXX, 'main.cpp', '--embed-file', 'tst', '--exclude-file', '*.exe'])
      self.assertEqual(self.run_js('a.out.js').strip(), '')
  
    def test_dylink_exceptions_and_assetions(self):
      # Linking side modules using the STL and exceptions should not abort with
      # "function in Table but not functionsInTableMap" when using ASSERTIONS=2
  
      # A side module that uses the STL enables exceptions.
      create_file('side.cpp', r'''
        #include <vector>
        std::vector<int> v;
        std::vector<int> side(int n) {
            for (int i=0; i<n; i++) v.push_back(i);
            return v;
        }
        ''')
      self.run_process([
        EMXX,
        '-o', 'side.wasm',
        'side.cpp',
        '-sSIDE_MODULE=1',
        '-sDISABLE_EXCEPTION_CATCHING=0',
        '-sASSERTIONS=2'])
  
      create_file('main.cpp', r'''
        #include <stdio.h>
        #include <vector>
        std::vector<int> side(int n);
        int main(void) {
            auto v = side(10);
            for (auto i : v) printf("%d", i);
            printf("\n");
            return 0;
        }
        ''')
  
      self.do_smart_test(
        'main.cpp',
        ['0123456789'],
        emcc_args=[
          '-sEXIT_RUNTIME=1',
          '-sMAIN_MODULE=1',
          '-sDISABLE_EXCEPTION_CATCHING=0',
          '-sASSERTIONS=2',
          'side.wasm',
        ])
  
    def test_multidynamic_link(self):
      # Linking the same dynamic library in statically will error, normally, since we statically link
      # it, causing dupe symbols
  
      def test(link_flags, lib_suffix):
        print(link_flags, lib_suffix)
  
        self.clear()
        ensure_dir('libdir')
  
        create_file('main.cpp', r'''
          #include <stdio.h>
          extern void printey();
          extern void printother();
          int main() {
            printf("*");
            printey();
            printf("\n");
            printother();
            printf("\n");
            printf("*");
            return 0;
          }
        ''')
  
        create_file('libdir/libfile.cpp', '''
          #include <stdio.h>
          void printey() {
            printf("hello from lib");
          }
        ''')
  
        create_file('libdir/libother.cpp', '''
          #include <stdio.h>
          extern void printey();
          void printother() {
            printf("|");
            printey();
            printf("|");
          }
        ''')
  
        # Build libfile normally into an .so
        self.run_process([EMXX, Path('libdir/libfile.cpp'), '-shared', '-o', Path('libdir/libfile.so' + lib_suffix)])
        # Build libother and dynamically link it to libfile
        self.run_process([EMXX, '-Llibdir', Path('libdir/libother.cpp')] + link_flags + ['-shared', '-o', Path('libdir/libother.so')])
        # Build the main file, linking in both the libs
        self.run_process([EMXX, '-Llibdir', os.path.join('main.cpp')] + link_flags + ['-lother', '-c'])
        print('...')
        # The normal build system is over. We need to do an additional step to link in the dynamic
        # libraries, since we ignored them before
        self.run_process([EMCC, '-Llibdir', 'main.o'] + link_flags + ['-lother', '-sEXIT_RUNTIME'])
  
        self.assertContained('*hello from lib\n|hello from lib|\n*', self.run_js('a.out.js'))
  
      test(['-lfile'], '') # -l, auto detection from library path
      test([self.in_dir('libdir', 'libfile.so.3.1.4.1.5.9')], '.3.1.4.1.5.9') # handle libX.so.1.2.3 as well
  
    @node_pthreads
    def test_dylink_pthread_static_data(self):
      # Test that a side module uses the same static data region for global objects across all threads
  
      # A side module with a global object with a constructor.
      # * The global object must have a non-zero initial value to make sure that
      #   the memory is zero-initialized only once (and not once per thread).
      # * The global object must have a constructor to make sure that it is
      #   constructed only once (and not once per thread).
      create_file('side.cpp', r'''
        struct Data {
            Data() : value(42) {}
            int value;
        } data;
        int * get_address() {
            return &data.value;
        }
        ''')
      self.run_process([
        EMCC,
        '-o', 'side.wasm',
        'side.cpp',
        '-pthread', '-Wno-experimental',
        '-sSIDE_MODULE=1'])
  
      create_file('main.cpp', r'''
        #include <stdio.h>
        #include <thread>
        int * get_address();
        int main(void) {
            *get_address() = 123;
            std::thread([]{
              printf("%d\n", *get_address());
            }).join();
            return 0;
        }
        ''')
  
      self.do_smart_test(
        'main.cpp',
        ['123'],
        emcc_args=[
          '-pthread', '-Wno-experimental',
          '-sPROXY_TO_PTHREAD',
          '-sEXIT_RUNTIME=1',
          '-sMAIN_MODULE=1',
          'side.wasm',
        ])
  
    def test_dylink_pthread_warning(self):
      err = self.expect_fail([EMCC, '-Werror', '-sMAIN_MODULE', '-sUSE_PTHREADS', test_file('hello_world.c')])
      self.assertContained('error: -s MAIN_MODULE + pthreads is experimental', err)
  
    def test_dylink_no_autoload(self):
      create_file('main.c', r'''
        #include <stdio.h>
        int sidey();
        int main() {
          printf("sidey: %d\n", sidey());
          return 0;
        }''')
      create_file('side.c', 'int sidey() { return 42; }')
      self.run_process([EMCC, '-sSIDE_MODULE', 'side.c', '-o', 'libside.wasm'])
  
      # First show everything working as expected with AUTOLOAD_DYLIBS
      self.run_process([EMCC, '-sMAIN_MODULE=2', 'main.c', 'libside.wasm'])
      output = self.run_js('a.out.js')
      self.assertContained('sidey: 42\n', output)
  
      # Same again but with NO_AUTOLOAD_DYLIBS.   This time we expect the call to sidey
      # to fail at runtime.
      self.run_process([EMCC, '-sMAIN_MODULE=2', 'main.c', 'libside.wasm', '-sNO_AUTOLOAD_DYLIBS'])
      output = self.run_js('a.out.js', assert_returncode=NON_ZERO)
      self.assertContained("external symbol 'sidey' is missing. perhaps a side module was not linked in?", output)
  
      # Now with NO_AUTOLOAD_DYLIBS, but with manual loading of libside.wasm using loadDynamicLibrary
      create_file('pre.js', '''
      Module.preRun = function() { loadDynamicLibrary('libside.wasm'); }
      ''')
      self.run_process([EMCC, '-sMAIN_MODULE=2', 'main.c', 'libside.wasm', '-sNO_AUTOLOAD_DYLIBS', '--pre-js=pre.js'])
      output = self.run_js('a.out.js')
      self.assertContained('sidey: 42\n', output)
  
    def test_js_link(self):
      create_file('main.cpp', '''
        #include <stdio.h>
        int main() {
          printf("hello from main\\n");
          return 0;
        }
      ''')
      create_file('before.js', '''
        var MESSAGE = 'hello from js';
        // Module is initialized with empty object by default, so if there are no keys - nothing was run yet
        if (Object.keys(Module).length) throw 'This code should run before anything else!';
      ''')
      create_file('after.js', '''
        out(MESSAGE);
      ''')
  
      self.run_process([EMXX, 'main.cpp', '--pre-js', 'before.js', '--post-js', 'after.js', '-sWASM_ASYNC_COMPILATION=0'])
      self.assertContained('hello from main\nhello from js\n', self.run_js('a.out.js'))
  
    def test_sdl_endianness(self):
      create_file('main.cpp', r'''
        #include <stdio.h>
        #include <SDL/SDL.h>
  
        int main() {
          printf("%d, %d, %d\n", SDL_BYTEORDER, SDL_LIL_ENDIAN, SDL_BIG_ENDIAN);
          return 0;
        }
      ''')
      self.run_process([EMXX, 'main.cpp'])
      self.assertContained('1234, 1234, 4321\n', self.run_js('a.out.js'))
  
    def test_sdl2_mixer_wav(self):
      self.emcc(test_file('sdl2_mixer_wav.c'), ['-sUSE_SDL_MIXER=2'], output_filename='a.out.js')
  
    def test_sdl2_linkable(self):
      # Ensure that SDL2 can be built with LINKABLE.  This implies there are no undefined
      # symbols in the library (because LINKABLE includes the entire library).
      self.emcc(test_file('sdl2_misc.c'), ['-sLINKABLE', '-sUSE_SDL=2'], output_filename='a.out.js')
  
    def test_sdl2_gfx_linkable(self):
      # Same as above but for sdl2_gfx library
      self.emcc(test_file('sdl2_misc.c'), ['-Wl,-fatal-warnings', '-sLINKABLE', '-sUSE_SDL_GFX=2'], output_filename='a.out.js')
  
    def test_libpng(self):
      shutil.copyfile(test_file('third_party/libpng/pngtest.png'), 'pngtest.png')
      self.emcc(test_file('third_party/libpng/pngtest.c'), ['--embed-file', 'pngtest.png', '-sUSE_LIBPNG'], output_filename='a.out.js')
      output = self.run_js('a.out.js')
      self.assertContained('libpng passes test', output)
  
    def test_giflib(self):
      shutil.copyfile(test_file('third_party/giflib/treescap.gif'), 'treescap.gif')
      self.emcc(test_file('third_party/giflib/giftext.c'), ['--embed-file', 'treescap.gif', '-sUSE_GIFLIB'], output_filename='a.out.js')
      self.assertContained('GIF file terminated normally', self.run_js('a.out.js', args=['treescap.gif']))
  
    def test_libjpeg(self):
      shutil.copyfile(test_file('screenshot.jpg'), 'screenshot.jpg')
      self.emcc(test_file('jpeg_test.c'), ['--embed-file', 'screenshot.jpg', '-sUSE_LIBJPEG'], output_filename='a.out.js')
      self.assertContained('Image is 600 by 450 with 3 components', self.run_js('a.out.js', args=['screenshot.jpg']))
  
    def test_bullet(self):
      self.emcc(test_file('bullet_hello_world.cpp'), ['-sUSE_BULLET'], output_filename='a.out.js')
      self.assertContained('BULLET RUNNING', self.run_process(config.JS_ENGINES[0] + ['a.out.js'], stdout=PIPE, stderr=PIPE).stdout)
  
    def test_vorbis(self):
      # This will also test if ogg compiles, because vorbis depends on ogg
      self.emcc(test_file('vorbis_test.c'), ['-sUSE_VORBIS'], output_filename='a.out.js')
      self.assertContained('ALL OK', self.run_process(config.JS_ENGINES[0] + ['a.out.js'], stdout=PIPE, stderr=PIPE).stdout)
  
    def test_bzip2(self):
      self.emcc(test_file('bzip2_test.c'), ['-sUSE_BZIP2=1'], output_filename='a.out.js')
      self.assertContained("usage: unzcrash filename", self.run_process(config.JS_ENGINES[0] + ['a.out.js'], stdout=PIPE, stderr=PIPE).stdout)
  
    def test_freetype(self):
      # copy the Liberation Sans Bold truetype file located in the
      # <emscripten_root>/tests/freetype to the compilation folder
      shutil.copy2(test_file('freetype/LiberationSansBold.ttf'), os.getcwd())
      # build test program with the font file embed in it
      self.emcc(test_file('freetype_test.c'), ['-sUSE_FREETYPE', '--embed-file', 'LiberationSansBold.ttf'], output_filename='a.out.js')
      # the test program will print an ascii representation of a bitmap where the
      # 'w' character has been rendered using the Liberation Sans Bold font
      expectedOutput = '                \n' + \
                       '                \n' + \
                       '                \n' + \
                       '                \n' + \
                       '***    +***+    \n' + \
                       '***+   *****   +\n' + \
                       '+**+   *****   +\n' + \
                       '+***  +**+**+  *\n' + \
                       ' ***+ ***+**+ +*\n' + \
                       ' +**+ *** *** +*\n' + \
                       ' +**++**+ +**+**\n' + \
                       '  ***+**+ +**+**\n' + \
                       '  ******   *****\n' + \
                       '  +****+   +****\n' + \
                       '  +****+   +****\n' + \
                       '   ****     ****'
      self.assertContained(expectedOutput, self.run_process(config.JS_ENGINES[0] + ['a.out.js'], stdout=PIPE, stderr=PIPE).stdout)
  
    def test_icu(self):
      self.set_setting('USE_ICU')
      self.do_runf(test_file('other/test_icu.cpp'))
  
    def test_sdl2_ttf(self):
      # This is a compile-only to test to verify that sdl2-ttf (and freetype and harfbuzz) are buildable.
      self.emcc(test_file('sdl2_ttf.c'), args=['-sUSE_SDL=2', '-sUSE_SDL_TTF=2'], output_filename='a.out.js')
  
    def test_link_memcpy(self):
      # memcpy can show up *after* optimizations, so after our opportunity to link in libc, so it must be special-cased
      create_file('main.cpp', r'''
        #include <stdio.h>
  
        int main(int argc, char **argv) {
          int num = argc + 10;
          char buf[num], buf2[num];
          for (int i = 0; i < num; i++) {
            buf[i] = i*i+i/3;
          }
          for (int i = 1; i < num; i++) {
            buf[i] += buf[i-1];
          }
          for (int i = 0; i < num; i++) {
            buf2[i] = buf[i];
          }
          for (int i = 1; i < num; i++) {
            buf2[i] += buf2[i-1];
          }
          for (int i = 0; i < num; i++) {
            printf("%d:%d\n", i, buf2[i]);
          }
          return 0;
        }
      ''')
      self.run_process([EMXX, '-O2', 'main.cpp'])
      output = self.run_js('a.out.js')
      self.assertContained('''0:0
  1:1
  2:6
  3:21
  4:53
  5:111
  6:-49
  7:98
  8:55
  9:96
  10:-16
  ''', output)
      self.assertNotContained('warning: library.js memcpy should not be running, it is only for testing!', output)
  
    def test_undefined_exported_function(self):
      cmd = [EMXX, test_file('hello_world.cpp')]
      self.run_process(cmd)
  
      # adding a missing symbol to EXPORTED_FUNCTIONS should cause failure
      cmd += ['-s', "EXPORTED_FUNCTIONS=foobar"]
      err = self.expect_fail(cmd)
      self.assertContained('undefined exported symbol: "foobar"', err)
  
      # setting `-Wno-undefined` should suppress error
      cmd += ['-Wno-undefined']
      self.run_process(cmd)
  
    @parameterized({
      'warn': ('WARN',),
      'error': ('ERROR',),
      'ignore': (None,)
    })
    def test_undefined_symbols(self, action):
      create_file('main.cpp', r'''
        #include <stdio.h>
        #include <SDL.h>
        #include "SDL/SDL_opengl.h"
  
        extern "C" {
          void something();
          void elsey();
        }
  
        int main() {
          // pull in gl proc stuff, avoid warnings on emulation funcs
          printf("%p", SDL_GL_GetProcAddress("glGenTextures"));
          something();
          elsey();
          return 0;
        }
        ''')
  
      for args in ([], ['-O1'], ['-sMAX_WEBGL_VERSION=2']):
        for value in ([0, 1]):
          try_delete('a.out.js')
          print('checking "%s" %s' % (args, value))
          extra = ['-s', action + '_ON_UNDEFINED_SYMBOLS=%d' % value] if action else []
          proc = self.run_process([EMXX, 'main.cpp'] + extra + args, stderr=PIPE, check=False)
          print(proc.stderr)
          if value or action is None:
            # The default is that we error in undefined symbols
            self.assertContained('error: undefined symbol: something', proc.stderr)
            self.assertContained('error: undefined symbol: elsey', proc.stderr)
            check_success = False
          elif action == 'ERROR' and not value:
            # Error disables, should only warn
            self.assertContained('warning: undefined symbol: something', proc.stderr)
            self.assertContained('warning: undefined symbol: elsey', proc.stderr)
            self.assertNotContained('undefined symbol: emscripten_', proc.stderr)
            check_success = True
          elif action == 'WARN' and not value:
            # Disabled warning should imply disabling errors
            self.assertNotContained('undefined symbol', proc.stderr)
            check_success = True
  
          if check_success:
            self.assertEqual(proc.returncode, 0)
            self.assertTrue(os.path.exists('a.out.js'))
          else:
            self.assertNotEqual(proc.returncode, 0)
            self.assertFalse(os.path.exists('a.out.js'))
  
    def test_undefined_data_symbols(self):
      create_file('main.c', r'''
      extern int foo;
  
      int main() {
        return foo;
      }
      ''')
      output = self.expect_fail([EMCC, 'main.c'])
      self.assertContained('undefined symbol: foo', output)
  
      # With -Wl,--unresolved-symbols=ignore-all or -Wl,--allow-undefined
      # the linker should ignore any undefined data symbols.
      self.run_process([EMCC, 'main.c', '-Wl,--unresolved-symbols=ignore-all'])
      self.run_process([EMCC, 'main.c', '-Wl,--allow-undefined'])
  
    def test_GetProcAddress_LEGACY_GL_EMULATION(self):
      # without legacy gl emulation, getting a proc from there should fail
      self.do_other_test('test_GetProcAddress_LEGACY_GL_EMULATION.cpp', args=['0'], emcc_args=['-sLEGACY_GL_EMULATION=0'])
      # with it, it should work
      self.do_other_test('test_GetProcAddress_LEGACY_GL_EMULATION.cpp', args=['1'], emcc_args=['-sLEGACY_GL_EMULATION'])
  
    def test_prepost(self):
      create_file('main.cpp', '''
        #include <stdio.h>
        int main() {
          printf("hello from main\\n");
          return 0;
        }
        ''')
      create_file('pre.js', '''
        var Module = {
          preRun: function() { out('pre-run') },
          postRun: function() { out('post-run') }
        };
        ''')
  
      self.run_process([EMXX, 'main.cpp', '--pre-js', 'pre.js', '-sWASM_ASYNC_COMPILATION=0'])
      self.assertContained('pre-run\nhello from main\npost-run\n', self.run_js('a.out.js'))
  
      # addRunDependency during preRun should prevent main, and post-run from
      # running.
      with open('pre.js', 'a') as f:
        f.write('Module.preRun = function() { out("add-dep"); addRunDependency(); }\n')
      self.run_process([EMXX, 'main.cpp', '--pre-js', 'pre.js', '-sWASM_ASYNC_COMPILATION=0'])
      output = self.run_js('a.out.js')
      self.assertContained('add-dep\n', output)
      self.assertNotContained('hello from main\n', output)
      self.assertNotContained('post-run\n', output)
  
      # noInitialRun prevents run
      for no_initial_run, run_dep in [(0, 0), (1, 0), (0, 1)]:
        print(no_initial_run, run_dep)
        args = ['-sWASM_ASYNC_COMPILATION=0', '-sEXPORTED_RUNTIME_METHODS=callMain']
        if no_initial_run:
          args += ['-sINVOKE_RUN=0']
        if run_dep:
          create_file('pre.js', 'Module.preRun = function() { addRunDependency("test"); }')
          create_file('post.js', 'removeRunDependency("test");')
          args += ['--pre-js', 'pre.js', '--post-js', 'post.js']
  
        self.run_process([EMXX, 'main.cpp'] + args)
        output = self.run_js('a.out.js')
        self.assertContainedIf('hello from main', output, not no_initial_run)
  
        if no_initial_run:
          # Calling main later should still work, filesystem etc. must be set up.
          print('call main later')
          src = read_file('a.out.js')
          src += '\nconsole.log("callMain -> " + Module.callMain());\n'
          create_file('a.out.js', src)
          self.assertContained('hello from main\ncallMain -> 0\n', self.run_js('a.out.js'))
  
      # Use postInit
      create_file('pre.js', '''
        var Module = {
          preRun: function() { out('pre-run') },
          postRun: function() { out('post-run') },
          preInit: function() { out('pre-init') }
        };
      ''')
      self.run_process([EMXX, 'main.cpp', '--pre-js', 'pre.js'])
      self.assertContained('pre-init\npre-run\nhello from main\npost-run\n', self.run_js('a.out.js'))
  
    def test_prepost2(self):
      create_file('main.cpp', '''
        #include <stdio.h>
        int main() {
          printf("hello from main\\n");
          return 0;
        }
      ''')
      create_file('pre.js', '''
        var Module = {
          preRun: function() { out('pre-run') },
        };
      ''')
      create_file('pre2.js', '''
        Module.postRun = function() { out('post-run') };
      ''')
      self.run_process([EMXX, 'main.cpp', '--pre-js', 'pre.js', '--pre-js', 'pre2.js'])
      self.assertContained('pre-run\nhello from main\npost-run\n', self.run_js('a.out.js'))
  
    def test_prepre(self):
      create_file('main.cpp', '''
        #include <stdio.h>
        int main() {
          printf("hello from main\\n");
          return 0;
        }
      ''')
      create_file('pre.js', '''
        var Module = {
          preRun: [function() { out('pre-run') }],
        };
      ''')
      create_file('pre2.js', '''
        Module.preRun.push(function() { out('prepre') });
      ''')
      self.run_process([EMXX, 'main.cpp', '--pre-js', 'pre.js', '--pre-js', 'pre2.js'])
      self.assertContained('prepre\npre-run\nhello from main\n', self.run_js('a.out.js'))
  
    def test_extern_prepost(self):
      create_file('extern-pre.js', '// I am an external pre.\n')
      create_file('extern-post.js', '// I am an external post.\n')
      self.run_process([EMCC, '-O2', test_file('hello_world.c'), '--extern-pre-js', 'extern-pre.js', '--extern-post-js', 'extern-post.js', '--closure=1'])
      # the files should be included, and externally - not as part of optimized
      # code, so they are the very first and last things, and they are not
      # minified.
      js = read_file('a.out.js')
      pre = js.index('// I am an external pre.')
      post = js.index('// I am an external post.')
      # ignore some slack - newlines and other things. we just care about the
      # big picture here
      SLACK = 50
      self.assertLess(pre, post)
      self.assertLess(pre, SLACK)
      self.assertGreater(post, len(js) - SLACK)
      # make sure the slack is tiny compared to the whole program
      self.assertGreater(len(js), 100 * SLACK)
  
    def test_js_optimizer(self):
      for input, expected, passes in [
        (test_file('optimizer/test-js-optimizer-minifyGlobals.js'), read_file(test_file('optimizer/test-js-optimizer-minifyGlobals-output.js')),
         ['minifyGlobals']),
        (test_file('optimizer/test-js-optimizer-minifyLocals.js'), read_file(test_file('optimizer/test-js-optimizer-minifyLocals-output.js')),
         ['minifyLocals']),
        (test_file('optimizer/JSDCE.js'), read_file(test_file('optimizer/JSDCE-output.js')),
         ['JSDCE']),
        (test_file('optimizer/JSDCE-hasOwnProperty.js'), read_file(test_file('optimizer/JSDCE-hasOwnProperty-output.js')),
         ['JSDCE']),
        (test_file('optimizer/JSDCE-fors.js'), read_file(test_file('optimizer/JSDCE-fors-output.js')),
         ['JSDCE']),
        (test_file('optimizer/AJSDCE.js'), read_file(test_file('optimizer/AJSDCE-output.js')),
         ['AJSDCE']),
        (test_file('optimizer/emitDCEGraph.js'), read_file(test_file('optimizer/emitDCEGraph-output.js')),
         ['emitDCEGraph', 'noPrint']),
        (test_file('optimizer/emitDCEGraph2.js'), read_file(test_file('optimizer/emitDCEGraph2-output.js')),
         ['emitDCEGraph', 'noPrint']),
        (test_file('optimizer/emitDCEGraph3.js'), read_file(test_file('optimizer/emitDCEGraph3-output.js')),
         ['emitDCEGraph', 'noPrint']),
        (test_file('optimizer/emitDCEGraph4.js'), read_file(test_file('optimizer/emitDCEGraph4-output.js')),
         ['emitDCEGraph', 'noPrint']),
        (test_file('optimizer/emitDCEGraph5.js'), read_file(test_file('optimizer/emitDCEGraph5-output.js')),
         ['emitDCEGraph', 'noPrint']),
        (test_file('optimizer/minimal-runtime-applyDCEGraphRemovals.js'), read_file(test_file('optimizer/minimal-runtime-applyDCEGraphRemovals-output.js')),
         ['applyDCEGraphRemovals']),
        (test_file('optimizer/applyDCEGraphRemovals.js'), read_file(test_file('optimizer/applyDCEGraphRemovals-output.js')),
         ['applyDCEGraphRemovals']),
        (test_file('optimizer/applyImportAndExportNameChanges.js'), read_file(test_file('optimizer/applyImportAndExportNameChanges-output.js')),
         ['applyImportAndExportNameChanges']),
        (test_file('optimizer/applyImportAndExportNameChanges2.js'), read_file(test_file('optimizer/applyImportAndExportNameChanges2-output.js')),
         ['applyImportAndExportNameChanges']),
        (test_file('optimizer/minimal-runtime-emitDCEGraph.js'), read_file(test_file('optimizer/minimal-runtime-emitDCEGraph-output.js')),
         ['emitDCEGraph', 'noPrint']),
        (test_file('optimizer/minimal-runtime-2-emitDCEGraph.js'), read_file(test_file('optimizer/minimal-runtime-2-emitDCEGraph-output.js')),
         ['emitDCEGraph', 'noPrint']),
        (test_file('optimizer/standalone-emitDCEGraph.js'), read_file(test_file('optimizer/standalone-emitDCEGraph-output.js')),
         ['emitDCEGraph', 'noPrint']),
        (test_file('optimizer/emittedJSPreservesParens.js'), read_file(test_file('optimizer/emittedJSPreservesParens-output.js')),
         []),
        (test_file('optimizer/test-growableHeap.js'), read_file(test_file('optimizer/test-growableHeap-output.js')),
         ['growableHeap']),
        (test_file('optimizer/test-unsignPointers.js'), read_file(test_file('optimizer/test-unsignPointers-output.js')),
         ['unsignPointers']),
        (test_file('optimizer/test-asanify.js'), read_file(test_file('optimizer/test-asanify-output.js')),
         ['asanify']),
        (test_file('optimizer/test-safeHeap.js'), read_file(test_file('optimizer/test-safeHeap-output.js')),
         ['safeHeap']),
        (test_file('optimizer/test-LittleEndianHeap.js'), read_file(test_file('optimizer/test-LittleEndianHeap-output.js')),
         ['littleEndianHeap']),
      ]:
        print(input, passes)
  
        if not isinstance(expected, list):
          expected = [expected]
        expected = [out.replace('\n\n', '\n').replace('\n\n', '\n') for out in expected]
  
        # test calling optimizer
        output = self.run_process(config.NODE_JS + [path_from_root('tools/acorn-optimizer.js'), input] + passes, stdin=PIPE, stdout=PIPE).stdout
  
        def check_js(js, expected):
          # print >> sys.stderr, 'chak\n==========================\n', js, '\n===========================\n'
          if 'registerizeHarder' in passes:
            # registerizeHarder is hard to test, as names vary by chance, nondeterminstically FIXME
            def fix(src):
              if type(src) is list:
                return list(map(fix, src))
              src = '\n'.join([line for line in src.split('\n') if 'var ' not in line]) # ignore vars
  
              def reorder(func):
                def swap(func, stuff):
                  # emit EYE_ONE always before EYE_TWO, replacing i1,i2 or i2,i1 etc
                  for i in stuff:
                    if i not in func:
                      return func
                  indexes = [[i, func.index(i)] for i in stuff]
                  indexes.sort(key=lambda x: x[1])
                  for j in range(len(indexes)):
                    func = func.replace(indexes[j][0], 'STD_' + str(j))
                  return func
                func = swap(func, ['i1', 'i2', 'i3'])
                func = swap(func, ['i1', 'i2'])
                func = swap(func, ['i4', 'i5'])
                return func
  
              src = 'function '.join(map(reorder, src.split('function ')))
              return src
            js = fix(js)
            expected = fix(expected)
          self.assertIdentical(expected, js.replace('\r\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n'))
  
        if input not in [ # tests that are native-optimizer only
          test_file('optimizer/asmLastOpts.js'),
          test_file('optimizer/3154.js')
        ]:
          check_js(output, expected)
        else:
          print('(skip non-native)')
  
    @parameterized({
      'wasm2js': ('wasm2js', ['minifyNames', 'last']),
      'constructor': ('constructor', ['minifyNames'])
    })
    def test_js_optimizer_py(self, name, passes):
      # run the js optimizer python script. this differs from test_js_optimizer
      # which runs the internal js optimizer JS script directly (which the python
      # script calls)
      shutil.copyfile(test_file('optimizer', name + '.js'), name + '.js')
      self.run_process([PYTHON, path_from_root('tools/js_optimizer.py'), name + '.js'] + passes)
      expected = read_file(test_file('optimizer', name + '-output.js'))
      actual = read_file(name + '.js.jsopt.js')
      self.assertIdentical(expected, actual)
  
    def test_m_mm(self):
      create_file('foo.c', '#include <emscripten.h>')
      for opt in ['M', 'MM']:
        proc = self.run_process([EMCC, 'foo.c', '-' + opt], stdout=PIPE, stderr=PIPE)
        self.assertContained('foo.o: ', proc.stdout)
        self.assertNotContained('error', proc.stderr)
  
    @uses_canonical_tmp
    def test_emcc_debug_files(self):
      for opts in [0, 1, 2, 3]:
        for debug in [None, '1', '2']:
          print(opts, debug)
          if os.path.exists(self.canonical_temp_dir):
            shutil.rmtree(self.canonical_temp_dir)
  
          with env_modify({'EMCC_DEBUG': debug}):
            self.run_process([EMXX, test_file('hello_world.cpp'), '-O' + str(opts)], stderr=PIPE)
            if debug is None:
              self.assertFalse(os.path.exists(self.canonical_temp_dir))
            elif debug == '1':
              self.assertExists(os.path.join(self.canonical_temp_dir, 'emcc-3-original.js'))
            elif debug == '2':
              self.assertExists(os.path.join(self.canonical_temp_dir, 'emcc-3-original.js'))
  
    def test_debuginfo(self):
      for args, expect_debug in [
          (['-O0'], False),
          (['-O0', '-g'], True),
          (['-O0', '-gsource-map'], True),
          (['-O1'], False),
          (['-O1', '-g'], True),
          (['-O2'], False),
          (['-O2', '-g'], True),
        ]:
        print(args, expect_debug)
        err = self.run_process([EMXX, '-v', test_file('hello_world.cpp')] + args, stdout=PIPE, stderr=PIPE).stderr
        lines = err.splitlines()
        finalize = [l for l in lines if 'wasm-emscripten-finalize' in l][0]
        if expect_debug:
          self.assertIn(' -g ', finalize)
        else:
          self.assertNotIn(' -g ', finalize)
  
    def test_debuginfo_line_tables_only(self):
      def test(do_compile):
        do_compile([])
        no_size = os.path.getsize('a.out.wasm')
        do_compile(['-gline-tables-only'])
        line_size = os.path.getsize('a.out.wasm')
        do_compile(['-g'])
        full_size = os.path.getsize('a.out.wasm')
        return (no_size, line_size, full_size)
  
      def compile_to_object(compile_args):
        self.run_process([EMXX, test_file('hello_world.cpp'), '-c', '-o', 'a.out.wasm'] + compile_args)
  
      no_size, line_size, full_size = test(compile_to_object)
      self.assertLess(no_size, line_size)
      self.assertLess(line_size, full_size)
  
      def compile_to_executable(compile_args, link_args):
        # compile with the specified args
        self.run_process([EMXX, test_file('hello_world.cpp'), '-c', '-o', 'a.o'] + compile_args)
        # link with debug info
        self.run_process([EMCC, 'a.o'] + link_args)
  
      def compile_to_debug_executable(compile_args):
        return compile_to_executable(compile_args, ['-g'])
  
      no_size, line_size, full_size = test(compile_to_debug_executable)
      self.assertLess(no_size, line_size)
      self.assertLess(line_size, full_size)
  
      def compile_to_release_executable(compile_args):
        return compile_to_executable(compile_args, ['-O1'])
  
      no_size, line_size, full_size = test(compile_to_release_executable)
      self.assertEqual(no_size, line_size)
      self.assertEqual(line_size, full_size)
  
      # "-O0 executable" means compiling without optimizations but *also* without
      # -g (so, not a true debug build). the results here may change over time,
      # since we are telling emcc both to try to do as little as possible during
      # link (-O0), but also that debug info is not needed (no -g). if we end up
      # doing post-link changes then we will strip the debug info, but if not then
      # we don't.
      def compile_to_O0_executable(compile_args):
        return compile_to_executable(compile_args, [])
  
      no_size, line_size, full_size = test(compile_to_O0_executable)
      self.assertEqual(no_size, line_size)
      self.assertEqual(line_size, full_size)
  
    def test_dwarf(self):
      def compile_with_dwarf(args, output):
        # Test that -g enables dwarf info in object files and linked wasm
        self.run_process([EMXX, test_file('hello_world.cpp'), '-o', output, '-g'] + args)
  
      def verify(output):
        info = self.run_process([LLVM_DWARFDUMP, '--all', output], stdout=PIPE).stdout
        self.assertIn('DW_TAG_subprogram', info) # Ensure there's a subprogram entry in .debug_info
        self.assertIn('debug_line[0x', info) # Ensure there's a line table
  
      compile_with_dwarf(['-c'], 'a.o')
      verify('a.o')
      compile_with_dwarf([], 'a.js')
      verify('a.wasm')
  
    @unittest.skipIf(not scons_path, 'scons not found in PATH')
    @with_env_modify({'EMSCRIPTEN_ROOT': path_from_root()})
    def test_scons(self):
      # this test copies the site_scons directory alongside the test
      shutil.copytree(test_file('scons'), 'test')
      shutil.copytree(path_from_root('tools/scons/site_scons'), Path('test/site_scons'))
      with utils.chdir('test'):
        self.run_process(['scons'])
        output = self.run_js('scons_integration.js', assert_returncode=5)
      self.assertContained('If you see this - the world is all right!', output)
  
    @unittest.skipIf(not scons_path, 'scons not found in PATH')
    @with_env_modify({'EMSCRIPTEN_TOOLPATH': path_from_root('tools/scons/site_scons'),
                      'EMSCRIPTEN_ROOT': path_from_root()})
    def test_emscons(self):
      # uses the emscons wrapper which requires EMSCRIPTEN_TOOLPATH to find
      # site_scons
      shutil.copytree(test_file('scons'), 'test')
      with utils.chdir('test'):
        self.run_process([path_from_root('emscons'), 'scons'])
        output = self.run_js('scons_integration.js', assert_returncode=5)
      self.assertContained('If you see this - the world is all right!', output)
  
    def test_embind_fail(self):
      out = self.expect_fail([EMXX, test_file('embind/test_unsigned.cpp')])
      self.assertContained("undefined symbol: _embind_register_function", out)
  
    def test_embind_asyncify(self):
      create_file('post.js', '''
        addOnPostRun(function() {
          Module.sleep(10);
          out('done');
        });
      ''')
      create_file('main.cpp', r'''
        #include <emscripten.h>
        #include <emscripten/bind.h>
        using namespace emscripten;
        EMSCRIPTEN_BINDINGS(asyncify) {
            function("sleep", &emscripten_sleep);
        }
      ''')
      self.run_process([EMXX, 'main.cpp', '-lembind', '-sASYNCIFY', '--post-js', 'post.js'])
      self.assertContained('done', self.run_js('a.out.js'))
  
    def test_embind_closure_no_dynamic_execution(self):
      create_file('post.js', '''
        Module['onRuntimeInitialized'] = function() {
          out(Module['foo'](10));
          out(Module['bar']());
        };
      ''')
      create_file('main.cpp', r'''
        #include <string>
        #include <emscripten/console.h>
        #include <emscripten/bind.h>
        int foo(int x) { return x; }
        void bar() {
          emscripten::val(123).call<std::string>("toString");
          emscripten::val jarray = emscripten::val::global("Float32Array").new_(10);
          emscripten_console_log("ok");
        }
        EMSCRIPTEN_BINDINGS(baz) {
          emscripten::function("foo", &foo);
          emscripten::function("bar", &bar);
        }
      ''')
      self.run_process([EMXX, 'main.cpp', '-lembind', '-O2', '--closure', '1',
                        '-sNO_DYNAMIC_EXECUTION', '--post-js', 'post.js'])
      self.assertContained('10\nok\n', self.run_js('a.out.js'))
  
    @is_slow_test
    @with_env_modify({'EMCC_CLOSURE_ARGS': '--externs ' + shlex.quote(test_file('embind/underscore-externs.js'))})
    def test_embind(self):
      test_cases = [
          (['-lembind']),
          (['-lembind', '-O1']),
          (['-lembind', '-O2']),
          (['-lembind', '-O2', '-sALLOW_MEMORY_GROWTH', test_file('embind/isMemoryGrowthEnabled=true.cpp')]),
      ]
      without_utf8_args = ['-sEMBIND_STD_STRING_IS_UTF8=0']
      test_cases_without_utf8 = []
      for args in test_cases:
          test_cases_without_utf8.append((args + without_utf8_args))
      test_cases += test_cases_without_utf8
      test_cases.extend([(args[:] + ['-sDYNAMIC_EXECUTION=0']) for args in test_cases])
      # closure compiler doesn't work with DYNAMIC_EXECUTION=0
      test_cases.append((['-lembind', '-O2', '--closure=1']))
      for args in test_cases:
        print(args)
        self.clear()
  
        testFiles = [
          test_file('embind/underscore-1.4.2.js'),
          test_file('embind/imvu_test_adapter.js'),
          test_file('embind/embind.test.js'),
        ]
  
        self.run_process(
          [EMXX, test_file('embind/embind_test.cpp'),
           '--pre-js', test_file('embind/test.pre.js'),
           '--post-js', test_file('embind/test.post.js'),
           '-sWASM_ASYNC_COMPILATION=0',
           '-sIN_TEST_HARNESS'] + args)
  
        if '-sDYNAMIC_EXECUTION=0' in args:
          js_binary_str = read_file('a.out.js')
          self.assertNotContained('new Function(', js_binary_str)
          self.assertNotContained('eval(', js_binary_str)
  
        with open('a.out.js', 'ab') as f:
          for tf in testFiles:
            f.write(read_binary(tf))
  
        output = self.run_js('a.out.js')
        self.assertNotContained('FAIL', output)
  
    def test_emconfig(self):
      output = self.run_process([emconfig, 'LLVM_ROOT'], stdout=PIPE).stdout.strip()
      self.assertEqual(output, config.LLVM_ROOT)
      # EMSCRIPTEN_ROOT is kind of special since it should always report the locaton of em-config
      # itself (its not configurable via the config file but driven by the location for arg0)
      output = self.run_process([emconfig, 'EMSCRIPTEN_ROOT'], stdout=PIPE).stdout.strip()
      self.assertEqual(output, os.path.dirname(emconfig))
      invalid = 'Usage: em-config VAR_NAME'
      # Don't accept variables that do not exist
      output = self.expect_fail([emconfig, 'VAR_WHICH_DOES_NOT_EXIST']).strip()
      self.assertEqual(output, invalid)
      # Don't accept no arguments
      output = self.expect_fail([emconfig]).strip()
      self.assertEqual(output, invalid)
      # Don't accept more than one variable
      output = self.expect_fail([emconfig, 'LLVM_ROOT', 'EMCC']).strip()
      self.assertEqual(output, invalid)
      # Don't accept arbitrary python code
      output = self.expect_fail([emconfig, 'sys.argv[1]']).strip()
      self.assertEqual(output, invalid)
  
    def test_link_s(self):
      # -s OPT=VALUE can conflict with -s as a linker option. We warn and ignore
      create_file('main.cpp', r'''
        extern "C" {
          void something();
        }
  
        int main() {
          something();
          return 0;
        }
      ''')
      create_file('supp.cpp', r'''
        #include <stdio.h>
  
        extern "C" {
          void something() {
            printf("yello\n");
          }
        }
      ''')
      self.run_process([EMXX, '-c', 'main.cpp', '-o', 'main.o'])
      self.run_process([EMXX, '-c', 'supp.cpp', '-o', 'supp.o'])
  
      self.run_process([EMCC, 'main.o', '-s', 'supp.o', '-sSAFE_HEAP'])
      self.assertContained('yello', self.run_js('a.out.js'))
      # Check that valid -s option had an effect'
      self.assertContained('SAFE_HEAP', read_file('a.out.js'))
  
    def test_conftest_s_flag_passing(self):
      create_file('conftest.c', r'''
        int main() {
          return 0;
        }
      ''')
      # the name "conftest.c" is enough to make us use a configure-like mode,
      # the same as if EMMAKEN_JUST_CONFIGURE=1 were set in the env.
      cmd = [EMCC, '-sASSERTIONS', 'conftest.c', '-o', 'conftest']
      output = self.run_process(cmd, stderr=PIPE)
      self.assertNotContained('emcc: warning: treating -s as linker option', output.stderr)
      self.assertExists('conftest')
  
    def test_file_packager(self):
      ensure_dir('subdir')
      create_file('data1.txt', 'data1')
  
      os.chdir('subdir')
      create_file('data2.txt', 'data2')
  
      def check(text):
        self.assertGreater(len(text), 0)
        empty_lines = 0
        # check the generated is relatively tidy
        for line in text.splitlines():
          if line and line[-1].isspace():
            self.fail('output contains trailing whitespace: `%s`' % line)
  
          if line.strip():
            empty_lines = 0
          else:
            empty_lines += 1
            if empty_lines > 1:
              self.fail('output contains more then one empty line in row')
  
      # relative path to below the current dir is invalid
      stderr = self.expect_fail([FILE_PACKAGER, 'test.data', '--preload', '../data1.txt'])
      self.assertContained('below the current directory', stderr)
  
      # relative path that ends up under us is cool
      proc = self.run_process([FILE_PACKAGER, 'test.data', '--preload', '../subdir/data2.txt'], stderr=PIPE, stdout=PIPE)
      self.assertNotContained('below the current directory', proc.stderr)
      check(proc.stdout)
  
      # direct path leads to the same code being generated - relative path does not make us do anything different
      proc2 = self.run_process([FILE_PACKAGER, 'test.data', '--preload', 'data2.txt'], stderr=PIPE, stdout=PIPE)
      check(proc2.stdout)
      self.assertNotContained('below the current directory', proc2.stderr)
  
      def clean(txt):
        lines = txt.splitlines()
        lines = [l for l in lines if 'PACKAGE_UUID' not in l and 'loadPackage({' not in l]
        return ''.join(lines)
  
      self.assertTextDataIdentical(clean(proc.stdout), clean(proc2.stdout))
  
      # verify '--separate-metadata' option produces separate metadata file
      os.chdir('..')
  
      self.run_process([FILE_PACKAGER, 'test.data', '--preload', 'data1.txt', '--preload', 'subdir/data2.txt', '--js-output=immutable.js', '--separate-metadata'])
      self.assertExists('immutable.js.metadata')
      # verify js output JS file is not touched when the metadata is separated
      orig_timestamp = os.path.getmtime('immutable.js')
      orig_content = read_file('immutable.js')
      # ensure some time passes before running the packager again so that if it does touch the
      # js file it will end up with the different timestamp.
      time.sleep(1.0)
      self.run_process([FILE_PACKAGER, 'test.data', '--preload', 'data1.txt', '--preload', 'subdir/data2.txt', '--js-output=immutable.js', '--separate-metadata'])
      # assert both file content and timestamp are the same as reference copy
      self.assertTextDataIdentical(orig_content, read_file('immutable.js'))
      self.assertEqual(orig_timestamp, os.path.getmtime('immutable.js'))
      # verify the content of metadata file is correct
      metadata = json.loads(read_file('immutable.js.metadata'))
      self.assertEqual(len(metadata['files']), 2)
      assert metadata['files'][0]['start'] == 0 and metadata['files'][0]['end'] == len('data1') and metadata['files'][0]['filename'] == '/data1.txt'
      assert metadata['files'][1]['start'] == len('data1') and metadata['files'][1]['end'] == len('data1') + len('data2') and metadata['files'][1]['filename'] == '/subdir/data2.txt'
      assert metadata['remote_package_size'] == len('data1') + len('data2')
  
      # can only assert the uuid format is correct, the uuid's value is expected to differ in between invocation
      uuid.UUID(metadata['package_uuid'], version=4)
  
    def test_file_packager_unicode(self):
      unicode_name = 'unicode…☃'
      try:
        ensure_dir(unicode_name)
      except OSError:
        print("we failed to even create a unicode dir, so on this OS, we can't test this")
        return
      full = os.path.join(unicode_name, 'data.txt')
      create_file(full, 'data')
      proc = self.run_process([FILE_PACKAGER, 'test.data', '--preload', full], stdout=PIPE, stderr=PIPE)
      assert len(proc.stdout), proc.stderr
      assert json.dumps(unicode_name) in proc.stdout, proc.stdout
      print(len(proc.stderr))
  
    def test_file_packager_directory_with_single_quote(self):
      single_quote_name = "direc'tory"
      ensure_dir(single_quote_name)
      full = os.path.join(single_quote_name, 'data.txt')
      create_file(full, 'data')
      proc = self.run_process([FILE_PACKAGER, 'test.data', '--preload', full], stdout=PIPE, stderr=PIPE)
      assert len(proc.stdout), proc.stderr
      # ensure not invalid JavaScript
      assert "'direc'tory'" not in proc.stdout
      assert json.dumps("direc'tory") in proc.stdout
  
    def test_file_packager_mention_FORCE_FILESYSTEM(self):
      MESSAGE = 'Remember to build the main file with `-sFORCE_FILESYSTEM` so that it includes support for loading this file package'
      create_file('data.txt', 'data1')
      # mention when running standalone
      err = self.run_process([FILE_PACKAGER, 'test.data', '--preload', 'data.txt'], stdout=PIPE, stderr=PIPE).stderr
      self.assertContained(MESSAGE, err)
      # do not mention from emcc
      err = self.run_process([EMCC, test_file('hello_world.c'), '--preload-file', 'data.txt'], stdout=PIPE, stderr=PIPE).stderr
      self.assertEqual(len(err), 0)
  
    def test_file_packager_returns_error_if_target_equal_to_jsoutput(self):
      MESSAGE = 'error: TARGET should not be the same value of --js-output'
      result = self.run_process([FILE_PACKAGER, 'test.data', '--js-output=test.data'], check=False, stdout=PIPE, stderr=PIPE)
      self.assertEqual(result.returncode, 1)
      self.assertContained(MESSAGE, result.stderr)
  
    def test_file_packager_embed(self):
      create_file('data.txt', 'hello data')
  
      # Without --obj-output we issue a warning
      err = self.run_process([FILE_PACKAGER, 'test.data', '--embed', 'data.txt', '--js-output=data.js'], stderr=PIPE).stderr
      self.assertContained('--obj-output is recommended when using --embed', err)
  
      self.run_process([FILE_PACKAGER, 'test.data', '--embed', 'data.txt', '--obj-output=data.o', '--js-output=data.js'])
  
      create_file('test.c', '''
      #include <stdio.h>
  
      int main() {
        FILE* f = fopen("data.txt", "r");
        char buf[64];
        int rtn = fread(buf, 1, 64, f);
        buf[rtn] = '\\0';
        fclose(f);
        printf("%s\\n", buf);
        return 0;
      }
      ''')
      self.run_process([EMCC, '--pre-js=data.js', 'test.c', 'data.o', '-sFORCE_FILESYSTEM'])
      output = self.run_js('a.out.js')
      self.assertContained('hello data', output)
  
    def test_headless(self):
      shutil.copyfile(test_file('screenshot.png'), 'example.png')
      self.run_process([EMCC, test_file('sdl_headless.c'), '-sHEADLESS'])
      output = self.run_js('a.out.js')
      assert '''Init: 0
  Font: 0x1
  Sum: 0
  you should see two lines of text in different colors and a blue rectangle
  SDL_Quit called (and ignored)
  done.
  ''' in output, output
  
    def test_preprocess(self):
      # Pass -Werror to prevent regressions such as https://github.com/emscripten-core/emscripten/pull/9661
      out = self.run_process([EMCC, test_file('hello_world.c'), '-E', '-Werror'], stdout=PIPE).stdout
      self.assertNotExists('a.out.js')
      self.assertNotExists('a.out')
      # Test explicitly that the output contains a line typically written by the preprocessor.
      self.assertContained('# 1 ', out)
      self.assertContained('hello_world.c"', out)
      self.assertContained('printf("hello, world!', out)
  
    def test_preprocess_multi(self):
      out = self.run_process([EMCC, test_file('hello_world.c'), test_file('hello_world.c'), '-E'], stdout=PIPE).stdout
      self.assertEqual(out.count('printf("hello, world!'), 2)
  
    def test_syntax_only_valid(self):
      result = self.run_process([EMCC, test_file('hello_world.c'), '-fsyntax-only'], stdout=PIPE, stderr=STDOUT)
      self.assertEqual(result.stdout, '')
      self.assertNotExists('a.out.js')
  
    def test_syntax_only_invalid(self):
      create_file('src.c', 'int main() {')
      result = self.run_process([EMCC, 'src.c', '-fsyntax-only'], stdout=PIPE, check=False, stderr=STDOUT)
      self.assertNotEqual(result.returncode, 0)
      self.assertContained("src.c:1:13: error: expected '}'", result.stdout)
      self.assertNotExists('a.out.js')
  
    def test_demangle(self):
      create_file('src.cpp', '''
        #include <stdio.h>
        #include <emscripten.h>
        void two(char c) {
          EM_ASM(out(stackTrace()));
        }
        void one(int x) {
          two(x % 17);
        }
        int main() {
          EM_ASM(out(demangle('__Znwm'))); // check for no aborts
          EM_ASM(out(demangle('_main')));
          EM_ASM(out(demangle('__Z2f2v')));
          EM_ASM(out(demangle('__Z12abcdabcdabcdi')));
          EM_ASM(out(demangle('__ZL12abcdabcdabcdi')));
          EM_ASM(out(demangle('__Z4testcsifdPvPiPc')));
          EM_ASM(out(demangle('__ZN4test5moarrEcslfdPvPiPc')));
          EM_ASM(out(demangle('__ZN4Waka1f12a234123412345pointEv')));
          EM_ASM(out(demangle('__Z3FooIiEvv')));
          EM_ASM(out(demangle('__Z3FooIidEvi')));
          EM_ASM(out(demangle('__ZN3Foo3BarILi5EEEvv')));
          EM_ASM(out(demangle('__ZNK10__cxxabiv120__si_class_type_info16search_below_dstEPNS_19__dynamic_cast_infoEPKvib')));
          EM_ASM(out(demangle('__Z9parsewordRPKciRi')));
          EM_ASM(out(demangle('__Z5multiwahtjmxyz')));
          EM_ASM(out(demangle('__Z1aA32_iPA5_c')));
          EM_ASM(out(demangle('__ZN21FWakaGLXFleeflsMarfooC2EjjjPKvbjj')));
          EM_ASM(out(demangle('__ZN5wakaw2Cm10RasterBaseINS_6watwat9PolocatorEE8merbine1INS4_2OREEEvPKjj'))); // we get this wrong, but at least emit a '?'
          one(17);
          return 0;
        }
      ''')
  
      # full demangle support
  
      self.run_process([EMXX, 'src.cpp', '-sDEMANGLE_SUPPORT'])
      output = self.run_js('a.out.js')
      self.assertContained('''operator new(unsigned long)
  _main
  f2()
  abcdabcdabcd(int)
  abcdabcdabcd(int)
  test(char, short, int, float, double, void*, int*, char*)
  test::moarr(char, short, long, float, double, void*, int*, char*)
  Waka::f::a23412341234::point()
  void Foo<int>()
  void Foo<int, double>(int)
  void Foo::Bar<5>()
  __cxxabiv1::__si_class_type_info::search_below_dst(__cxxabiv1::__dynamic_cast_info*, void const*, int, bool) const
  parseword(char const*&, int, int&)
  multi(wchar_t, signed char, unsigned char, unsigned short, unsigned int, unsigned long, long long, unsigned long long, ...)
  a(int [32], char (*) [5])
  FWakaGLXFleeflsMarfoo::FWakaGLXFleeflsMarfoo(unsigned int, unsigned int, unsigned int, void const*, bool, unsigned int, unsigned int)
  void wakaw::Cm::RasterBase<wakaw::watwat::Polocator>::merbine1<wakaw::Cm::RasterBase<wakaw::watwat::Polocator>::OR>(unsigned int const*, unsigned int)
  ''', output)
      # test for multiple functions in one stack trace
      self.run_process([EMXX, 'src.cpp', '-sDEMANGLE_SUPPORT', '-g'])
      output = self.run_js('a.out.js')
      self.assertIn('one(int)', output)
      self.assertIn('two(char)', output)
  
    def test_demangle_cpp(self):
      create_file('src.cpp', '''
        #include <stdio.h>
        #include <emscripten.h>
        #include <cxxabi.h>
        #include <assert.h>
  
        int main() {
          char out[256];
          int status = 1;
          size_t length = 255;
          abi::__cxa_demangle("_ZN4Waka1f12a234123412345pointEv", out, &length, &status);
          assert(status == 0);
          printf("%s\\n", out);
          return 0;
        }
      ''')
  
      self.run_process([EMXX, 'src.cpp'])
      output = self.run_js('a.out.js')
      self.assertContained('Waka::f::a23412341234::point()', output)
  
    # Test that malloc() -> OOM -> abort() -> stackTrace() -> jsStackTrace() -> demangleAll() -> demangle() -> malloc()
    # cycle will not produce an infinite loop.
    def test_demangle_malloc_infinite_loop_crash(self):
      self.run_process([EMXX, test_file('malloc_demangle_infinite_loop.cpp'), '-g', '-sABORTING_MALLOC', '-sDEMANGLE_SUPPORT'])
      output = self.run_js('a.out.js', assert_returncode=NON_ZERO)
      if output.count('Cannot enlarge memory arrays') > 5:
        print(output)
      self.assertLess(output.count('Cannot enlarge memory arrays'),  6)
  
    @require_node
    def test_module_exports_with_closure(self):
      # This test checks that module.export is retained when JavaScript
      # is minified by compiling with --closure 1
      # This is important as if module.export is not present the Module
      # object will not be visible to node.js
  
      # compile with -O2 --closure 0
      self.run_process([EMCC, test_file('Module-exports/test.c'),
                        '-o', 'test.js', '-O2', '--closure', '0',
                        '--pre-js', test_file('Module-exports/setup.js'),
                        '-sEXPORTED_FUNCTIONS=_bufferTest,_malloc,_free',
                        '-sEXPORTED_RUNTIME_METHODS=ccall,cwrap',
                        '-sWASM_ASYNC_COMPILATION=0'])
  
      # Check that compilation was successful
      self.assertExists('test.js')
      test_js_closure_0 = read_file('test.js')
  
      # Check that test.js compiled with --closure 0 contains "module['exports'] = Module;"
      assert ("module['exports'] = Module;" in test_js_closure_0) or ('module["exports"]=Module' in test_js_closure_0) or ('module["exports"] = Module;' in test_js_closure_0)
  
      # Check that main.js (which requires test.js) completes successfully when run in node.js
      # in order to check that the exports are indeed functioning correctly.
      shutil.copyfile(test_file('Module-exports/main.js'), 'main.js')
      self.assertContained('bufferTest finished', self.run_js('main.js'))
  
      # Delete test.js again and check it's gone.
      try_delete('test.js')
      self.assertNotExists('test.js')
  
      # compile with -O2 --closure 1
      self.run_process([EMCC, test_file('Module-exports/test.c'),
                        '-o', 'test.js', '-O2', '--closure=1',
                        '--pre-js', test_file('Module-exports/setup.js'),
                        '-sEXPORTED_FUNCTIONS=_bufferTest,_malloc,_free',
                        '-sEXPORTED_RUNTIME_METHODS=ccall,cwrap',
                        '-sWASM_ASYNC_COMPILATION=0'])
  
      # Check that compilation was successful
      self.assertExists('test.js')
      test_js_closure_1 = read_file('test.js')
  
      # Check that test.js compiled with --closure 1 contains "module.exports", we want to verify that
      # "module['exports']" got minified to "module.exports" when compiling with --closure 1
      self.assertContained("module.exports", test_js_closure_1)
  
      # Check that main.js (which requires test.js) completes successfully when run in node.js
      # in order to check that the exports are indeed functioning correctly.
      self.assertContained('bufferTest finished', self.run_js('main.js'))
  
    @require_node
    def test_node_catch_exit(self):
      # Test that in node.js exceptions are not caught if NODEJS_EXIT_CATCH=0
      create_file('count.c', '''
        #include <string.h>
        int count(const char *str) {
            return (int)strlen(str);
        }
      ''')
  
      create_file('index.js', '''
        const count = require('./count.js');
  
        console.log(xxx); //< here is the ReferenceError
      ''')
  
      reference_error_text = 'console.log(xxx); //< here is the ReferenceError'
  
      self.run_process([EMCC, 'count.c', '-o', 'count.js'])
  
      # Check that the ReferenceError is caught and rethrown and thus the original error line is masked
      self.assertNotContained(reference_error_text,
                              self.run_js('index.js', assert_returncode=NON_ZERO))
  
      self.run_process([EMCC, 'count.c', '-o', 'count.js', '-sNODEJS_CATCH_EXIT=0'])
  
      # Check that the ReferenceError is not caught, so we see the error properly
      self.assertContained(reference_error_text,
                           self.run_js('index.js', assert_returncode=NON_ZERO))
  
    @require_node
    def test_exported_runtime_methods(self):
      # Test with node.js that the EXPORTED_RUNTIME_METHODS setting is
      # considered by libraries
      create_file('count.c', '''
        #include <string.h>
        int count(const char *str) {
            return (int)strlen(str);
        }
      ''')
  
      create_file('index.js', '''
        const count = require('./count.js');
  
        console.log(count.FS_writeFile);
      ''')
  
      reference_error_text = 'undefined'
  
      self.run_process([EMCC, 'count.c', '-sFORCE_FILESYSTEM', '-s',
                       'EXPORTED_RUNTIME_METHODS=FS_writeFile', '-o', 'count.js'])
  
      # Check that the Module.FS_writeFile exists
      self.assertNotContained(reference_error_text, self.run_js('index.js'))
  
      self.run_process([EMCC, 'count.c', '-sFORCE_FILESYSTEM', '-o', 'count.js'])
  
      # Check that the Module.FS_writeFile is not exported
      out = self.run_js('index.js')
      self.assertContained(reference_error_text, out)
  
    def test_fs_stream_proto(self):
      open('src.cpp', 'wb').write(br'''
  #include <stdio.h>
  #include <fcntl.h>
  #include <unistd.h>
  #include <sys/stat.h>
  #include <errno.h>
  #include <string.h>
  
  int main()
  {
      long file_size = 0;
      int h = open("src.cpp", O_RDONLY, 0666);
      if (0 != h)
      {
          FILE* file = fdopen(h, "rb");
          if (0 != file)
          {
              fseek(file, 0, SEEK_END);
              file_size = ftell(file);
              fseek(file, 0, SEEK_SET);
          }
          else
          {
              printf("fdopen() failed: %s\n", strerror(errno));
              return 10;
          }
          close(h);
          printf("File size: %ld\n", file_size);
      }
      else
      {
          printf("open() failed: %s\n", strerror(errno));
          return 10;
      }
      return 0;
  }
      ''')
      self.run_process([EMXX, 'src.cpp', '--embed-file', 'src.cpp', '-sENVIRONMENT=node,shell'])
      for engine in config.JS_ENGINES:
        out = self.run_js('a.out.js', engine=engine)
        self.assertContained('File size: 724', out)
  
    @require_node
    def test_node_emscripten_num_logical_cores(self):
      # Test with node.js that the emscripten_num_logical_cores method is working
      create_file('src.cpp', r'''
  #include <emscripten/threading.h>
  #include <stdio.h>
  #include <assert.h>
  
  int main() {
    int num = emscripten_num_logical_cores();
    assert(num != 0);
    puts("ok");
  }
  ''')
      self.run_process([EMXX, 'src.cpp', '-sUSE_PTHREADS', '-sENVIRONMENT=node'])
      ret = self.run_process(config.NODE_JS + ['--experimental-wasm-threads', 'a.out.js'], stdout=PIPE).stdout
      self.assertContained('ok', ret)
  
    def test_proxyfs(self):
      # This test supposes that 3 different programs share the same directory and files.
      # The same JS object is not used for each of them
      # But 'require' function caches JS objects.
      # If we just load same js-file multiple times like following code,
      # these programs (m0,m1,m2) share the same JS object.
      #
      #   var m0 = require('./proxyfs_test.js');
      #   var m1 = require('./proxyfs_test.js');
      #   var m2 = require('./proxyfs_test.js');
      #
      # To separate js-objects for each of them, following 'require' use different js-files.
      #
      #   var m0 = require('./proxyfs_test.js');
      #   var m1 = require('./proxyfs_test1.js');
      #   var m2 = require('./proxyfs_test2.js');
      #
      create_file('proxyfs_test_main.js', r'''
  var m0 = require('./proxyfs_test.js');
  var m1 = require('./proxyfs_test1.js');
  var m2 = require('./proxyfs_test2.js');
  
  var section;
  function print(str){
    process.stdout.write(section+":"+str+":");
  }
  
  m0.FS.mkdir('/working');
  m0.FS.mount(m0.PROXYFS,{root:'/',fs:m1.FS},'/working');
  m0.FS.mkdir('/working2');
  m0.FS.mount(m0.PROXYFS,{root:'/',fs:m2.FS},'/working2');
  
  section = "child m1 reads and writes local file.";
  print("m1 read embed");
  m1.ccall('myreade','number',[],[]);
  print("m1 write");console.log("");
  m1.ccall('mywrite0','number',['number'],[1]);
  print("m1 read");
  m1.ccall('myread0','number',[],[]);
  
  
  section = "child m2 reads and writes local file.";
  print("m2 read embed");
  m2.ccall('myreade','number',[],[]);
  print("m2 write");console.log("");
  m2.ccall('mywrite0','number',['number'],[2]);
  print("m2 read");
  m2.ccall('myread0','number',[],[]);
  
  section = "child m1 reads local file.";
  print("m1 read");
  m1.ccall('myread0','number',[],[]);
  
  section = "parent m0 reads and writes local and children's file.";
  print("m0 read embed");
  m0.ccall('myreade','number',[],[]);
  print("m0 read m1");
  m0.ccall('myread1','number',[],[]);
  print("m0 read m2");
  m0.ccall('myread2','number',[],[]);
  
  section = "m0,m1 and m2 verify local files.";
  print("m0 write");console.log("");
  m0.ccall('mywrite0','number',['number'],[0]);
  print("m0 read");
  m0.ccall('myread0','number',[],[]);
  print("m1 read");
  m1.ccall('myread0','number',[],[]);
  print("m2 read");
  m2.ccall('myread0','number',[],[]);
  
  print("m0 read embed");
  m0.ccall('myreade','number',[],[]);
  print("m1 read embed");
  m1.ccall('myreade','number',[],[]);
  print("m2 read embed");
  m2.ccall('myreade','number',[],[]);
  
  section = "parent m0 writes and reads children's files.";
  print("m0 write m1");console.log("");
  m0.ccall('mywrite1','number',[],[]);
  print("m0 read m1");
  m0.ccall('myread1','number',[],[]);
  print("m0 write m2");console.log("");
  m0.ccall('mywrite2','number',[],[]);
  print("m0 read m2");
  m0.ccall('myread2','number',[],[]);
  print("m1 read");
  m1.ccall('myread0','number',[],[]);
  print("m2 read");
  m2.ccall('myread0','number',[],[]);
  print("m0 read m0");
  m0.ccall('myread0','number',[],[]);
  
  section = "test seek.";
  print("file size");
  m0.ccall('myreadSeekEnd', 'number', [], []);
  ''')
  
      create_file('proxyfs_pre.js', r'''
  if (typeof Module === 'undefined') Module = {};
  Module["noInitialRun"]=true;
  Module["noExitRuntime"]=true;
  ''')
  
      create_file('proxyfs_embed.txt', 'test\n')
  
      create_file('proxyfs_test.c', r'''
  #include <stdio.h>
  #include <emscripten/emscripten.h>
  
  EMSCRIPTEN_KEEPALIVE int mywrite1() {
    FILE* out = fopen("/working/hoge.txt","w");
    fprintf(out,"test1\n");
    fclose(out);
    return 0;
  }
  
  EMSCRIPTEN_KEEPALIVE int myread1() {
    FILE* in = fopen("/working/hoge.txt","r");
    char buf[1024];
    int len;
    if(in==NULL)
      printf("open failed\n");
  
    while(! feof(in)){
      if(fgets(buf,sizeof(buf),in)==buf){
        printf("%s",buf);
      }
    }
    fclose(in);
    return 0;
  }
  
  EMSCRIPTEN_KEEPALIVE int mywrite2() {
    FILE* out = fopen("/working2/hoge.txt","w");
    fprintf(out,"test2\n");
    fclose(out);
    return 0;
  }
  
  EMSCRIPTEN_KEEPALIVE int myread2() {
    FILE* in = fopen("/working2/hoge.txt","r");
    char buf[1024];
    int len;
    if(in==NULL)
      printf("open failed\n");
  
    while(! feof(in)){
      if(fgets(buf,sizeof(buf),in)==buf){
        printf("%s",buf);
      }
    }
    fclose(in);
    return 0;
  }
  
  EMSCRIPTEN_KEEPALIVE int mywrite0(int i) {
    FILE* out = fopen("hoge.txt","w");
    fprintf(out,"test0_%d\n",i);
    fclose(out);
    return 0;
  }
  
  EMSCRIPTEN_KEEPALIVE int myread0() {
    FILE* in = fopen("hoge.txt","r");
    char buf[1024];
    int len;
    if(in==NULL)
      printf("open failed\n");
  
    while(! feof(in)){
      if(fgets(buf,sizeof(buf),in)==buf){
        printf("%s",buf);
      }
    }
    fclose(in);
    return 0;
  }
  
  EMSCRIPTEN_KEEPALIVE int myreade() {
    FILE* in = fopen("proxyfs_embed.txt","r");
    char buf[1024];
    int len;
    if(in==NULL)
      printf("open failed\n");
  
    while(! feof(in)){
      if(fgets(buf,sizeof(buf),in)==buf){
        printf("%s",buf);
      }
    }
    fclose(in);
    return 0;
  }
  
  EMSCRIPTEN_KEEPALIVE int myreadSeekEnd() {
    FILE* in = fopen("/working2/hoge.txt","r");
  
    fseek(in, 0L, SEEK_END);
    int fileSize = ftell(in);
    fseek(in, 0L, SEEK_SET);
    printf("%d\n", fileSize);
  
    fclose(in);
    return 0;
  }
  ''')
  
      self.run_process([EMCC,
                        '-o', 'proxyfs_test.js', 'proxyfs_test.c',
                        '--embed-file', 'proxyfs_embed.txt', '--pre-js', 'proxyfs_pre.js',
                        '-sEXPORTED_RUNTIME_METHODS=ccall,cwrap,FS,PROXYFS',
                        '-lproxyfs.js',
                        '-sWASM_ASYNC_COMPILATION=0'])
      # Following shutil.copyfile just prevent 'require' of node.js from caching js-object.
      # See https://nodejs.org/api/modules.html
      shutil.copyfile('proxyfs_test.js', 'proxyfs_test1.js')
      shutil.copyfile('proxyfs_test.js', 'proxyfs_test2.js')
      out = self.run_js('proxyfs_test_main.js')
      section = "child m1 reads and writes local file."
      self.assertContained(section + ":m1 read embed:test", out)
      self.assertContained(section + ":m1 write:", out)
      self.assertContained(section + ":m1 read:test0_1", out)
      section = "child m2 reads and writes local file."
      self.assertContained(section + ":m2 read embed:test", out)
      self.assertContained(section + ":m2 write:", out)
      self.assertContained(section + ":m2 read:test0_2", out)
      section = "child m1 reads local file."
      self.assertContained(section + ":m1 read:test0_1", out)
      section = "parent m0 reads and writes local and children's file."
      self.assertContained(section + ":m0 read embed:test", out)
      self.assertContained(section + ":m0 read m1:test0_1", out)
      self.assertContained(section + ":m0 read m2:test0_2", out)
      section = "m0,m1 and m2 verify local files."
      self.assertContained(section + ":m0 write:", out)
      self.assertContained(section + ":m0 read:test0_0", out)
      self.assertContained(section + ":m1 read:test0_1", out)
      self.assertContained(section + ":m2 read:test0_2", out)
      self.assertContained(section + ":m0 read embed:test", out)
      self.assertContained(section + ":m1 read embed:test", out)
      self.assertContained(section + ":m2 read embed:test", out)
      section = "parent m0 writes and reads children's files."
      self.assertContained(section + ":m0 write m1:", out)
      self.assertContained(section + ":m0 read m1:test1", out)
      self.assertContained(section + ":m0 write m2:", out)
      self.assertContained(section + ":m0 read m2:test2", out)
      self.assertContained(section + ":m1 read:test1", out)
      self.assertContained(section + ":m2 read:test2", out)
      self.assertContained(section + ":m0 read m0:test0_0", out)
      section = "test seek."
      self.assertContained(section + ":file size:6", out)
  
    def test_dependency_file(self):
      # Issue 1732: -MMD (and friends) create dependency files that need to be
      # copied from the temporary directory.
  
      create_file('test.cpp', r'''
        #include "test.hpp"
  
        void my_function()
        {
        }
      ''')
      create_file('test.hpp', r'''
        void my_function();
      ''')
  
      self.run_process([EMXX, '-MMD', '-c', 'test.cpp', '-o', 'test.o'])
      self.assertExists('test.d')
      deps = read_file('test.d')
      # Look for ': ' instead of just ':' to not confuse C:\path\ notation with make "target: deps" rule. Not perfect, but good enough for this test.
      head, tail = deps.split(': ', 2)
      self.assertContained('test.o', head)
      self.assertContained('test.cpp', tail)
      self.assertContained('test.hpp', tail)
  
    def test_dependency_file_2(self):
      shutil.copyfile(test_file('hello_world.c'), 'a.c')
      self.run_process([EMCC, 'a.c', '-MMD', '-MF', 'test.d', '-c'])
      self.assertContained('a.o: a.c\n', read_file('test.d'))
  
      shutil.copyfile(test_file('hello_world.c'), 'a.c')
      self.run_process([EMCC, 'a.c', '-MMD', '-MF', 'test2.d', '-c', '-o', 'test.o'])
      self.assertContained('test.o: a.c\n', read_file('test2.d'))
  
      shutil.copyfile(test_file('hello_world.c'), 'a.c')
      ensure_dir('obj')
      self.run_process([EMCC, 'a.c', '-MMD', '-MF', 'test3.d', '-c', '-o', 'obj/test.o'])
      self.assertContained('obj/test.o: a.c\n', read_file('test3.d'))
  
    def test_compilation_database(self):
      shutil.copyfile(test_file('hello_world.c'), 'a.c')
      self.run_process([EMCC, 'a.c', '-MJ', 'hello.json', '-c', '-o', 'test.o'])
      self.assertContained('"file": "a.c", "output": "test.o"', read_file('hello.json'))
  
    def test_js_lib_quoted_key(self):
      create_file('lib.js', r'''
  mergeInto(LibraryManager.library, {
     __internal_data:{
      '<' : 0,
      'white space' : 1
    },
    printf__deps: ['__internal_data', 'fprintf']
  });
  ''')
  
      self.run_process([EMXX, test_file('hello_world.cpp'), '--js-library', 'lib.js'])
      self.assertContained('hello, world!', self.run_js('a.out.js'))
  
    def test_js_lib_exported(self):
      create_file('lib.js', r'''
  mergeInto(LibraryManager.library, {
   jslibfunc: function(x) { return 2 * x }
  });
  ''')
      create_file('src.cpp', r'''
  #include <emscripten.h>
  #include <stdio.h>
  extern "C" int jslibfunc(int x);
  int main() {
    printf("c calling: %d\n", jslibfunc(6));
    EM_ASM({
      out('js calling: ' + Module['_jslibfunc'](5) + '.');
    });
  }
  ''')
      self.run_process([EMXX, 'src.cpp', '--js-library', 'lib.js', '-sEXPORTED_FUNCTIONS=_main,_jslibfunc'])
      self.assertContained('c calling: 12\njs calling: 10.', self.run_js('a.out.js'))
  
    def test_js_lib_using_asm_lib(self):
      create_file('lib.js', r'''
  mergeInto(LibraryManager.library, {
    jslibfunc__deps: ['asmlibfunc'],
    jslibfunc: function(x) {
      return 2 * _asmlibfunc(x);
    },
  
    asmlibfunc__asm: true,
    asmlibfunc__sig: 'ii',
    asmlibfunc: function(x) {
      x = x | 0;
      return x + 1 | 0;
    }
  });
  ''')
      create_file('src.cpp', r'''
  #include <stdio.h>
  extern "C" int jslibfunc(int x);
  int main() {
    printf("c calling: %d\n", jslibfunc(6));
  }
  ''')
      self.run_process([EMXX, 'src.cpp', '--js-library', 'lib.js'])
      self.assertContained('c calling: 14\n', self.run_js('a.out.js'))
  
    def test_js_internal_deps(self):
      create_file('lib.js', r'''
  mergeInto(LibraryManager.library, {
    jslibfunc__deps: ['$callRuntimeCallbacks'],
    jslibfunc: function(x) {
      callRuntimeCallbacks();
    },
  });
  ''')
      create_file('src.cpp', r'''
  #include <stdio.h>
  extern "C" int jslibfunc();
  int main() {
    printf("c calling: %d\n", jslibfunc());
  }
  ''')
      err = self.run_process([EMXX, 'src.cpp', '--js-library', 'lib.js'], stderr=PIPE).stderr
      self.assertContained("warning: user library symbol 'jslibfunc' depends on internal symbol '$callRuntimeCallbacks'", err)
  
    def test_EMCC_BUILD_DIR(self):
      # EMCC_BUILD_DIR env var contains the dir we were building in, when running the js compiler (e.g. when
      # running a js library). We force the cwd to be src/ for technical reasons, so this lets you find out
      # where you were.
      create_file('lib.js', r'''
  printErr('dir was ' + process.env.EMCC_BUILD_DIR);
  ''')
      err = self.run_process([EMXX, test_file('hello_world.cpp'), '--js-library', 'lib.js'], stderr=PIPE).stderr
      self.assertContained('dir was ' + os.path.realpath(os.path.normpath(self.get_dir())), err)
  
    def test_float_h(self):
      process = self.run_process([EMCC, test_file('float+.c')], stdout=PIPE, stderr=PIPE)
      assert process.returncode == 0, 'float.h should agree with our system: ' + process.stdout + '\n\n\n' + process.stderr
  
    def test_output_is_dir(self):
      ensure_dir('out_dir')
      err = self.expect_fail([EMCC, '-c', test_file('hello_world.c'), '-o', 'out_dir/'])
      self.assertContained('error: unable to open output file', err)
  
    def test_default_obj_ext(self):
      self.run_process([EMCC, '-c', test_file('hello_world.c')])
      self.assertExists('hello_world.o')
  
      self.run_process([EMCC, '-c', test_file('hello_world.c'), '--default-obj-ext', 'obj'])
      self.assertExists('hello_world.obj')
  
    def test_doublestart_bug(self):
      create_file('code.cpp', r'''
  #include <stdio.h>
  #include <emscripten.h>
  
  void main_loop(void) {
      static int cnt = 0;
      if (++cnt >= 10) emscripten_cancel_main_loop();
  }
  
  int main(void) {
      printf("This should only appear once.\n");
      emscripten_set_main_loop(main_loop, 10, 0);
      return 0;
  }
  ''')
  
      create_file('pre.js', r'''
  if (!Module['preRun']) Module['preRun'] = [];
  Module["preRun"].push(function () {
      addRunDependency('test_run_dependency');
      removeRunDependency('test_run_dependency');
  });
  ''')
  
      self.run_process([EMXX, 'code.cpp', '--pre-js', 'pre.js'])
      output = self.run_js('a.out.js')
  
      assert output.count('This should only appear once.') == 1, output
  
    def test_module_print(self):
      create_file('code.cpp', r'''
  #include <stdio.h>
  int main(void) {
    printf("123456789\n");
    return 0;
  }
  ''')
  
      create_file('pre.js', r'''
  var Module = { print: function(x) { throw '<{(' + x + ')}>' } };
  ''')
  
      self.run_process([EMXX, 'code.cpp', '--pre-js', 'pre.js'])
      output = self.run_js('a.out.js', assert_returncode=NON_ZERO)
      self.assertContained(r'<{(123456789)}>', output)
  
    def test_precompiled_headers_warnings(self):
      # Check that we don't have any underlying warnings from clang, this can happen if we
      # pass any link flags to when building a pch.
      create_file('header.h', '#define X 5\n')
      self.run_process([EMCC, '-Werror', '-xc++-header', 'header.h'])
  
    @parameterized({
      'gch': ['gch'],
      'pch': ['pch'],
    })
    def test_precompiled_headers(self, suffix):
      create_file('header.h', '#define X 5\n')
      self.run_process([EMCC, '-xc++-header', 'header.h', '-c'])
      self.assertExists('header.h.gch') # default output is gch
      if suffix != 'gch':
        self.run_process([EMCC, '-xc++-header', 'header.h', '-o', 'header.h.' + suffix])
        self.assertBinaryEqual('header.h.gch', 'header.h.' + suffix)
  
      create_file('src.cpp', r'''
  #include <stdio.h>
  int main() {
  printf("|%d|\n", X);
  return 0;
  }
  ''')
      self.run_process([EMXX, 'src.cpp', '-include', 'header.h'])
  
      output = self.run_js('a.out.js')
      self.assertContained('|5|', output)
  
      # also verify that the gch is actually used
      err = self.run_process([EMXX, 'src.cpp', '-include', 'header.h', '-Xclang', '-print-stats'], stderr=PIPE).stderr
      self.assertTextDataContained('*** PCH/Modules Loaded:\nModule: header.h.' + suffix, err)
      # and sanity check it is not mentioned when not
      try_delete('header.h.' + suffix)
      err = self.run_process([EMXX, 'src.cpp', '-include', 'header.h', '-Xclang', '-print-stats'], stderr=PIPE).stderr
      self.assertNotContained('*** PCH/Modules Loaded:\nModule: header.h.' + suffix, err.replace('\r\n', '\n'))
  
      # with specified target via -o
      try_delete('header.h.' + suffix)
      self.run_process([EMCC, '-xc++-header', 'header.h', '-o', 'my.' + suffix])
      self.assertExists('my.' + suffix)
  
      # -include-pch flag
      self.run_process([EMCC, '-xc++-header', 'header.h', '-o', 'header.h.' + suffix])
      self.run_process([EMXX, 'src.cpp', '-include-pch', 'header.h.' + suffix])
      output = self.run_js('a.out.js')
      self.assertContained('|5|', output)
  
    def test_LEGACY_VM_SUPPORT(self):
      # when modern features are lacking, we can polyfill them or at least warn
      create_file('pre.js', 'Math.imul = undefined;')
  
      def test(expected, opts=[]):
        print(opts)
        result = self.run_process([EMCC, test_file('hello_world.c'), '--pre-js', 'pre.js'] + opts, stderr=PIPE, check=False)
        if result.returncode == 0:
          self.assertContained(expected, self.run_js('a.out.js', assert_returncode=0 if opts else NON_ZERO))
        else:
          self.assertContained(expected, result.stderr)
  
      # when legacy is needed, we show an error indicating so
      test('build with LEGACY_VM_SUPPORT')
      # legacy + disabling wasm works
      test('hello, world!', ['-sLEGACY_VM_SUPPORT', '-sWASM=0'])
  
    def test_on_abort(self):
      expected_output = 'Module.onAbort was called'
  
      def add_on_abort_and_verify(extra=''):
        js = read_file('a.out.js')
        with open('a.out.js', 'w') as f:
          f.write("var Module = { onAbort: function() { console.log('%s') } };\n" % expected_output)
          f.write(extra + '\n')
          f.write(js)
        self.assertContained(expected_output, self.run_js('a.out.js', assert_returncode=NON_ZERO))
  
      # test direct abort() C call
  
      create_file('src.c', '''
          #include <stdlib.h>
          int main() {
            abort();
          }
        ''')
      self.run_process([EMCC, 'src.c', '-sWASM_ASYNC_COMPILATION=0'])
      add_on_abort_and_verify()
  
      # test direct abort() JS call
  
      create_file('src.c', '''
          #include <emscripten.h>
          int main() {
            EM_ASM({ abort() });
          }
        ''')
      self.run_process([EMCC, 'src.c', '-sWASM_ASYNC_COMPILATION=0'])
      add_on_abort_and_verify()
  
      # test throwing in an abort handler, and catching that
  
      create_file('src.c', '''
          #include <emscripten.h>
          int main() {
            EM_ASM({
              try {
                out('first');
                abort();
              } catch (e) {
                out('second');
                abort();
                throw e;
              }
            });
          }
        ''')
      self.run_process([EMCC, 'src.c', '-sWASM_ASYNC_COMPILATION=0'])
      js = read_file('a.out.js')
      with open('a.out.js', 'w') as f:
        f.write("var Module = { onAbort: function() { console.log('%s'); throw 're-throw'; } };\n" % expected_output)
        f.write(js)
      out = self.run_js('a.out.js', assert_returncode=NON_ZERO)
      print(out)
      self.assertContained(expected_output, out)
      self.assertContained('re-throw', out)
      self.assertContained('first', out)
      self.assertContained('second', out)
      self.assertEqual(out.count(expected_output), 2)
  
      # test an abort during startup
      self.run_process([EMCC, test_file('hello_world.c')])
      os.remove('a.out.wasm') # trigger onAbort by intentionally causing startup to fail
      add_on_abort_and_verify()
  
    def test_no_exit_runtime(self):
      create_file('code.cpp', r'''
  #include <stdio.h>
  
  template<int x>
  struct Waste {
    Waste() {
      printf("coming around %d\n", x);
    }
    ~Waste() {
      printf("going away %d\n", x);
    }
  };
  
  Waste<1> w1;
  Waste<2> w2;
  Waste<3> w3;
  Waste<4> w4;
  Waste<5> w5;
  
  int main(int argc, char **argv) {
    return 0;
  }
      ''')
  
      for wasm in [0, 1]:
        for no_exit in [1, 0]:
          for opts in [[], ['-O1'], ['-O2', '-g2'], ['-O2', '-g2', '-flto']]:
            print(wasm, no_exit, opts)
            cmd = [EMXX] + opts + ['code.cpp', '-sEXIT_RUNTIME=' + str(1 - no_exit), '-sWASM=' + str(wasm)]
            if wasm:
              cmd += ['--profiling-funcs'] # for function names
            self.run_process(cmd)
            output = self.run_js('a.out.js')
            src = read_file('a.out.js')
            if wasm:
              src += '\n' + self.get_wasm_text('a.out.wasm')
            exit = 1 - no_exit
            print('  exit:', exit, 'opts:', opts)
            self.assertContained('coming around', output)
            self.assertContainedIf('going away', output, exit)
            # The wasm backend uses atexit to register destructors when
            # constructors are called  There is currently no way to exclude
            # these destructors from the wasm binary.
            # TODO(sbc): Re-enabled these assertions once the wasm backend
            # is able to eliminate these.
            # assert ('atexit(' in src) == exit, 'atexit should not appear in src when EXIT_RUNTIME=0'
            # assert ('_ZN5WasteILi2EED' in src) == exit, 'destructors should not appear if no exit:\n' + src
  
    def test_no_exit_runtime_warnings_flush(self):
      # check we warn if there is unflushed info
      create_file('code.c', r'''
  #include <stdio.h>
  #include <emscripten/emscripten.h>
  int main(int argc, char **argv) {
    printf("hello\n");
    printf("world"); // no newline, not flushed
  #if FLUSH
    printf("\n");
  #endif
  #if KEEPALIVE
    emscripten_exit_with_live_runtime();
  #endif
  }
  ''')
      create_file('code.cpp', r'''
  #include <iostream>
  #include <emscripten/emscripten.h>
  int main() {
    using namespace std;
    cout << "hello" << std::endl;
    cout << "world"; // no newline, not flushed
  #if FLUSH
    std::cout << std::endl;
  #endif
  #if KEEPALIVE
    emscripten_exit_with_live_runtime();
  #endif
  }
  ''')
      warning = 'stdio streams had content in them that was not flushed. you should set EXIT_RUNTIME to 1'
  
      def test(cxx, no_exit, assertions, flush=0, keepalive=0, filesystem=1):
        if cxx:
          cmd = [EMXX, 'code.cpp']
        else:
          cmd = [EMCC, 'code.c']
        print('%s: no_exit=%d assertions=%d flush=%d keepalive=%d filesystem=%d' % (cmd[1], no_exit, assertions, flush, keepalive, filesystem))
        cmd += ['-sEXIT_RUNTIME=%d' % (1 - no_exit), '-sASSERTIONS=%d' % assertions]
        if flush:
          cmd += ['-DFLUSH']
        if keepalive:
          cmd += ['-DKEEPALIVE']
        if not filesystem:
          cmd += ['-sNO_FILESYSTEM']
        self.run_process(cmd)
        output = self.run_js('a.out.js')
        exit = 1 - no_exit
        self.assertContained('hello', output)
        self.assertContainedIf('world', output, exit or flush)
        self.assertContainedIf(warning, output, no_exit and assertions and not flush and not keepalive)
  
      # Run just one test with KEEPALIVE set.  In this case we don't expect to see any kind
      # of warning becasue we are explictly requesting the runtime stay alive for later use.
      test(cxx=0, no_exit=1, assertions=1, keepalive=1)
      test(cxx=0, no_exit=1, assertions=1, filesystem=0)
  
      for cxx in [0, 1]:
        for no_exit in [0, 1]:
          for assertions in [0, 1]:
            for flush in [0, 1]:
              test(cxx, no_exit, assertions, flush)
  
    def test_fs_after_main(self):
      for args in [[], ['-O1']]:
        print(args)
        self.run_process([EMXX, test_file('fs_after_main.cpp')])
        self.assertContained('Test passed.', self.run_js('a.out.js'))
  
    def test_os_oz(self):
      for opt in ['-O1', '-O2', '-Os', '-Oz', '-O3']:
        print(opt)
        proc = self.run_process([EMXX, '-v', test_file('hello_world.cpp'), opt], stderr=PIPE)
        self.assertContained(opt, proc.stderr)
        self.assertContained('hello, world!', self.run_js('a.out.js'))
  
    def test_oz_size(self):
      sizes = {}
      for name, args in [
          ('0', []),
          ('1', ['-O1']),
          ('2', ['-O2']),
          ('s', ['-Os']),
          ('z', ['-Oz']),
          ('3', ['-O3']),
        ]:
        print(name, args)
        self.clear()
        self.run_process([EMCC, '-c', path_from_root('system/lib/dlmalloc.c')] + args)
        sizes[name] = os.path.getsize('dlmalloc.o')
      print(sizes)
      opt_min = min(sizes['1'], sizes['2'], sizes['3'], sizes['s'], sizes['z'])
      opt_max = max(sizes['1'], sizes['2'], sizes['3'], sizes['s'], sizes['z'])
      # 'opt builds are all fairly close'
      self.assertLess(opt_min - opt_max, opt_max * 0.1)
      # unopt build is quite larger'
      self.assertGreater(sizes['0'], (1.20 * opt_max))
  
    @disabled('relies on fastcomp EXIT_RUNTIME=0 optimization not implemented/disabled')
    def test_global_inits(self):
      create_file('inc.h', r'''
  #include <stdio.h>
  
  template<int x>
  struct Waste {
    int state;
    Waste() : state(10) {}
    void test(int a) {
      printf("%d\n", a + state);
    }
    ~Waste() {
      printf("going away %d\n", x);
    }
  };
  
  Waste<3> *getMore();
  ''')
  
      create_file('main.cpp', r'''
  #include "inc.h"
  
  Waste<1> mw1;
  Waste<2> mw2;
  
  int main(int argc, char **argv) {
    printf("argc: %d\n", argc);
    mw1.state += argc;
    mw2.state += argc;
    mw1.test(5);
    mw2.test(6);
    getMore()->test(0);
    return 0;
  }
  ''')
  
      create_file('side.cpp', r'''
  #include "inc.h"
  
  Waste<3> sw3;
  
  Waste<3> *getMore() {
    return &sw3;
  }
  ''')
  
      for opts, has_global in [
        (['-O2', '-g', '-sEXIT_RUNTIME'], True),
        # no-exit-runtime removes the atexits, and then globalgce can work
        # it's magic to remove the global initializer entirely
        (['-O2', '-g'], False),
        (['-Os', '-g', '-sEXIT_RUNTIME'], True),
        (['-Os', '-g'], False),
        (['-O2', '-g', '-flto', '-sEXIT_RUNTIME'], True),
        (['-O2', '-g', '-flto'], False),
      ]:
        print(opts, has_global)
        self.run_process([EMXX, 'main.cpp', '-c'] + opts)
        self.run_process([EMXX, 'side.cpp', '-c'] + opts)
        self.run_process([EMCC, 'main.o', 'side.o'] + opts)
        self.run_js('a.out.js')
        src = read_file('a.out.js')
        self.assertContained('argc: 1\n16\n17\n10\n', self.run_js('a.out.js'))
        self.assertContainedIf('globalCtors', src, has_global)
  
    def test_implicit_func(self):
      create_file('src.c', r'''
  #include <stdio.h>
  int main()
  {
      printf("hello %d\n", strnlen("waka", 2)); // Implicit declaration, no header, for strnlen
      int (*my_strnlen)(char*, ...) = strnlen;
      printf("hello %d\n", my_strnlen("shaka", 2));
      return 0;
  }
  ''')
  
      IMPLICIT_WARNING = "warning: implicit declaration of function 'strnlen' is invalid in C99"
      IMPLICIT_ERROR = "error: implicit declaration of function 'strnlen' is invalid in C99"
      INCOMPATIBLE_WARNINGS = ('warning: incompatible pointer types', 'warning: incompatible function pointer types')
  
      for opts, expected, compile_expected in [
        ([], None, [IMPLICIT_ERROR]),
        (['-Wno-error=implicit-function-declaration'], ['hello '], [IMPLICIT_WARNING]), # turn error into warning
        (['-Wno-implicit-function-declaration'], ['hello '], []), # turn error into nothing at all (runtime output is incorrect)
      ]:
        print(opts, expected)
        try_delete('a.out.js')
        stderr = self.run_process([EMCC, 'src.c'] + opts, stderr=PIPE, check=False).stderr
        for ce in compile_expected + [INCOMPATIBLE_WARNINGS]:
          self.assertContained(ce, stderr)
        if expected is None:
          self.assertNotExists('a.out.js')
        else:
          output = self.run_js('a.out.js')
          for e in expected:
            self.assertContained(e, output)
  
    @requires_native_clang
    def test_bad_triple(self):
      # compile a minimal program, with as few dependencies as possible, as
      # native building on CI may not always work well
      create_file('minimal.cpp', 'int main() { return 0; }')
      self.run_process([CLANG_CXX, 'minimal.cpp', '-target', 'x86_64-linux', '-c', '-emit-llvm', '-o', 'a.bc'] + clang_native.get_clang_native_args(), env=clang_native.get_clang_native_env())
      # wasm backend will hard fail where as fastcomp only warns
      err = self.expect_fail([EMCC, 'a.bc'])
      self.assertContained('machine type must be wasm32', err)
  
    def test_valid_abspath(self):
      # Test whether abspath warning appears
      abs_include_path = os.path.abspath(self.get_dir())
      err = self.run_process([EMCC, '-I%s' % abs_include_path, '-Wwarn-absolute-paths', test_file('hello_world.c')], stderr=PIPE).stderr
      warning = '-I or -L of an absolute path "-I%s" encountered. If this is to a local system header/library, it may cause problems (local system files make sense for compiling natively on your system, but not necessarily to JavaScript).' % abs_include_path
      self.assertContained(warning, err)
  
      # Passing an absolute path to a directory inside the emscripten tree is always ok and should not issue a warning.
      abs_include_path = TEST_ROOT
      err = self.run_process([EMCC, '-I%s' % abs_include_path, '-Wwarn-absolute-paths', test_file('hello_world.c')], stderr=PIPE).stderr
      warning = '-I or -L of an absolute path "-I%s" encountered. If this is to a local system header/library, it may cause problems (local system files make sense for compiling natively on your system, but not necessarily to JavaScript).' % abs_include_path
      self.assertNotContained(warning, err)
  
      # Hide warning for this include path
      err = self.run_process([EMCC, '--valid-abspath', abs_include_path, '-I%s' % abs_include_path, '-Wwarn-absolute-paths', test_file('hello_world.c')], stderr=PIPE).stderr
      self.assertNotContained(warning, err)
  
    def test_valid_abspath_2(self):
      if WINDOWS:
        abs_include_path = 'C:\\nowhere\\at\\all'
      else:
        abs_include_path = '/nowhere/at/all'
      cmd = [EMCC, test_file('hello_world.c'), '--valid-abspath', abs_include_path, '-I%s' % abs_include_path]
      print(' '.join(cmd))
      self.run_process(cmd)
      self.assertContained('hello, world!', self.run_js('a.out.js'))
  
    def test_warn_dylibs(self):
      shared_suffixes = ['.so', '.dylib', '.dll']
  
      for suffix in ['.o', '.bc', '.so', '.dylib', '.js', '.html']:
        print(suffix)
        cmd = [EMCC, test_file('hello_world.c'), '-o', 'out' + suffix]
        if suffix in ['.o', '.bc']:
          cmd.append('-c')
        if suffix in ['.dylib', '.so']:
          cmd.append('-shared')
        err = self.run_process(cmd, stderr=PIPE).stderr
        warning = 'linking a library with `-shared` will emit a static object file'
        self.assertContainedIf(warning, err, suffix in shared_suffixes)
  
    @parameterized({
      'O2': [['-O2']],
      'O3': [['-O3']],
    })
    def test_symbol_map(self, opts):
      def get_symbols_lines(symbols_file):
        self.assertTrue(os.path.isfile(symbols_file), "Symbols file %s isn't created" % symbols_file)
        # check that the map is correct
        symbols = read_file(symbols_file)
        lines = [line.split(':') for line in symbols.strip().split('\n')]
        return lines
  
      def get_minified_middle(symbols_file):
        minified_middle = None
        for minified, full in get_symbols_lines(symbols_file):
          # handle both fastcomp and wasm backend notation
          if full == 'middle':
            minified_middle = minified
            break
        return minified_middle
  
      def guess_symbols_file_type(symbols_file):
        for minified, full in get_symbols_lines(symbols_file):
          # define symbolication file by JS specific entries
          if full in ['FUNCTION_TABLE', 'HEAP32']:
            return 'js'
        return 'wasm'
  
      UNMINIFIED_HEAP8 = 'var HEAP8 = new '
      UNMINIFIED_MIDDLE = 'function middle'
  
      for wasm in [0, 1, 2]:
        print(opts, wasm)
        self.clear()
        create_file('src.c', r'''
  #include <emscripten.h>
  
  EM_JS(int, run_js, (), {
  out(new Error().stack);
  return 0;
  });
  
  EMSCRIPTEN_KEEPALIVE
  void middle() {
  if (run_js()) {
    // fake recursion that is never reached, to avoid inlining in binaryen and LLVM
    middle();
  }
  }
  
  int main() {
  EM_ASM({ _middle() });
  }
  ''')
        cmd = [EMCC, 'src.c', '--emit-symbol-map'] + opts
        if wasm != 1:
          cmd.append(f'-sWASM={wasm}')
        self.run_process(cmd)
  
        minified_middle = get_minified_middle('a.out.js.symbols')
        self.assertNotEqual(minified_middle, None, "Missing minified 'middle' function")
        if wasm:
          # stack traces are standardized enough that we can easily check that the
          # minified name is actually in the output
          stack_trace_reference = 'wasm-function[%s]' % minified_middle
          out = self.run_js('a.out.js')
          self.assertContained(stack_trace_reference, out)
          # make sure there are no symbols in the wasm itself
          wat = self.run_process([wasm_dis, 'a.out.wasm'], stdout=PIPE).stdout
          for func_start in ('(func $middle', '(func $_middle'):
            self.assertNotContained(func_start, wat)
  
        # Ensure symbols file type according to `-s WASM=` mode
        if wasm == 0:
          self.assertEqual(guess_symbols_file_type('a.out.js.symbols'), 'js', 'Primary symbols file should store JS mappings')
        elif wasm == 1:
          self.assertEqual(guess_symbols_file_type('a.out.js.symbols'), 'wasm', 'Primary symbols file should store Wasm mappings')
        elif wasm == 2:
          # special case when both JS and Wasm targets are created
          minified_middle_2 = get_minified_middle('a.out.wasm.js.symbols')
          self.assertNotEqual(minified_middle_2, None, "Missing minified 'middle' function")
          self.assertEqual(guess_symbols_file_type('a.out.js.symbols'), 'wasm', 'Primary symbols file should store Wasm mappings')
          self.assertEqual(guess_symbols_file_type('a.out.wasm.js.symbols'), 'js', 'Secondary symbols file should store JS mappings')
  
        # check we don't keep unnecessary debug info with wasm2js when emitting
        # a symbol map
        if wasm == 0 and '-O' in str(opts):
          js = read_file('a.out.js')
          self.assertNotContained(UNMINIFIED_HEAP8, js)
          self.assertNotContained(UNMINIFIED_MIDDLE, js)
          # verify those patterns would exist with more debug info
          self.run_process(cmd + ['--profiling-funcs'])
          js = read_file('a.out.js')
          self.assertContained(UNMINIFIED_HEAP8, js)
          self.assertContained(UNMINIFIED_MIDDLE, js)
  
    @parameterized({
      '': [[]],
      # bigint support is interesting to test here because it changes which
      # binaryen tools get run, which can affect how debug info is kept around
      'bigint': [['-sWASM_BIGINT']],
    })
    def test_symbol_map_output_size(self, args):
      # build with and without a symbol map and verify that the sizes are the
      # same. using a symbol map should add the map on the side, but not increase
      # the build size.
      # -Oz is used here to run as many optimizations as possible, to check for
      # any difference in how the optimizer operates
      self.run_process([EMCC, test_file('hello_world.c'), '-Oz', '-o', 'test1.js'] + args)
      self.run_process([EMCC, test_file('hello_world.c'), '-Oz', '-o', 'test2.js', '--emit-symbol-map'] + args)
      self.assertEqual(os.path.getsize('test1.js'), os.path.getsize('test2.js'))
      self.assertEqual(os.path.getsize('test1.wasm'), os.path.getsize('test2.wasm'))
  
    def test_bc_to_bc(self):
      # emcc should 'process' bitcode to bitcode. build systems can request this if
      # e.g. they assume our 'executable' extension is bc, and compile an .o to a .bc
      # (the user would then need to build bc to js of course, but we need to actually
      # emit the bc)
      self.run_process([EMCC, '-flto', '-c', test_file('hello_world.c')])
      self.assertExists('hello_world.o')
      err = self.run_process([EMCC, '-flto', '-r', 'hello_world.o', '-o', 'hello_world.bc'], stderr=PIPE).stderr
      self.assertContained('emcc: warning: bitcode linking with llvm-link is deprecated', err)
      self.assertExists('hello_world.o')
      self.assertExists('hello_world.bc')
  
    def test_bad_function_pointer_cast(self):
      create_file('src.cpp', r'''
  #include <stdio.h>
  
  typedef int (*callback) (int, ...);
  
  int impl(int foo) {
    printf("Hello, world.\n");
    return 0;
  }
  
  int main() {
    volatile callback f = (callback) impl;
    f(0); /* This fails with or without additional arguments. */
    return 0;
  }
  ''')
  
      for opts in [0, 1, 2]:
        for safe in [0, 1]:
          for emulate_casts in [0, 1]:
            for relocatable in [0, 1]:
              for wasm in [0, 1]:
                # wasm2js is not compatible with relocatable mode
                if wasm == 0 and relocatable:
                  continue
                cmd = [EMXX, 'src.cpp', '-O' + str(opts)]
                if not wasm:
                  cmd += ['-sWASM=0']
                if safe:
                  cmd += ['-sSAFE_HEAP']
                if emulate_casts:
                  cmd += ['-sEMULATE_FUNCTION_POINTER_CASTS']
                if relocatable:
                  cmd += ['-sRELOCATABLE'] # disables asm-optimized safe heap
                print(cmd)
                self.run_process(cmd)
                returncode = 0 if emulate_casts or wasm == 0 else NON_ZERO
                output = self.run_js('a.out.js', assert_returncode=returncode)
                if emulate_casts or wasm == 0:
                  # success!
                  self.assertContained('Hello, world.', output)
                else:
                  # otherwise, the error depends on the mode we are in
                  # wasm trap raised by the vm
                  self.assertContained('function signature mismatch', output)
  
    def test_bad_export(self):
      for m in ['', ' ']:
        self.clear()
        cmd = [EMCC, test_file('hello_world.c'), '-sEXPORTED_FUNCTIONS=["' + m + '_main"]']
        print(cmd)
        stderr = self.run_process(cmd, stderr=PIPE, check=False).stderr
        if m:
          self.assertContained('undefined exported symbol: " _main"', stderr)
        else:
          self.assertContained('hello, world!', self.run_js('a.out.js'))
  
    def test_no_dynamic_execution(self):
      self.run_process([EMCC, test_file('hello_world.c'), '-O1', '-sDYNAMIC_EXECUTION=0'])
      self.assertContained('hello, world!', self.run_js('a.out.js'))
      src = read_file('a.out.js')
      self.assertNotContained('eval(', src)
      self.assertNotContained('eval.', src)
      self.assertNotContained('new Function', src)
      try_delete('a.out.js')
  
      # Test that --preload-file doesn't add an use of eval().
      create_file('temp.txt', "foo\n")
      self.run_process([EMCC, test_file('hello_world.c'), '-O1',
                        '-sDYNAMIC_EXECUTION=0', '--preload-file', 'temp.txt'])
      src = read_file('a.out.js')
      self.assertNotContained('eval(', src)
      self.assertNotContained('eval.', src)
      self.assertNotContained('new Function', src)
      try_delete('a.out.js')
  
      # Test that -s DYNAMIC_EXECUTION=1 and -s RELOCATABLE=1 are not allowed together.
      self.expect_fail([EMCC, test_file('hello_world.c'), '-O1',
                        '-sDYNAMIC_EXECUTION=0', '-sRELOCATABLE'])
      try_delete('a.out.js')
  
      create_file('test.c', r'''
        #include <emscripten/emscripten.h>
        int main() {
          emscripten_run_script("console.log('hello from script');");
          return 0;
        }
        ''')
  
      # Test that emscripten_run_script() aborts when -s DYNAMIC_EXECUTION=0
      self.run_process([EMCC, 'test.c', '-O1', '-sDYNAMIC_EXECUTION=0'])
      self.assertContained('DYNAMIC_EXECUTION=0 was set, cannot eval', self.run_js('a.out.js', assert_returncode=NON_ZERO))
      try_delete('a.out.js')
  
      # Test that emscripten_run_script() posts a warning when -s DYNAMIC_EXECUTION=2
      self.run_process([EMCC, 'test.c', '-O1', '-sDYNAMIC_EXECUTION=2'])
      self.assertContained('Warning: DYNAMIC_EXECUTION=2 was set, but calling eval in the following location:', self.run_js('a.out.js'))
      self.assertContained('hello from script', self.run_js('a.out.js'))
      try_delete('a.out.js')
  
    def test_init_file_at_offset(self):
      create_file('src.cpp', r'''
        #include <stdio.h>
        int main() {
          int data = 0x12345678;
          FILE *f = fopen("test.dat", "wb");
          fseek(f, 100, SEEK_CUR);
          fwrite(&data, 4, 1, f);
          fclose(f);
  
          int data2;
          f = fopen("test.dat", "rb");
          fread(&data2, 4, 1, f); // should read 0s, not that int we wrote at an offset
          printf("read: %d\n", data2);
          fseek(f, 0, SEEK_END);
          long size = ftell(f); // should be 104, not 4
          fclose(f);
          printf("file size is %ld\n", size);
        }
      ''')
      self.run_process([EMXX, 'src.cpp'])
      self.assertContained('read: 0\nfile size is 104\n', self.run_js('a.out.js'))
  
    def test_unlink(self):
      self.do_other_test('test_unlink.cpp')
  
    def test_argv0_node(self):
      create_file('code.cpp', r'''
  #include <stdio.h>
  int main(int argc, char **argv) {
    printf("I am %s.\n", argv[0]);
    return 0;
  }
  ''')
  
      self.run_process([EMXX, 'code.cpp'])
      self.assertContained('I am ' + os.path.realpath(self.get_dir()).replace('\\', '/') + '/a.out.js', self.run_js('a.out.js').replace('\\', '/'))
  
    @parameterized({
      'no_exit_runtime': [True],
      '': [False],
    })
    def test_returncode(self, no_exit):
      create_file('src.cpp', r'''
        #include <stdio.h>
        #include <stdlib.h>
        int main() {
        #if CALL_EXIT
          exit(CODE);
        #else
          return CODE;
        #endif
        }
      ''')
      for code in [0, 123]:
        for call_exit in [0, 1]:
          for async_compile in [0, 1]:
            self.run_process([EMXX, 'src.cpp', '-sENVIRONMENT=node,shell', '-DCODE=%d' % code, '-sEXIT_RUNTIME=%d' % (1 - no_exit), '-DCALL_EXIT=%d' % call_exit, '-sWASM_ASYNC_COMPILATION=%d' % async_compile])
            for engine in config.JS_ENGINES:
              # async compilation can't return a code in d8
              if async_compile and engine == config.V8_ENGINE:
                continue
              print(code, call_exit, async_compile, engine)
              proc = self.run_process(engine + ['a.out.js'], stderr=PIPE, check=False)
              msg = 'but EXIT_RUNTIME is not set, so halting execution but not exiting the runtime or preventing further async execution (build with EXIT_RUNTIME=1, if you want a true shutdown)'
              if no_exit and call_exit:
                self.assertContained(msg, proc.stderr)
              else:
                self.assertNotContained(msg, proc.stderr)
              # we always emit the right exit code, whether we exit the runtime or not
              self.assertEqual(proc.returncode, code)
  
    def test_emscripten_force_exit_NO_EXIT_RUNTIME(self):
      create_file('src.cpp', r'''
        #include <emscripten.h>
        int main() {
        #if CALL_EXIT
          emscripten_force_exit(0);
        #endif
        }
      ''')
      for no_exit in [0, 1]:
        for call_exit in [0, 1]:
          self.run_process([EMXX, 'src.cpp', '-sEXIT_RUNTIME=%d' % (1 - no_exit), '-DCALL_EXIT=%d' % call_exit])
          print(no_exit, call_exit)
          out = self.run_js('a.out.js')
          assert ('emscripten_force_exit cannot actually shut down the runtime, as the build does not have EXIT_RUNTIME set' in out) == (no_exit and call_exit), out
  
    def test_mkdir_silly(self):
      create_file('src.cpp', r'''
  #include <stdio.h>
  #include <dirent.h>
  #include <errno.h>
  #include <sys/stat.h>
  #include <sys/types.h>
  #include <unistd.h>
  
  int main(int argc, char **argv) {
    printf("\n");
    for (int i = 1; i < argc; i++) {
      printf("%d:\n", i);
      int ok = mkdir(argv[i], S_IRWXU|S_IRWXG|S_IRWXO);
      printf("  make %s: %d\n", argv[i], ok);
      DIR *dir = opendir(argv[i]);
      printf("  open %s: %d\n", argv[i], dir != NULL);
      if (dir) {
        struct dirent *entry;
        while ((entry = readdir(dir))) {
          printf("  %s, %d\n", entry->d_name, entry->d_type);
        }
      }
    }
  }
      ''')
      self.run_process([EMXX, 'src.cpp'])
  
      # cannot create /, can open
      self.assertContained(r'''
  1:
    make /: -1
    open /: 1
    ., 4
    .., 4
    tmp, 4
    home, 4
    dev, 4
    proc, 4
  ''', self.run_js('a.out.js', args=['/']))
      # cannot create empty name, cannot open
      self.assertContained(r'''
  1:
    make : -1
    open : 0
  ''', self.run_js('a.out.js', args=['']))
      # can create unnormalized path, can open
      self.assertContained(r'''
  1:
    make /a//: 0
    open /a//: 1
    ., 4
    .., 4
  ''', self.run_js('a.out.js', args=['/a//']))
      # can create child unnormalized
      self.assertContained(r'''
  1:
    make /a: 0
    open /a: 1
    ., 4
    .., 4
  2:
    make /a//b//: 0
    open /a//b//: 1
    ., 4
    .., 4
  ''', self.run_js('a.out.js', args=['/a', '/a//b//']))
  
    def test_stat_silly(self):
      create_file('src.cpp', r'''
  #include <stdio.h>
  #include <errno.h>
  #include <sys/stat.h>
  
  int main(int argc, char **argv) {
    for (int i = 1; i < argc; i++) {
      const char *path = argv[i];
      struct stat path_stat;
      if (stat(path, &path_stat) != 0) {
        printf("Failed to stat path: '%s'; errno=%d\n", path, errno);
      } else {
        printf("stat success on '%s'\n", path);
      }
    }
  }
      ''')
      self.do_runf('src.cpp', r'''Failed to stat path: '/a'; errno=44
  Failed to stat path: ''; errno=44
  ''', args=['/a', ''])
  
    def test_symlink_silly(self):
      create_file('src.cpp', r'''
  #include <dirent.h>
  #include <errno.h>
  #include <sys/stat.h>
  #include <sys/types.h>
  #include <unistd.h>
  #include <stdio.h>
  
  int main(int argc, char **argv) {
    if (symlink(argv[1], argv[2]) != 0) {
      printf("Failed to symlink paths: %s, %s; errno=%d\n", argv[1], argv[2], errno);
    } else {
      printf("ok\n");
    }
  }
      ''')
      self.run_process([EMXX, 'src.cpp'])
  
      # cannot symlink nonexistents
      self.assertContained(r'Failed to symlink paths: , abc; errno=44', self.run_js('a.out.js', args=['', 'abc']))
      self.assertContained(r'Failed to symlink paths: , ; errno=44', self.run_js('a.out.js', args=['', '']))
      self.assertContained(r'ok', self.run_js('a.out.js', args=['123', 'abc']))
      self.assertContained(r'Failed to symlink paths: abc, ; errno=44', self.run_js('a.out.js', args=['abc', '']))
  
    def test_rename_silly(self):
      create_file('src.cpp', r'''
  #include <stdio.h>
  #include <errno.h>
  
  int main(int argc, char **argv) {
    if (rename(argv[1], argv[2]) != 0) {
      printf("Failed to rename paths: %s, %s; errno=%d\n", argv[1], argv[2], errno);
    } else {
      printf("ok\n");
    }
  }
  ''')
      self.run_process([EMXX, 'src.cpp'])
  
      # cannot symlink nonexistents
      self.assertContained(r'Failed to rename paths: , abc; errno=44', self.run_js('a.out.js', args=['', 'abc']))
      self.assertContained(r'Failed to rename paths: , ; errno=44', self.run_js('a.out.js', args=['', '']))
      self.assertContained(r'Failed to rename paths: 123, abc; errno=44', self.run_js('a.out.js', args=['123', 'abc']))
      self.assertContained(r'Failed to rename paths: abc, ; errno=44', self.run_js('a.out.js', args=['abc', '']))
  
    def test_readdir_r_silly(self):
      create_file('src.cpp', r'''
  #include <iostream>
  #include <cstring>
  #include <cerrno>
  #include <unistd.h>
  #include <fcntl.h>
  #include <cstdlib>
  #include <dirent.h>
  #include <sys/stat.h>
  #include <sys/types.h>
  using std::endl;
  namespace
  {
    void check(const bool result)
    {
      if(not result) {
        std::cout << "Check failed!" << endl;
        throw "bad";
      }
    }
    // Do a recursive directory listing of the directory whose path is specified
    // by \a name.
    void ls(const std::string& name, std::size_t indent = 0)
    {
      ::DIR *dir;
      struct ::dirent *entry;
      if(indent == 0) {
        std::cout << name << endl;
        ++indent;
      }
      // Make sure we can open the directory.  This should also catch cases where
      // the empty string is passed in.
      if (not (dir = ::opendir(name.c_str()))) {
        const int error = errno;
        std::cout
          << "Failed to open directory: " << name << "; " << error << endl;
        return;
      }
      // Just checking the sanity.
      if (name.empty()) {
        std::cout
          << "Managed to open a directory whose name was the empty string.."
          << endl;
        check(::closedir(dir) != -1);
        return;
      }
      // Iterate over the entries in the directory.
      while ((entry = ::readdir(dir))) {
        const std::string entryName(entry->d_name);
        if (entryName == "." || entryName == "..") {
          // Skip the dot entries.
          continue;
        }
        const std::string indentStr(indent * 2, ' ');
        if (entryName.empty()) {
          std::cout
            << indentStr << "\"\": Found empty string as a "
            << (entry->d_type == DT_DIR ? "directory" : "file")
            << " entry!" << endl;
          continue;
        } else {
          std::cout << indentStr << entryName
                    << (entry->d_type == DT_DIR ? "/" : "") << endl;
        }
        if (entry->d_type == DT_DIR) {
          // We found a subdirectory; recurse.
          ls(std::string(name + (name == "/" ? "" : "/" ) + entryName),
             indent + 1);
        }
      }
      // Close our handle.
      check(::closedir(dir) != -1);
    }
    void touch(const std::string &path)
    {
      const int fd = ::open(path.c_str(), O_CREAT | O_TRUNC, 0644);
      check(fd != -1);
      check(::close(fd) != -1);
    }
  }
  int main()
  {
    check(::mkdir("dir", 0755) == 0);
    touch("dir/a");
    touch("dir/b");
    touch("dir/c");
    touch("dir/d");
    touch("dir/e");
    std::cout << "Before:" << endl;
    ls("dir");
    std::cout << endl;
    // Attempt to delete entries as we walk the (single) directory.
    ::DIR * const dir = ::opendir("dir");
    check(dir != NULL);
    struct ::dirent *entry;
    while((entry = ::readdir(dir)) != NULL) {
      const std::string name(entry->d_name);
      // Skip "." and "..".
      if(name == "." || name == "..") {
        continue;
      }
      // Unlink it.
      std::cout << "Unlinking " << name << endl;
      check(::unlink(("dir/" + name).c_str()) != -1);
    }
    check(::closedir(dir) != -1);
    std::cout << "After:" << endl;
    ls("dir");
    std::cout << endl;
    return 0;
  }
      ''')
      # cannot symlink nonexistents
      self.do_runf('src.cpp', r'''Before:
  dir
    a
    b
    c
    d
    e
  
  Unlinking a
  Unlinking b
  Unlinking c
  Unlinking d
  Unlinking e
  After:
  dir
  ''', args=['', 'abc'])
  
    def test_emversion(self):
      create_file('src.cpp', r'''
        #include <stdio.h>
        #include <emscripten/version.h>
        int main() {
          printf("major: %d\n", __EMSCRIPTEN_major__);
          printf("minor: %d\n", __EMSCRIPTEN_minor__);
          printf("tiny: %d\n", __EMSCRIPTEN_tiny__);
        }
      ''')
      expected = '''\
  major: %d
  minor: %d
  tiny: %d
  ''' % (shared.EMSCRIPTEN_VERSION_MAJOR, shared.EMSCRIPTEN_VERSION_MINOR, shared.EMSCRIPTEN_VERSION_TINY)
      self.do_runf('src.cpp', expected)
      self.do_runf('src.cpp', expected, emcc_args=['-sSTRICT'])
  
    def test_libc_files_without_syscalls(self):
      # a program which includes FS due to libc js library support, but has no syscalls,
      # so full FS support would normally be optimized out
      create_file('src.cpp', r'''
  #include <sys/time.h>
  #include <stddef.h>
  int main() {
      return utimes(NULL, NULL);
  }''')
      self.run_process([EMXX, 'src.cpp'])
  
    def test_syscall_without_filesystem(self):
      # a program which includes a non-trivial syscall, but disables the filesystem.
      create_file('src.c', r'''
  #include <sys/time.h>
  #include <fcntl.h>
  int main() {
    return openat(0, "foo", 0);
  }''')
      self.run_process([EMCC, 'src.c', '-sNO_FILESYSTEM'])
  
    def test_dashS(self):
      self.run_process([EMCC, test_file('hello_world.c'), '-S'])
      self.assertExists('hello_world.s')
  
    def assertIsLLVMAsm(self, filename):
      bitcode = read_file(filename)
      self.assertContained('target triple = "', bitcode)
  
    def test_dashS_ll_input(self):
      self.run_process([EMCC, test_file('hello_world.c'), '-S', '-emit-llvm'])
      self.assertIsLLVMAsm('hello_world.ll')
      self.run_process([EMCC, 'hello_world.ll', '-S', '-emit-llvm', '-o', 'another.ll'])
      self.assertIsLLVMAsm('another.ll')
  
    def test_dashS_stdout(self):
      stdout = self.run_process([EMCC, test_file('hello_world.c'), '-S', '-o', '-'], stdout=PIPE).stdout
      self.assertEqual(os.listdir('.'), [])
      self.assertContained('hello_world.c', stdout)
  
    def test_emit_llvm(self):
      # TODO(https://github.com/emscripten-core/emscripten/issues/9016):
      # We shouldn't need to copy the file here but if we don't then emcc will
      # internally clobber the hello_world.ll in tests.
      self.run_process([EMCC, test_file('hello_world.c'), '-S', '-emit-llvm'])
      self.assertIsLLVMAsm('hello_world.ll')
  
      self.run_process([EMCC, test_file('hello_world.c'), '-c', '-emit-llvm'])
      self.assertTrue(building.is_bitcode('hello_world.bc'))
  
    def test_compile_ll_file(self):
      self.run_process([EMCC, test_file('hello_world.c'), '-S', '-emit-llvm'])
      err = self.run_process([EMCC, '-v', '-c', 'hello_world.ll', '-o', 'hello_world.o'], stderr=PIPE).stderr
      # Verify that `-mllvm` flags are passed when compiling `.ll` files.
      self.assertContained('-mllvm -enable-emscripten-sjlj', err)
      self.run_process([EMCC, 'hello_world.o'])
      self.assertContained('hello, world!', self.run_js('a.out.js'))
  
    def test_dashE(self):
      create_file('src.cpp', r'''#include <emscripten.h>
  __EMSCRIPTEN_major__ __EMSCRIPTEN_minor__ __EMSCRIPTEN_tiny__ EMSCRIPTEN_KEEPALIVE
  ''')
  
      def test(args=[]):
        print(args)
        out = self.run_process([EMXX, 'src.cpp', '-E'] + args, stdout=PIPE).stdout
        self.assertContained('%d %d %d __attribute__((used))' % (shared.EMSCRIPTEN_VERSION_MAJOR, shared.EMSCRIPTEN_VERSION_MINOR, shared.EMSCRIPTEN_VERSION_TINY), out)
  
      test()
      test(['-lembind'])
  
    def test_dashE_respect_dashO(self):
      # issue #3365
      with_dash_o = self.run_process([EMXX, test_file('hello_world.cpp'), '-E', '-o', 'ignored.js'], stdout=PIPE, stderr=PIPE).stdout
      without_dash_o = self.run_process([EMXX, test_file('hello_world.cpp'), '-E'], stdout=PIPE, stderr=PIPE).stdout
      self.assertEqual(len(with_dash_o), 0)
      self.assertNotEqual(len(without_dash_o), 0)
  
    def test_dashM(self):
      out = self.run_process([EMXX, test_file('hello_world.cpp'), '-M'], stdout=PIPE).stdout
      self.assertContained('hello_world.o:', out) # Verify output is just a dependency rule instead of bitcode or js
  
    def test_dashM_respect_dashO(self):
      # issue #3365
      with_dash_o = self.run_process([EMXX, test_file('hello_world.cpp'), '-M', '-o', 'ignored.js'], stdout=PIPE).stdout
      without_dash_o = self.run_process([EMXX, test_file('hello_world.cpp'), '-M'], stdout=PIPE).stdout
      self.assertEqual(len(with_dash_o), 0)
      self.assertNotEqual(len(without_dash_o), 0)
  
    def test_malloc_implicit(self):
      self.do_other_test('test_malloc_implicit.cpp')
  
    def test_switch64phi(self):
      # issue 2539, fastcomp segfault on phi-i64 interaction
      create_file('src.cpp', r'''
  #include <cstdint>
  #include <limits>
  #include <cstdio>
  
  //============================================================================
  
  namespace
  {
    class int_adapter {
    public:
      typedef ::int64_t int_type;
  
      int_adapter(int_type v = 0)
        : value_(v) {}
      static const int_adapter pos_infinity() {
        return (::std::numeric_limits<int_type>::max)();
      }
      static const int_adapter neg_infinity() {
        return (::std::numeric_limits<int_type>::min)();
      }
      static const int_adapter not_a_number() {
        return (::std::numeric_limits<int_type>::max)()-1;
      }
      static bool is_neg_inf(int_type v) {
        return (v == neg_infinity().as_number());
      }
      static bool is_pos_inf(int_type v) {
        return (v == pos_infinity().as_number());
      }
      static bool is_not_a_number(int_type v) {
        return (v == not_a_number().as_number());
      }
  
      bool is_infinity() const {
        return (value_ == neg_infinity().as_number() ||
                value_ == pos_infinity().as_number());
      }
      bool is_special() const {
        return(is_infinity() || value_ == not_a_number().as_number());
      }
      bool operator<(const int_adapter& rhs) const {
        if(value_ == not_a_number().as_number()
           || rhs.value_ == not_a_number().as_number()) {
          return false;
        }
        if(value_ < rhs.value_) return true;
        return false;
      }
      int_type as_number() const {
        return value_;
      }
  
      int_adapter operator-(const int_adapter& rhs) const {
        if(is_special() || rhs.is_special()) {
          if (rhs.is_pos_inf(rhs.as_number())) {
            return int_adapter(1);
          }
          if (rhs.is_neg_inf(rhs.as_number())) {
            return int_adapter();
          }
        }
        return int_adapter();
      }
  
  
    private:
      int_type value_;
    };
  
    class time_iterator {
    public:
      time_iterator(int_adapter t, int_adapter d)
        : current_(t),
          offset_(d)
      {}
  
      time_iterator& operator--() {
        current_ = int_adapter(current_ - offset_);
        return *this;
      }
  
      bool operator>=(const int_adapter& t) {
        return not (current_ < t);
      }
  
    private:
      int_adapter current_;
      int_adapter offset_;
    };
  
    void iterate_backward(const int_adapter *answers, const int_adapter& td) {
      int_adapter end = answers[0];
      time_iterator titr(end, td);
  
      std::puts("");
      for (; titr >= answers[0]; --titr) {
      }
    }
  }
  
  int main() {
    const int_adapter answer1[] = {};
    iterate_backward(NULL, int_adapter());
    iterate_backward(answer1, int_adapter());
  }
      ''')
      self.run_process([EMXX, 'src.cpp', '-O2', '-sSAFE_HEAP'])
  
    def test_bad_lookup(self):
      self.do_runf(path_from_root('tests/filesystem/bad_lookup.cpp'), expected_output='ok')
  
    @parameterized({
      'none': [{'EMCC_FORCE_STDLIBS': None}, False],
      # forced libs is ok, they were there anyhow
      'normal': [{'EMCC_FORCE_STDLIBS': 'libc,libc++abi,libc++'}, False],
      # partial list, but ok since we grab them as needed
      'parial': [{'EMCC_FORCE_STDLIBS': 'libc++'}, False],
      # fail! not enough stdlibs
      'partial_only': [{'EMCC_FORCE_STDLIBS': 'libc++,libc,libc++abi', 'EMCC_ONLY_FORCED_STDLIBS': '1'}, True],
      # force all the needed stdlibs, so this works even though we ignore the input file
      'full_only': [{'EMCC_FORCE_STDLIBS': 'libc,libc++abi,libc++,libmalloc', 'EMCC_ONLY_FORCED_STDLIBS': '1'}, False],
    })
    def test_only_force_stdlibs(self, env, fail):
      with env_modify(env):
        err = self.run_process([EMXX, test_file('hello_libcxx.cpp'), '-sWARN_ON_UNDEFINED_SYMBOLS=0'], stderr=PIPE).stderr
        if 'EMCC_ONLY_FORCED_STDLIBS' in env:
          self.assertContained('EMCC_ONLY_FORCED_STDLIBS is deprecated', err)
        if fail:
          output = self.expect_fail(config.NODE_JS + ['a.out.js'], stdout=PIPE)
          self.assertContained('missing function', output)
        else:
          self.assertContained('hello, world!', self.run_js('a.out.js'))
  
    def test_only_force_stdlibs_2(self):
      create_file('src.cpp', r'''
  #include <iostream>
  #include <stdexcept>
  
  int main()
  {
    try {
      throw std::exception();
      std::cout << "got here" << std::endl;
    }
    catch (const std::exception& ex) {
      std::cout << "Caught exception: " << ex.what() << std::endl;
    }
  }
  ''')
      with env_modify({'EMCC_FORCE_STDLIBS': 'libc,libc++abi,libc++,libmalloc', 'EMCC_ONLY_FORCED_STDLIBS': '1'}):
        self.run_process([EMXX, 'src.cpp', '-sDISABLE_EXCEPTION_CATCHING=0'])
      self.assertContained('Caught exception: std::exception', self.run_js('a.out.js'))
  
    def test_strftime_zZ(self):
      create_file('src.cpp', r'''
  #include <cerrno>
  #include <cstring>
  #include <ctime>
  #include <iostream>
  
  int main()
  {
    // Buffer to hold the current hour of the day.  Format is HH + nul
    // character.
    char hour[3];
  
    // Buffer to hold our ISO 8601 formatted UTC offset for the current
    // timezone.  Format is [+-]hhmm + nul character.
    char utcOffset[6];
  
    // Buffer to hold the timezone name or abbreviation.  Just make it
    // sufficiently large to hold most timezone names.
    char timezone[128];
  
    std::tm tm;
  
    // Get the current timestamp.
    const std::time_t now = std::time(NULL);
  
    // What time is that here?
    if (::localtime_r(&now, &tm) == NULL) {
      const int error = errno;
      std::cout
        << "Failed to get localtime for timestamp=" << now << "; errno=" << error
        << "; " << std::strerror(error) << std::endl;
      return 1;
    }
  
    size_t result = 0;
  
    // Get the formatted hour of the day.
    if ((result = std::strftime(hour, 3, "%H", &tm)) != 2) {
      const int error = errno;
      std::cout
        << "Failed to format hour for timestamp=" << now << "; result="
        << result << "; errno=" << error << "; " << std::strerror(error)
        << std::endl;
      return 1;
    }
    std::cout << "The current hour of the day is: " << hour << std::endl;
  
    // Get the formatted UTC offset in ISO 8601 format.
    if ((result = std::strftime(utcOffset, 6, "%z", &tm)) != 5) {
      const int error = errno;
      std::cout
        << "Failed to format UTC offset for timestamp=" << now << "; result="
        << result << "; errno=" << error << "; " << std::strerror(error)
        << std::endl;
      return 1;
    }
    std::cout << "The current timezone offset is: " << utcOffset << std::endl;
  
    // Get the formatted timezone name or abbreviation.  We don't know how long
    // this will be, so just expect some data to be written to the buffer.
    if ((result = std::strftime(timezone, 128, "%Z", &tm)) == 0) {
      const int error = errno;
      std::cout
        << "Failed to format timezone for timestamp=" << now << "; result="
        << result << "; errno=" << error << "; " << std::strerror(error)
        << std::endl;
      return 1;
    }
    std::cout << "The current timezone is: " << timezone << std::endl;
  
    std::cout << "ok!\n";
  }
  ''')
      self.run_process([EMXX, 'src.cpp'])
      self.assertContained('ok!', self.run_js('a.out.js'))
  
    def test_strptime_symmetry(self):
      self.do_runf(test_file('strptime_symmetry.cpp'), 'TEST PASSED')
  
    def test_truncate_from_0(self):
      create_file('src.cpp', r'''
  #include <cerrno>
  #include <cstring>
  #include <iostream>
  
  #include <fcntl.h>
  #include <sys/stat.h>
  #include <sys/types.h>
  #include <unistd.h>
  
  using std::endl;
  
  //============================================================================
  // :: Helpers
  
  namespace
  {
    // Returns the size of the regular file specified as 'path'.
    ::off_t getSize(const char* const path)
    {
      // Stat the file and make sure that it's the expected size.
      struct ::stat path_stat;
      if (::stat(path, &path_stat) != 0) {
        const int error = errno;
        std::cout
          << "Failed to lstat path: " << path << "; errno=" << error << "; "
          << std::strerror(error) << endl;
        return -1;
      }
  
      std::cout
        << "Size of file is: " << path_stat.st_size << endl;
      return path_stat.st_size;
    }
  
    // Causes the regular file specified in 'path' to have a size of 'length'
    // bytes.
    void resize(const char* const path,
                const ::off_t length)
    {
      std::cout
        << "Truncating file=" << path << " to length=" << length << endl;
      if (::truncate(path, length) == -1)
      {
        const int error = errno;
        std::cout
          << "Failed to truncate file=" << path << "; errno=" << error
          << "; " << std::strerror(error) << endl;
      }
  
      const ::off_t size = getSize(path);
      if (size != length) {
        std::cout
          << "Failed to truncate file=" << path << " to length=" << length
          << "; got size=" << size << endl;
      }
    }
  
    // Helper to create a file with the given content.
    void createFile(const std::string& path, const std::string& content)
    {
      std::cout
        << "Creating file: " << path << " with content=" << content << endl;
  
      const int fd = ::open(path.c_str(), O_CREAT | O_WRONLY, 0644);
      if (fd == -1) {
        const int error = errno;
        std::cout
          << "Failed to open file for writing: " << path << "; errno=" << error
          << "; " << std::strerror(error) << endl;
        return;
      }
  
      if (::write(fd, content.c_str(), content.size()) != content.size()) {
        const int error = errno;
        std::cout
          << "Failed to write content=" << content << " to file=" << path
          << "; errno=" << error << "; " << std::strerror(error) << endl;
  
        // Fall through to close FD.
      }
  
      ::close(fd);
    }
  }
  
  //============================================================================
  // :: Entry Point
  int main()
  {
    const char* const file = "/tmp/file";
    createFile(file, "This is some content");
    getSize(file);
    resize(file, 32);
    resize(file, 17);
    resize(file, 0);
  
    // This throws a JS exception.
    resize(file, 32);
    return 0;
  }
  ''')
      self.do_runf('src.cpp', r'''Creating file: /tmp/file with content=This is some content
  Size of file is: 20
  Truncating file=/tmp/file to length=32
  Size of file is: 32
  Truncating file=/tmp/file to length=17
  Size of file is: 17
  Truncating file=/tmp/file to length=0
  Size of file is: 0
  Truncating file=/tmp/file to length=32
  Size of file is: 32
  ''')
  
    def test_create_readonly(self):
      create_file('src.cpp', r'''
  #include <cerrno>
  #include <cstring>
  #include <iostream>
  
  #include <fcntl.h>
  #include <unistd.h>
  
  using std::endl;
  
  //============================================================================
  // :: Helpers
  
  namespace
  {
    // Helper to create a read-only file with content.
    void readOnlyFile(const std::string& path, const std::string& content)
    {
      std::cout
        << "Creating file: " << path << " with content of size="
        << content.size() << endl;
  
      const int fd = ::open(path.c_str(), O_CREAT | O_WRONLY, 0400);
      if (fd == -1) {
        const int error = errno;
        std::cout
          << "Failed to open file for writing: " << path << "; errno=" << error
          << "; " << std::strerror(error) << endl;
        return;
      }
  
      // Write the content to the file.
      ssize_t result = 0;
      if ((result = ::write(fd, content.data(), content.size()))
          != ssize_t(content.size()))
      {
        const int error = errno;
        std::cout
          << "Failed to write to file=" << path << "; errno=" << error
          << "; " << std::strerror(error) << endl;
        // Fall through to close the file.
      }
      else {
        std::cout
          << "Data written to file=" << path << "; successfully wrote "
          << result << " bytes" << endl;
      }
  
      ::close(fd);
    }
  }
  
  //============================================================================
  // :: Entry Point
  
  int main() {
    const char* const file = "/tmp/file";
    unlink(file);
    readOnlyFile(file, "This content should get written because the file "
                       "does not yet exist and so, only the mode of the "
                       "containing directory will influence my ability to "
                       "create and open the file. The mode of the file only "
                       "applies to opening of the stream, not subsequent stream "
                       "operations after stream has opened.\n\n");
    readOnlyFile(file, "This should not get written because the file already "
                       "exists and is read-only.\n\n");
  }
  ''')
      self.do_runf('src.cpp', r'''Creating file: /tmp/file with content of size=292
  Data written to file=/tmp/file; successfully wrote 292 bytes
  Creating file: /tmp/file with content of size=79
  Failed to open file for writing: /tmp/file; errno=2; Permission denied
  ''')
  
    def test_embed_file_large(self):
      # If such long files are encoded on one line,
      # they overflow the interpreter's limit
      large_size = int(1500000)
      create_file('large.txt', 'x' * large_size)
      create_file('src.c', r'''
        #include <stdio.h>
        #include <unistd.h>
        int main() {
            FILE* fp = fopen("large.txt", "r");
            if (fp) {
                printf("ok\n");
                fseek(fp, 0L, SEEK_END);
                printf("%ld\n", ftell(fp));
            } else {
                printf("failed to open large file.txt\n");
            }
            return 0;
        }
      ''')
      self.run_process([EMCC, 'src.c', '--embed-file', 'large.txt'])
      for engine in config.JS_ENGINES:
        if engine == config.V8_ENGINE:
          continue # ooms
        print(engine)
        self.assertContained('ok\n' + str(large_size) + '\n', self.run_js('a.out.js', engine=engine))
  
    def test_force_exit(self):
      create_file('src.c', r'''
  #include <emscripten/emscripten.h>
  
  EMSCRIPTEN_KEEPALIVE void callback() {
    EM_ASM({ out('callback pre()') });
    emscripten_force_exit(42);
    EM_ASM({ out('callback post()') });
  }
  
  int main() {
    EM_ASM({ setTimeout(function() { out("calling callback()"); _callback() }, 100) });
    emscripten_exit_with_live_runtime();
    return 123;
  }
  ''')
      self.run_process([EMCC, 'src.c'])
      output = self.run_js('a.out.js', assert_returncode=42)
      self.assertContained('callback pre()', output)
      self.assertNotContained('callback post()', output)
  
    def test_bad_locale(self):
      create_file('src.cpp', r'''
  #include <locale.h>
  #include <stdio.h>
  #include <wctype.h>
  
  int main(int argc, char **argv) {
    const char *locale = (argc > 1 ? argv[1] : "C");
    const char *actual = setlocale(LC_ALL, locale);
    if (actual == NULL) {
      printf("%s locale not supported\n", locale);
      return 0;
    }
    printf("locale set to %s: %s\n", locale, actual);
  }
  ''')
      self.run_process([EMXX, 'src.cpp'])
  
      self.assertContained('locale set to C: C',
                           self.run_js('a.out.js', args=['C']))
      self.assertContained('locale set to waka: waka',
                           self.run_js('a.out.js', args=['waka']))
  
    def test_browser_language_detection(self):
      # Test HTTP Accept-Language parsing by simulating navigator.languages #8751
      self.run_process([EMCC,
                        test_file('test_browser_language_detection.c')])
      self.assertContained('C.UTF-8', self.run_js('a.out.js'))
  
      # Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3
      create_file('preamble.js', r'''navigator = {};
        navigator.languages = [ "fr", "fr-FR", "en-US", "en" ];''')
      self.run_process([EMCC, '--pre-js', 'preamble.js',
                        test_file('test_browser_language_detection.c')])
      self.assertContained('fr.UTF-8', self.run_js('a.out.js'))
  
      # Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.5,en;q=0.3
      create_file('preamble.js', r'''navigator = {};
        navigator.languages = [ "fr-FR", "fr", "en-US", "en" ];''')
      self.emcc_args += ['--pre-js', 'preamble.js']
      self.do_runf(test_file('test_browser_language_detection.c'), 'fr_FR.UTF-8')
  
    def test_js_main(self):
      # try to add a main() from JS, at runtime. this is not supported (the
      # compiler needs to know at compile time about main).
      create_file('pre_main.js', r'''
        var Module = {
          '_main': function() {
          }
        };
      ''')
      create_file('src.cpp', '')
      self.emcc_args += ['--pre-js', 'pre_main.js']
      self.do_runf('src.cpp', 'compiled without a main, but one is present. if you added it from JS, use Module["onRuntimeInitialized"]', assert_returncode=NON_ZERO)
  
    def test_locale_wrong(self):
      create_file('src.cpp', r'''
  #include <locale>
  #include <iostream>
  #include <stdexcept>
  
  int main(const int argc, const char * const * const argv) {
    const char * const name = argc > 1 ? argv[1] : "C";
  
    try {
      const std::locale locale(name);
      std::cout
        << "Constructed locale \"" << name << "\"\n"
        << "This locale is "
        << (locale == std::locale::global(locale) ? "" : "not ")
        << "the global locale.\n"
        << "This locale is " << (locale == std::locale::classic() ? "" : "not ")
        << "the C locale." << std::endl;
  
    } catch(const std::runtime_error &ex) {
      std::cout
        << "Can't construct locale \"" << name << "\": " << ex.what()
        << std::endl;
      return 1;
  
    } catch(...) {
      std::cout
        << "FAIL: Unexpected exception constructing locale \"" << name << '\"'
        << std::endl;
      return 127;
    }
  }
  ''')
      self.run_process([EMXX, 'src.cpp', '-sEXIT_RUNTIME', '-sDISABLE_EXCEPTION_CATCHING=0'])
      self.assertContained('''\
  Constructed locale "C"
  This locale is the global locale.
  This locale is the C locale.
  ''', self.run_js('a.out.js', args=['C']))
      self.assertContained('''\
  Constructed locale "waka"
  This locale is not the global locale.
  This locale is not the C locale.
  ''', self.run_js('a.out.js', args=['waka']))
  
    def test_cleanup_os(self):
      # issue 2644
      def test(args, be_clean):
        print(args)
        self.clear()
        shutil.copyfile(test_file('hello_world.c'), 'a.c')
        create_file('b.c', ' ')
        self.run_process([EMCC, 'a.c', 'b.c'] + args)
        clutter = glob.glob('*.o')
        if be_clean:
          assert len(clutter) == 0, 'should not leave clutter ' + str(clutter)
        else:
           assert len(clutter) == 2, 'should leave .o files'
      test(['-o', 'c.so', '-r'], True)
      test(['-o', 'c.js'], True)
      test(['-o', 'c.html'], True)
      test(['-c'], False)
  
    def test_dash_g_object(self):
      def test(opts):
        print(opts)
        self.run_process([EMCC, '-c', test_file('hello_world.c'), '-o', 'a_.o'] + opts)
        sizes = {'_': os.path.getsize('a_.o')}
        self.run_process([EMCC, '-c', test_file('hello_world.c'), '-g', '-o', 'ag.o'] + opts)
        sizes['g'] = os.path.getsize('ag.o')
        for i in range(5):
          self.run_process([EMCC, '-c', test_file('hello_world.c'), '-g' + str(i), '-o', 'a' + str(i) + '.o'] + opts)
          sizes[i] = os.path.getsize('a' + str(i) + '.o')
        print('  ', sizes)
        assert sizes['_'] == sizes[0] == sizes[1] == sizes[2], 'no debug means no llvm debug info ' + str(sizes)
        assert sizes['g'] == sizes[3] == sizes[4], '-g or -gsource-map means llvm debug info ' + str(sizes)
        assert sizes['_'] < sizes['g'], 'llvm debug info has positive size ' + str(sizes)
      test([])
      test(['-O1'])
  
    def test_no_filesystem(self):
      FS_MARKER = 'var FS'
      # fopen forces full filesystem support
      self.run_process([EMCC, test_file('hello_world_fopen.c'), '-sASSERTIONS=0'])
      yes_size = os.path.getsize('a.out.js')
      self.assertContained('hello, world!', self.run_js('a.out.js'))
      self.assertContained(FS_MARKER, read_file('a.out.js'))
      self.run_process([EMCC, test_file('hello_world.c'), '-sASSERTIONS=0'])
      no_size = os.path.getsize('a.out.js')
      self.assertContained('hello, world!', self.run_js('a.out.js'))
      self.assertNotContained(FS_MARKER, read_file('a.out.js'))
      print('yes fs, no fs:', yes_size, no_size)
      # ~100K of FS code is removed
      self.assertGreater(yes_size - no_size, 90000)
      self.assertLess(no_size, 360000)
  
    def test_no_filesystem_libcxx(self):
      self.set_setting('FILESYSTEM', 0)
      self.do_runf(test_file('hello_libcxx.cpp'), 'hello, world!')
  
    @is_slow_test
    def test_no_nuthin(self):
      # check FILESYSTEM is automatically set, and effective
  
      def test(opts, absolute):
        print('opts, absolute:', opts, absolute)
        sizes = {}
  
        def do(name, source, moar_opts):
          self.clear()
          # pad the name to a common length so that doesn't effect the size of the
          # output
          padded_name = name + '_' * (20 - len(name))
          self.run_process([EMCC, test_file(source), '-o', padded_name + '.js'] + opts + moar_opts)
          sizes[name] = os.path.getsize(padded_name + '.js')
          if os.path.exists(padded_name + '.wasm'):
            sizes[name] += os.path.getsize(padded_name + '.wasm')
          self.assertContained('hello, world!', self.run_js(padded_name + '.js'))
  
        do('normal', 'hello_world_fopen.c', [])
        do('no_fs', 'hello_world.c', []) # without fopen, we should auto-detect we do not need full fs support and can do FILESYSTEM=0
        do('no_fs_manual', 'hello_world.c', ['-sFILESYSTEM=0'])
        print('  ', sizes)
        self.assertLess(sizes['no_fs'], sizes['normal'])
        self.assertLess(sizes['no_fs'], absolute)
        # manual can usually remove a tiny bit more
        self.assertLess(sizes['no_fs_manual'], sizes['no_fs'] + 30)
  
      test(['-sASSERTIONS=0'], 120000) # we don't care about code size with assertions
      test(['-O1'], 91000)
      test(['-O2'], 46000)
      test(['-O3', '--closure=1'], 17000)
      # js too
      test(['-O3', '--closure=1', '-sWASM=0'], 36000)
      test(['-O3', '--closure', '2', '-sWASM=0'], 33000) # might change now and then
  
    def test_no_browser(self):
      BROWSER_INIT = 'var Browser'
  
      self.run_process([EMCC, test_file('hello_world.c')])
      self.assertNotContained(BROWSER_INIT, read_file('a.out.js'))
  
      # uses emscripten_set_main_loop, which needs Browser
      self.run_process([EMCC, test_file('browser_main_loop.c')])
      self.assertContained(BROWSER_INIT, read_file('a.out.js'))
  
    def test_EXPORTED_RUNTIME_METHODS(self):
      def test(opts, has, not_has):
        print(opts, has, not_has)
        self.clear()
        # check without assertions, as with assertions we add stubs for the things we remove (which
        # print nice error messages)
        self.run_process([EMCC, test_file('hello_world.c'), '-sASSERTIONS=0'] + opts)
        self.assertContained('hello, world!', self.run_js('a.out.js'))
        src = read_file('a.out.js')
        self.assertContained(has, src)
        self.assertNotContained(not_has, src)
  
      test([], 'Module["', 'Module["waka')
      test(['-sEXPORTED_RUNTIME_METHODS=[]'], '', 'Module["addRunDependency')
      test(['-sEXPORTED_RUNTIME_METHODS=addRunDependency'], 'Module["addRunDependency', 'Module["waka')
      test(['-sEXPORTED_RUNTIME_METHODS=[]', '-sEXPORTED_RUNTIME_METHODS=addRunDependency'], 'Module["addRunDependency', 'Module["waka')
  
    def test_stat_fail_alongtheway(self):
      create_file('src.cpp', r'''
  #include <errno.h>
  #include <stdio.h>
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <unistd.h>
  #include <stdlib.h>
  #include <fcntl.h>
  #include <string.h>
  
  #define CHECK(expression) \
    if(!(expression)) {                            \
      error = errno;                               \
      printf("FAIL: %s\n", #expression); fail = 1; \
    } else {                                       \
      error = errno;                               \
      printf("pass: %s\n", #expression);           \
    }                                              \
  
  int main() {
    int error;
    int fail = 0;
    CHECK(mkdir("path", 0777) == 0);
    CHECK(close(open("path/file", O_CREAT | O_WRONLY, 0644)) == 0);
    {
      struct stat st;
      CHECK(stat("path", &st) == 0);
      CHECK(st.st_mode = 0777);
    }
    {
      struct stat st;
      CHECK(stat("path/nosuchfile", &st) == -1);
      printf("info: errno=%d %s\n", error, strerror(error));
      CHECK(error == ENOENT);
    }
    {
      struct stat st;
      CHECK(stat("path/file", &st) == 0);
      CHECK(st.st_mode = 0666);
    }
    {
      struct stat st;
      CHECK(stat("path/file/impossible", &st) == -1);
      printf("info: errno=%d %s\n", error, strerror(error));
      CHECK(error == ENOTDIR);
    }
    {
      struct stat st;
      CHECK(lstat("path/file/impossible", &st) == -1);
      printf("info: errno=%d %s\n", error, strerror(error));
      CHECK(error == ENOTDIR);
    }
    return fail;
  }
  ''')
      self.do_runf('src.cpp', r'''pass: mkdir("path", 0777) == 0
  pass: close(open("path/file", O_CREAT | O_WRONLY, 0644)) == 0
  pass: stat("path", &st) == 0
  pass: st.st_mode = 0777
  pass: stat("path/nosuchfile", &st) == -1
  info: errno=44 No such file or directory
  pass: error == ENOENT
  pass: stat("path/file", &st) == 0
  pass: st.st_mode = 0666
  pass: stat("path/file/impossible", &st) == -1
  info: errno=54 Not a directory
  pass: error == ENOTDIR
  pass: lstat("path/file/impossible", &st) == -1
  info: errno=54 Not a directory
  pass: error == ENOTDIR
  ''')
  
    def test_link_with_a_static(self):
      create_file('x.c', r'''
  int init_weakref(int a, int b) {
    return a + b;
  }
  ''')
      create_file('y.c', r'''
  static int init_weakref(void) { // inlined in -O2, not in -O0 where it shows up in llvm-nm as 't'
    return 150;
  }
  
  int testy(void) {
    return init_weakref();
  }
  ''')
      create_file('z.c', r'''
  extern int init_weakref(int, int);
  extern int testy(void);
  
  int main(void) {
    return testy() + init_weakref(5, 6);
  }
  ''')
      self.run_process([EMCC, '-c', 'x.c', '-o', 'x.o'])
      self.run_process([EMCC, '-c', 'y.c', '-o', 'y.o'])
      self.run_process([EMCC, '-c', 'z.c', '-o', 'z.o'])
      try_delete('libtest.a')
      self.run_process([EMAR, 'rc', 'libtest.a', 'y.o'])
      self.run_process([EMAR, 'rc', 'libtest.a', 'x.o'])
      self.run_process([EMRANLIB, 'libtest.a'])
  
      for args in [[], ['-O2']]:
        print('args:', args)
        self.run_process([EMCC, 'z.o', 'libtest.a', '-sEXIT_RUNTIME'] + args)
        self.run_js('a.out.js', assert_returncode=161)
  
    def test_link_with_bad_o_in_a(self):
      # when building a .a, we force-include all the objects inside it. but, some
      # may not be valid bitcode, e.g. if it contains metadata or something else
      # weird. we should just ignore those
      self.run_process([EMCC, '-c', test_file('hello_world.c'), '-o', 'hello_world.o'])
      create_file('bad.obj', 'this is not a good file, it should be ignored!')
      self.run_process([LLVM_AR, 'cr', 'libfoo.a', 'hello_world.o', 'bad.obj'])
      self.run_process([EMCC, 'libfoo.a'])
      self.assertContained('hello, world!', self.run_js('a.out.js'))
  
    @require_node
    def test_require(self):
      inname = test_file('hello_world.c')
      self.emcc(inname, args=['-sASSERTIONS=0'], output_filename='a.out.js')
      output = self.run_process(config.NODE_JS + ['-e', 'require("./a.out.js")'], stdout=PIPE, stderr=PIPE)
      assert output.stdout == 'hello, world!\n' and output.stderr == '', 'expected no output, got\n===\nSTDOUT\n%s\n===\nSTDERR\n%s\n===\n' % (output.stdout, output.stderr)
  
    @require_node
    def test_require_modularize(self):
      self.run_process([EMCC, test_file('hello_world.c'), '-sMODULARIZE', '-sASSERTIONS=0'])
      src = read_file('a.out.js')
      self.assertContained('module.exports = Module;', src)
      output = self.run_process(config.NODE_JS + ['-e', 'var m = require("./a.out.js"); m();'], stdout=PIPE, stderr=PIPE)
      self.assertFalse(output.stderr)
      self.assertEqual(output.stdout, 'hello, world!\n')
      self.run_process([EMCC, test_file('hello_world.c'), '-sMODULARIZE', '-sEXPORT_NAME="NotModule"', '-sASSERTIONS=0'])
      src = read_file('a.out.js')
      self.assertContained('module.exports = NotModule;', src)
      output = self.run_process(config.NODE_JS + ['-e', 'var m = require("./a.out.js"); m();'], stdout=PIPE, stderr=PIPE)
      self.assertFalse(output.stderr)
      self.assertEqual(output.stdout, 'hello, world!\n')
      self.run_process([EMCC, test_file('hello_world.c'), '-sMODULARIZE'])
      # We call require() twice to ensure it returns wrapper function each time
      output = self.run_process(config.NODE_JS + ['-e', 'require("./a.out.js")();var m = require("./a.out.js"); m();'], stdout=PIPE, stderr=PIPE)
      self.assertFalse(output.stderr)
      self.assertEqual(output.stdout, 'hello, world!\nhello, world!\n')
  
    def test_define_modularize(self):
      self.run_process([EMCC, test_file('hello_world.c'), '-sMODULARIZE', '-sASSERTIONS=0'])
      src = 'var module = 0; ' + read_file('a.out.js')
      create_file('a.out.js', src)
      assert "define([], function() { return Module; });" in src
      output = self.run_process(config.NODE_JS + ['-e', 'var m; (global.define = function(deps, factory) { m = factory(); }).amd = true; require("./a.out.js"); m();'], stdout=PIPE, stderr=PIPE)
      assert output.stdout == 'hello, world!\n' and output.stderr == '', 'expected output, got\n===\nSTDOUT\n%s\n===\nSTDERR\n%s\n===\n' % (output.stdout, output.stderr)
      self.run_process([EMCC, test_file('hello_world.c'), '-sMODULARIZE', '-sEXPORT_NAME="NotModule"', '-sASSERTIONS=0'])
      src = 'var module = 0; ' + read_file('a.out.js')
      create_file('a.out.js', src)
      assert "define([], function() { return NotModule; });" in src
      output = self.run_process(config.NODE_JS + ['-e', 'var m; (global.define = function(deps, factory) { m = factory(); }).amd = true; require("./a.out.js"); m();'], stdout=PIPE, stderr=PIPE)
      assert output.stdout == 'hello, world!\n' and output.stderr == '', 'expected output, got\n===\nSTDOUT\n%s\n===\nSTDERR\n%s\n===\n' % (output.stdout, output.stderr)
  
    def test_EXPORT_NAME_with_html(self):
      result = self.run_process([EMCC, test_file('hello_world.c'), '-o', 'a.html', '-sEXPORT_NAME=Other'], stdout=PIPE, check=False, stderr=STDOUT)
      self.assertNotEqual(result.returncode, 0)
      self.assertContained('Customizing EXPORT_NAME requires that the HTML be customized to use that name', result.stdout)
  
    def test_modularize_sync_compilation(self):
      create_file('post.js', r'''
  console.log('before');
  var result = Module();
  // It should be an object.
  console.log(typeof result);
  // And it should have the exports that Module has, showing it is Module in fact.
  console.log(typeof result._main);
  // And it should not be a Promise.
  console.log(typeof result.then);
  console.log('after');
  ''')
      self.run_process([EMCC, test_file('hello_world.c'),
                        '-sMODULARIZE',
                        '-sWASM_ASYNC_COMPILATION=0',
                        '--extern-post-js', 'post.js'])
      self.assertContained('''\
  before
  hello, world!
  object
  function
  undefined
  after
  ''', self.run_js('a.out.js'))
  
    def test_export_all_3142(self):
      create_file('src.cpp', r'''
  typedef unsigned int Bit32u;
  
  struct S_Descriptor {
      Bit32u limit_0_15   :16;
      Bit32u base_0_15    :16;
      Bit32u base_16_23   :8;
  };
  
  class Descriptor {
  public:
      Descriptor() { saved.fill[0] = saved.fill[1] = 0; }
      union {
          S_Descriptor seg;
          Bit32u fill[2];
      } saved;
  };
  
  Descriptor desc;
      ''')
      self.run_process([EMXX, 'src.cpp', '-O2', '-sEXPORT_ALL'])
      self.assertExists('a.out.js')
  
    def test_emmake_emconfigure(self):
      def check(what, args, fail=True, expect=''):
        args = [what] + args
        print(what, args, fail, expect)
        output = self.run_process(args, stdout=PIPE, stderr=PIPE, check=False)
        assert ('is a helper for' in output.stderr) == fail
        assert ('Typical usage' in output.stderr) == fail
        self.assertContained(expect, output.stdout)
      check(emmake, [])
      check(EMCONFIGURE, [])
      check(emmake, ['--version'])
      check(EMCONFIGURE, ['--version'])
      check(emmake, ['make'], fail=False)
      check(EMCONFIGURE, ['configure'], fail=False)
      check(EMCONFIGURE, ['./configure'], fail=False)
      check(EMCMAKE, ['cmake'], fail=False)
  
      create_file('test.py', '''
  import os
  print(os.environ.get('CROSS_COMPILE'))
  ''')
      check(EMCONFIGURE, [PYTHON, 'test.py'], expect=path_from_root('em'), fail=False)
      check(emmake, [PYTHON, 'test.py'], expect=path_from_root('em'), fail=False)
  
      create_file('test.py', '''
  import os
  print(os.environ.get('NM'))
  ''')
      check(EMCONFIGURE, [PYTHON, 'test.py'], expect=shared.LLVM_NM, fail=False)
  
    def test_emmake_python(self):
      # simulates a configure/make script that looks for things like CC, AR, etc., and which we should
      # not confuse by setting those vars to something containing `python X` as the script checks for
      # the existence of an executable.
      self.run_process([emmake, PYTHON, test_file('emmake/make.py')])
  
    def test_sdl2_config(self):
      for args, expected in [
        [['--version'], '2.0.10'],
        [['--cflags'], '-s USE_SDL=2'],
        [['--libs'], '-s USE_SDL=2'],
        [['--cflags', '--libs'], '-s USE_SDL=2'],
      ]:
        print(args, expected)
        out = self.run_process([PYTHON, shared.Cache.get_sysroot_dir('bin', 'sdl2-config')] + args, stdout=PIPE, stderr=PIPE).stdout
        self.assertContained(expected, out)
        print('via emmake')
        out = self.run_process([emmake, 'sdl2-config'] + args, stdout=PIPE, stderr=PIPE).stdout
        self.assertContained(expected, out)
  
    def test_module_onexit(self):
      create_file('src.cpp', r'''
  #include <emscripten.h>
  int main() {
    EM_ASM({
      Module['onExit'] = function(status) { out('exiting now, status ' + status) };
    });
    return 14;
  }
  ''')
      self.run_process([EMXX, 'src.cpp', '-sEXIT_RUNTIME'])
      self.assertContained('exiting now, status 14', self.run_js('a.out.js', assert_returncode=14))
  
    def test_NO_aliasing(self):
      # the NO_ prefix flips boolean options
      self.run_process([EMCC, test_file('hello_world.c'), '-sEXIT_RUNTIME'])
      exit_1 = read_file('a.out.js')
      self.run_process([EMCC, test_file('hello_world.c'), '-sNO_EXIT_RUNTIME=0'])
      no_exit_0 = read_file('a.out.js')
      self.run_process([EMCC, test_file('hello_world.c'), '-sEXIT_RUNTIME=0'])
      exit_0 = read_file('a.out.js')
  
      assert exit_1 == no_exit_0
      assert exit_1 != exit_0
  
    def test_underscore_exit(self):
      create_file('src.cpp', r'''
  #include <unistd.h>
  int main() {
    _exit(0); // should not end up in an infinite loop with non-underscore exit
  }
  ''')
      self.run_process([EMXX, 'src.cpp'])
      self.assertContained('', self.run_js('a.out.js'))
  
    def test_file_packager_huge(self):
      MESSAGE = 'warning: file packager is creating an asset bundle of 257 MB. this is very large, and browsers might have trouble loading it'
      create_file('huge.dat', 'a' * (1024 * 1024 * 257))
      create_file('tiny.dat', 'a')
      err = self.run_process([FILE_PACKAGER, 'test.data', '--preload', 'tiny.dat'], stdout=PIPE, stderr=PIPE).stderr
      self.assertNotContained(MESSAGE, err)
      err = self.run_process([FILE_PACKAGER, 'test.data', '--preload', 'huge.dat'], stdout=PIPE, stderr=PIPE).stderr
      self.assertContained(MESSAGE, err)
      self.clear()
  
    @parameterized({
      '': (True,),
      'wasm2js': (False,),
    })
    def test_massive_alloc(self, wasm):
      create_file('main.cpp', r'''
  #include <stdio.h>
  #include <stdlib.h>
  
  int main() {
    volatile long x = (long)malloc(1024 * 1024 * 1400);
    return x == 0; // can't alloc it, but don't fail catastrophically, expect null
  }
      ''')
      cmd = [EMXX, 'main.cpp', '-sALLOW_MEMORY_GROWTH']
      if not wasm:
        cmd += ['-sWASM=0']
      self.run_process(cmd)
      # just care about message regarding allocating over 1GB of memory
      output = self.run_js('a.out.js')
      if not wasm:
        self.assertContained('Warning: Enlarging memory arrays, this is not fast! 16777216,1473314816\n', output)
  
    def test_failing_alloc(self):
      for pre_fail, post_fail, opts in [
        ('', '', []),
        ('EM_ASM( Module.temp = _sbrk() );', 'EM_ASM( assert(Module.temp === _sbrk(), "must not adjust brk when an alloc fails!") );', []),
        # also test non-wasm in normal mode
        ('', '', ['-sWASM=0']),
        ('EM_ASM( Module.temp = _sbrk() );', 'EM_ASM( assert(Module.temp === _sbrk(), "must not adjust brk when an alloc fails!") );', ['-sWASM=0']),
      ]:
        for growth in [0, 1]:
          for aborting_args in [[], ['-sABORTING_MALLOC=0']]:
            create_file('main.cpp', r'''
  #include <stdio.h>
  #include <stdlib.h>
  #include <vector>
  #include <assert.h>
  #include <emscripten.h>
  
  #define CHUNK_SIZE (10 * 1024 * 1024)
  
  int main() {
    std::vector<void*> allocs;
    bool has = false;
    while (1) {
      printf("trying an allocation\n");
      %s
      void* curr = malloc(CHUNK_SIZE);
      if (!curr) {
        %s
        break;
      }
      has = true;
      printf("allocated another chunk, %%zu so far\n", allocs.size());
      allocs.push_back(curr);
    }
    assert(has);
    printf("an allocation failed!\n");
  #ifdef SPLIT
    return 0;
  #endif
    while (1) {
      assert(allocs.size() > 0);
      void *curr = allocs.back();
      allocs.pop_back();
      free(curr);
      printf("freed one\n");
      if (malloc(CHUNK_SIZE)) break;
    }
    printf("managed another malloc!\n");
  }
  ''' % (pre_fail, post_fail))
            args = [EMXX, 'main.cpp', '-sEXPORTED_FUNCTIONS=_main,_sbrk'] + opts + aborting_args
            args += ['-sTEST_MEMORY_GROWTH_FAILS'] # In this test, force memory growing to fail
            if growth:
              args += ['-sALLOW_MEMORY_GROWTH']
            # growth disables aborting by default, but it can be overridden
            aborting = not aborting_args and not growth
            print('test_failing_alloc', args, pre_fail)
            self.run_process(args)
            # growth also disables aborting
            can_manage_another = not aborting
            split = '-DSPLIT' in args
            print('can manage another:', can_manage_another, 'split:', split, 'aborting:', aborting)
            output = self.run_js('a.out.js', assert_returncode=0 if can_manage_another else NON_ZERO)
            if can_manage_another:
              self.assertContained('an allocation failed!\n', output)
              if not split:
                # split memory allocation may fail due to GC objects no longer being allocatable,
                # and we can't expect to recover from that deterministically. So just check we
                # get to the fail.
                # otherwise, we should fail eventually, then free, then succeed
                self.assertContained('managed another malloc!\n', output)
            else:
              # we should see an abort
              self.assertContained('Aborted(Cannot enlarge memory arrays', output)
              if growth:
                # when growth is enabled, the default is to not abort, so just explain that
                self.assertContained('If you want malloc to return NULL (0) instead of this abort, do not link with -s ABORTING_MALLOC=1', output)
              else:
                # when growth is not enabled, suggest 3 possible solutions (start with more memory, allow growth, or don't abort)
                self.assertContained(('higher than the current value 16777216,', 'higher than the current value 33554432,'), output)
                self.assertContained('compile with  -s ALLOW_MEMORY_GROWTH=1 ', output)
                self.assertContained('compile with  -s ABORTING_MALLOC=0 ', output)
  
    def test_failing_growth_2gb(self):
      create_file('test.cpp', r'''
  #include <stdio.h>
  #include <stdlib.h>
  
  void* out;
  int main() {
    while (1) {
      puts("loop...");
      out = malloc(1024 * 1024);
      if (!out) {
        puts("done");
        return 0;
      }
    }
  }
  ''')
  
      self.run_process([EMXX, '-O1', 'test.cpp', '-sALLOW_MEMORY_GROWTH'])
      self.assertContained('done', self.run_js('a.out.js'))
  
    def test_libcxx_minimal(self):
      create_file('vector.cpp', r'''
  #include <vector>
  int main(int argc, char** argv) {
    std::vector<void*> v;
    for (int i = 0 ; i < argc; i++) {
      v.push_back(nullptr);
    }
    return v.size();
  }
  ''')
  
      self.run_process([EMXX, '-O2', 'vector.cpp', '-o', 'vector.js'])
      self.run_process([EMXX, '-O2', test_file('hello_libcxx.cpp'), '-o', 'iostream.js'])
  
      vector = os.path.getsize('vector.js')
      iostream = os.path.getsize('iostream.js')
      print(vector, iostream)
  
      self.assertGreater(vector, 1000)
      # we can strip out almost all of libcxx when just using vector
      self.assertLess(2.25 * vector, iostream)
  
    @parameterized({
      '': (True,),
      # TODO(sbc): make dynamic linking work with wasm2js
      # 'wasm2js': (False,)
    })
    def test_minimal_dynamic(self, wasm):
      library_file = 'library.wasm' if wasm else 'library.js'
  
      def test(name, main_args, library_args=[], expected='hello from main\nhello from library', assert_returncode=0):
        print(f'testing {name}', main_args, library_args)
        self.clear()
        create_file('library.c', r'''
          #include <stdio.h>
          void library_func() {
          #ifdef USE_PRINTF
            printf("hello from library: %p\n", &library_func);
          #else
            puts("hello from library");
          #endif
          }
        ''')
        # -fno-builtin to prevent printf -> iprintf optimization
        self.run_process([EMCC, 'library.c', '-fno-builtin', '-sSIDE_MODULE', '-O2', '-o', library_file, '-sWASM=' + str(wasm), '-sEXPORT_ALL'] + library_args)
        create_file('main.c', r'''
          #include <dlfcn.h>
          #include <stdio.h>
          int main() {
            puts("hello from main");
            void *lib_handle = dlopen("%s", RTLD_NOW);
            if (!lib_handle) {
              puts("cannot load side module");
              puts(dlerror());
              return 1;
            }
            typedef void (*voidfunc)();
            voidfunc x = (voidfunc)dlsym(lib_handle, "library_func");
            if (!x) puts("cannot find side function");
            else x();
          }
        ''' % library_file)
        self.run_process([EMCC, 'main.c', '--embed-file', library_file, '-O2', '-sWASM=' + str(wasm)] + main_args)
        self.assertContained(expected, self.run_js('a.out.js', assert_returncode=assert_returncode))
        size = os.path.getsize('a.out.js')
        if wasm:
          size += os.path.getsize('a.out.wasm')
        side_size = os.path.getsize(library_file)
        print(f'  sizes {name}: {size}, {side_size}')
        return (size, side_size)
  
      def percent_diff(x, y):
        small = min(x, y)
        large = max(x, y)
        return float(100 * large) / small - 100
  
      full = test('full', main_args=['-sMAIN_MODULE'])
      # printf is not used in main, but libc was linked in, so it's there
      printf = test('printf', main_args=['-sMAIN_MODULE'], library_args=['-DUSE_PRINTF'])
  
      # main module tests
  
      # dce in main, and it fails since puts is not exported
      test('dce', main_args=['-sMAIN_MODULE=2'], expected=('cannot', 'undefined'), assert_returncode=NON_ZERO)
  
      # with exporting, it works
      dce = test('dce', main_args=['-sMAIN_MODULE=2', '-sEXPORTED_FUNCTIONS=_main,_puts'])
  
      # printf is not used in main, and we dce, so we failz
      dce_fail = test('dce_fail', main_args=['-sMAIN_MODULE=2'], library_args=['-DUSE_PRINTF'], expected=('cannot', 'undefined'), assert_returncode=NON_ZERO)
  
      # exporting printf in main keeps it alive for the library
      test('dce_save', main_args=['-sMAIN_MODULE=2', '-sEXPORTED_FUNCTIONS=_main,_printf,_puts'], library_args=['-DUSE_PRINTF'])
  
      self.assertLess(percent_diff(full[0], printf[0]), 4)
      self.assertLess(percent_diff(dce[0], dce_fail[0]), 4)
      self.assertLess(dce[0], 0.2 * full[0]) # big effect, 80%+ is gone
  
      # side module tests
  
      # mode 2, so dce in side, but library_func is not exported, so it is dce'd
      side_dce_fail = test('side_dce_fail', main_args=['-sMAIN_MODULE'], library_args=['-sSIDE_MODULE=2'], expected='cannot find side function')
      # mode 2, so dce in side, and library_func is not exported
      side_dce_work = test('side_dce_fail', main_args=['-sMAIN_MODULE'], library_args=['-sSIDE_MODULE=2', '-sEXPORTED_FUNCTIONS=_library_func'], expected='hello from library')
  
      self.assertLess(side_dce_fail[1], 0.95 * side_dce_work[1]) # removing that function saves a chunk
  
    def test_RUNTIME_LINKED_LIBS(self):
      # Verify that the legacy `-s RUNTIME_LINKED_LIBS` option acts the same as passing a
      # library on the command line directly.
      create_file('side.c', 'int foo() { return 42; }')
      create_file('main.c', '#include <assert.h>\nextern int foo(); int main() { assert(foo() == 42); return 0; }')
  
      self.run_process([EMCC, '-O2', 'side.c', '-sSIDE_MODULE', '-o', 'side.wasm'])
      self.run_process([EMCC, '-O2', 'main.c', '-sMAIN_MODULE', '-o', 'main.js', 'side.wasm'])
      self.run_js('main.js')
  
      err = self.run_process([EMCC, '-O2', 'main.c', '-sMAIN_MODULE', '-o', 'main2.js', '-sRUNTIME_LINKED_LIBS=side.wasm'], stderr=PIPE).stderr
      self.assertContained('emcc: warning: RUNTIME_LINKED_LIBS is deprecated', err)
      self.run_js('main2.js')
  
      self.assertBinaryEqual('main.wasm', 'main2.wasm')
  
    def test_ld_library_path(self):
      create_file('hello1.c', r'''
  #include <stdio.h>
  
  void hello1() {
    printf("Hello1\n");
    return;
  }
  ''')
      create_file('hello2.c', r'''
  #include <stdio.h>
  
  void hello2() {
    printf("Hello2\n");
    return;
  }
  ''')
      create_file('hello3.c', r'''
  #include <stdio.h>
  
  void hello3() {
    printf ("Hello3\n");
    return;
  }
  ''')
      create_file('hello4.c', r'''
  #include <stdio.h>
  #include <math.h>
  
  double hello4(double x) {
    printf("Hello4\n");
    return fmod(x, 2.0);
  }
  ''')
      create_file('pre.js', r'''
  Module['preRun'].push(function (){
    ENV['LD_LIBRARY_PATH']='/lib:/usr/lib';
  });
  ''')
      create_file('main.c', r'''
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <dlfcn.h>
  
  int main() {
    void *h;
    void (*f)();
    double (*f2)(double);
  
    h = dlopen("libhello1.wasm", RTLD_NOW);
    f = dlsym(h, "hello1");
    f();
    dlclose(h);
  
    h = dlopen("libhello2.wasm", RTLD_NOW);
    f = dlsym(h, "hello2");
    f();
    dlclose(h);
  
    h = dlopen("libhello3.wasm", RTLD_NOW);
    f = dlsym(h, "hello3");
    f();
    dlclose(h);
  
    h = dlopen("/usr/local/lib/libhello4.wasm", RTLD_NOW);
    f2 = dlsym(h, "hello4");
    double result = f2(5.5);
    dlclose(h);
  
    if (result == 1.5) {
      printf("Ok\n");
    }
    return 0;
  }
  ''')
      self.run_process([EMCC, '-o', 'libhello1.wasm', 'hello1.c', '-sSIDE_MODULE'])
      self.run_process([EMCC, '-o', 'libhello2.wasm', 'hello2.c', '-sSIDE_MODULE'])
      self.run_process([EMCC, '-o', 'libhello3.wasm', 'hello3.c', '-sSIDE_MODULE'])
      self.run_process([EMCC, '-o', 'libhello4.wasm', 'hello4.c', '-sSIDE_MODULE'])
      self.run_process([EMCC, '--profiling-funcs', '-o', 'main.js', 'main.c', '-sMAIN_MODULE=2', '-sINITIAL_MEMORY=32Mb',
                        '--embed-file', 'libhello1.wasm@/lib/libhello1.wasm',
                        '--embed-file', 'libhello2.wasm@/usr/lib/libhello2.wasm',
                        '--embed-file', 'libhello3.wasm@/libhello3.wasm',
                        '--embed-file', 'libhello4.wasm@/usr/local/lib/libhello4.wasm',
                        'libhello1.wasm', 'libhello2.wasm', 'libhello3.wasm', 'libhello4.wasm', '-sNO_AUTOLOAD_DYLIBS',
                        '--pre-js', 'pre.js'])
      out = self.run_js('main.js')
      self.assertContained('Hello1', out)
      self.assertContained('Hello2', out)
      self.assertContained('Hello3', out)
      self.assertContained('Hello4', out)
      self.assertContained('Ok', out)
  
    def test_dlopen_bad_flags(self):
      create_file('main.c', r'''
  #include <dlfcn.h>
  #include <stdio.h>
  
  int main() {
    void* h = dlopen("lib.so", 0);
    if (h) {
      printf("expected dlopen to fail\n");
      return 1;
    }
    printf("%s\n", dlerror());
    return 0;
  }
  ''')
      self.run_process([EMCC, 'main.c', '-sMAIN_MODULE=2'])
      out = self.run_js('a.out.js')
      self.assertContained('invalid mode for dlopen(): Either RTLD_LAZY or RTLD_NOW is required', out)
  
    def test_dlopen_rtld_global(self):
      # This test checks RTLD_GLOBAL where a module is loaded
      # before the module providing a global it needs is. in asm.js we use JS
      # to create a redirection function. In wasm we just have wasm, so we
      # need to introspect the wasm module. Browsers may add that eventually,
      # or we could ship a little library that does it.
      create_file('hello1.c', r'''
  #include <stdio.h>
  
  extern int hello1_val;
  int hello1_val = 3;
  
  void hello1(int i) {
    printf("hello1_val by hello1:%d\n",hello1_val);
    printf("Hello%d\n",i);
  }
  ''')
      create_file('hello2.c', r'''
  #include <stdio.h>
  
  extern int hello1_val;
  extern void hello1(int);
  
  void hello2(int i) {
    void (*f) (int);
    printf("hello1_val by hello2:%d\n",hello1_val);
    f = hello1;
    f(i);
  }
  ''')
      create_file('main.c', r'''
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <dlfcn.h>
  
  int main(int argc,char** argv) {
    void *h;
    void *h2;
    void (*f) (int);
    h = dlopen("libhello1.wasm", RTLD_NOW|RTLD_GLOBAL);
    h2 = dlopen("libhello2.wasm", RTLD_NOW|RTLD_GLOBAL);
    f = dlsym(h, "hello1");
    f(1);
    f = dlsym(h2, "hello2");
    f(2);
    dlclose(h);
    dlclose(h2);
    return 0;
  }
  ''')
  
      self.run_process([EMCC, '-o', 'libhello1.wasm', 'hello1.c', '-sSIDE_MODULE', '-sEXPORT_ALL'])
      self.run_process([EMCC, '-o', 'libhello2.wasm', 'hello2.c', '-sSIDE_MODULE', '-sEXPORT_ALL'])
      self.run_process([EMCC, '-o', 'main.js', 'main.c', '-sMAIN_MODULE',
                        '--embed-file', 'libhello1.wasm',
                        '--embed-file', 'libhello2.wasm'])
      out = self.run_js('main.js')
      self.assertContained('Hello1', out)
      self.assertContained('Hello2', out)
      self.assertContained('hello1_val by hello1:3', out)
      self.assertContained('hello1_val by hello2:3', out)
  
    def test_dlopen_async(self):
      create_file('side.c', 'int foo = 42;\n')
      self.run_process([EMCC, 'side.c', '-o', 'libside.so', '-sSIDE_MODULE'])
      self.set_setting('MAIN_MODULE', 2)
      self.set_setting('EXIT_RUNTIME')
      self.do_other_test('test_dlopen_async.c')
  
    def test_dlopen_blocking(self):
      create_file('side.c', 'int foo = 42;\n')
      self.run_process([EMCC, 'side.c', '-o', 'libside.so', '-sSIDE_MODULE'])
      self.set_setting('MAIN_MODULE', 2)
      self.set_setting('EXIT_RUNTIME')
      # Under node this should work both with and without ASYNCIFY
      # because we can do synchronous readBinary
      self.do_other_test('test_dlopen_blocking.c')
      self.set_setting('ASYNCIFY')
      self.do_other_test('test_dlopen_blocking.c')
  
    def test_dlsym_rtld_default(self):
      create_file('side.c', r'''
      int baz() {
        return 99;
      }
      ''')
      self.run_process([EMCC, '-o', 'libside.so', 'side.c', '-sSIDE_MODULE'])
      create_file('main.c', r'''
  #include <assert.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <dlfcn.h>
  #include <emscripten.h>
  
  EMSCRIPTEN_KEEPALIVE int foo() {
    return 42;
  }
  
  EMSCRIPTEN_KEEPALIVE int64_t foo64() {
    return 64;
  }
  
  int main(int argc, char** argv) {
    int (*f)();
    f = dlsym(RTLD_DEFAULT, "foo");
    assert(f);
    printf("foo -> %d\n", f());
    assert(f() == 42);
  
    int64_t (*f64)();
    f64 = dlsym(RTLD_DEFAULT, "foo64");
    assert(f64);
    printf("foo64 -> %lld\n", f64());
    assert(f64() == 64);
  
    // Missing function
    f = dlsym(RTLD_DEFAULT, "bar");
    printf("bar -> %p\n", f);
    assert(f == NULL);
  
    // Function from side module that was loaded at startup
    f = dlsym(RTLD_DEFAULT, "baz");
    assert(f);
    printf("baz -> %p\n", f);
    assert(f() == 99);
  
    // Check that dlopen()'ing libside.so gives that same
    // address for baz.
    void* handle = dlopen("libside.so", RTLD_NOW);
    assert(handle);
    int (*baz)() = dlsym(handle, "baz");
    assert(baz);
    printf("baz -> %p\n", baz);
    assert(baz() == 99);
    assert(baz == f);
  
    return 0;
  }
  ''')
      self.do_runf('main.c', emcc_args=['-sMAIN_MODULE=2', 'libside.so'])
  
    def test_dlsym_rtld_default_js_symbol(self):
      create_file('lib.js', '''
        mergeInto(LibraryManager.library, {
         foo__sig: 'ii',
         foo: function(f) { return f + 10; },
         bar: function(f) { return f + 10; },
        });
        ''')
      create_file('main.c', r'''
  #include <stdio.h>
  #include <utime.h>
  #include <sys/types.h>
  #include <dlfcn.h>
  
  typedef int (*func_type_t)(int arg);
  
  int main(int argc, char** argv) {
    func_type_t fp = (func_type_t)dlsym(RTLD_DEFAULT, argv[1]);
    if (!fp) {
      printf("dlsym failed: %s\n", dlerror());
      return 1;
    }
    printf("%s -> %d\n", argv[1], fp(10));
    return 0;
  }
  ''')
      self.run_process([EMCC, 'main.c',
                        '--js-library=lib.js',
                        '-sMAIN_MODULE=2',
                        '-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=foo,bar',
                        '-sEXPORTED_FUNCTIONS=_main,_foo,_bar'])
  
      # Fist test the successful use of a JS function with dlsym
      out = self.run_js('a.out.js', args=['foo'])
      self.assertContained('foo -> 20', out)
  
      # Now test the failure case for when __sig is not present
      out = self.run_js('a.out.js', args=['bar'], assert_returncode=NON_ZERO)
      self.assertContained('Missing signature argument to addFunction: function _bar', out)
  
    def test_main_module_without_exceptions_message(self):
      # A side module that needs exceptions needs a main module with that
      # support enabled; show a clear message in that case.
      create_file('side.cpp', r'''
        #include <exception>
        #include <stdio.h>
  
        extern "C" void test_throw() {
          try {
            throw 42;
          } catch(int x) {
            printf("catch %d.\n", x);
            return;
          }
          puts("bad location");
        }
      ''')
      create_file('main.cpp', r'''
        #include <assert.h>
        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
        #include <dlfcn.h>
  
        typedef void (*voidf)();
  
        int main() {
          void* h = dlopen("libside.wasm", RTLD_NOW);
          assert(h);
          voidf f = (voidf)dlsym(h, "test_throw");
          assert(f);
          f();
          return 0;
        }
        ''')
      self.run_process([EMXX, '-o', 'libside.wasm', 'side.cpp', '-sSIDE_MODULE', '-fexceptions'])
  
      def build_main(args):
        print(args)
        with env_modify({'EMCC_FORCE_STDLIBS': 'libc++abi'}):
          self.run_process([EMXX, 'main.cpp', '-sMAIN_MODULE',
                            '--embed-file', 'libside.wasm'] + args)
  
      build_main([])
      out = self.run_js('a.out.js', assert_returncode=NON_ZERO)
      self.assertContained('Exception catching is disabled, this exception cannot be caught.', out)
      self.assertContained('note: in dynamic linking, if a side module wants exceptions, the main module must be built with that support', out)
  
      build_main(['-fexceptions'])
      out = self.run_js('a.out.js')
      self.assertContained('catch 42', out)
  
    def test_debug_asmLastOpts(self):
      create_file('src.c', r'''
  #include <stdio.h>
  struct Dtlink_t {   struct Dtlink_t*   right;  /* right child      */
          union
          { unsigned int  _hash;  /* hash value       */
            struct Dtlink_t* _left;  /* left child       */
          } hl;
  };
  int treecount(register struct Dtlink_t* e) {
    return e ? treecount(e->hl._left) + treecount(e->right) + 1 : 0;
  }
  int main() {
    printf("hello, world!\n");
  }
  ''')
      self.run_process([EMCC, 'src.c', '-sEXPORTED_FUNCTIONS=_main,_treecount', '--minify=0', '-gsource-map', '-Oz'])
      self.assertContained('hello, world!', self.run_js('a.out.js'))
  
    def test_emscripten_print_double(self):
      create_file('src.c', r'''
  #include <stdio.h>
  #include <assert.h>
  #include <emscripten.h>
  
  void test(double d) {
    char buffer[100], buffer2[100];
    unsigned len, len2, len3;
    len = emscripten_print_double(d, NULL, -1);
    len2 = emscripten_print_double(d, buffer, len+1);
    assert(len == len2);
    buffer[len] = 0;
    len3 = snprintf(buffer2, 100, "%g", d);
    printf("|%g : %u : %s : %s : %d|\n", d, len, buffer, buffer2, len3);
  }
  
  int main() {
    printf("\n");
    test(0);
    test(1);
    test(-1);
    test(1.234);
    test(-1.234);
    test(1.1234E20);
    test(-1.1234E20);
    test(1.1234E-20);
    test(-1.1234E-20);
    test(1.0/0.0);
    test(-1.0/0.0);
  }
  ''')
      self.run_process([EMCC, 'src.c'])
      out = self.run_js('a.out.js')
      self.assertContained('''
  |0 : 1 : 0 : 0 : 1|
  |1 : 1 : 1 : 1 : 1|
  |-1 : 2 : -1 : -1 : 2|
  |1.234 : 5 : 1.234 : 1.234 : 5|
  |-1.234 : 6 : -1.234 : -1.234 : 6|
  |1.1234e+20 : 21 : 112340000000000000000 : 1.1234e+20 : 10|
  |-1.1234e+20 : 22 : -112340000000000000000 : -1.1234e+20 : 11|
  |1.1234e-20 : 10 : 1.1234e-20 : 1.1234e-20 : 10|
  |-1.1234e-20 : 11 : -1.1234e-20 : -1.1234e-20 : 11|
  |inf : 8 : Infinity : inf : 3|
  |-inf : 9 : -Infinity : -inf : 4|
  ''', out)
  
    def test_emscripten_scan_stack(self):
      create_file('src.cpp', r'''
  #include <set>
  #include <emscripten.h>
  #include <stdio.h>
  #include <assert.h>
  
  std::set<int> seenInts;
  
  void scan(void* x, void* y) {
    printf("scan\n");
    int* p = (int*)x;
    int* q = (int*)y;
    // The callback sends us the [low, high) range.
    assert(p < q);
    // The range is of a reasonable size - not all of memory.
    assert(q - p < 100);
    while (p < q) {
      seenInts.insert(*p);
      p++;
    }
  }
  
  int main() {
    int x;
    int* y = &x;
    *y = 12345678;
    emscripten_scan_stack(scan);
    assert(seenInts.count(12345678));
    puts("ok");
  }
  ''')
      self.run_process([EMXX, 'src.cpp'])
      self.assertContained('ok', self.run_js('a.out.js'))
  
    def test_no_warn_exported_jslibfunc(self):
      self.run_process([EMCC, test_file('hello_world.c'),
                        '-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=alGetError',
                        '-sEXPORTED_FUNCTIONS=_main,_alGetError'])
  
      # Same again but with `_alGet` wich does not exist.  This is a regression
      # test for a bug we had where any prefix of a valid function was accepted.
      err = self.expect_fail([EMCC, test_file('hello_world.c'),
                              '-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=alGetError',
                              '-sEXPORTED_FUNCTIONS=_main,_alGet'])
      self.assertContained('undefined exported symbol: "_alGet"', err)
  
    def test_musl_syscalls(self):
      self.run_process([EMCC, test_file('hello_world.c')])
      src = read_file('a.out.js')
      # there should be no musl syscalls in hello world output
      self.assertNotContained('__syscall', src)
  
    def test_emcc_dev_null(self):
      out = self.run_process([EMCC, '-dM', '-E', '-x', 'c', os.devnull], stdout=PIPE).stdout
      self.assertContained('#define __EMSCRIPTEN__ 1', out) # all our defines should show up
  
    def test_umask_0(self):
      create_file('src.c', r'''\
  #include <sys/stat.h>
  #include <stdio.h>
  int main() {
    umask(0);
    printf("hello, world!\n");
  }
  ''')
      self.run_process([EMCC, 'src.c'])
      self.assertContained('hello, world!', self.run_js('a.out.js'))
  
    def test_no_missing_symbols(self): # simple hello world should not show any missing symbols
      self.run_process([EMCC, test_file('hello_world.c')])
  
      # main() is implemented in C, and even if requested from JS, we should not warn
      create_file('library_foo.js', '''
  mergeInto(LibraryManager.library, {
    my_js__deps: ['main'],
    my_js: (function() {
        return function() {
          console.log("hello " + _nonexistingvariable);
        };
    }()),
  });
  ''')
      create_file('test.cpp', '''\
  #include <stdio.h>
  #include <stdlib.h>
  
  extern "C" {
    extern void my_js();
  }
  
  int main() {
    my_js();
    return EXIT_SUCCESS;
  }
  ''')
      self.run_process([EMXX, 'test.cpp', '--js-library', 'library_foo.js'])
  
      # but we do error on a missing js var
      create_file('library_foo_missing.js', '''
  mergeInto(LibraryManager.library, {
    my_js__deps: ['main', 'nonexistingvariable'],
    my_js: (function() {
        return function() {
          console.log("hello " + _nonexistingvariable);
        };
    }()),
  });
  ''')
      err = self.expect_fail([EMXX, 'test.cpp', '--js-library', 'library_foo_missing.js'])
      self.assertContained('undefined symbol: nonexistingvariable', err)
  
      # and also for missing C code, of course (without the --js-library, it's just a missing C method)
      err = self.expect_fail([EMXX, 'test.cpp'])
      self.assertContained('undefined symbol: my_js', err)
  
    def test_js_lib_to_system_lib(self):
      # memset is in compiled code, so a js library __deps can't access it. it
      # would need to be in deps_info.json or EXPORTED_FUNCTIONS
      create_file('lib.js', r'''
  mergeInto(LibraryManager.library, {
    depper__deps: ['memset'],
    depper: function(ptr) {
      _memset(ptr, 'd'.charCodeAt(0), 10);
    },
  });
  ''')
      create_file('test.cpp', r'''
  #include <string.h>
  #include <stdio.h>
  
  extern "C" {
  extern void depper(char*);
  }
  
  int main(int argc, char** argv) {
    char buffer[11];
    buffer[10] = '\0';
    // call by a pointer, to force linking of memset, no llvm intrinsic here
    volatile auto ptr = memset;
    (*ptr)(buffer, 'a', 10);
    depper(buffer);
    puts(buffer);
  }
  ''')
  
      err = self.expect_fail([EMXX, 'test.cpp', '--js-library', 'lib.js'])
      self.assertContained('_memset may need to be added to EXPORTED_FUNCTIONS if it arrives from a system library', err)
  
      # without the dep, and with EXPORTED_FUNCTIONS, it works ok
      create_file('lib.js', r'''
  mergeInto(LibraryManager.library, {
    depper: function(ptr) {
      _memset(ptr, 'd'.charCodeAt(0), 10);
    },
  });
  ''')
      self.run_process([EMXX, 'test.cpp', '--js-library', 'lib.js', '-sEXPORTED_FUNCTIONS=_main,_memset'])
      self.assertContained('dddddddddd', self.run_js('a.out.js'))
  
    def test_realpath(self):
      create_file('src.c', r'''
  #include <stdlib.h>
  #include <stdio.h>
  #include <errno.h>
  
  int main(int argc, char **argv) {
    char *t_realpath_buf = realpath("/boot/README.txt", NULL);
    if (!t_realpath_buf) {
      perror("Resolve failed");
      return 1;
    }
  
    printf("Resolved: %s\n", t_realpath_buf);
    free(t_realpath_buf);
    return 0;
  }
  ''')
      ensure_dir('boot')
      create_file('boot/README.txt', ' ')
      self.run_process([EMCC, 'src.c', '-sSAFE_HEAP', '--embed-file', 'boot'])
      self.assertContained('Resolved: /boot/README.txt', self.run_js('a.out.js'))
  
    def test_realpath_nodefs(self):
      create_file('src.c', r'''
  #include <stdlib.h>
  #include <stdio.h>
  #include <errno.h>
  #include <emscripten.h>
  
  #define TEST_PATH "/working/TEST_NODEFS.txt"
  
  int main(int argc, char **argv) {
    errno = 0;
    EM_ASM({
      FS.mkdir('/working');
      FS.mount(NODEFS, { root: '.' }, '/working');
    });
    char *t_realpath_buf = realpath(TEST_PATH, NULL);
    if (NULL == t_realpath_buf) {
      perror("Resolve failed");
      return 1;
    } else {
      printf("Resolved: %s\n", t_realpath_buf);
      free(t_realpath_buf);
      return 0;
    }
  }
  ''')
      create_file('TEST_NODEFS.txt', ' ')
      self.run_process([EMCC, 'src.c', '-lnodefs.js'])
      self.assertContained('Resolved: /working/TEST_NODEFS.txt', self.run_js('a.out.js'))
  
    def test_realpath_2(self):
      ensure_dir('Folder')
      create_file('src.c', r'''
  #include <stdlib.h>
  #include <stdio.h>
  #include <errno.h>
  
  int testrealpath(const char* path)    {
    errno = 0;
    char *t_realpath_buf = realpath(path, NULL);
    if (NULL == t_realpath_buf) {
      printf("Resolve failed: \"%s\"\n",path);fflush(stdout);
      return 1;
    } else {
      printf("Resolved: \"%s\" => \"%s\"\n", path, t_realpath_buf);fflush(stdout);
      free(t_realpath_buf);
      return 0;
    }
  }
  
  int main(int argc, char **argv)
  {
      // files:
      testrealpath("testfile.txt");
      testrealpath("Folder/testfile.txt");
      testrealpath("testnonexistentfile.txt");
      // folders
      testrealpath("Folder");
      testrealpath("/Folder");
      testrealpath("./");
      testrealpath("");
      testrealpath("/");
      return 0;
  }
  ''')
      create_file('testfile.txt', '')
      create_file('Folder/testfile.txt', '')
      self.run_process([EMCC, 'src.c', '--embed-file', 'testfile.txt', '--embed-file', 'Folder'])
      self.assertContained('''Resolved: "testfile.txt" => "/testfile.txt"
  Resolved: "Folder/testfile.txt" => "/Folder/testfile.txt"
  Resolve failed: "testnonexistentfile.txt"
  Resolved: "Folder" => "/Folder"
  Resolved: "/Folder" => "/Folder"
  Resolved: "./" => "/"
  Resolve failed: ""
  Resolved: "/" => "/"
  ''', self.run_js('a.out.js'))
  
    def test_no_warnings(self):
      # build once before to make sure system libs etc. exist
      self.run_process([EMXX, test_file('hello_libcxx.cpp')])
      # check that there is nothing in stderr for a regular compile
      err = self.run_process([EMXX, test_file('hello_libcxx.cpp')], stderr=PIPE).stderr
      self.assertEqual(err, '')
  
    def test_dlmalloc_modes(self):
      create_file('src.cpp', r'''
        #include <stdlib.h>
        #include <stdio.h>
        int main() {
          void* c = malloc(1024);
          free(c);
          free(c);
          printf("double-freed\n");
        }
      ''')
      self.run_process([EMXX, 'src.cpp'])
      self.assertContained('double-freed', self.run_js('a.out.js'))
      # in debug mode, the double-free is caught
      self.run_process([EMXX, 'src.cpp', '-sASSERTIONS=2'])
      seen_error = False
      out = '?'
      try:
        out = self.run_js('a.out.js')
      except Exception:
        seen_error = True
      self.assertTrue(seen_error, out)
  
    def test_mallocs(self):
      def run(opts):
        print(opts)
        sizes = {}
        for malloc, name in (
          ('dlmalloc', 'dlmalloc'),
          (None, 'default'),
          ('emmalloc', 'emmalloc')
        ):
          print(malloc, name)
          cmd = [EMXX, test_file('hello_libcxx.cpp'), '-o', 'a.out.js'] + opts
          if malloc:
            cmd += ['-sMALLOC="%s"' % malloc]
          print(cmd)
          self.run_process(cmd)
          sizes[name] = os.path.getsize('a.out.wasm')
        print(sizes)
        # dlmalloc is the default
        self.assertEqual(sizes['dlmalloc'], sizes['default'])
        # emmalloc is much smaller
        self.assertLess(sizes['emmalloc'], sizes['dlmalloc'] - 5000)
      run([])
      run(['-O2'])
  
    def test_emmalloc_2GB(self):
      def test(args, text=None):
        if text:
          stderr = self.expect_fail([EMCC, test_file('hello_world.c'), '-sMALLOC=emmalloc'] + args)
          self.assertContained(text, stderr)
        else:
          self.run_process([EMCC, test_file('hello_world.c'), '-sMALLOC=emmalloc'] + args)
  
      test(['-sALLOW_MEMORY_GROWTH'])
      test(['-sALLOW_MEMORY_GROWTH', '-sMAXIMUM_MEMORY=1GB'])
      test(['-sALLOW_MEMORY_GROWTH', '-sMAXIMUM_MEMORY=4GB'])
  
    def test_2GB_plus(self):
      # when the heap size can be over 2GB, we rewrite pointers to be unsigned
      def test(page_diff):
        args = [EMCC, test_file('hello_world.c'), '-O2', '-sALLOW_MEMORY_GROWTH']
        if page_diff is not None:
          args += ['-sMAXIMUM_MEMORY=%d' % (2**31 + page_diff * 64 * 1024)]
        print(args)
        self.run_process(args)
        return os.path.getsize('a.out.js')
  
      less = test(-1)
      equal = test(0)
      more = test(1)
      none = test(None)
  
      # exactly 2GB still doesn't require unsigned pointers, as we can't address
      # the 2GB location in memory
      self.assertEqual(less, equal)
      self.assertLess(equal, more)
      # not specifying maximum memory does not result in unsigned pointers, as the
      # default maximum memory is 2GB.
      self.assertEqual(less, none)
  
    @parameterized({
      'normal': (['-sWASM_BIGINT=0'], 'testbind.js'),
      'bigint': (['-sWASM_BIGINT'], 'testbind_bigint.js'),
    })
    def test_sixtyfour_bit_return_value(self, args, bind_js):
      # This test checks that the most significant 32 bits of a 64 bit long are correctly made available
      # to native JavaScript applications that wish to interact with compiled code returning 64 bit longs.
      # The MS 32 bits should be available in Runtime.getTempRet0() even when compiled with -O2 --closure 1
  
      # Compile test.c and wrap it in a native JavaScript binding so we can call our compiled function from JS.
      self.run_process([EMCC, test_file('return64bit/test.c'),
                        '--pre-js', test_file('return64bit/testbindstart.js'),
                        '--pre-js', test_file('return64bit', bind_js),
                        '--post-js', test_file('return64bit/testbindend.js'),
                        '-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$dynCall',
                        '-sEXPORTED_FUNCTIONS=_test_return64', '-o', 'test.js', '-O2',
                        '--closure=1', '-g1', '-sWASM_ASYNC_COMPILATION=0'] + args)
  
      # Simple test program to load the test.js binding library and call the binding to the
      # C function returning the 64 bit long.
      create_file('testrun.js', '''
        var test = require("./test.js");
        test.runtest();
      ''')
  
      # Run the test and confirm the output is as expected.
      out = self.run_js('testrun.js', engine=config.NODE_JS + ['--experimental-wasm-bigint'])
      self.assertContained('''\
  input = 0xaabbccdd11223344
  low = 5678
  high = 1234
  input = 0xabcdef1912345678
  low = 5678
  high = 1234
  ''', out)
  
    def test_lib_include_flags(self):
      self.run_process([EMCC] + '-l m -l c -I'.split() + [test_file('include_test'), test_file('lib_include_flags.c')])
  
    def test_dash_s_link_flag(self):
      # -s is also a valid link flag.  We try to distingish between this case and when
      # its used to set a settings based on looking at the argument that follows.
  
      # Test the case when -s is the last flag
      self.run_process([EMXX, test_file('hello_world.cpp'), '-s'])
      self.assertContained('hello, world!', self.run_js('a.out.js'))
  
      # Test the case when the following flag is all uppercase but starts with a `-`
      self.run_process([EMXX, test_file('hello_world.cpp'), '-s', '-DFOO'])
      self.assertContained('hello, world!', self.run_js('a.out.js'))
  
      # Test that case when the following flag is not all uppercase
      self.run_process([EMXX, '-s', test_file('hello_world.cpp')])
      self.assertContained('hello, world!', self.run_js('a.out.js'))
  
    def test_dash_s_response_file_string(self):
      create_file('response_file.txt', 'MyModule\n')
      create_file('response_file.json', '"MyModule"\n')
      self.run_process([EMXX, test_file('hello_world.cpp'), '-sEXPORT_NAME=@response_file.txt'])
      self.run_process([EMXX, test_file('hello_world.cpp'), '-sEXPORT_NAME=@response_file.json'])
  
    def test_dash_s_response_file_list(self):
      create_file('response_file.txt', '_main\n_malloc\n')
      create_file('response_file.json', '["_main", "_malloc"]\n')
      self.run_process([EMXX, test_file('hello_world.cpp'), '-sEXPORTED_FUNCTIONS=@response_file.txt'])
      self.run_process([EMXX, test_file('hello_world.cpp'), '-sEXPORTED_FUNCTIONS=@response_file.json'])
  
    def test_dash_s_response_file_misssing(self):
      err = self.expect_fail([EMXX, test_file('hello_world.cpp'), '-sEXPORTED_FUNCTIONS=@foo'])
      self.assertContained('error: foo: file not found parsing argument: EXPORTED_FUNCTIONS=@foo', err)
  
    def test_dash_s_unclosed_quote(self):
      # Unclosed quote
      err = self.run_process([EMXX, test_file('hello_world.cpp'), '-s', "TEST_KEY='MISSING_QUOTE"], stderr=PIPE, check=False).stderr
      self.assertNotContained('AssertionError', err) # Do not mention that it is an assertion error
      self.assertContained('unclosed opened quoted string. expected final character to be "\'"', err)
  
    def test_dash_s_single_quote(self):
      # Only one quote
      err = self.run_process([EMXX, test_file('hello_world.cpp'), "-sTEST_KEY='"], stderr=PIPE, check=False).stderr
      self.assertNotContained('AssertionError', err) # Do not mention that it is an assertion error
      self.assertContained('unclosed opened quoted string.', err)
  
    def test_dash_s_unclosed_list(self):
      # Unclosed list
      err = self.expect_fail([EMXX, test_file('hello_world.cpp'), "-sTEST_KEY=[Value1, Value2"])
      self.assertNotContained('AssertionError', err) # Do not mention that it is an assertion error
      self.assertContained('unclosed opened string list. expected final character to be "]"', err)
  
    def test_dash_s_valid_list(self):
      err = self.expect_fail([EMXX, test_file('hello_world.cpp'), "-sTEST_KEY=[Value1, \"Value2\"]"])
      self.assertNotContained('a problem occurred in evaluating the content after a "-s", specifically', err)
  
    def test_dash_s_wrong_type(self):
      err = self.expect_fail([EMXX, test_file('hello_world.cpp'), '-sEXIT_RUNTIME=[foo,bar]'])
      self.assertContained("error: setting `EXIT_RUNTIME` expects `<class 'int'>` but got `<class 'list'>`", err)
  
    def test_dash_s_typo(self):
      # with suggestions
      stderr = self.expect_fail([EMCC, test_file('hello_world.c'), '-sDISABLE_EXCEPTION_CATCH'])
      self.assertContained("Attempt to set a non-existent setting: 'DISABLE_EXCEPTION_CATCH'", stderr)
      self.assertContained('did you mean one of DISABLE_EXCEPTION_CATCHING', stderr)
      # no suggestions
      stderr = self.expect_fail([EMCC, test_file('hello_world.c'), '-sCHEEZ'])
      self.assertContained("perhaps a typo in emcc\'s  -s X=Y  notation?", stderr)
      self.assertContained('(see src/settings.js for valid values)', stderr)
      # suggestions do not include renamed legacy settings
      stderr = self.expect_fail([EMCC, test_file('hello_world.c'), '-sZBINARYEN_ASYNC_COMPILATION'])
      self.assertContained("Attempt to set a non-existent setting: 'ZBINARYEN_ASYNC_COMPILATION'", stderr)
      self.assertNotContained(' BINARYEN_ASYNC_COMPILATION', stderr)
  
    def test_dash_s_with_space(self):
      self.run_process([EMCC, test_file('hello_world.c'), '-s', 'EXPORT_ALL'])
      err = self.expect_fail([EMXX, test_file('hello_world.cpp'), '-s', 'EXPORTED_FUNCTIONS=foo'])
      self.assertContained('error: undefined exported symbol: "foo"', err)
  
    def test_dash_s_hex(self):
      self.run_process([EMCC, test_file('hello_world.c'), '-nostdlib', '-sERROR_ON_UNDEFINED_SYMBOLS=0'])
      # Ensure that 0x0 is parsed as a zero and not as the string '0x0'.
      self.run_process([EMCC, test_file('hello_world.c'), '-nostdlib', '-sERROR_ON_UNDEFINED_SYMBOLS=0x0'])
  
    def test_zeroinit(self):
      create_file('src.c', r'''
  #include <stdio.h>
  int buf[1048576];
  int main() {
    printf("hello, world! %d\n", buf[123456]);
    return 0;
  }
  ''')
      self.run_process([EMCC, 'src.c', '-O2'])
      size = os.path.getsize('a.out.wasm')
      # size should be much smaller than the size of that zero-initialized buffer
      self.assertLess(size, 123456 / 2)
  
    def test_canonicalize_nan_warning(self):
      create_file('src.cpp', r'''
  #include <stdio.h>
  
  union U {
    int x;
    float y;
  } a;
  
  
  int main() {
    a.x = 0x7FC01234;
    printf("%f\n", a.y);
    printf("0x%x\n", a.x);
    return 0;
  }
  ''')
  
      self.run_process([EMXX, 'src.cpp', '-O1'])
      out = self.run_js('a.out.js')
      self.assertContained('nan\n', out)
      self.assertContained('0x7fc01234\n', out)
  
    def test_memory_growth_noasm(self):
      self.run_process([EMCC, test_file('hello_world.c'), '-O2', '-sALLOW_MEMORY_GROWTH'])
      src = read_file('a.out.js')
      assert 'use asm' not in src
  
    def test_EM_ASM_i64(self):
      create_file('src.cpp', '''
  #include <stdint.h>
  #include <emscripten.h>
  
  int main() {
    EM_ASM({
      out('inputs: ' + $0 + ', ' + $1 + '.');
    }, int64_t(0x12345678ABCDEF1FLL));
  }
  ''')
      self.expect_fail([EMXX, 'src.cpp', '-Oz'])
  
    def test_eval_ctor_ordering(self):
      # ensure order of execution remains correct, even with a bad ctor
      def test(p1, p2, p3, last, expected):
        src = r'''
          #include <stdio.h>
          #include <stdlib.h>
          volatile int total = 0;
          struct C {
            C(int x) {
              volatile int y = x;
              y++;
              y--;
              if (y == 0xf) {
                // A printf can't be evalled ahead of time, so this will stop
                // us.
                printf("you can't eval me ahead of time\n");
              }
              total <<= 4;
              total += int(y);
            }
          };
          C __attribute__((init_priority(%d))) c1(0x5);
          C __attribute__((init_priority(%d))) c2(0x8);
          C __attribute__((init_priority(%d))) c3(%d);
          int main() {
            printf("total is 0x%%x.\n", total);
          }
        ''' % (p1, p2, p3, last)
        create_file('src.cpp', src)
        self.run_process([EMXX, 'src.cpp', '-O2', '-sEVAL_CTORS', '-profiling-funcs'])
        self.assertContained('total is %s.' % hex(expected), self.run_js('a.out.js'))
        shutil.copyfile('a.out.js', 'x' + hex(expected) + '.js')
        shutil.copyfile('a.out.wasm', 'x' + hex(expected) + '.wasm')
        return os.path.getsize('a.out.wasm')
  
      print('no bad ctor')
      first  = test(1000, 2000, 3000, 0xe, 0x58e) # noqa
      second = test(3000, 1000, 2000, 0xe, 0x8e5) # noqa
      third  = test(2000, 3000, 1000, 0xe, 0xe58) # noqa
      print(first, second, third)
      assert first == second and second == third
      print('with bad ctor')
      first  = test(1000, 2000, 3000, 0xf, 0x58f) # noqa; 2 will succeed
      second = test(3000, 1000, 2000, 0xf, 0x8f5) # noqa; 1 will succedd
      third  = test(2000, 3000, 1000, 0xf, 0xf58) # noqa; 0 will succeed
      print(first, second, third)
      self.assertLess(first, second)
      self.assertLess(second, third)
  
    @uses_canonical_tmp
    @with_env_modify({'EMCC_DEBUG': '1'})
    def test_eval_ctors_debug_output(self):
      create_file('lib.js', r'''
  mergeInto(LibraryManager.library, {
  external_thing: function() {}
  });
  ''')
      create_file('src.cpp', r'''
  extern "C" void external_thing();
  struct C {
    C() { external_thing(); } // don't remove this!
  };
  C c;
  int main() {}
      ''')
      err = self.run_process([EMXX, 'src.cpp', '--js-library', 'lib.js', '-O2', '-sEVAL_CTORS'], stderr=PIPE).stderr
      # logging should show we failed, and why
      self.assertNotContained('ctor_evaller: not successful', err)
      self.assertContained('stopping since could not eval: call import: env.external_thing', err)
  
    def test_override_js_execution_environment(self):
      create_file('main.cpp', r'''
        #include <emscripten.h>
        int main() {
          EM_ASM({
            out('environment is WEB? ' + ENVIRONMENT_IS_WEB);
            out('environment is WORKER? ' + ENVIRONMENT_IS_WORKER);
            out('environment is NODE? ' + ENVIRONMENT_IS_NODE);
            out('environment is SHELL? ' + ENVIRONMENT_IS_SHELL);
          });
        }
  ''')
      # use SINGLE_FILE since we don't want to depend on loading a side .wasm file on the environment in this test;
      # with the wrong env we have very odd failures
      self.run_process([EMXX, 'main.cpp', '-sSINGLE_FILE'])
      src = read_file('a.out.js')
      envs = ['web', 'worker', 'node', 'shell']
      for env in envs:
        for engine in config.JS_ENGINES:
          if engine == config.V8_ENGINE:
            continue # ban v8, weird failures
          actual = 'NODE' if engine == config.NODE_JS else 'SHELL'
          print(env, actual, engine)
          module = {'ENVIRONMENT': env}
          if env != actual:
            # avoid problems with arguments detection, which may cause very odd failures with the wrong environment code
            module['arguments'] = []
          curr = 'var Module = %s;\n' % str(module)
          print('    ' + curr)
          create_file('test.js', curr + src)
          seen = self.run_js('test.js', engine=engine, assert_returncode=NON_ZERO)
          self.assertContained('Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -s ENVIRONMENT=web or -s ENVIRONMENT=node', seen)
  
    def test_override_c_environ(self):
      create_file('pre.js', r'''
        var Module = {
          preRun: [function() { ENV.hello = 'world'; ENV.LANG = undefined; }]
        };
      ''')
      create_file('src.cpp', r'''
        #include <stdlib.h>
        #include <stdio.h>
        int main() {
          printf("|%s|\n", getenv("hello"));
          printf("LANG is %s\n", getenv("LANG") ? "set" : "not set");
        }
      ''')
      self.run_process([EMXX, 'src.cpp', '--pre-js', 'pre.js'])
      output = self.run_js('a.out.js')
      self.assertContained('|world|', output)
      self.assertContained('LANG is not set', output)
  
      create_file('pre.js', r'''
        var Module = {
          preRun: [function(module) { module.ENV.hello = 'world' }]
        };
      ''')
      self.run_process([EMXX, 'src.cpp', '--pre-js', 'pre.js', '-sEXPORTED_RUNTIME_METHODS=ENV'])
      self.assertContained('|world|', self.run_js('a.out.js'))
  
      self.run_process([EMXX, 'src.cpp', '--pre-js', 'pre.js', '-sEXPORTED_RUNTIME_METHODS=ENV', '-sMODULARIZE'])
      output = self.run_process(config.NODE_JS + ['-e', 'require("./a.out.js")();'], stdout=PIPE, stderr=PIPE)
      self.assertContained('|world|', output.stdout)
  
    def test_warn_no_filesystem(self):
      error = 'Filesystem support (FS) was not included. The problem is that you are using files from JS, but files were not used from C/C++, so filesystem support was not auto-included. You can force-include filesystem support with  -s FORCE_FILESYSTEM=1'
  
      self.run_process([EMCC, test_file('hello_world.c')])
      seen = self.run_js('a.out.js')
      self.assertNotContained(error, seen)
  
      def test(contents):
        create_file('src.cpp', r'''
    #include <stdio.h>
    #include <emscripten.h>
    int main() {
      EM_ASM({ %s });
      printf("hello, world!\n");
      return 0;
    }
    ''' % contents)
        self.run_process([EMXX, 'src.cpp'])
        self.assertContained(error, self.run_js('a.out.js', assert_returncode=NON_ZERO))
  
      # might appear in handwritten code
      test("FS.init()")
      test("FS.createPreloadedFile('waka waka, just warning check')")
      test("FS.createDataFile('waka waka, just warning check')")
      test("FS.analyzePath('waka waka, just warning check')")
      test("FS.loadFilesFromDB('waka waka, just warning check')")
      # might appear in filesystem code from a separate script tag
      test("Module['FS_createDataFile']('waka waka, just warning check')")
      test("Module['FS_createPreloadedFile']('waka waka, just warning check')")
  
      # text is in the source when needed, but when forcing FS, it isn't there
      self.run_process([EMXX, 'src.cpp'])
      self.assertContained(error, read_file('a.out.js'))
      self.run_process([EMXX, 'src.cpp', '-sFORCE_FILESYSTEM']) # forcing FS means no need
      self.assertNotContained(error, read_file('a.out.js'))
      self.run_process([EMXX, 'src.cpp', '-sASSERTIONS=0']) # no assertions, no need
      self.assertNotContained(error, read_file('a.out.js'))
      self.run_process([EMXX, 'src.cpp', '-O2']) # optimized, so no assertions
      self.assertNotContained(error, read_file('a.out.js'))
  
    def test_warn_module_print_err(self):
      error = 'was not exported. add it to EXPORTED_RUNTIME_METHODS (see the FAQ)'
  
      def test(contents, expected, args=[], assert_returncode=0):
        create_file('src.cpp', r'''
    #include <emscripten.h>
    int main() {
      EM_ASM({ %s });
      return 0;
    }
    ''' % contents)
        self.run_process([EMXX, 'src.cpp'] + args)
        self.assertContained(expected, self.run_js('a.out.js', assert_returncode=assert_returncode))
  
      # error shown (when assertions are on)
      test("Module.print('x')", error, assert_returncode=NON_ZERO)
      test("Module['print']('x')", error, assert_returncode=NON_ZERO)
      test("Module.printErr('x')", error, assert_returncode=NON_ZERO)
      test("Module['printErr']('x')", error, assert_returncode=NON_ZERO)
  
      # when exported, all good
      test("Module['print']('print'); Module['printErr']('err'); ", 'print\nerr', ['-sEXPORTED_RUNTIME_METHODS=print,printErr'])
  
    def test_warn_unexported_main(self):
      WARNING = 'main() is in the input files, but "_main" is not in EXPORTED_FUNCTIONS, which means it may be eliminated as dead code. Export it if you want main() to run.'
  
      proc = self.run_process([EMCC, test_file('hello_world.c'), '-sEXPORTED_FUNCTIONS=[]'], stderr=PIPE)
      self.assertContained(WARNING, proc.stderr)
  
    def test_source_file_with_fixed_language_mode(self):
      create_file('src_tmp_fixed_lang', '''
  #include <string>
  #include <iostream>
  
  int main() {
    std::cout << "Test_source_fixed_lang_hello" << std::endl;
    return 0;
  }
      ''')
      self.run_process([EMXX, '-Wall', '-x', 'c++', 'src_tmp_fixed_lang'])
      self.assertContained('Test_source_fixed_lang_hello', self.run_js('a.out.js'))
  
      stderr = self.expect_fail([EMXX, '-Wall', 'src_tmp_fixed_lang'])
      self.assertContained('unknown file type: src_tmp_fixed_lang', stderr)
  
    def test_disable_inlining(self):
      create_file('test.c', r'''
  #include <stdio.h>
  
  void foo() {
    printf("foo\n");
  }
  
  int main() {
    foo();
    return 0;
  }
  ''')
  
      # Without the 'INLINING_LIMIT', -O2 inlines foo()
      cmd = [EMCC, '-c', 'test.c', '-O2', '-o', 'test.o', '-sINLINING_LIMIT', '-flto']
      self.run_process(cmd)
      # If foo() had been wrongly inlined above, internalizing foo and running
      # global DCE makes foo DCE'd
      opts = ['-internalize', '-internalize-public-api-list=main', '-globaldce']
      self.run_process([shared.LLVM_OPT] + opts + ['test.o', '-o', 'test2.o'])
  
      # To this test to be successful, foo() shouldn't have been inlined above and
      # foo() should be in the function list
      output = self.run_process([shared.EM_NM, 'test2.o'], stdout=PIPE).stdout
      self.assertContained('foo', output)
  
    def test_output_eol(self):
      for params in [[], ['--proxy-to-worker'], ['--proxy-to-worker', '-sWASM=0']]:
        for output_suffix in ['html', 'js']:
          for eol in ['windows', 'linux']:
            files = ['a.js']
            if output_suffix == 'html':
              files += ['a.html']
            cmd = [EMCC, test_file('hello_world.c'), '-o', 'a.' + output_suffix, '--output_eol', eol] + params
            self.run_process(cmd)
            for f in files:
              print(str(cmd) + ' ' + str(params) + ' ' + eol + ' ' + f)
              self.assertExists(f)
              if eol == 'linux':
                expected_ending = '\n'
              else:
                expected_ending = '\r\n'
  
              ret = line_endings.check_line_endings(f, expect_only=expected_ending)
              assert ret == 0
  
            for f in files:
              try_delete(f)
  
    def test_binaryen_names(self):
      sizes = {}
      for args, expect_names in [
          ([], False),
          (['-g'], True),
          (['-O1'], False),
          (['-O2'], False),
          (['-O2', '-g'], True),
          (['-O2', '-g1'], False),
          (['-O2', '-g2'], True),
          (['-O2', '--profiling'], True),
          (['-O2', '--profiling-funcs'], True),
        ]:
        print(args, expect_names)
        try_delete('a.out.js')
        # we use dlmalloc here, as emmalloc has a bunch of asserts that contain the text "malloc" in
        # them, which makes counting harder
        self.run_process([EMXX, test_file('hello_world.cpp')] + args + ['-sMALLOC="dlmalloc"', '-sEXPORTED_FUNCTIONS=_main,_malloc'])
        code = read_binary('a.out.wasm')
        if '-g' in args:
          # With -g we get full dwarf info which means we there are many occurances of malloc
          self.assertGreater(code.count(b'malloc'), 2)
        else:
          if expect_names:
            # name section adds the name of malloc (there is also another one for the export)
            self.assertEqual(code.count(b'malloc'), 2)
          else:
            # should be just malloc for the export
            self.assertEqual(code.count(b'malloc'), 1)
        sizes[str(args)] = os.path.getsize('a.out.wasm')
      print(sizes)
      # when -profiling-funcs, the size increases due to function names
      self.assertLess(sizes["['-O2']"], sizes["['-O2', '--profiling-funcs']"])
  
    def test_binaryen_warn_mem(self):
      # if user changes INITIAL_MEMORY at runtime, the wasm module may not accept the memory import if
      # it is too big/small
      create_file('pre.js', 'var Module = { INITIAL_MEMORY: 50 * 1024 * 1024 };\n')
      self.run_process([EMXX, test_file('hello_world.cpp'), '-sINITIAL_MEMORY=' + str(16 * 1024 * 1024), '--pre-js', 'pre.js', '-sWASM_ASYNC_COMPILATION=0', '-sIMPORTED_MEMORY'])
      out = self.run_js('a.out.js', assert_returncode=NON_ZERO)
      self.assertContained('LinkError', out)
      self.assertContained('Memory size incompatibility issues may be due to changing INITIAL_MEMORY at runtime to something too large. Use ALLOW_MEMORY_GROWTH to allow any size memory (and also make sure not to set INITIAL_MEMORY at runtime to something smaller than it was at compile time).', out)
      self.assertNotContained('hello, world!', out)
      # and with memory growth, all should be good
      self.run_process([EMXX, test_file('hello_world.cpp'), '-sINITIAL_MEMORY=' + str(16 * 1024 * 1024), '--pre-js', 'pre.js', '-sALLOW_MEMORY_GROWTH', '-sWASM_ASYNC_COMPILATION=0', '-sIMPORTED_MEMORY'])
      self.assertContained('hello, world!', self.run_js('a.out.js'))
  
    def test_memory_size(self):
      for args, expect_initial, expect_max in [
          ([], 320, 320),
          (['-sALLOW_MEMORY_GROWTH'], 320, 32768),
          (['-sALLOW_MEMORY_GROWTH', '-sMAXIMUM_MEMORY=40MB'], 320, 640),
        ]:
        cmd = [EMCC, test_file('hello_world.c'), '-O2', '-sINITIAL_MEMORY=20MB'] + args
        print(' '.join(cmd))
        self.run_process(cmd)
        wat = self.run_process([wasm_dis, 'a.out.wasm'], stdout=PIPE).stdout
        memories = [l for l in wat.splitlines() if '(memory ' in l]
        self.assertEqual(len(memories), 2)
        line = memories[0]
        parts = line.strip().replace('(', '').replace(')', '').split()
        print(parts)
        self.assertEqual(parts[2], str(expect_initial))
        self.assertEqual(parts[3], str(expect_max))
  
    def test_invalid_mem(self):
      # A large amount is fine, multiple of 16MB or not
      self.run_process([EMCC, test_file('hello_world.c'), '-sINITIAL_MEMORY=33MB'])
      self.run_process([EMCC, test_file('hello_world.c'), '-sINITIAL_MEMORY=32MB'])
  
      # A tiny amount is fine in wasm
      self.run_process([EMCC, test_file('hello_world.c'), '-sINITIAL_MEMORY=65536', '-sTOTAL_STACK=1024'])
      # And the program works!
      self.assertContained('hello, world!', self.run_js('a.out.js'))
  
      # Must be a multiple of 64KB
      ret = self.expect_fail([EMCC, test_file('hello_world.c'), '-sINITIAL_MEMORY=33554433']) # 32MB + 1 byte
      self.assertContained('INITIAL_MEMORY must be a multiple of WebAssembly page size (64KiB)', ret)
  
      self.run_process([EMCC, test_file('hello_world.c'), '-sMAXIMUM_MEMORY=33MB', '-sALLOW_MEMORY_GROWTH'])
  
      ret = self.expect_fail([EMCC, test_file('hello_world.c'), '-sMAXIMUM_MEMORY=34603009', '-sALLOW_MEMORY_GROWTH']) # 33MB + 1 byte
      self.assertContained('MAXIMUM_MEMORY must be a multiple of WebAssembly page size (64KiB)', ret)
  
    def test_invalid_memory_max(self):
      err = self.expect_fail([EMCC, '-Werror', test_file('hello_world.c'), '-sMAXIMUM_MEMORY=41943040'])
      self.assertContained('emcc: error: MAXIMUM_MEMORY is only meaningful with ALLOW_MEMORY_GROWTH', err)
  
    def test_dasho_invalid_dir(self):
      ret = self.expect_fail([EMCC, test_file('hello_world.c'), '-o', Path('NONEXISTING_DIRECTORY/out.js')])
      self.assertContained('specified output file (NONEXISTING_DIRECTORY%sout.js) is in a directory that does not exist' % os.path.sep, ret)
  
    def test_dasho_is_dir(self):
      ret = self.expect_fail([EMCC, test_file('hello_world.c'), '-o', '.'])
      self.assertContained('emcc: error: cannot write output file `.`: Is a directory', ret)
  
      ret = self.expect_fail([EMCC, test_file('hello_world.c'), '-o', '.', '--oformat=wasm'])
      self.assertContained('wasm-ld: error: cannot open output file .:', ret)
      # Linux/Mac and Windows's error messages are slightly different
      self.assertContained(['Is a directory', 'is a directory'], ret)
  
      ret = self.expect_fail([EMCC, test_file('hello_world.c'), '-o', '.', '--oformat=html'])
      self.assertContained('emcc: error: cannot write output file:', ret)
      # Linux/Mac and Windows's error codes and messages are different
      self.assertContained(['Is a directory', 'Permission denied'], ret)
  
    def test_binaryen_ctors(self):
      # ctor order must be identical to js builds, deterministically
      create_file('src.cpp', r'''
        #include <stdio.h>
        struct A {
          A() { puts("constructing A!"); }
        };
        A a;
        struct B {
          B() { puts("constructing B!"); }
        };
        B b;
        int main() {}
      ''')
      self.run_process([EMXX, 'src.cpp'])
      correct = self.run_js('a.out.js')
      for args in [[], ['-sRELOCATABLE']]:
        print(args)
        self.run_process([EMXX, 'src.cpp', '-o', 'b.out.js'] + args)
        seen = self.run_js('b.out.js')
        assert correct == seen, correct + '\n vs \n' + seen
  
    # test debug info and debuggability of JS output
    def test_binaryen_debug(self):
      for args, expect_dash_g, expect_emit_text, expect_clean_js, expect_whitespace_js, expect_closured in [
          (['-O0'], False, False, False, True, False),
          (['-O0', '-g1'], False, False, False, True, False),
          (['-O0', '-g2'], True, False, False, True, False), # in -g2+, we emit -g to asm2wasm so function names are saved
          (['-O0', '-g'], True, True, False, True, False),
          (['-O0', '--profiling-funcs'], True, False, False, True, False),
          (['-O1'],        False, False, False, True, False),
          (['-O2'],        False, False, True,  False, False),
          (['-O2', '-g1'], False, False, True,  True, False),
          (['-O2', '-g'],  True,  True,  False, True, False),
          (['-O2', '--closure=1'],         False, False, True, False, True),
          (['-O2', '--closure=1', '-g1'],  False, False, True, True,  True),
        ]:
        print(args, expect_dash_g, expect_emit_text)
        try_delete('a.out.wat')
        cmd = [EMXX, test_file('hello_world.cpp')] + args
        print(' '.join(cmd))
        self.run_process(cmd)
        js = read_file('a.out.js')
        assert expect_clean_js == ('// ' not in js), 'cleaned-up js must not have comments'
        assert expect_whitespace_js == ('{\n  ' in js), 'whitespace-minified js must not have excess spacing'
        assert expect_closured == ('var a;' in js or 'var a,' in js or 'var a=' in js or 'var a ' in js), 'closured js must have tiny variable names'
  
    @uses_canonical_tmp
    def test_binaryen_ignore_implicit_traps(self):
      sizes = []
      with env_modify({'EMCC_DEBUG': '1'}):
        for args, expect in [
            ([], False),
            (['-sBINARYEN_IGNORE_IMPLICIT_TRAPS'], True),
          ]:
          print(args, expect)
          cmd = [EMXX, test_file('hello_libcxx.cpp'), '-O3'] + args
          print(' '.join(cmd))
          err = self.run_process(cmd, stdout=PIPE, stderr=PIPE).stderr
          self.assertContainedIf('--ignore-implicit-traps ', err, expect)
          sizes.append(os.path.getsize('a.out.wasm'))
      print('sizes:', sizes)
      # sizes must be different, as the flag has an impact
      self.assertEqual(len(set(sizes)), 2)
  
    def test_binaryen_passes_extra(self):
      def build(args=[]):
        return self.run_process([EMXX, test_file('hello_world.cpp'), '-O3'] + args, stdout=PIPE).stdout
  
      build()
      base_size = os.path.getsize('a.out.wasm')
      out = build(['-sBINARYEN_EXTRA_PASSES="--metrics"'])
      # and --metrics output appears
      self.assertContained('[funcs]', out)
      # adding --metrics should not affect code size
      self.assertEqual(base_size, os.path.getsize('a.out.wasm'))
  
    def check_expected_size_in_file(self, desc, filename, size):
      if common.EMTEST_REBASELINE:
        with open(filename, 'w') as f:
          f.write(f'{size}\n')
      size_slack = 0.05
      expected_size = int(read_file(filename).strip())
      delta = size - expected_size
      ratio = abs(delta) / float(expected_size)
      print('  seen %s size: %d (expected: %d) (delta: %d), ratio to expected: %f' % (desc, size, expected_size, delta, ratio))
      self.assertLess(ratio, size_slack)
  
    def run_metadce_test(self, filename, args, expected_exists, expected_not_exists, check_size=True,
                         check_sent=True, check_imports=True, check_exports=True, check_funcs=True):
  
      # in -Os, -Oz, we remove imports wasm doesn't need
      print('Running metadce test: %s:' % filename, args, expected_exists,
            expected_not_exists, check_sent, check_imports, check_exports, check_funcs)
      filename = test_file('other/metadce', filename)
  
      def clean_arg(arg):
        return arg.replace('-', '')
  
      def args_to_filename(args):
        result = ''
        for a in args:
          a = a.replace('-s', '')
          a = a.replace('-', '')
          a = a.replace('=1', '')
          a = a.replace('=[]', '_NONE')
          a = a.replace('=', '_')
          if a:
            result += '_' + a
  
        return result
  
      expected_basename = os.path.splitext(filename)[0]
      expected_basename += args_to_filename(args)
  
      self.run_process([compiler_for(filename), filename, '-g2'] + args)
      # find the imports we send from JS
      js = read_file('a.out.js')
      start = js.find('asmLibraryArg = ')
      end = js.find('}', start) + 1
      start = js.find('{', start)
      relevant = js[start + 2:end - 2]
      relevant = relevant.replace(' ', '').replace('"', '').replace("'", '').split(',')
      sent = [x.split(':')[0].strip() for x in relevant]
      sent = [x for x in sent if x]
      sent.sort()
  
      for exists in expected_exists:
        self.assertIn(exists, sent)
      for not_exists in expected_not_exists:
        self.assertNotIn(not_exists, sent)
  
      if check_size:
        # measure the wasm size without the name section
        self.run_process([wasm_opt, 'a.out.wasm', '--strip-debug', '--all-features', '-o', 'a.out.nodebug.wasm'])
        wasm_size = os.path.getsize('a.out.nodebug.wasm')
        size_file = expected_basename + '.size'
        js_size = os.path.getsize('a.out.js')
        js_size_file = expected_basename + '.jssize'
        self.check_expected_size_in_file('wasm', size_file, wasm_size)
        self.check_expected_size_in_file('js', js_size_file, js_size)
  
      imports, exports, funcs = parse_wasm('a.out.wasm')
      imports.sort()
      exports.sort()
      funcs.sort()
  
      # filter out _NNN suffixed that can be the result of bitcode linking when
      # internal symbol names collide.
      def strip_numeric_suffixes(funcname):
        parts = funcname.split('_')
        while parts:
          if parts[-1].isdigit():
            parts.pop()
          else:
            break
        return '_'.join(parts)
  
      funcs = [strip_numeric_suffixes(f) for f in funcs]
  
      if check_sent:
        sent_file = expected_basename + '.sent'
        sent_data = '\n'.join(sent) + '\n'
        self.assertFileContents(sent_file, sent_data)
  
      if check_imports:
        filename = expected_basename + '.imports'
        data = '\n'.join(imports) + '\n'
        self.assertFileContents(filename, data)
  
      if check_exports:
        filename = expected_basename + '.exports'
        data = '\n'.join(exports) + '\n'
        self.assertFileContents(filename, data)
  
      if check_funcs:
        filename = expected_basename + '.funcs'
        data = '\n'.join(funcs) + '\n'
        self.assertFileContents(filename, data)
  
    @parameterized({
      'O0': ([],      [], ['waka']), # noqa
      'O1': (['-O1'], [], ['waka']), # noqa
      'O2': (['-O2'], [], ['waka']), # noqa
      # in -O3, -Os and -Oz we metadce, and they shrink it down to the minimal output we want
      'O3': (['-O3'], [], []), # noqa
      'Os': (['-Os'], [], []), # noqa
      'Oz': (['-Oz'], [], []), # noqa
      'Os_mr': (['-Os', '-sMINIMAL_RUNTIME'], [], [], 74), # noqa
      # EVAL_CTORS also removes the __wasm_call_ctors function
      'Oz-ctors': (['-Oz', '-sEVAL_CTORS'], [], []), # noqa
    })
    def test_metadce_minimal(self, *args):
      self.run_metadce_test('minimal.c', *args)
  
    @node_pthreads
    def test_metadce_minimal_pthreads(self):
      self.run_metadce_test('minimal_main.c', ['-Oz', '-sUSE_PTHREADS', '-sPROXY_TO_PTHREAD'], [], [])
  
    @parameterized({
      'noexcept': (['-O2'],                    [], ['waka']), # noqa
      # exceptions increases code size significantly
      'except':   (['-O2', '-fexceptions'],    [], ['waka']), # noqa
      # exceptions does not pull in demangling by default, which increases code size
      'mangle':   (['-O2', '-fexceptions',
                    '-sDEMANGLE_SUPPORT'], [], ['waka']), # noqa
      # eval_ctors 1 can partially optimize, but runs into getenv() for locale
      # code. mode 2 ignores those and fully optimizes out the ctors
      'ctors1':    (['-O2', '-sEVAL_CTORS'],   [], ['waka']), # noqa
      'ctors2':    (['-O2', '-sEVAL_CTORS=2'], [], ['waka']), # noqa
    })
    def test_metadce_cxx(self, *args):
      # do not check functions in this test as there are a lot of libc++ functions
      # pulled in here, and small LLVM backend changes can affect their size and
      # lead to different inlining decisions which add or remove a function
      self.run_metadce_test('hello_libcxx.cpp', *args, check_funcs=False)
  
    @parameterized({
      'O0': ([],      [], ['waka']), # noqa
      'O1': (['-O1'], [], ['waka']), # noqa
      'O2': (['-O2'], [], ['waka']), # noqa
      'O3': (['-O3'], [], []), # noqa; in -O3, -Os and -Oz we metadce
      'Os': (['-Os'], [], []), # noqa
      'Oz': (['-Oz'], [], []), # noqa
      # finally, check what happens when we export nothing. wasm should be almost empty
      'export_nothing':
            (['-Os', '-sEXPORTED_FUNCTIONS=[]'],    [], []), # noqa
      # we don't metadce with linkable code! other modules may want stuff
      # TODO(sbc): Investivate why the number of exports is order of magnitude
      # larger for wasm backend.
      'main_module_2': (['-O3', '-sMAIN_MODULE=2'], [], []), # noqa
    })
    def test_metadce_hello(self, *args):
      self.run_metadce_test('hello_world.cpp', *args)
  
    @parameterized({
      'O3':                 ('mem.c', ['-O3'],
                             [], []),         # noqa
      # argc/argv support code etc. is in the wasm
      'O3_standalone':      ('mem.c', ['-O3', '-sSTANDALONE_WASM'],
                             [], []),         # noqa
      # without argc/argv, no support code for them is emitted
      'O3_standalone_narg': ('mem_no_argv.c', ['-O3', '-sSTANDALONE_WASM'],
                             [], []),         # noqa
      # without main, no support code for argc/argv is emitted either
      'O3_standalone_lib':  ('mem_no_main.c', ['-O3', '-sSTANDALONE_WASM', '--no-entry'],
                             [], []),         # noqa
      # Growth support code is in JS, no significant change in the wasm
      'O3_grow':            ('mem.c', ['-O3', '-sALLOW_MEMORY_GROWTH'],
                             [], []),         # noqa
      # Growth support code is in the wasm
      'O3_grow_standalone': ('mem.c', ['-O3', '-sALLOW_MEMORY_GROWTH', '-sSTANDALONE_WASM'],
                             [], []),         # noqa
      # without argc/argv, no support code for them is emitted, even with lto
      'O3_standalone_narg_flto':
                            ('mem_no_argv.c', ['-O3', '-sSTANDALONE_WASM', '-flto'],
                             [], []),         # noqa
    })
    def test_metadce_mem(self, filename, *args):
      self.run_metadce_test(filename, *args)
  
    @parameterized({
      'O3':                 ('libcxxabi_message.cpp', ['-O3'],
                             [], []), # noqa
      # argc/argv support code etc. is in the wasm
      'O3_standalone':      ('libcxxabi_message.cpp', ['-O3', '-sSTANDALONE_WASM'],
                             [], []), # noqa
    })
    def test_metadce_libcxxabi_message(self, filename, *args):
      self.run_metadce_test(filename, *args)
  
    # ensures runtime exports work, even with metadce
    @parameterized({
      '': (False,),
      'legacy': (True,)
    })
    def test_exported_runtime_methods_metadce(self, use_legacy_name):
      exports = ['stackSave', 'stackRestore', 'stackAlloc', 'FS']
      setting_name = 'EXPORTED_RUNTIME_METHODS'
      if use_legacy_name:
        setting_name = 'EXTRA_EXPORTED_RUNTIME_METHODS'
      err = self.run_process([EMXX, test_file('hello_world.cpp'), '-Os', '-s%s=%s' % (setting_name, ','.join(exports))], stderr=PIPE).stderr
      if use_legacy_name:
        self.assertContained('warning: EXTRA_EXPORTED_RUNTIME_METHODS is deprecated, please use EXPORTED_RUNTIME_METHODS instead [-Wdeprecated]', err)
      js = read_file('a.out.js')
      for export in exports:
        self.assertContained(f'Module["{export}"]', js)
  
    def test_legalize_js_ffi(self):
      # test disabling of JS FFI legalization
      for (args, js_ffi) in [
          (['-sLEGALIZE_JS_FFI=1', '-sSIDE_MODULE', '-O1', '-sEXPORT_ALL'], True),
          (['-sLEGALIZE_JS_FFI=0', '-sSIDE_MODULE', '-O1', '-sEXPORT_ALL'], False),
          (['-sLEGALIZE_JS_FFI=0', '-sSIDE_MODULE', '-O0', '-sEXPORT_ALL'], False),
          (['-sLEGALIZE_JS_FFI=0', '-sWARN_ON_UNDEFINED_SYMBOLS=0', '-O0'], False),
        ]:
        if '-sSIDE_MODULE' in args:
          continue
        print(args)
        try_delete('a.out.wasm')
        try_delete('a.out.wat')
        cmd = [EMCC, test_file('other/ffi.c'), '-g', '-o', 'a.out.wasm'] + args
        print(' '.join(cmd))
        self.run_process(cmd)
        self.run_process([wasm_dis, 'a.out.wasm', '-o', 'a.out.wat'])
        text = read_file('a.out.wat')
        # remove internal comments and extra whitespace
        text = re.sub(r'\(;[^;]+;\)', '', text)
        text = re.sub(r'\$var\$*.', '', text)
        text = re.sub(r'param \$\d+', 'param ', text)
        text = re.sub(r' +', ' ', text)
        # TODO: remove the unecessary ".*" in e_* regexs after binaryen #2510 lands
        e_add_f32 = re.search(r'func \$_?add_f .*\(param f32\) \(param f32\) \(result f32\)', text)
        i_i64_i32 = re.search(r'import .*"_?import_ll" .*\(param i32 i32\) \(result i32\)', text)
        i_f32_f64 = re.search(r'import .*"_?import_f" .*\(param f64\) \(result f64\)', text)
        i_i64_i64 = re.search(r'import .*"_?import_ll" .*\(param i64\) \(result i64\)', text)
        i_f32_f32 = re.search(r'import .*"_?import_f" .*\(param f32\) \(result f32\)', text)
        e_i64_i32 = re.search(r'func \$_?add_ll .*\(param i32\) \(param i32\) \(param i32\) \(param i32\) \(result i32\)', text)
        e_f32_f64 = re.search(r'func \$legalstub\$_?add_f .*\(param f64\) \(param f64\) \(result f64\)', text)
        e_i64_i64 = re.search(r'func \$_?add_ll .*\(param i64\) \(param i64\) \(result i64\)', text)
        assert e_add_f32, 'add_f export missing'
        if js_ffi:
          assert i_i64_i32,     'i64 not converted to i32 in imports'
          assert i_f32_f64,     'f32 not converted to f64 in imports'
          assert not i_i64_i64, 'i64 not converted to i32 in imports'
          assert not i_f32_f32, 'f32 not converted to f64 in imports'
          assert e_i64_i32,     'i64 not converted to i32 in exports'
          assert not e_f32_f64, 'f32 not converted to f64 in exports'
          assert not e_i64_i64, 'i64 not converted to i64 in exports'
        else:
          assert not i_i64_i32, 'i64 converted to i32 in imports'
          assert not i_f32_f64, 'f32 converted to f64 in imports'
          assert i_i64_i64,     'i64 converted to i32 in imports'
          assert i_f32_f32,     'f32 converted to f64 in imports'
          assert not e_i64_i32, 'i64 converted to i32 in exports'
          assert not e_f32_f64, 'f32 converted to f64 in exports'
          assert e_i64_i64,     'i64 converted to i64 in exports'
  
    def test_no_legalize_js_ffi(self):
      # test minimal JS FFI legalization for invoke and dyncalls
      for (args, js_ffi) in [
          (['-sLEGALIZE_JS_FFI=0', '-sMAIN_MODULE=2', '-O3', '-sDISABLE_EXCEPTION_CATCHING=0'], False),
        ]:
        print(args)
        try_delete('a.out.wasm')
        try_delete('a.out.wat')
        with env_modify({'EMCC_FORCE_STDLIBS': 'libc++'}):
          cmd = [EMXX, test_file('other/noffi.cpp'), '-g', '-o', 'a.out.js'] + args
        print(' '.join(cmd))
        self.run_process(cmd)
        self.run_process([wasm_dis, 'a.out.wasm', '-o', 'a.out.wat'])
        text = read_file('a.out.wat')
        # remove internal comments and extra whitespace
        text = re.sub(r'\(;[^;]+;\)', '', text)
        text = re.sub(r'\$var\$*.', '', text)
        text = re.sub(r'param \$\d+', 'param ', text)
        text = re.sub(r' +', ' ', text)
        # print("text: %s" % text)
        i_legalimport_i64 = re.search(r'\(import.*\$legalimport\$invoke_j.*', text)
        e_legalstub_i32 = re.search(r'\(func.*\$legalstub\$dyn.*\(result i32\)', text)
        assert i_legalimport_i64, 'legal import not generated for invoke call'
        assert e_legalstub_i32, 'legal stub not generated for dyncall'
  
    def test_export_aliasee(self):
      # build side module
      args = ['-sSIDE_MODULE']
      cmd = [EMCC, test_file('other/alias/side.c'), '-g', '-o', 'side.wasm'] + args
      print(' '.join(cmd))
      self.run_process(cmd)
  
      # build main module
      args = ['-g', '-sEXPORTED_FUNCTIONS=_main,_foo', '-sMAIN_MODULE=2', '-sEXIT_RUNTIME', '-lnodefs.js']
      cmd = [EMCC, test_file('other/alias/main.c'), '-o', 'main.js'] + args
      print(' '.join(cmd))
      self.run_process(cmd)
  
      # run the program
      self.assertContained('success', self.run_js('main.js'))
  
    def test_sysconf_phys_pages(self):
      def run(args, expected):
        cmd = [EMCC, test_file('unistd/sysconf_phys_pages.c')] + args
        print(str(cmd))
        self.run_process(cmd)
        result = self.run_js('a.out.js').strip()
        self.assertEqual(result, f'{expected}, errno: 0')
  
      run([], 256)
      run(['-sINITIAL_MEMORY=32MB'], 512)
      run(['-sINITIAL_MEMORY=32MB', '-sALLOW_MEMORY_GROWTH'], (2 * 1024 * 1024 * 1024) // webassembly.WASM_PAGE_SIZE)
      run(['-sINITIAL_MEMORY=32MB', '-sALLOW_MEMORY_GROWTH', '-sWASM=0'], (2 * 1024 * 1024 * 1024) // webassembly.WASM_PAGE_SIZE)
  
    def test_wasm_target_and_STANDALONE_WASM(self):
      # STANDALONE_WASM means we never minify imports and exports.
      for opts, potentially_expect_minified_exports_and_imports in (
        ([],                               False),
        (['-sSTANDALONE_WASM'],        False),
        (['-O2'],                          False),
        (['-O3'],                          True),
        (['-O3', '-sSTANDALONE_WASM'], False),
        (['-Os'],                          True),
      ):
        # targeting .wasm (without .js) means we enable STANDALONE_WASM automatically, and don't minify imports/exports
        for target in ('out.js', 'out.wasm'):
          expect_minified_exports_and_imports = potentially_expect_minified_exports_and_imports and target.endswith('.js')
          standalone = target.endswith('.wasm') or 'STANDALONE_WASM' in opts
          print(opts, potentially_expect_minified_exports_and_imports, target, ' => ', expect_minified_exports_and_imports, standalone)
  
          self.clear()
          self.run_process([EMXX, test_file('hello_world.cpp'), '-o', target] + opts)
          self.assertExists('out.wasm')
          if target.endswith('.wasm'):
            # only wasm requested
            self.assertNotExists('out.js')
          wat = self.run_process([wasm_dis, 'out.wasm'], stdout=PIPE).stdout
          wat_lines = wat.split('\n')
          exports = [line.strip().split(' ')[1].replace('"', '') for line in wat_lines if "(export " in line]
          imports = [line.strip().split(' ')[2].replace('"', '') for line in wat_lines if "(import " in line]
          exports_and_imports = exports + imports
          print('  exports', exports)
          print('  imports', imports)
          if expect_minified_exports_and_imports:
            assert 'a' in exports_and_imports
          else:
            assert 'a' not in exports_and_imports
          if standalone:
            assert 'fd_write' in exports_and_imports, 'standalone mode preserves import names for WASI APIs'
          # verify the wasm runs with the JS
          if target.endswith('.js'):
            self.assertContained('hello, world!', self.run_js('out.js'))
          # verify a standalone wasm
          if standalone:
            for engine in config.WASM_ENGINES:
              print(engine)
              self.assertContained('hello, world!', self.run_js('out.wasm', engine=engine))
  
    def test_side_module_naming(self):
      # SIDE_MODULE should work with any arbirary filename
      for opts, target in [([], 'a.out.wasm'),
                           (['-o', 'lib.wasm'], 'lib.wasm'),
                           (['-o', 'lib.so'], 'lib.so'),
                           (['-o', 'foo.bar'], 'foo.bar')]:
        # specified target
        print('building: ' + target)
        self.clear()
        self.run_process([EMCC, test_file('hello_world.c'), '-sSIDE_MODULE', '-Werror'] + opts)
        for x in os.listdir('.'):
          self.assertFalse(x.endswith('.js'))
        self.assertTrue(building.is_wasm_dylib(target))
  
        create_file('main.c', '')
        self.run_process([EMCC, '-sMAIN_MODULE=2', 'main.c', '-Werror', target])
        self.run_js('a.out.js')
  
    def test_side_module_missing(self):
      self.run_process([EMCC, test_file('hello_world.c'), '-sSIDE_MODULE', '-o', 'libside1.wasm'])
      self.run_process([EMCC, test_file('hello_world.c'), '-sSIDE_MODULE', '-o', 'libside2.wasm', 'libside1.wasm'])
      # When linking against `libside2.wasm` (which depends on libside1.wasm) that library path is used
      # to locate `libside1.wasm`.  Expect the link to fail with an unmodified library path.
      err = self.expect_fail([EMCC, '-sMAIN_MODULE=2', test_file('hello_world.c'), 'libside2.wasm'])
      self.assertContained('libside2.wasm: shared library dependency not found: `libside1.wasm`', err)
  
      # But succeed if `.` is added the library path.
      self.run_process([EMCC, '-sMAIN_MODULE=2', test_file('hello_world.c'), '-L.', 'libside2.wasm'])
  
    def test_side_module_transitive_deps(self):
      # Build three side modules in a dependency chain
      self.run_process([EMCC, test_file('hello_world.c'), '-sSIDE_MODULE', '-o', 'libside1.wasm'])
      self.run_process([EMCC, test_file('hello_world.c'), '-sSIDE_MODULE', '-o', 'libside2.wasm', 'libside1.wasm'])
      self.run_process([EMCC, test_file('hello_world.c'), '-sSIDE_MODULE', '-o', 'libside3.wasm', 'libside2.wasm'])
  
      # Link should succeed if and only if the end of the chain can be found
      final_link = [EMCC, '-sMAIN_MODULE=2', test_file('hello_world.c'), '-L.', 'libside3.wasm']
      self.run_process(final_link)
      os.remove('libside1.wasm')
      err = self.expect_fail(final_link)
      self.assertContained('error: libside2.wasm: shared library dependency not found: `libside1.wasm`', err)
  
    def test_side_module_folder_deps(self):
      # Build side modules in a subfolder
      os.mkdir('subdir')
      self.run_process([EMCC, test_file('hello_world.c'), '-sSIDE_MODULE', '-o', 'subdir/libside1.so'])
      self.run_process([EMCC, test_file('hello_world.c'), '-sSIDE_MODULE', '-o', 'subdir/libside2.so', '-L', 'subdir', '-lside1'])
      self.run_process([EMCC, test_file('hello_world.c'), '-sMAIN_MODULE', '-o', 'main.js', '-L', 'subdir', '-lside2'])
  
    @is_slow_test
    def test_lto(self):
      # test building of non-wasm-object-files libraries, building with them, and running them
  
      src = test_file('hello_libcxx.cpp')
      # test codegen in lto mode, and compare to normal (wasm object) mode
      for args in [[], ['-O1'], ['-O2'], ['-O3'], ['-Os'], ['-Oz']]:
        print(args)
  
        print('wasm in object')
        self.run_process([EMXX, src] + args + ['-c', '-o', 'hello_obj.o'])
        self.assertTrue(building.is_wasm('hello_obj.o'))
        self.assertFalse(building.is_bitcode('hello_obj.o'))
  
        print('bitcode in object')
        self.run_process([EMXX, src] + args + ['-c', '-o', 'hello_bitcode.o', '-flto'])
        self.assertFalse(building.is_wasm('hello_bitcode.o'))
        self.assertTrue(building.is_bitcode('hello_bitcode.o'))
  
        print('use bitcode object (LTO)')
        self.run_process([EMXX, 'hello_bitcode.o'] + args + ['-flto'])
        self.assertContained('hello, world!', self.run_js('a.out.js'))
        print('use bitcode object (non-LTO)')
        self.run_process([EMXX, 'hello_bitcode.o'] + args)
        self.assertContained('hello, world!', self.run_js('a.out.js'))
  
        print('use native object (LTO)')
        self.run_process([EMXX, 'hello_obj.o'] + args + ['-flto'])
        self.assertContained('hello, world!', self.run_js('a.out.js'))
        print('use native object (non-LTO)')
        self.run_process([EMXX, 'hello_obj.o'] + args)
        self.assertContained('hello, world!', self.run_js('a.out.js'))
  
    @parameterized({
      'noexcept': [],
      'except': ['-sDISABLE_EXCEPTION_CATCHING=0']
    })
    def test_lto_libcxx(self, *args):
      self.run_process([EMXX, test_file('hello_libcxx.cpp'), '-flto'] + list(args))
  
    def test_lto_flags(self):
      for flags, expect_bitcode in [
        ([], False),
        (['-flto'], True),
        (['-flto=thin'], True),
        (['-sWASM_OBJECT_FILES=0'], True),
        (['-sWASM_OBJECT_FILES'], False),
      ]:
        self.run_process([EMXX, test_file('hello_world.cpp')] + flags + ['-c', '-o', 'a.o'])
        seen_bitcode = building.is_bitcode('a.o')
        self.assertEqual(expect_bitcode, seen_bitcode, 'must emit LTO-capable bitcode when flags indicate so (%s)' % str(flags))
  
    # We have LTO tests covered in 'wasmltoN' targets in test_core.py, but they
    # don't run as a part of Emscripten CI, so we add a separate LTO test here.
    @require_v8
    def test_lto_wasm_exceptions(self):
      self.set_setting('EXCEPTION_DEBUG')
      self.emcc_args += ['-fwasm-exceptions', '-flto']
      self.v8_args.append('--experimental-wasm-eh')
      self.do_run_from_file(test_file('core/test_exceptions.cpp'), test_file('core/test_exceptions_caught.out'))
  
    def test_wasm_nope(self):
      for opts in [[], ['-O2']]:
        print(opts)
        # check we show a good error message if there is no wasm support
        create_file('pre.js', 'WebAssembly = undefined;\n')
        self.run_process([EMXX, test_file('hello_world.cpp'), '--pre-js', 'pre.js'] + opts)
        out = self.run_js('a.out.js', assert_returncode=NON_ZERO)
        self.assertContained('no native wasm support detected', out)
  
    @require_node
    def test_jsrun(self):
      print(config.NODE_JS)
      jsrun.WORKING_ENGINES = {}
      # Test that engine check passes
      self.assertTrue(jsrun.check_engine(config.NODE_JS))
      # Run it a second time (cache hit)
      self.assertTrue(jsrun.check_engine(config.NODE_JS))
  
      # Test that engine check fails
      bogus_engine = ['/fake/inline4']
      self.assertFalse(jsrun.check_engine(bogus_engine))
      self.assertFalse(jsrun.check_engine(bogus_engine))
  
      # Test the other possible way (list vs string) to express an engine
      if type(config.NODE_JS) is list:
        engine2 = config.NODE_JS[0]
      else:
        engine2 = [config.NODE_JS]
      self.assertTrue(jsrun.check_engine(engine2))
  
      # Test that self.run_js requires the engine
      self.run_js(test_file('hello_world.js'), config.NODE_JS)
      caught_exit = 0
      try:
        self.run_js(test_file('hello_world.js'), bogus_engine)
      except SystemExit as e:
        caught_exit = e.code
      self.assertEqual(1, caught_exit, 'Did not catch SystemExit with bogus JS engine')
  
    def test_error_on_missing_libraries(self):
      # -llsomenonexistingfile is an error by default
      err = self.expect_fail([EMXX, test_file('hello_world.cpp'), '-lsomenonexistingfile'])
      self.assertContained('wasm-ld: error: unable to find library -lsomenonexistingfile', err)
  
    # Tests that if user accidentally attempts to link native object code, we show an error
    def test_native_link_error_message(self):
      self.run_process([CLANG_CC, '-c', test_file('hello_123.c'), '-o', 'hello_123.o'])
      err = self.expect_fail([EMCC, 'hello_123.o', '-o', 'hello_123.js'])
      self.assertContained('unknown file type: hello_123.o', err)
  
    # Tests that we should give a clear error on INITIAL_MEMORY not being enough for static initialization + stack
    def test_clear_error_on_massive_static_data(self):
      with open('src.cpp', 'w') as f:
        f.write('''
          char muchData[128 * 1024];
          int main() {
            return (int)(long)&muchData;
          }
        ''')
      err = self.expect_fail([EMXX, 'src.cpp', '-sTOTAL_STACK=1KB', '-sINITIAL_MEMORY=64KB'])
      self.assertContained('wasm-ld: error: initial memory too small', err)
  
    def test_o_level_clamp(self):
      for level in [3, 4, 20]:
        err = self.run_process([EMCC, '-O' + str(level), test_file('hello_world.c')], stderr=PIPE).stderr
        self.assertContainedIf("optimization level '-O" + str(level) + "' is not supported; using '-O3' instead", err, level > 3)
  
    # Tests that if user specifies multiple -o output directives, then the last one will take precedence
    def test_multiple_o_files(self):
      self.run_process([EMCC, test_file('hello_world.c'), '-o', 'a.js', '-o', 'b.js'])
      assert os.path.isfile('b.js')
      assert not os.path.isfile('a.js')
  
    # Tests that Emscripten-provided header files can be cleanly included standalone.
    # Also check they can be included in C code (where possible).
    @is_slow_test
    def test_standalone_system_headers(self):
      # Test oldest C standard, and the default C standard
      # This also tests that each header file is self contained and includes
      # everything it needs.
      directories = {'': []}
      for elem in os.listdir(path_from_root('system/include')):
        if elem == 'compat':
          continue
        full = path_from_root('system/include', elem)
        if os.path.isdir(full):
          directories[elem] = os.listdir(full)
        else:
          directories[''].append(elem)
  
      for directory, headers in directories.items():
        print('dir: ' + directory)
        for header in headers:
          if not header.endswith('.h'):
            continue
          print('header: ' + header)
          # These headers cannot be included in isolation.
          # e.g: error: unknown type name 'EGLDisplay'
          if header in ['eglext.h', 'SDL_config_macosx.h', 'glext.h', 'gl2ext.h']:
            continue
          # These headers are C++ only and cannot be included from C code.
          # But we still want to check they can be included on there own without
          # any errors or warnings.
          cxx_only = header in ['wire.h', 'val.h', 'bind.h', 'webgpu_cpp.h']
          if directory:
            header = f'{directory}/{header}'
          inc = f'#include <{header}>\n__attribute__((weak)) int foo;\n'
          if cxx_only:
            create_file('a.cxx', inc)
            create_file('b.cxx', inc)
            self.run_process([EMXX, '-Werror', '-Wall', '-pedantic', 'a.cxx', 'b.cxx'])
          else:
            create_file('a.c', inc)
            create_file('b.c', inc)
            for std in [[], ['-std=c89']]:
              self.run_process([EMCC] + std + ['-Werror', '-Wall', '-pedantic', 'a.c', 'b.c'])
  
    @is_slow_test
    def test_single_file(self):
      for (single_file_enabled,
           meminit1_enabled,
           debug_enabled,
           closure_enabled,
           wasm_enabled) in itertools.product([True, False], repeat=5):
        # skip unhelpful option combinations
        if wasm_enabled and meminit1_enabled:
          continue
        if closure_enabled and debug_enabled:
          continue
  
        expect_wasm = wasm_enabled
        expect_meminit = meminit1_enabled and not wasm_enabled
  
        cmd = [EMCC, test_file('hello_world.c')]
  
        if single_file_enabled:
          expect_meminit = False
          expect_wasm = False
          cmd += ['-sSINGLE_FILE']
        if meminit1_enabled:
          cmd += ['--memory-init-file', '1']
        if debug_enabled:
          cmd += ['-g']
        if closure_enabled:
          cmd += ['--closure=1']
        if not wasm_enabled:
          cmd += ['-sWASM=0']
  
        self.clear()
  
        def do_test(cmd):
          print(' '.join(cmd))
          self.run_process(cmd)
          print(os.listdir('.'))
          assert expect_meminit == (os.path.exists('a.out.mem') or os.path.exists('a.out.js.mem'))
          assert expect_wasm == os.path.exists('a.out.wasm')
          assert not os.path.exists('a.out.wat')
          self.assertContained('hello, world!', self.run_js('a.out.js'))
  
        do_test(cmd)
  
        # additional combinations that are not part of the big product()
  
        if debug_enabled:
          separate_dwarf_cmd = cmd + ['-gseparate-dwarf']
          if wasm_enabled:
            do_test(separate_dwarf_cmd)
            self.assertExists('a.out.wasm.debug.wasm')
          else:
            self.expect_fail(separate_dwarf_cmd)
  
    def test_emar_M(self):
      create_file('file1', ' ')
      create_file('file2', ' ')
      self.run_process([EMAR, 'cr', 'file1.a', 'file1'])
      self.run_process([EMAR, 'cr', 'file2.a', 'file2'])
      self.run_process([EMAR, '-M'], input='''create combined.a
  addlib file1.a
  addlib file2.a
  save
  end
  ''')
      result = self.run_process([EMAR, 't', 'combined.a'], stdout=PIPE).stdout
      self.assertContained('file1', result)
      self.assertContained('file2', result)
  
    def test_emar_duplicate_inputs(self):
      # Verify the we can supply the same intput muliple times without
      # confusing emar.py:
      # See https://github.com/emscripten-core/emscripten/issues/9733
      create_file('file1', ' ')
      self.run_process([EMAR, 'cr', 'file1.a', 'file1', 'file1'])
  
    def test_emar_response_file(self):
      # Test that special character such as single quotes in filenames survive being
      # sent via response file
      create_file("file'1", ' ')
      create_file("file'2", ' ')
      create_file("hyvää päivää", ' ')
      create_file("snowman freezes covid ☃ 🦠", ' ')
      rsp = response_file.create_response_file(("file'1", "file'2", "hyvää päivää", "snowman freezes covid ☃ 🦠"), shared.TEMP_DIR)
      building.emar('cr', 'libfoo.a', ['@' + rsp])
  
    def test_archive_empty(self):
      # This test added because we had an issue with the AUTO_ARCHIVE_INDEXES failing on empty
      # archives (which inherently don't have indexes).
      self.run_process([EMAR, 'crS', 'libfoo.a'])
      self.run_process([EMCC, '-Werror', 'libfoo.a', test_file('hello_world.c')])
  
    def test_archive_no_index(self):
      create_file('foo.c', 'int foo = 1;')
      self.run_process([EMCC, '-c', 'foo.c'])
      self.run_process([EMCC, '-c', test_file('hello_world.c')])
      # The `S` flag means don't add an archive index
      self.run_process([EMAR, 'crS', 'libfoo.a', 'foo.o'])
      # The llvm backend (link GNU ld and lld) doesn't support linking archives with no index.
      # However we have logic that will automatically add indexes (unless running with
      # NO_AUTO_ARCHIVE_INDEXES).
      stderr = self.expect_fail([EMCC, '-sNO_AUTO_ARCHIVE_INDEXES', 'libfoo.a', 'hello_world.o'])
      self.assertContained('libfoo.a: archive has no index; run ranlib to add one', stderr)
      # The default behavior is to add archive indexes automatically.
      self.run_process([EMCC, 'libfoo.a', 'hello_world.o'])
  
    def test_archive_non_objects(self):
      create_file('file.txt', 'test file')
      self.run_process([EMCC, '-c', test_file('hello_world.c')])
      # No index added.
      # --format=darwin (the default on OSX has a strange issue where it add extra
      # newlines to files: https://bugs.llvm.org/show_bug.cgi?id=42562
      self.run_process([EMAR, 'crS', '--format=gnu', 'libfoo.a', 'file.txt', 'hello_world.o'])
      self.run_process([EMCC, test_file('hello_world.c'), 'libfoo.a'])
  
    def test_archive_thin(self):
      self.run_process([EMCC, '-c', test_file('hello_world.c')])
      # The `T` flag means "thin"
      self.run_process([EMAR, 'crT', 'libhello.a', 'hello_world.o'])
      self.run_process([EMCC, 'libhello.a'])
      self.assertContained('hello, world!', self.run_js('a.out.js'))
  
    def test_flag_aliases(self):
      def assert_aliases_match(flag1, flag2, flagarg, extra_args=[]):
        results = {}
        for f in (flag1, flag2):
          self.run_process([EMCC, test_file('hello_world.c'), '-s', f + '=' + flagarg] + extra_args)
          results[f + '.js'] = read_file('a.out.js')
          results[f + '.wasm'] = read_binary('a.out.wasm')
        self.assertEqual(results[flag1 + '.js'], results[flag2 + '.js'], 'js results should be identical')
        self.assertEqual(results[flag1 + '.wasm'], results[flag2 + '.wasm'], 'wasm results should be identical')
  
      assert_aliases_match('INITIAL_MEMORY', 'TOTAL_MEMORY', '16777216')
      assert_aliases_match('INITIAL_MEMORY', 'TOTAL_MEMORY', '64MB')
      assert_aliases_match('MAXIMUM_MEMORY', 'WASM_MEM_MAX', '16777216', ['-sALLOW_MEMORY_GROWTH'])
      assert_aliases_match('MAXIMUM_MEMORY', 'BINARYEN_MEM_MAX', '16777216', ['-sALLOW_MEMORY_GROWTH'])
  
    def test_IGNORE_CLOSURE_COMPILER_ERRORS(self):
      create_file('pre.js', r'''
        // make closure compiler very very angry
        var dupe = 1;
        var dupe = 2;
        function Node() {
          throw 'Node is a DOM thing too, and use the ' + dupe;
        }
        function Node() {
          throw '(duplicate) Node is a DOM thing too, and also use the ' + dupe;
        }
      ''')
  
      def test(check, extra=[]):
        cmd = [EMCC, test_file('hello_world.c'), '-O2', '--closure=1', '--pre-js', 'pre.js'] + extra
        proc = self.run_process(cmd, check=check, stderr=PIPE)
        if not check:
          self.assertNotEqual(proc.returncode, 0)
        return proc
  
      WARNING = 'Variable dupe declared more than once'
  
      proc = test(check=False)
      self.assertContained(WARNING, proc.stderr)
      proc = test(check=True, extra=['-sIGNORE_CLOSURE_COMPILER_ERRORS'])
      self.assertNotContained(WARNING, proc.stderr)
  
    def test_full_js_library(self):
      self.run_process([EMCC, test_file('hello_world.c'), '-sSTRICT_JS', '-sINCLUDE_FULL_LIBRARY'])
  
    def test_full_js_library_undefined(self):
      create_file('main.c', 'void foo(); int main() { foo(); return 0; }')
      err = self.expect_fail([EMCC, 'main.c', '-sSTRICT_JS', '-sINCLUDE_FULL_LIBRARY'])
      self.assertContained('error: undefined symbol: foo', err)
  
    def test_full_js_library_except(self):
      self.set_setting('INCLUDE_FULL_LIBRARY', 1)
      self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
      self.do_other_test('test_full_js_library_except.cpp')
  
    def test_full_js_library_gl_emu(self):
      self.run_process([EMCC, test_file('hello_world.c'), '-sSTRICT_JS', '-sINCLUDE_FULL_LIBRARY', '-sLEGACY_GL_EMULATION'])
  
    def test_full_js_library_no_exception_throwing(self):
      self.run_process([EMCC, test_file('hello_world.c'), '-sSTRICT_JS', '-sINCLUDE_FULL_LIBRARY', '-sDISABLE_EXCEPTION_THROWING'])
  
    def test_full_js_library_minimal_runtime(self):
      self.run_process([EMCC, test_file('hello_world.c'), '-sSTRICT_JS', '-sINCLUDE_FULL_LIBRARY', '-sMINIMAL_RUNTIME'])
  
    def test_closure_full_js_library(self):
      # test for closure errors in the entire JS library
      # We must ignore various types of errors that are expected in this situation, as we
      # are including a lot of JS without corresponding compiled code for it. This still
      # lets us catch all other errors.
  
      # USE_WEBGPU is specified here to make sure that it's closure-safe.
      # It can be removed if USE_WEBGPU is later included in INCLUDE_FULL_LIBRARY.
      self.run_process([EMCC, test_file('hello_world.c'), '-O1', '--closure=1', '-g1', '-sINCLUDE_FULL_LIBRARY', '-sUSE_WEBGPU', '-sERROR_ON_UNDEFINED_SYMBOLS=0'])
  
    # Tests --closure-args command line flag
    def test_closure_externs(self):
      # Test with relocate path to the externs file to ensure that incoming relative paths
      # are translated correctly (Since closure runs with a different CWD)
      shutil.copyfile(test_file('test_closure_externs.js'), 'local_externs.js')
      self.run_process([EMCC, test_file('hello_world.c'),
                        '--closure=1',
                        '--pre-js', test_file('test_closure_externs_pre_js.js'),
                        '--closure-args', '--externs "local_externs.js"'])
  
    # Tests that it is possible to enable the Closure compiler via --closure=1 even if any of the input files reside in a path with unicode characters.
    def test_closure_cmdline_utf8_chars(self):
      test = "☃ äö Ć € ' 🦠.c"
      shutil.copyfile(test_file('hello_world.c'), test)
      externs = '💩' + test
      create_file(externs, '')
      self.run_process([EMCC, test, '--closure=1', '--closure-args', '--externs "' + externs + '"'])
  
    @with_env_modify({'EMPROFILE': '1'})
    def test_toolchain_profiler(self):
      # Verify some basic functionality of EMPROFILE
      self.run_process([emprofile, '--reset'])
      err = self.expect_fail([emprofile, '--graph'])
      self.assertContained('No profiler logs were found', err)
  
      self.run_process([EMCC, test_file('hello_world.c')])
      self.assertEqual('hello, world!', self.run_js('a.out.js').strip())
  
      self.run_process([emprofile, '--graph'])
      self.assertTrue(glob.glob('toolchain_profiler.results*.html'))
  
    @with_env_modify({'EMPROFILE': '2'})
    def test_toolchain_profiler_stderr(self):
      stderr = self.run_process([EMCC, test_file('hello_world.c')], stderr=PIPE).stderr
      self.assertContained('start block "main"', stderr)
      self.assertContained('block "main" took', stderr)
  
    def test_noderawfs(self):
      fopen_write = read_file(test_file('asmfs/fopen_write.cpp'))
      create_file('main.cpp', fopen_write)
      self.run_process([EMXX, 'main.cpp', '-sNODERAWFS'])
      self.assertContained("read 11 bytes. Result: Hello data!", self.run_js('a.out.js'))
  
      # NODERAWFS should directly write on OS file system
      self.assertEqual("Hello data!", read_file('hello_file.txt'))
  
    def test_noderawfs_disables_embedding(self):
      expected = '--preload-file and --embed-file cannot be used with NODERAWFS which disables virtual filesystem'
      base = [EMCC, test_file('hello_world.c'), '-sNODERAWFS']
      create_file('somefile', 'foo')
      err = self.expect_fail(base + ['--preload-file', 'somefile'])
      self.assertContained(expected, err)
      err = self.expect_fail(base + ['--embed-file', 'somefile'])
      self.assertContained(expected, err)
  
    @disabled('https://github.com/nodejs/node/issues/18265')
    def test_node_code_caching(self):
      self.run_process([EMCC, test_file('hello_world.c'),
                        '-sNODE_CODE_CACHING',
                        '-sWASM_ASYNC_COMPILATION=0'])
  
      def get_cached():
        cached = glob.glob('a.out.wasm.*.cached')
        if not cached:
          return None
        self.assertEqual(len(cached), 1)
        return cached[0]
  
      # running the program makes it cache the code
      self.assertFalse(get_cached())
      self.assertEqual('hello, world!', self.run_js('a.out.js').strip())
      self.assertTrue(get_cached(), 'should be a cache file')
  
      # hard to test it actually uses it to speed itself up, but test that it
      # does try to deserialize it at least
      with open(get_cached(), 'w') as f:
        f.write('waka waka')
      ERROR = 'NODE_CODE_CACHING: failed to deserialize, bad cache file?'
      self.assertContained(ERROR, self.run_js('a.out.js'))
      # we cached proper code after showing that error
      self.assertEqual(read_binary(get_cached()).count(b'waka'), 0)
      self.assertNotContained(ERROR, self.run_js('a.out.js'))
  
    @with_env_modify({'LC_ALL': 'C'})
    def test_autotools_shared_check(self):
      expected = ': supported targets:.* elf'
      out = self.run_process([EMCC, '--help'], stdout=PIPE).stdout
      assert re.search(expected, out)
  
    def test_ioctl_window_size(self):
        self.do_other_test('test_ioctl_window_size.cpp')
  
    def test_fd_closed(self):
      self.do_other_test('test_fd_closed.cpp')
  
    def test_fflush(self):
      # fflush without the full filesystem won't quite work
      self.do_other_test('test_fflush.cpp')
  
    def test_fflush_fs(self):
      # fflush with the full filesystem will flush from libc, but not the JS logging, which awaits a newline
      self.do_other_test('test_fflush_fs.cpp', emcc_args=['-sFORCE_FILESYSTEM'])
  
    def test_fflush_fs_exit(self):
      # on exit, we can send out a newline as no more code will run
      self.do_other_test('test_fflush_fs_exit.cpp', emcc_args=['-sFORCE_FILESYSTEM', '-sEXIT_RUNTIME'])
  
    def test_extern_weak(self):
      self.do_other_test('test_extern_weak.c')
  
    @disabled('https://github.com/emscripten-core/emscripten/issues/12819')
    def test_extern_weak_dynamic(self):
      self.do_other_test('test_extern_weak.c', emcc_args=['-sMAIN_MODULE=2'])
  
    def test_main_module_without_main(self):
      create_file('pre.js', r'''
  var Module = {
    onRuntimeInitialized: function() {
      Module._foo();
    }
  };
  ''')
      create_file('src.c', r'''
  #include <emscripten.h>
  EMSCRIPTEN_KEEPALIVE void foo() {
    EM_ASM({ console.log("bar") });
  }
  ''')
      self.run_process([EMCC, 'src.c', '--pre-js', 'pre.js', '-sMAIN_MODULE=2'])
      self.assertContained('bar', self.run_js('a.out.js'))
  
    def test_js_optimizer_parse_error(self):
      # check we show a proper understandable error for JS parse problems
      create_file('src.cpp', r'''
  #include <emscripten.h>
  int main() {
    EM_ASM({
      var x = !<->5.; // wtf
    });
  }
  ''')
      stderr = self.expect_fail([EMXX, 'src.cpp', '-O2'])
      # wasm backend output doesn't have spaces in the EM_ASM function bodies
      self.assertContained(('''
  var ASM_CONSTS = [function() { var x = !<->5.; }];
                                          ^
  ''', '''
    1025: function() {var x = !<->5.;}
                               ^
  '''), stderr)
  
    def test_js_optimizer_chunk_size_determinism(self):
      def build():
        self.run_process([EMCC, test_file('hello_world.c'), '-O3', '-sWASM=0'])
        # FIXME: newline differences can exist, ignore for now
        return read_file('a.out.js').replace('\n', '')
  
      normal = build()
  
      with env_modify({
        'EMCC_JSOPT_MIN_CHUNK_SIZE': '1',
        'EMCC_JSOPT_MAX_CHUNK_SIZE': '1'
      }):
        tiny = build()
  
      with env_modify({
        'EMCC_JSOPT_MIN_CHUNK_SIZE': '4294967296',
        'EMCC_JSOPT_MAX_CHUNK_SIZE': '4294967296'
      }):
        huge = build()
  
      self.assertIdentical(normal, tiny)
      self.assertIdentical(normal, huge)
  
    def test_js_optimizer_verbose(self):
      # build at -O3 with wasm2js to use as much as possible of the JS
      # optimization code, and verify it works ok in verbose mode
      self.run_process([EMCC, test_file('hello_world.c'), '-O3', '-sWASM=0',
                        '-sVERBOSE'], stdout=PIPE, stderr=PIPE)
  
    def test_pthreads_growth_and_unsigned(self):
      create_file('src.cpp', r'''
  #include <emscripten.h>
  
  int main() {
    EM_ASM({
      HEAP8.set([1,2,3], $0);
    }, 1024);
  }''')
      self.run_process([EMXX, 'src.cpp', '-O2', '--profiling', '-pthread',
                        '-sMAXIMUM_MEMORY=4GB', '-sALLOW_MEMORY_GROWTH'])
      # growable-heap must not interfere with heap unsigning, and vice versa:
      # we must have both applied, that is
      #   - GROWABLE_HEAP_I8() replaces HEAP8
      #   - $0 gets an >>> 0 unsigning
      self.assertContained('GROWABLE_HEAP_I8().set([ 1, 2, 3 ], $0 >>> 0)',
                           read_file('a.out.js'))
  
    @parameterized({
      '': ([],), # noqa
      'O3': (['-O3'],), # noqa
      'closure': (['--closure=1'],), # noqa
      'closure_O3': (['--closure=1', '-O3'],), # noqa
    })
    def test_EM_ASM_ES6(self, args):
      create_file('src.cpp', r'''
  #include <emscripten.h>
  int main() {
    EM_ASM({
      let x = (a, b) => 5; // valid ES6
      async function y() {} // valid ES2017
      out('hello!');
      return x;
    });
  }
  ''')
      self.run_process([EMXX, 'src.cpp'] + args)
      self.assertContained('hello!', self.run_js('a.out.js'))
  
    def test_check_sourcemapurl(self):
      if not self.is_wasm():
        self.skipTest('only supported with wasm')
      self.run_process([EMCC, test_file('hello_123.c'), '-gsource-map', '-o', 'a.js', '--source-map-base', 'dir/'])
      output = read_binary('a.wasm')
      # has sourceMappingURL section content and points to 'dir/a.wasm.map' file
      source_mapping_url_content = webassembly.toLEB(len('sourceMappingURL')) + b'sourceMappingURL' + webassembly.toLEB(len('dir/a.wasm.map')) + b'dir/a.wasm.map'
      self.assertEqual(output.count(source_mapping_url_content), 1)
      # make sure no DWARF debug info sections remain - they would just waste space
      self.assertNotIn(b'.debug_', output)
  
    def test_check_source_map_args(self):
      # -gsource-map is needed for source maps; -g is not enough
      self.run_process([EMCC, test_file('hello_world.c'), '-g'])
      self.assertNotExists('a.out.wasm.map')
      self.run_process([EMCC, test_file('hello_world.c'), '-gsource-map'])
      self.assertExists('a.out.wasm.map')
      os.unlink('a.out.wasm.map')
      err = self.run_process([EMCC, test_file('hello_world.c'), '-g4'], stderr=subprocess.PIPE).stderr
      self.assertIn('please replace -g4 with -gsource-map', err)
      self.assertExists('a.out.wasm.map')
  
    @parameterized({
      'normal': [],
      'profiling': ['--profiling'] # -gsource-map --profiling should still emit a source map; see #8584
    })
    def test_check_sourcemapurl_default(self, *args):
      if not self.is_wasm():
        self.skipTest('only supported with wasm')
  
      self.run_process([EMCC, test_file('hello_123.c'), '-gsource-map', '-o', 'a.js'] + list(args))
      output = read_binary('a.wasm')
      # has sourceMappingURL section content and points to 'a.wasm.map' file
      source_mapping_url_content = webassembly.toLEB(len('sourceMappingURL')) + b'sourceMappingURL' + webassembly.toLEB(len('a.wasm.map')) + b'a.wasm.map'
      self.assertIn(source_mapping_url_content, output)
  
    def test_wasm_sourcemap(self):
      # The no_main.c will be read (from relative location) due to speficied "-s"
      shutil.copyfile(test_file('other/wasm_sourcemap/no_main.c'), 'no_main.c')
      wasm_map_cmd = [PYTHON, path_from_root('tools/wasm-sourcemap.py'),
                      '--sources', '--prefix', '=wasm-src://',
                      '--load-prefix', '/emscripten/tests/other/wasm_sourcemap=.',
                      '--dwarfdump-output',
                      test_file('other/wasm_sourcemap/foo.wasm.dump'),
                      '-o', 'a.out.wasm.map',
                      test_file('other/wasm_sourcemap/foo.wasm'),
                      '--basepath=' + os.getcwd()]
      self.run_process(wasm_map_cmd)
      output = read_file('a.out.wasm.map')
      # has "sources" entry with file (includes also `--prefix =wasm-src:///` replacement)
      self.assertIn('wasm-src:///emscripten/tests/other/wasm_sourcemap/no_main.c', output)
      # has "sourcesContent" entry with source code (included with `-s` option)
      self.assertIn('int foo()', output)
      # has some entries
      self.assertRegexpMatches(output, r'"mappings":\s*"[A-Za-z0-9+/]')
  
    def test_wasm_sourcemap_dead(self):
      wasm_map_cmd = [PYTHON, path_from_root('tools/wasm-sourcemap.py'),
                      '--dwarfdump-output',
                      test_file('other/wasm_sourcemap_dead/t.wasm.dump'),
                      '-o', 'a.out.wasm.map',
                      test_file('other/wasm_sourcemap_dead/t.wasm'),
                      '--basepath=' + os.getcwd()]
      self.run_process(wasm_map_cmd, stdout=PIPE, stderr=PIPE)
      output = read_file('a.out.wasm.map')
      # has only two entries
      self.assertRegexpMatches(output, r'"mappings":\s*"[A-Za-z0-9+/]+,[A-Za-z0-9+/]+"')
  
    def test_wasm_sourcemap_relative_paths(self):
      def test(infile, source_map_added_dir=''):
        expected_source_map_path = 'a.cpp'
        if source_map_added_dir:
          expected_source_map_path = source_map_added_dir + '/' + expected_source_map_path
        print(infile, expected_source_map_path)
        shutil.copyfile(test_file('hello_123.c'), infile)
        infiles = [
          infile,
          os.path.abspath(infile),
          './' + infile
        ]
        for curr in infiles:
          print('  ', curr)
          self.run_process([EMCC, curr, '-gsource-map'])
          self.assertIn('"%s"' % expected_source_map_path, read_file('a.out.wasm.map'))
  
      test('a.cpp')
  
      ensure_dir('inner')
      test('inner/a.cpp', 'inner')
  
    def test_emsymbolizer(self):
      # Test DWARF output
      self.run_process([EMCC, test_file('core/test_dwarf.c'),
                        '-g', '-O1', '-o', 'test_dwarf.js'])
  
      # Use hard-coded addresses. This is potentially brittle, but LLVM's
      # O1 output is pretty minimal so hopefully it won't break too much?
      # Another option would be to disassemble the binary to look for certain
      # instructions or code sequences.
  
      def get_addr(address):
        return self.run_process(
            [PYTHON, path_from_root('emsymbolizer.py'), 'test_dwarf.wasm', address],
            stdout=PIPE).stdout
  
      # Check a location in foo(), not inlined.
      self.assertIn('test_dwarf.c:6:3', get_addr('0x101'))
      # Check that both bar (inlined) and main (inlinee) are in the output,
      # as described by the DWARF.
      # TODO: consider also checking the function names once the output format
      # stabilizes more
      self.assertRegex(get_addr('0x124').replace('\n', ''),
                       'test_dwarf.c:15:3.*test_dwarf.c:20:3')
  
    def test_separate_dwarf(self):
      self.run_process([EMCC, test_file('hello_world.c'), '-g'])
      self.assertExists('a.out.wasm')
      self.assertNotExists('a.out.wasm.debug.wasm')
      self.run_process([EMCC, test_file('hello_world.c'), '-gseparate-dwarf'])
      self.assertExists('a.out.wasm')
      self.assertExists('a.out.wasm.debug.wasm')
      self.assertLess(os.path.getsize('a.out.wasm'), os.path.getsize('a.out.wasm.debug.wasm'))
      # the special section should also exist, that refers to the side debug file
      wasm = read_binary('a.out.wasm')
      self.assertIn(b'external_debug_info', wasm)
      self.assertIn(b'a.out.wasm.debug.wasm', wasm)
  
      # building to a subdirectory should still leave a relative path, which
      # assumes the debug file is alongside the main one
      os.mkdir('subdir')
      self.run_process([EMCC, test_file('hello_world.c'),
                        '-gseparate-dwarf',
                        '-o', Path('subdir/output.js')])
      wasm = read_binary('subdir/output.wasm')
      self.assertIn(b'output.wasm.debug.wasm', wasm)
      # check both unix-style slashes and the system's slashes, so that we don't
      # assume the encoding of the section in this test
      self.assertNotIn(b'subdir/output.wasm.debug.wasm', wasm)
      self.assertNotIn(bytes(os.path.join('subdir', 'output.wasm.debug.wasm'), 'ascii'), wasm)
  
      # Check that the dwarf file has only dwarf and name sections
      debug_wasm = webassembly.Module('subdir/output.wasm.debug.wasm')
      if not debug_wasm.has_name_section():
        self.fail('name section not found in separate dwarf file')
      for sec in debug_wasm.sections():
        if sec.type != webassembly.SecType.CUSTOM:
          self.fail(f'non-custom section type {sec.type} found in separate dwarf file')
        elif sec.name != 'name' and not sec.name.startswith('.debug'):
          self.fail(f'non-debug section "{sec.name}" found in separate dwarf file')
  
    def test_separate_dwarf_with_filename(self):
      self.run_process([EMCC, test_file('hello_world.c'), '-gseparate-dwarf=with_dwarf.wasm'])
      self.assertNotExists('a.out.wasm.debug.wasm')
      self.assertExists('with_dwarf.wasm')
      # the correct notation is to have exactly one '=' and in the right place
      for invalid in ('-gseparate-dwarf=x=', '-gseparate-dwarfy=', '-gseparate-dwarf-hmm'):
        stderr = self.expect_fail([EMCC, test_file('hello_world.c'), invalid])
        self.assertContained('invalid -gseparate-dwarf=FILENAME notation', stderr)
  
      # building to a subdirectory, but with the debug file in another place,
      # should leave a relative path to the debug wasm
      os.mkdir('subdir')
      self.run_process([EMCC, test_file('hello_world.c'),
                        '-o', Path('subdir/output.js'),
                        '-gseparate-dwarf=with_dwarf2.wasm'])
      self.assertExists('with_dwarf2.wasm')
      wasm = read_binary('subdir/output.wasm')
      self.assertIn(b'../with_dwarf2.wasm', wasm)
  
    def test_separate_dwarf_with_filename_and_path(self):
      self.run_process([EMCC, test_file('hello_world.c'), '-gseparate-dwarf=with_dwarf.wasm'])
      self.assertIn(b'with_dwarf.wasm', read_binary('a.out.wasm'))
      self.run_process([EMCC, test_file('hello_world.c'), '-gseparate-dwarf=with_dwarf.wasm',
                        '-sSEPARATE_DWARF_URL=http://somewhere.com/hosted.wasm'])
      self.assertIn(b'somewhere.com/hosted.wasm', read_binary('a.out.wasm'))
  
    @parameterized({
      'O0': (['-O0'],),
      'O1': (['-O1'],),
      'O2': (['-O2'],),
    })
    def test_wasm_producers_section(self, args):
      self.run_process([EMCC, test_file('hello_world.c')] + args)
      data = read_binary('a.out.wasm')
      # if there is no producers section expected by default, verify that, and
      # see that the flag works to add it.
      self.assertNotIn('clang', str(data))
      size = os.path.getsize('a.out.wasm')
      self.run_process([EMCC, test_file('hello_world.c'), '-sEMIT_PRODUCERS_SECTION'] + args)
      self.assertIn(b'clang', read_binary('a.out.wasm'))
      size_with_section = os.path.getsize('a.out.wasm')
      self.assertLess(size, size_with_section)
  
    def test_js_preprocess(self):
      # Use stderr rather than stdout here because stdout is redirected to the output JS file itself.
      create_file('lib.js', '''
  #if MAIN_MODULE == 1
  console.error('JSLIB: MAIN_MODULE=1');
  #elif MAIN_MODULE == 2
  console.error('JSLIB: MAIN_MODULE=2');
  #elif EXIT_RUNTIME
  console.error('JSLIB: EXIT_RUNTIME');
  #else
  console.error('JSLIB: none of the above');
  #endif
  ''')
  
      err = self.run_process([EMCC, test_file('hello_world.c'), '--js-library', 'lib.js'], stderr=PIPE).stderr
      self.assertContained('JSLIB: none of the above', err)
      self.assertEqual(err.count('JSLIB'), 1)
  
      err = self.run_process([EMCC, test_file('hello_world.c'), '--js-library', 'lib.js', '-sMAIN_MODULE'], stderr=PIPE).stderr
      self.assertContained('JSLIB: MAIN_MODULE=1', err)
      self.assertEqual(err.count('JSLIB'), 1)
  
      err = self.run_process([EMCC, test_file('hello_world.c'), '--js-library', 'lib.js', '-sMAIN_MODULE=2'], stderr=PIPE).stderr
      self.assertContained('JSLIB: MAIN_MODULE=2', err)
      self.assertEqual(err.count('JSLIB'), 1)
  
      err = self.run_process([EMCC, test_file('hello_world.c'), '--js-library', 'lib.js', '-sEXIT_RUNTIME'], stderr=PIPE).stderr
      self.assertContained('JSLIB: EXIT_RUNTIME', err)
      self.assertEqual(err.count('JSLIB'), 1)
  
    def test_html_preprocess(self):
      src_file = test_file('module/test_stdin.c')
      output_file = 'test_stdin.html'
      shell_file = test_file('module/test_html_preprocess.html')
  
      self.run_process([EMCC, '-o', output_file, src_file, '--shell-file', shell_file, '-sASSERTIONS=0'], stdout=PIPE, stderr=PIPE)
      output = read_file(output_file)
      self.assertContained("""<style>
  /* Disable preprocessing inside style block as syntax is ambiguous with CSS */
  #include {background-color: black;}
  #if { background-color: red;}
  #else {background-color: blue;}
  #endif {background-color: green;}
  #xxx {background-color: purple;}
  </style>
  T1:(else) ASSERTIONS != 1
  T2:ASSERTIONS != 1
  T3:ASSERTIONS < 2
  T4:(else) ASSERTIONS <= 1
  T5:(else) ASSERTIONS
  T6:!ASSERTIONS""", output)
  
      self.run_process([EMCC, '-o', output_file, src_file, '--shell-file', shell_file, '-sASSERTIONS'], stdout=PIPE, stderr=PIPE)
      output = read_file(output_file)
      self.assertContained("""<style>
  /* Disable preprocessing inside style block as syntax is ambiguous with CSS */
  #include {background-color: black;}
  #if { background-color: red;}
  #else {background-color: blue;}
  #endif {background-color: green;}
  #xxx {background-color: purple;}
  </style>
  T1:ASSERTIONS == 1
  T2:(else) ASSERTIONS == 1
  T3:ASSERTIONS < 2
  T4:(else) ASSERTIONS <= 1
  T5:ASSERTIONS
  T6:(else) !ASSERTIONS""", output)
  
      self.run_process([EMCC, '-o', output_file, src_file, '--shell-file', shell_file, '-sASSERTIONS=2'], stdout=PIPE, stderr=PIPE)
      output = read_file(output_file)
      self.assertContained("""<style>
  /* Disable preprocessing inside style block as syntax is ambiguous with CSS */
  #include {background-color: black;}
  #if { background-color: red;}
  #else {background-color: blue;}
  #endif {background-color: green;}
  #xxx {background-color: purple;}
  </style>
  T1:(else) ASSERTIONS != 1
  T2:ASSERTIONS != 1
  T3:(else) ASSERTIONS >= 2
  T4:ASSERTIONS > 1
  T5:ASSERTIONS
  T6:(else) !ASSERTIONS""", output)
  
    # Tests that Emscripten-compiled applications can be run from a relative path with node command line that is different than the current working directory.
    @require_node
    def test_node_js_run_from_different_directory(self):
      ensure_dir('subdir')
      self.run_process([EMCC, test_file('hello_world.c'), '-o', Path('subdir/a.js'), '-O3'])
      ret = self.run_process(config.NODE_JS + [Path('subdir/a.js')], stdout=PIPE).stdout
      self.assertContained('hello, world!', ret)
  
    # Tests that a pthreads + modularize build can be run in node js
    @require_node
    def test_node_js_pthread_module(self):
      # create module loader script
      moduleLoader = 'moduleLoader.js'
      moduleLoaderContents = '''
  const test_module = require("./module");
  test_module().then((test_module_instance) => {
    test_module_instance._main();
    process.exit(0);
  });
  '''
      ensure_dir('subdir')
      create_file(os.path.join('subdir', moduleLoader), moduleLoaderContents)
  
      # build hello_world.c
      self.run_process([EMCC, test_file('hello_world.c'), '-o', Path('subdir/module.js'), '-sUSE_PTHREADS', '-sPTHREAD_POOL_SIZE=2', '-sMODULARIZE', '-sEXPORT_NAME=test_module', '-sENVIRONMENT=worker,node'])
  
      # run the module
      ret = self.run_process(config.NODE_JS + ['--experimental-wasm-threads'] + [os.path.join('subdir', moduleLoader)], stdout=PIPE).stdout
      self.assertContained('hello, world!', ret)
  
    @no_windows('node system() does not seem to work, see https://github.com/emscripten-core/emscripten/pull/10547')
    def test_node_js_system(self):
      self.run_process([EMCC, '-DENV_NODE', test_file('system.c'), '-o', 'a.js', '-O3'])
      ret = self.run_process(config.NODE_JS + ['a.js'], stdout=PIPE).stdout
      self.assertContained('OK', ret)
  
    def test_node_eval(self):
      self.run_process([EMCC, '-sENVIRONMENT=node', test_file('hello_world.c'), '-o', 'a.js', '-O3'])
      js = open('a.js').read()
      ret = self.run_process(config.NODE_JS + ['-e', js], stdout=PIPE).stdout
      self.assertContained('hello, world!', ret)
  
    def test_is_bitcode(self):
      fname = 'tmp.o'
  
      with open(fname, 'wb') as f:
        f.write(b'foo')
      self.assertFalse(building.is_bitcode(fname))
  
      with open(fname, 'wb') as f:
        f.write(b'\xDE\xC0\x17\x0B')
        f.write(16 * b'\x00')
        f.write(b'BC')
      self.assertTrue(building.is_bitcode(fname))
  
      with open(fname, 'wb') as f:
        f.write(b'BC')
      self.assertTrue(building.is_bitcode(fname))
  
    def test_is_ar(self):
      fname = 'tmp.a'
  
      with open(fname, 'wb') as f:
        f.write(b'foo')
      self.assertFalse(building.is_ar(fname))
  
      with open(fname, 'wb') as f:
        f.write(b'!<arch>\n')
      self.assertTrue(building.is_ar(fname))
  
    def test_dash_s_list_parsing(self):
      create_file('src.c', r'''
          #include <stdio.h>
          void a() { printf("a\n"); }
          void b() { printf("b\n"); }
          void c() { printf("c\n"); }
          void d() { printf("d\n"); }
        ''')
      create_file('response.json', '''\
  [
  "_a",
  "_b",
  "_c",
  "_d"
  ]
  ''')
      create_file('response.txt', '''\
  _a
  _b
  _c
  _d
  ''')
  
      for export_arg, expected in [
        # No quotes needed
        ('EXPORTED_FUNCTIONS=[_a,_b,_c,_d]', ''),
        # No quotes with spaces
        ('EXPORTED_FUNCTIONS=[_a, _b, _c, _d]', ''),
        # No brackets needed either
        ('EXPORTED_FUNCTIONS=_a,_b,_c,_d', ''),
        # No brackets with spaced
        ('EXPORTED_FUNCTIONS=_a, _b, _c, _d', ''),
        # extra space at end - should be ignored
        ("EXPORTED_FUNCTIONS=['_a', '_b', '_c', '_d' ]", ''),
        # extra newline in response file - should be ignored
        ("EXPORTED_FUNCTIONS=@response.json", ''),
        # Simple one-per-line response file format
        ("EXPORTED_FUNCTIONS=@response.txt", ''),
        # stray slash
        ("EXPORTED_FUNCTIONS=['_a', '_b', \\'_c', '_d']", '''undefined exported symbol: "\\\\'_c'"'''),
        # stray slash
        ("EXPORTED_FUNCTIONS=['_a', '_b',\\ '_c', '_d']", '''undefined exported symbol: "\\\\ '_c'"'''),
        # stray slash
        ('EXPORTED_FUNCTIONS=["_a", "_b", \\"_c", "_d"]', 'undefined exported symbol: "\\\\"_c""'),
        # stray slash
        ('EXPORTED_FUNCTIONS=["_a", "_b",\\ "_c", "_d"]', 'undefined exported symbol: "\\\\ "_c"'),
        # missing comma
        ('EXPORTED_FUNCTIONS=["_a", "_b" "_c", "_d"]', 'undefined exported symbol: "_b" "_c"'),
      ]:
        print(export_arg)
        proc = self.run_process([EMCC, 'src.c', '-s', export_arg], stdout=PIPE, stderr=PIPE, check=not expected)
        print(proc.stderr)
        if not expected:
          self.assertFalse(proc.stderr)
          js = open('a.out.js').read()
          for sym in ('_a', '_b', '_c', '_d'):
            self.assertContained(f'var {sym} = ', js)
        else:
          self.assertNotEqual(proc.returncode, 0)
          self.assertContained(expected, proc.stderr)
  
    def test_asyncify_escaping(self):
      proc = self.run_process([EMCC, test_file('hello_world.c'), '-sASYNCIFY', '-s', "ASYNCIFY_ONLY=[DOS_ReadFile(unsigned short, unsigned char*, unsigned short*, bool)]"], stdout=PIPE, stderr=PIPE)
      self.assertContained('emcc: ASYNCIFY list contains an item without balanced parentheses', proc.stderr)
      self.assertContained('   DOS_ReadFile(unsigned short', proc.stderr)
      self.assertContained('Try using a response file', proc.stderr)
  
    def test_asyncify_response_file(self):
      create_file('a.txt', r'''[
    "DOS_ReadFile(unsigned short, unsigned char*, unsigned short*, bool)"
  ]
  ''')
  
      create_file('b.txt', 'DOS_ReadFile(unsigned short, unsigned char*, unsigned short*, bool)')
      for file in ('a.txt', 'b.txt'):
        proc = self.run_process([EMCC, test_file('hello_world.c'), '-sASYNCIFY', f'-sASYNCIFY_ONLY=@{file}'], stdout=PIPE, stderr=PIPE)
        # we should parse the response file properly, and then issue a proper warning for the missing function
        self.assertContained(
            'Asyncify onlylist contained a non-matching pattern: DOS_ReadFile(unsigned short, unsigned char*, unsigned short*, bool)',
            proc.stderr)
  
    def test_asyncify_advise(self):
      src = test_file('other/asyncify_advise.c')
  
      self.set_setting('ASYNCIFY')
      self.set_setting('ASYNCIFY_ADVISE')
      self.set_setting('ASYNCIFY_IMPORTS', ['async_func'])
  
      out = self.run_process([EMCC, src, '-o', 'asyncify_advise.js'] + self.get_emcc_args(), stdout=PIPE).stdout
      self.assertContained('[asyncify] main can', out)
      self.assertContained('[asyncify] a can', out)
      self.assertContained('[asyncify] c can', out)
      self.assertContained('[asyncify] e can', out)
      self.assertContained('[asyncify] g can', out)
      self.assertContained('[asyncify] i can', out)
  
      self.set_setting('ASYNCIFY_REMOVE', ['e'])
      out = self.run_process([EMCC, src, '-o', 'asyncify_advise.js'] + self.get_emcc_args(), stdout=PIPE).stdout
      self.assertContained('[asyncify] main can', out)
      self.assertNotContained('[asyncify] a can', out)
      self.assertNotContained('[asyncify] c can', out)
      self.assertNotContained('[asyncify] e can', out)
      self.assertContained('[asyncify] g can', out)
      self.assertContained('[asyncify] i can', out)
  
    # Sockets and networking
  
    def test_inet(self):
      self.do_runf(test_file('sha1.c'), 'SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6')
      src = r'''
        #include <stdio.h>
        #include <arpa/inet.h>
  
        int main() {
          printf("*%x,%x,%x,%x,%x,%x*\n", htonl(0xa1b2c3d4), htonl(0xfe3572e0), htonl(0x07abcdf0), htons(0xabcd), ntohl(0x43211234), ntohs(0xbeaf));
          in_addr_t i = inet_addr("190.180.10.78");
          printf("%x\n", i);
          return 0;
        }
      '''
      self.do_run(src, '*d4c3b2a1,e07235fe,f0cdab07,cdab,34122143,afbe*\n4e0ab4be\n')
  
    def test_inet2(self):
      src = r'''
        #include <stdio.h>
        #include <arpa/inet.h>
  
        int main() {
          struct in_addr x, x2;
          int *y = (int*)&x;
          *y = 0x12345678;
          printf("%s\n", inet_ntoa(x));
          int r = inet_aton(inet_ntoa(x), &x2);
          printf("%s\n", inet_ntoa(x2));
          return 0;
        }
      '''
      self.do_run(src, '120.86.52.18\n120.86.52.18\n')
  
    def test_inet3(self):
      src = r'''
        #include <stdio.h>
        #include <arpa/inet.h>
        #include <sys/socket.h>
        int main() {
          char dst[64];
          struct in_addr x, x2;
          int *y = (int*)&x;
          *y = 0x12345678;
          printf("%s\n", inet_ntop(AF_INET,&x,dst,sizeof dst));
          int r = inet_aton(inet_ntoa(x), &x2);
          printf("%s\n", inet_ntop(AF_INET,&x2,dst,sizeof dst));
          return 0;
        }
      '''
      self.do_run(src, '120.86.52.18\n120.86.52.18\n')
  
    def test_inet4(self):
      src = r'''
        #include <stdio.h>
        #include <arpa/inet.h>
        #include <sys/socket.h>
  
        void test(const char *test_addr, bool first=true){
            char str[40];
            struct in6_addr addr;
            unsigned char *p = (unsigned char*)&addr;
            int ret;
            ret = inet_pton(AF_INET6,test_addr,&addr);
            if(ret == -1) return;
            if(ret == 0) return;
            if(inet_ntop(AF_INET6,&addr,str,sizeof(str)) == NULL ) return;
            printf("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x - %s\n",
                 p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7],p[8],p[9],p[10],p[11],p[12],p[13],p[14],p[15],str);
            if (first) test(str, false); // check again, on our output
        }
        int main(){
            test("::");
            test("::1");
            test("::1.2.3.4");
            test("::17.18.19.20");
            test("::ffff:1.2.3.4");
            test("1::ffff");
            test("::255.255.255.255");
            test("0:ff00:1::");
            test("0:ff::");
            test("abcd::");
            test("ffff::a");
            test("ffff::a:b");
            test("ffff::a:b:c");
            test("ffff::a:b:c:d");
            test("ffff::a:b:c:d:e");
            test("::1:2:0:0:0");
            test("0:0:1:2:3::");
            test("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
            test("1::255.255.255.255");
  
            //below should fail and not produce results..
            test("1.2.3.4");
            test("");
            test("-");
  
            printf("ok.\n");
        }
      '''
      self.do_run(src, r'''0000:0000:0000:0000:0000:0000:0000:0000 - ::
  0000:0000:0000:0000:0000:0000:0000:0000 - ::
  0000:0000:0000:0000:0000:0000:0000:0001 - ::1
  0000:0000:0000:0000:0000:0000:0000:0001 - ::1
  0000:0000:0000:0000:0000:0000:0102:0304 - ::102:304
  0000:0000:0000:0000:0000:0000:0102:0304 - ::102:304
  0000:0000:0000:0000:0000:0000:1112:1314 - ::1112:1314
  0000:0000:0000:0000:0000:0000:1112:1314 - ::1112:1314
  0000:0000:0000:0000:0000:ffff:0102:0304 - ::ffff:1.2.3.4
  0000:0000:0000:0000:0000:ffff:0102:0304 - ::ffff:1.2.3.4
  0001:0000:0000:0000:0000:0000:0000:ffff - 1::ffff
  0001:0000:0000:0000:0000:0000:0000:ffff - 1::ffff
  0000:0000:0000:0000:0000:0000:ffff:ffff - ::ffff:ffff
  0000:0000:0000:0000:0000:0000:ffff:ffff - ::ffff:ffff
  0000:ff00:0001:0000:0000:0000:0000:0000 - 0:ff00:1::
  0000:ff00:0001:0000:0000:0000:0000:0000 - 0:ff00:1::
  0000:00ff:0000:0000:0000:0000:0000:0000 - 0:ff::
  0000:00ff:0000:0000:0000:0000:0000:0000 - 0:ff::
  abcd:0000:0000:0000:0000:0000:0000:0000 - abcd::
  abcd:0000:0000:0000:0000:0000:0000:0000 - abcd::
  ffff:0000:0000:0000:0000:0000:0000:000a - ffff::a
  ffff:0000:0000:0000:0000:0000:0000:000a - ffff::a
  ffff:0000:0000:0000:0000:0000:000a:000b - ffff::a:b
  ffff:0000:0000:0000:0000:0000:000a:000b - ffff::a:b
  ffff:0000:0000:0000:0000:000a:000b:000c - ffff::a:b:c
  ffff:0000:0000:0000:0000:000a:000b:000c - ffff::a:b:c
  ffff:0000:0000:0000:000a:000b:000c:000d - ffff::a:b:c:d
  ffff:0000:0000:0000:000a:000b:000c:000d - ffff::a:b:c:d
  ffff:0000:0000:000a:000b:000c:000d:000e - ffff::a:b:c:d:e
  ffff:0000:0000:000a:000b:000c:000d:000e - ffff::a:b:c:d:e
  0000:0000:0000:0001:0002:0000:0000:0000 - ::1:2:0:0:0
  0000:0000:0000:0001:0002:0000:0000:0000 - ::1:2:0:0:0
  0000:0000:0001:0002:0003:0000:0000:0000 - 0:0:1:2:3::
  0000:0000:0001:0002:0003:0000:0000:0000 - 0:0:1:2:3::
  ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff - ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
  ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff - ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
  0001:0000:0000:0000:0000:0000:ffff:ffff - 1::ffff:ffff
  0001:0000:0000:0000:0000:0000:ffff:ffff - 1::ffff:ffff
  ok.
  ''')
  
    def test_getsockname_unconnected_socket(self):
      self.do_run(r'''
        #include <sys/socket.h>
        #include <stdio.h>
        #include <assert.h>
        #include <sys/socket.h>
        #include <netinet/in.h>
        #include <arpa/inet.h>
        #include <string.h>
        int main() {
          int fd;
          int z;
          fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
          struct sockaddr_in adr_inet;
          socklen_t len_inet = sizeof adr_inet;
          z = getsockname(fd, (struct sockaddr *)&adr_inet, &len_inet);
          if (z != 0) {
            perror("getsockname error");
            return 1;
          }
          char buffer[1000];
          sprintf(buffer, "%s:%u", inet_ntoa(adr_inet.sin_addr), (unsigned)ntohs(adr_inet.sin_port));
          const char *correct = "0.0.0.0:0";
          printf("got (expected) socket: %s (%s), size %lu (%lu)\n", buffer, correct, strlen(buffer), strlen(correct));
          assert(strlen(buffer) == strlen(correct));
          assert(strcmp(buffer, correct) == 0);
          puts("success.");
        }
      ''', 'success.')
  
    def test_getpeername_unconnected_socket(self):
      self.do_run(r'''
        #include <sys/socket.h>
        #include <stdio.h>
        #include <assert.h>
        #include <sys/socket.h>
        #include <netinet/in.h>
        #include <arpa/inet.h>
        #include <string.h>
        int main() {
          int fd;
          int z;
          fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
          struct sockaddr_in adr_inet;
          socklen_t len_inet = sizeof adr_inet;
          z = getpeername(fd, (struct sockaddr *)&adr_inet, &len_inet);
          if (z != 0) {
            perror("getpeername error");
            return 1;
          }
          puts("unexpected success.");
        }
      ''', 'getpeername error: Socket not connected', assert_returncode=NON_ZERO)
  
    def test_getsockname_addrlen(self):
      self.do_runf(test_file('sockets/test_getsockname_addrlen.c'), 'success')
  
    def test_sin_zero(self):
      self.do_runf(test_file('sockets/test_sin_zero.c'), 'success')
  
    def test_getaddrinfo(self):
      self.do_runf(test_file('sockets/test_getaddrinfo.c'), 'success')
  
    def test_getnameinfo(self):
      self.do_runf(test_file('sockets/test_getnameinfo.c'), 'success')
  
    def test_gethostbyname(self):
      self.do_runf(test_file('sockets/test_gethostbyname.c'), 'success')
  
    def test_getprotobyname(self):
      self.do_runf(test_file('sockets/test_getprotobyname.c'), 'success')
  
    def test_socketpair(self):
      self.do_run(r'''
        #include <sys/socket.h>
        #include <stdio.h>
        int main() {
          int fd[2];
          int err;
          err = socketpair(AF_INET, SOCK_STREAM, 0, fd);
          if (err != 0) {
            perror("socketpair error");
            return 1;
          }
          puts("unexpected success.");
        }
      ''', 'socketpair error: Function not implemented', assert_returncode=NON_ZERO)
  
    def test_link(self):
      self.do_run(r'''
  #include <netdb.h>
  
  #include <sys/types.h>
  #include <sys/socket.h>
  
  int main () {
      void* thing = gethostbyname("bing.com");
      ssize_t rval = recv (0, thing, 0, 0);
      rval = send (0, thing, 0, 0);
      return 0;
  }''', '', force_c=True)
  
    def test_linking_recv(self):
      self.do_run(r'''
        #include <sys/types.h>
        #include <sys/socket.h>
        int main(void) {
          recv(0, 0, 0, 0);
          return 0;
        }
      ''', '', force_c=True)
  
    def test_linking_send(self):
      self.do_run(r'''
        #include <sys/types.h>
        #include <sys/socket.h>
        int main(void) {
          send(0, 0, 0, 0);
          return 0;
        }
      ''', '', force_c=True)
  
    # This test verifies that function names embedded into the build with --js-library (JS functions exported to wasm)
    # are minified when -O3 is used
    def test_js_function_names_are_minified(self):
      def check_size(f, expected_size):
        if not os.path.isfile(f):
          return # Nonexistent file passes in this check
        obtained_size = os.path.getsize(f)
        print('size of generated ' + f + ': ' + str(obtained_size))
        try_delete(f)
        self.assertLess(obtained_size, expected_size)
  
      self.run_process([PYTHON, test_file('gen_many_js_functions.py'), 'library_long.js', 'main_long.c'])
      for wasm in [[], ['-sWASM=0']]:
        # Currently we rely on Closure for full minification of every appearance of JS function names.
        # TODO: Add minification also for non-Closure users and add [] to this list to test minification without Closure.
        for closure in [['--closure=1']]:
          args = [EMCC, '-O3', '--js-library', 'library_long.js', 'main_long.c', '-o', 'a.html'] + wasm + closure
          print(' '.join(args))
          self.run_process(args)
  
          ret = self.run_process(config.NODE_JS + ['a.js'], stdout=PIPE).stdout
          self.assertTextDataIdentical('Sum of numbers from 1 to 1000: 500500 (expected 500500)', ret.strip())
  
          check_size('a.js', 150000)
          check_size('a.wasm', 80000)
  
    # Checks that C++ exceptions managing invoke_*() wrappers will not be generated if exceptions are disabled
    def test_no_invoke_functions_are_generated_if_exception_catching_is_disabled(self):
      self.skipTest('Skipping other.test_no_invoke_functions_are_generated_if_exception_catching_is_disabled: Enable after new version of fastcomp has been tagged')
      for args in [[], ['-sWASM=0']]:
        self.run_process([EMXX, test_file('hello_world.cpp'), '-sDISABLE_EXCEPTION_CATCHING', '-o', 'a.html'] + args)
        output = read_file('a.js')
        self.assertContained('_main', output) # Smoke test that we actually compiled
        self.assertNotContained('invoke_', output)
  
    # Verifies that only the minimal needed set of invoke_*() functions will be generated when C++ exceptions are enabled
    def test_no_excessive_invoke_functions_are_generated_when_exceptions_are_enabled(self):
      self.skipTest('Skipping other.test_no_excessive_invoke_functions_are_generated_when_exceptions_are_enabled: Enable after new version of fastcomp has been tagged')
      for args in [[], ['-sWASM=0']]:
        self.run_process([EMXX, test_file('invoke_i.cpp'), '-sDISABLE_EXCEPTION_CATCHING=0', '-o', 'a.html'] + args)
        output = read_file('a.js')
        self.assertContained('invoke_i', output)
        self.assertNotContained('invoke_ii', output)
        self.assertNotContained('invoke_v', output)
  
    @parameterized({
      'O0': (False, ['-O0']), # noqa
      'O0_emit': (True, ['-O0', '-sEMIT_EMSCRIPTEN_LICENSE']), # noqa
      'O2': (False, ['-O2']), # noqa
      'O2_emit': (True, ['-O2', '-sEMIT_EMSCRIPTEN_LICENSE']), # noqa
      'O2_js_emit': (True, ['-O2', '-sEMIT_EMSCRIPTEN_LICENSE', '-sWASM=0']), # noqa
      'O2_closure': (False, ['-O2', '--closure=1']), # noqa
      'O2_closure_emit': (True, ['-O2', '-sEMIT_EMSCRIPTEN_LICENSE', '--closure=1']), # noqa
      'O2_closure_js_emit': (True, ['-O2', '-sEMIT_EMSCRIPTEN_LICENSE', '--closure=1', '-sWASM=0']), # noqa
    })
    def test_emscripten_license(self, expect_license, args):
      # fastcomp does not support the new license flag
      self.run_process([EMCC, test_file('hello_world.c')] + args)
      js = read_file('a.out.js')
      licenses_found = len(re.findall('Copyright [0-9]* The Emscripten Authors', js))
      if expect_license:
        self.assertNotEqual(licenses_found, 0, 'Unable to find license block in output file!')
        self.assertEqual(licenses_found, 1, 'Found too many license blocks in the output file!')
      else:
        self.assertEqual(licenses_found, 0, 'Found a license block in the output file, but it should not have been there!')
  
    # This test verifies that the generated exports from wasm module only reference the
    # unminified exported name exactly once.  (need to contain the export name once for unminified
    # access from calling code, and should not have the unminified name exist more than once, that
    # would be wasteful for size)
    def test_function_exports_are_small(self):
      def test(args, closure, opt):
        extra_args = args + opt + closure
        print(extra_args)
        args = [EMCC, test_file('long_function_name_in_export.c'), '-o', 'a.html', '-sENVIRONMENT=web', '-sDECLARE_ASM_MODULE_EXPORTS=0', '-Werror'] + extra_args
        self.run_process(args)
  
        output = read_file('a.js')
        try_delete('a.js')
        self.assertNotContained('asm["_thisIsAFunctionExportedFromAsmJsOrWasmWithVeryLongFunction"]', output)
  
        # TODO: Add stricter testing when Wasm side is also optimized: (currently Wasm does still need
        # to reference exports multiple times)
        if '-sWASM=0' in args:
          num_times_export_is_referenced = output.count('thisIsAFunctionExportedFromAsmJsOrWasmWithVeryLongFunction')
          self.assertEqual(num_times_export_is_referenced, 1)
  
      for closure in [[], ['--closure=1']]:
        for opt in [['-O2'], ['-O3'], ['-Os']]:
          test(['-sWASM=0'], closure, opt)
          test(['-sWASM_ASYNC_COMPILATION=0'], closure, opt)
  
    # TODO: Debug why the code size is different on Windows and Mac. Also, for
    # some unknown reason (at time of writing), this test is not skipped on the
    # Windows and Mac autorollers, despite the bot being correctly configured to
    # skip this test in all three platforms (Linux, Mac, and Windows).
    # The no_windows/no_mac decorators also solve that problem.
    @no_windows("Code size is slightly different on Windows")
    @no_mac("Code size is slightly different on Mac")
    @parameterized({
      'hello_world_wasm': ('hello_world', False, True),
      'hello_world_wasm2js': ('hello_world', True, True),
      'random_printf_wasm': ('random_printf', False),
      'random_printf_wasm2js': ('random_printf', True),
      'hello_webgl_wasm': ('hello_webgl', False),
      'hello_webgl_wasm2js': ('hello_webgl', True),
      'hello_webgl2_wasm': ('hello_webgl2', False),
      'hello_webgl2_wasm2js': ('hello_webgl2', True),
      'math': ('math', False),
    })
    def test_minimal_runtime_code_size(self, test_name, js, compare_js_output=False):
      smallest_code_size_args = ['-sMINIMAL_RUNTIME=2',
                                 '-sENVIRONMENT=web',
                                 '-sTEXTDECODER=2',
                                 '-sABORTING_MALLOC=0',
                                 '-sALLOW_MEMORY_GROWTH=0',
                                 '-sSUPPORT_ERRNO=0',
                                 '-sDECLARE_ASM_MODULE_EXPORTS',
                                 '-sMALLOC=emmalloc',
                                 '-sGL_EMULATE_GLES_VERSION_STRING_FORMAT=0',
                                 '-sGL_EXTENSIONS_IN_PREFIXED_FORMAT=0',
                                 '-sGL_SUPPORT_AUTOMATIC_ENABLE_EXTENSIONS=0',
                                 '-sGL_SUPPORT_SIMPLE_ENABLE_EXTENSIONS=0',
                                 '-sGL_TRACK_ERRORS=0',
                                 '-sGL_SUPPORT_EXPLICIT_SWAP_CONTROL=0',
                                 '-sGL_POOL_TEMP_BUFFERS=0',
                                 '-sMIN_CHROME_VERSION=58',
                                 '-sGL_WORKAROUND_SAFARI_GETCONTEXT_BUG=0',
                                 '-sNO_FILESYSTEM',
                                 '--output_eol', 'linux',
                                 '-Oz',
                                 '--closure=1',
                                 '-DNDEBUG',
                                 '-ffast-math']
  
      wasm2js = ['-sWASM=0', '--memory-init-file', '1']
  
      math_sources = [test_file('code_size/math.c')]
      hello_world_sources = [test_file('small_hello_world.c'),
                             '-sUSES_DYNAMIC_ALLOC=0']
      random_printf_sources = [test_file('hello_random_printf.c'),
                               '-sUSES_DYNAMIC_ALLOC=0',
                               '-sSINGLE_FILE']
      hello_webgl_sources = [test_file('minimal_webgl/main.cpp'),
                             test_file('minimal_webgl/webgl.c'),
                             '--js-library', test_file('minimal_webgl/library_js.js'),
                             '-sUSES_DYNAMIC_ALLOC', '-lwebgl.js',
                             '-sMODULARIZE']
      hello_webgl2_sources = hello_webgl_sources + ['-sMAX_WEBGL_VERSION=2']
  
      sources = {
        'hello_world': hello_world_sources,
        'random_printf': random_printf_sources,
        'hello_webgl': hello_webgl_sources,
        'math': math_sources,
        'hello_webgl2': hello_webgl2_sources}[test_name]
  
      def print_percent(actual, expected):
        if actual == expected:
          return ''
        return ' ({:+.2f}%)'.format((actual - expected) * 100.0 / expected)
  
      outputs = ['a.html', 'a.js']
  
      args = smallest_code_size_args[:]
  
      if js:
        outputs += ['a.mem']
        args += wasm2js
        test_name += '_wasm2js'
      else:
        outputs += ['a.wasm']
        test_name += '_wasm'
  
      if '-sSINGLE_FILE' in sources:
        outputs = ['a.html']
  
      results_file = test_file('code_size', test_name + '.json')
  
      expected_results = {}
      try:
        expected_results = json.loads(read_file(results_file))
      except Exception:
        if not common.EMTEST_REBASELINE:
          raise
  
      args = [EMCC, '-o', 'a.html'] + args + sources
      print(shared.shlex_join(args))
      self.run_process(args)
  
      def get_file_gzipped_size(f):
        f_gz = f + '.gz'
        with gzip.open(f_gz, 'wb') as gzf:
          gzf.write(read_binary(f))
        size = os.path.getsize(f_gz)
        try_delete(f_gz)
        return size
  
      # For certain tests, don't just check the output size but check
      # the full JS output matches the expectations.  That means that
      # any change that touches those core lines of output will need
      # to rebaseline this test.  However:
      # a) such changes deserve extra scrutiny
      # b) such changes should be few and far between
      # c) rebaselining is trivial (just run with --rebaseline)
      # Note that we do not compare the full wasm output since that is
      # even more fragile and can change with LLVM updates.
      if compare_js_output:
        js_out = test_file('code_size', test_name + '.js')
        terser = shared.get_npm_cmd('terser')
        # N.b. this requires node in PATH, it does not run against NODE from
        # Emscripten config file. If you have this line fail, make sure 'node' is
        # visible in PATH.
        self.run_process(terser + ['-b', 'beautify=true', 'a.js', '-o', 'pretty.js'])
        self.assertFileContents(js_out, read_file('pretty.js'))
  
      obtained_results = {}
      total_output_size = 0
      total_expected_size = 0
      total_output_size_gz = 0
      total_expected_size_gz = 0
      for f in outputs:
        f_gz = f + '.gz'
        expected_size = expected_results[f] if f in expected_results else float('inf')
        expected_size_gz = expected_results[f_gz] if f_gz in expected_results else float('inf')
        size = os.path.getsize(f)
        size_gz = get_file_gzipped_size(f)
  
        obtained_results[f] = size
        obtained_results[f_gz] = size_gz
  
        if size != expected_size and (f.endswith('.js') or f.endswith('.html')):
          print('Contents of ' + f + ': ')
          print(read_file(f))
  
        print('size of ' + f + ' == ' + str(size) + ', expected ' + str(expected_size) + ', delta=' + str(size - expected_size) + print_percent(size, expected_size))
        print('size of ' + f_gz + ' == ' + str(size_gz) + ', expected ' + str(expected_size_gz) + ', delta=' + str(size_gz - expected_size_gz) + print_percent(size_gz, expected_size_gz))
  
        # Hack: Generated .mem initializer files have different sizes on different
        # platforms (Windows gives x, CircleCI Linux gives x-17 bytes, my home
        # Linux gives x+2 bytes..). Likewise asm.js files seem to be affected by
        # the LLVM IR text names, which lead to asm.js names, which leads to
        # difference code size, which leads to different relooper choices,
        # as a result leading to slightly different total code sizes.
        # Also as of July 16, 2020, wasm2js files have different sizes on
        # different platforms (Windows and MacOS improved to give a slightly
        # better thing than Linux does, which didn't change; this just
        # started to happen on CI, not in response to a code update, so it
        # may have been present all along but just noticed now; it only
        # happens in wasm2js, so it may be platform-nondeterminism in closure
        # compiler).
        # TODO: identify what is causing this. meanwhile allow some amount of slop
        if not common.EMTEST_REBASELINE:
          if js:
            slop = 30
          else:
            slop = 20
          if size <= expected_size + slop and size >= expected_size - slop:
            size = expected_size
  
        # N.B. even though the test code above prints out gzip compressed sizes, regression testing is done against uncompressed sizes
        # this is because optimizing for compressed sizes can be unpredictable and sometimes counterproductive
        total_output_size += size
        total_expected_size += expected_size
  
        total_output_size_gz += size_gz
        total_expected_size_gz += expected_size_gz
  
      obtained_results['total'] = total_output_size
      obtained_results['total_gz'] = total_output_size_gz
      print('Total output size=' + str(total_output_size) + ' bytes, expected total size=' + str(total_expected_size) + ', delta=' + str(total_output_size - total_expected_size) + print_percent(total_output_size, total_expected_size))
      print('Total output size gzipped=' + str(total_output_size_gz) + ' bytes, expected total size gzipped=' + str(total_expected_size_gz) + ', delta=' + str(total_output_size_gz - total_expected_size_gz) + print_percent(total_output_size_gz, total_expected_size_gz))
  
      if common.EMTEST_REBASELINE:
        open(results_file, 'w').write(json.dumps(obtained_results, indent=2) + '\n')
      else:
        if total_output_size > total_expected_size:
          print(f'Oops, overall generated code size regressed by {total_output_size - total_expected_size} bytes!')
          print('If this is expected, rerun the test with --rebaseline to update the expected sizes')
        if total_output_size < total_expected_size:
          print(f'Hey amazing, overall generated code size was improved by {total_expected_size - total_output_size} bytes!')
          print('If this is expected, rerun the test with --rebaseline to update the expected sizes')
        self.assertEqual(total_output_size, total_expected_size)
  
    # Tests the library_c_preprocessor.js functionality.
    def test_c_preprocessor(self):
      self.run_process([EMXX, test_file('test_c_preprocessor.c'), '--js-library', path_from_root('src/library_c_preprocessor.js'), '-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$remove_cpp_comments_in_shaders,$preprocess_c_code'])
      normal = self.run_js('a.out.js')
      print(str(normal))
  
    # Test that legacy settings that have been fixed to a specific value and their value can no longer be changed,
    def test_legacy_settings_forbidden_to_change(self):
      stderr = self.expect_fail([EMCC, '-sMEMFS_APPEND_TO_TYPED_ARRAYS=0', test_file('hello_world.c')])
      self.assertContained('MEMFS_APPEND_TO_TYPED_ARRAYS=0 is no longer supported', stderr)
  
      self.run_process([EMCC, '-sMEMFS_APPEND_TO_TYPED_ARRAYS', test_file('hello_world.c')])
      self.run_process([EMCC, '-sPRECISE_I64_MATH=2', test_file('hello_world.c')])
  
    def test_jsmath(self):
      self.run_process([EMXX, test_file('other/jsmath.cpp'), '-Os', '-o', 'normal.js', '--closure', '0'])
      normal_js_size = os.path.getsize('normal.js')
      normal_wasm_size = os.path.getsize('normal.wasm')
      self.run_process([EMXX, test_file('other/jsmath.cpp'), '-Os', '-o', 'jsmath.js', '-sJS_MATH', '--closure', '0'])
      jsmath_js_size = os.path.getsize('jsmath.js')
      jsmath_wasm_size = os.path.getsize('jsmath.wasm')
      # js math increases JS size, but decreases wasm, and wins overall
      # it would win more with closure, but no point in making the test slower)
      self.assertLess(normal_js_size, jsmath_js_size)
      self.assertLess(jsmath_wasm_size, normal_wasm_size)
      self.assertLess(jsmath_js_size + jsmath_wasm_size, 0.90 * (normal_js_size + normal_wasm_size))
      # js math has almost identical output, but misses some corner cases, 4 out of 34
      normal = self.run_js('normal.js').splitlines()
      jsmath = self.run_js('jsmath.js').splitlines()
      assert len(normal) == len(jsmath)
      diff = 0
      for i in range(len(normal)):
        if normal[i] != jsmath[i]:
          diff += 1
      self.assertEqual(diff, 4)
  
    def test_strict_mode_hello_world(self):
      # Verify that strict mode can be used for simple hello world program both
      # via the environment EMCC_STRICT=1 and from the command line `-s STRICT`
      cmd = [EMCC, test_file('hello_world.c'), '-sSTRICT']
      self.run_process(cmd)
      with env_modify({'EMCC_STRICT': '1'}):
        self.do_runf(test_file('hello_world.c'), 'hello, world!')
  
    def test_legacy_settings(self):
      cmd = [EMCC, test_file('hello_world.c'), '-sSPLIT_MEMORY=0']
  
      # By default warnings are not shown
      stderr = self.run_process(cmd, stderr=PIPE).stderr
      self.assertNotContained('warning', stderr)
  
      # Adding or -Wlegacy-settings enables the warning
      stderr = self.run_process(cmd + ['-Wlegacy-settings'], stderr=PIPE).stderr
      self.assertContained('warning: use of legacy setting: SPLIT_MEMORY', stderr)
      self.assertContained('[-Wlegacy-settings]', stderr)
  
    def test_strict_mode_legacy_settings(self):
      cmd = [EMCC, test_file('hello_world.c'), '-sSPLIT_MEMORY=0']
      self.run_process(cmd)
  
      stderr = self.expect_fail(cmd + ['-sSTRICT'])
      self.assertContained('legacy setting used in strict mode: SPLIT_MEMORY', stderr)
  
      with env_modify({'EMCC_STRICT': '1'}):
        stderr = self.expect_fail(cmd)
        self.assertContained('legacy setting used in strict mode: SPLIT_MEMORY', stderr)
  
    def test_strict_mode_legacy_settings_runtime(self):
      # Verify that legacy settings are not accessible at runtime under strict
      # mode.
      self.set_setting('RETAIN_COMPILER_SETTINGS')
      src = r'''\
      #include <stdio.h>
      #include <emscripten.h>
  
      int main() {
        printf("BINARYEN_METHOD: %s\n", (char*)emscripten_get_compiler_setting("BINARYEN_METHOD"));
        return 0;
      }
      '''
      self.do_run(src, 'BINARYEN_METHOD: native-wasm')
      with env_modify({'EMCC_STRICT': '1'}):
        self.do_run(src, 'invalid compiler setting: BINARYEN_METHOD')
      self.set_setting('STRICT')
      self.do_run(src, 'invalid compiler setting: BINARYEN_METHOD')
  
    def test_renamed_setting(self):
      # Verify that renamed settings are available by either name (when not in
      # strict mode.
      self.set_setting('RETAIN_COMPILER_SETTINGS')
      src = r'''\
      #include <stdio.h>
      #include <emscripten.h>
  
      int main() {
        printf("%d %d\n",
          emscripten_get_compiler_setting("BINARYEN_ASYNC_COMPILATION"),
          emscripten_get_compiler_setting("WASM_ASYNC_COMPILATION"));
        return 0;
      }
      '''
  
      # Setting the new name should set both
      self.set_setting('WASM_ASYNC_COMPILATION', 0)
      self.do_run(src, '0 0')
      self.set_setting('WASM_ASYNC_COMPILATION')
      self.do_run(src, '1 1')
      self.clear_setting('WASM_ASYNC_COMPILATION')
  
      # Setting the old name should set both
      self.set_setting('BINARYEN_ASYNC_COMPILATION', 0)
      self.do_run(src, '0 0')
      self.set_setting('BINARYEN_ASYNC_COMPILATION')
      self.do_run(src, '1 1')
  
    def test_strict_mode_legacy_settings_library(self):
      create_file('lib.js', r'''
  #if SPLIT_MEMORY
  #endif
  ''')
      cmd = [EMCC, test_file('hello_world.c'), '-o', 'out.js', '--js-library', 'lib.js']
      self.run_process(cmd)
      self.assertContained('ReferenceError: SPLIT_MEMORY is not defined', self.expect_fail(cmd + ['-sSTRICT']))
      with env_modify({'EMCC_STRICT': '1'}):
        self.assertContained('ReferenceError: SPLIT_MEMORY is not defined', self.expect_fail(cmd))
  
    def test_strict_mode_link_cxx(self):
      # In strict mode C++ programs fail to link unless run with `em++`.
      self.run_process([EMXX, '-sSTRICT', test_file('hello_libcxx.cpp')])
      err = self.expect_fail([EMCC, '-sSTRICT', test_file('hello_libcxx.cpp')])
      self.assertContained('undefined symbol: std::__2::cout', err)
  
    def test_strict_mode_override(self):
      create_file('empty.c', '')
      # IGNORE_MISSING_MAIN default to 1 so this works by default
      self.run_process([EMCC, 'empty.c'])
      # strict mode disables it causing a link error
      self.expect_fail([EMCC, '-sSTRICT', 'empty.c'])
      # explicly setting IGNORE_MISSING_MAIN overrides the STRICT setting
      self.run_process([EMCC, '-sSTRICT', '-sIGNORE_MISSING_MAIN', 'empty.c'])
  
    # Tests the difference between options -s SAFE_HEAP=1 and -s SAFE_HEAP=2.
    def test_safe_heap_2(self):
      self.run_process([EMCC, test_file('safe_heap_2.c'), '-sSAFE_HEAP=1'])
      result = self.run_js('a.out.js', assert_returncode=NON_ZERO)
      self.assertContained('alignment fault', result)
  
      self.run_process([EMCC, test_file('safe_heap_2.c'), '-sSAFE_HEAP=2'])
      result = self.run_js('a.out.js')
      self.assertContained('0 1 2 3 4', result)
  
    def test_safe_heap_log(self):
      self.set_setting('SAFE_HEAP')
      self.set_setting('SAFE_HEAP_LOG')
      self.set_setting('EXIT_RUNTIME')
      src = read_file(test_file('hello_world.c'))
      self.do_run(src, 'SAFE_HEAP load: ')
  
      self.set_setting('WASM', 0)
      self.do_run(src, 'SAFE_HEAP load: ')
  
    def test_mini_printfs(self):
      def test(code):
        with open('src.c', 'w') as f:
          f.write('''
            #include <stdio.h>
            void* unknown_value;
            int main() {
              %s
            }
          ''' % code)
        self.run_process([EMCC, 'src.c', '-O1'])
        return os.path.getsize('a.out.wasm')
  
      i = test('printf("%d", *(int*)unknown_value);')
      f = test('printf("%f", *(double*)unknown_value);')
      lf = test('printf("%Lf", *(long double*)unknown_value);')
      both = test('printf("%d", *(int*)unknown_value); printf("%Lf", *(long double*)unknown_value);')
      print(f'int:{i} float:{f} double:{lf}: both{both}')
  
      # iprintf is much smaller than printf with float support
      self.assertGreater(i, f - 3500)
      self.assertLess(i, f - 3000)
      # __small_printf is somewhat smaller than printf with long double support
      self.assertGreater(f, lf - 900)
      self.assertLess(f, lf - 500)
      # both is a little bigger still
      self.assertGreater(lf, both - 110)
      self.assertLess(lf, both - 50)
  
    @parameterized({
      'normal': ([], '''\
  0.000051 => -5.123719529365189373493194580078e-05
  0.000051 => -5.123719300544352718866300544498e-05
  0.000051 => -5.123719300544352718866300544498e-05
  '''),
      'full_long_double': (['-sPRINTF_LONG_DOUBLE'], '''\
  0.000051 => -5.123719529365189373493194580078e-05
  0.000051 => -5.123719300544352718866300544498e-05
  0.000051 => -5.123719300544352710023893104250e-05
  '''),
    })
    def test_long_double_printing(self, args, expected):
      create_file('src.cpp', r'''
  #include <stdio.h>
  
  int main(void) {
      float f = 5.123456789e-5;
      double d = 5.123456789e-5;
      long double ld = 5.123456789e-5;
      printf("%f => %.30e\n", f, f  / (f - 1));
      printf("%f => %.30e\n", d, d / (d - 1));
      printf("%Lf => %.30Le\n", ld, ld / (ld - 1));
  }
      ''')
      self.run_process([EMXX, 'src.cpp'] + args)
      self.assertContained(expected, self.run_js('a.out.js'))
  
    # Tests that passing -s MALLOC=none will not include system malloc() to the build.
    def test_malloc_none(self):
      stderr = self.expect_fail([EMCC, test_file('malloc_none.c'), '-sMALLOC=none'])
      self.assertContained('undefined symbol: malloc', stderr)
  
    @parameterized({
      'c': ['c', []],
      'cpp': ['cpp', []],
      'growth': ['cpp', ['-sALLOW_MEMORY_GROWTH']],
    })
    def test_lsan_leaks(self, ext, args):
      self.do_smart_test(test_file('other/test_lsan_leaks.' + ext),
                         emcc_args=['-fsanitize=leak'] + args,
                         assert_returncode=NON_ZERO, literals=[
        'Direct leak of 2048 byte(s) in 1 object(s) allocated from',
        'Direct leak of 1337 byte(s) in 1 object(s) allocated from',
        'Direct leak of 42 byte(s) in 1 object(s) allocated from',
      ])
  
    @parameterized({
      'c': ['c', [
        r'in malloc .*lsan_interceptors\.cpp:\d+:\d+',
        r'(?im)in f (|[/a-z\.]:).*/test_lsan_leaks\.c:6:21$',
        r'(?im)in main (|[/a-z\.]:).*/test_lsan_leaks\.c:10:16$',
        r'(?im)in main (|[/a-z\.]:).*/test_lsan_leaks\.c:12:3$',
        r'(?im)in main (|[/a-z\.]:).*/test_lsan_leaks\.c:13:3$',
      ]],
      'cpp': ['cpp', [
        r'in operator new\[\]\(unsigned long\) .*lsan_interceptors\.cpp:\d+:\d+',
        r'(?im)in f\(\) (|[/a-z\.]:).*/test_lsan_leaks\.cpp:4:21$',
        r'(?im)in main (|[/a-z\.]:).*/test_lsan_leaks\.cpp:8:16$',
        r'(?im)in main (|[/a-z\.]:).*/test_lsan_leaks\.cpp:10:3$',
        r'(?im)in main (|[/a-z\.]:).*/test_lsan_leaks\.cpp:11:3$',
      ]],
    })
    def test_lsan_stack_trace(self, ext, regexes):
      self.do_smart_test(test_file('other/test_lsan_leaks.' + ext),
                         emcc_args=['-fsanitize=leak', '-gsource-map'],
                         assert_returncode=NON_ZERO, literals=[
        'Direct leak of 2048 byte(s) in 1 object(s) allocated from',
        'Direct leak of 1337 byte(s) in 1 object(s) allocated from',
        'Direct leak of 42 byte(s) in 1 object(s) allocated from',
      ], regexes=regexes)
  
    @parameterized({
      'c': ['c'],
      'cpp': ['cpp'],
    })
    def test_lsan_no_leak(self, ext):
      self.do_smart_test(test_file('other/test_lsan_no_leak.' + ext),
                         emcc_args=['-fsanitize=leak', '-sASSERTIONS=0'],
                         regexes=[r'^\s*$'])
  
    def test_lsan_no_stack_trace(self):
      self.do_smart_test(test_file('other/test_lsan_leaks.c'),
                         emcc_args=['-fsanitize=leak', '-DDISABLE_CONTEXT'],
                         assert_returncode=NON_ZERO, literals=[
        'Direct leak of 3427 byte(s) in 3 object(s) allocated from:',
        'SUMMARY: LeakSanitizer: 3427 byte(s) leaked in 3 allocation(s).',
      ])
  
    def test_asan_null_deref(self):
      self.do_smart_test(test_file('other/test_asan_null_deref.c'),
                         emcc_args=['-fsanitize=address'],
                         assert_returncode=NON_ZERO, literals=[
        'AddressSanitizer: null-pointer-dereference on address',
      ])
  
    def test_asan_memory_growth(self):
      self.do_smart_test(test_file('other/test_asan_null_deref.c'),
                         emcc_args=['-fsanitize=address', '-sALLOW_MEMORY_GROWTH'],
                         assert_returncode=NON_ZERO, literals=[
        'AddressSanitizer: null-pointer-dereference on address',
      ])
  
    def test_asan_no_stack_trace(self):
      self.do_smart_test(test_file('other/test_lsan_leaks.c'),
                         emcc_args=['-fsanitize=address', '-DDISABLE_CONTEXT', '-sEXIT_RUNTIME'],
                         assert_returncode=NON_ZERO, literals=[
        'Direct leak of 3427 byte(s) in 3 object(s) allocated from:',
        'SUMMARY: AddressSanitizer: 3427 byte(s) leaked in 3 allocation(s).',
      ])
  
    def test_asan_pthread_stubs(self):
      self.do_smart_test(test_file('other/test_asan_pthread_stubs.c'), emcc_args=['-fsanitize=address'])
  
    def test_asan_strncpy(self):
      # Regression test for asan false positives in strncpy:
      # https://github.com/emscripten-core/emscripten/issues/14618
      self.do_smart_test(test_file('other/test_asan_strncpy.c'), emcc_args=['-fsanitize=address'])
  
    @node_pthreads
    def test_proxy_to_pthread_stack(self):
      # Check that the proxied main gets run with TOTAL_STACK setting and not
      # DEFAULT_PTHREAD_STACK_SIZE.
      self.do_smart_test(test_file('other/test_proxy_to_pthread_stack.c'),
                         ['success'],
                         emcc_args=['-sUSE_PTHREADS', '-sPROXY_TO_PTHREAD',
                                    '-sDEFAULT_PTHREAD_STACK_SIZE=64kb',
                                    '-sTOTAL_STACK=128kb', '-sEXIT_RUNTIME',
                                    '--profiling-funcs'])
  
    @parameterized({
      'async': ['-sWASM_ASYNC_COMPILATION'],
      'sync': ['-sWASM_ASYNC_COMPILATION=0'],
    })
    def test_offset_converter(self, *args):
      self.do_smart_test(test_file('other/test_offset_converter.c'),
                         emcc_args=['-sUSE_OFFSET_CONVERTER', '-gsource-map'] + list(args), literals=['ok'])
  
    @no_windows('ptys and select are not available on windows')
    def test_build_error_color(self):
      create_file('src.c', 'int main() {')
      returncode, output = self.run_on_pty([EMCC, 'src.c'])
      self.assertNotEqual(returncode, 0)
      self.assertIn(b"\x1b[1msrc.c:1:13: \x1b[0m\x1b[0;1;31merror: \x1b[0m\x1b[1mexpected '}'\x1b[0m", output)
      self.assertIn(b"\x1b[31merror: ", output)
  
    @parameterized({
      'fno_diagnostics_color': ['-fno-diagnostics-color'],
      'fdiagnostics_color_never': ['-fdiagnostics-color=never'],
    })
    @no_windows('ptys and select are not available on windows')
    def test_pty_no_color(self, flag):
      create_file('src.c', 'int main() {')
  
      returncode, output = self.run_on_pty([EMCC, flag, 'src.c'])
      self.assertNotEqual(returncode, 0)
      self.assertNotIn(b'\x1b', output)
  
    def test_sanitizer_color(self):
      create_file('src.c', '''
        #include <emscripten.h>
        int main() {
          int *p = 0, q;
          EM_ASM({ Module.printWithColors = true; });
          q = *p;
        }
      ''')
      self.run_process([EMCC, '-fsanitize=null', 'src.c'])
      output = self.run_js('a.out.js')
      self.assertIn('\x1b[1msrc.c', output)
  
    def test_main_reads_params(self):
      create_file('no.c', '''
        int main() {
          return 42;
        }
      ''')
      self.run_process([EMCC, 'no.c', '-O3', '-o', 'no.js'])
      no = os.path.getsize('no.js')
      create_file('yes.c', '''
        int main(int argc, char **argv) {
          return argc;
        }
      ''')
      self.run_process([EMCC, 'yes.c', '-O3', '-o', 'yes.js'])
      yes = os.path.getsize('yes.js')
      # not having to set up argc/argv allows us to avoid including a
      # significant amount of JS for string support (which is not needed
      # otherwise in such a trivial program).
      self.assertLess(no, 0.95 * yes)
  
    def test_INCOMING_MODULE_JS_API(self):
      def test(args):
        self.run_process([EMCC, test_file('hello_world.c'), '-O3', '--closure=1', '-sENVIRONMENT=node,shell'] + args)
        for engine in config.JS_ENGINES:
          self.assertContained('hello, world!', self.run_js('a.out.js', engine=engine))
        # ignore \r which on windows can increase the size
        return len(read_file('a.out.js').replace('\r', ''))
      normal = test([])
      changed = test(['-sINCOMING_MODULE_JS_API=[]'])
      print('sizes', normal, changed)
      # Changing this option to [] should decrease code size.
      self.assertLess(changed, normal)
      # Check an absolute code size as well, with some slack.
      self.assertLess(abs(changed - 5150), 150)
  
    def test_llvm_includes(self):
      create_file('atomics.c', '#include <stdatomic.h>')
      self.build('atomics.c')
  
    def test_mmap_and_munmap(self):
      emcc_args = []
      for f in ['data_ro.dat', 'data_rw.dat']:
          create_file(f, 'Test file')
          emcc_args.extend(['--embed-file', f])
      self.do_other_test('test_mmap_and_munmap.cpp', emcc_args)
  
    def test_mmap_and_munmap_anonymous(self):
      self.do_other_test('test_mmap_and_munmap_anonymous.cpp', emcc_args=['-sNO_FILESYSTEM'])
  
    def test_mmap_and_munmap_anonymous_asan(self):
      self.do_other_test('test_mmap_and_munmap_anonymous.cpp', emcc_args=['-sNO_FILESYSTEM', '-fsanitize=address'])
  
    def test_mmap_memorygrowth(self):
      self.do_other_test('test_mmap_memorygrowth.cpp', ['-sALLOW_MEMORY_GROWTH'])
  
    def test_files_and_module_assignment(self):
      # a pre-js can set Module to a new object or otherwise undo file preloading/
      # embedding changes to Module.preRun. we show an error to avoid confusion
      create_file('pre.js', 'Module = {};')
      create_file('src.cpp', r'''
        #include <stdio.h>
        int main() {
          printf("file exists: %d\n", !!fopen("src.cpp", "rb"));
        }
      ''')
      self.run_process([EMXX, 'src.cpp', '--pre-js', 'pre.js', '--embed-file', 'src.cpp'])
      result = self.run_js('a.out.js', assert_returncode=NON_ZERO)
      self.assertContained('Module.preRun should exist because file support used it; did a pre-js delete it?', result)
  
      def test_error(pre):
        create_file('pre.js', pre)
        self.run_process([EMXX, 'src.cpp', '--pre-js', 'pre.js', '--embed-file', 'src.cpp'])
        result = self.run_js('a.out.js', assert_returncode=NON_ZERO)
        self.assertContained('All preRun tasks that exist before user pre-js code should remain after; did you replace Module or modify Module.preRun?', result)
  
      # error if the user replaces Module or Module.preRun
      test_error('Module = { preRun: [] };')
      test_error('Module.preRun = [];')
  
    def test_EMSCRIPTEN_and_STRICT(self):
      # __EMSCRIPTEN__ is the proper define; we support EMSCRIPTEN for legacy
      # code, unless STRICT is enabled.
      create_file('src.c', '''
        #ifndef EMSCRIPTEN
        #error "not defined"
        #endif
      ''')
      self.run_process([EMCC, 'src.c', '-c'])
      self.expect_fail([EMCC, 'src.c', '-sSTRICT', '-c'])
  
    def test_exception_settings(self):
      for catching, throwing, opts in itertools.product([0, 1], repeat=3):
        cmd = [EMXX, test_file('other/exceptions_modes_symbols_defined.cpp'), '-sDISABLE_EXCEPTION_THROWING=%d' % (1 - throwing), '-sDISABLE_EXCEPTION_CATCHING=%d' % (1 - catching), '-O%d' % opts]
        print(cmd)
        if not throwing and not catching:
          self.assertContained('DISABLE_EXCEPTION_THROWING was set (likely due to -fno-exceptions), which means no C++ exception throwing support code is linked in, but such support is required', self.expect_fail(cmd))
        elif not throwing and catching:
          self.assertContained('DISABLE_EXCEPTION_THROWING was set (probably from -fno-exceptions) but is not compatible with enabling exception catching (DISABLE_EXCEPTION_CATCHING=0)', self.expect_fail(cmd))
        else:
          self.run_process(cmd)
  
    def test_fignore_exceptions(self):
      # the new clang flag -fignore-exceptions basically is the same as -s DISABLE_EXCEPTION_CATCHING=1,
      # that is, it allows throwing, but emits no support code for catching.
      self.run_process([EMXX, test_file('other/exceptions_modes_symbols_defined.cpp'), '-sDISABLE_EXCEPTION_CATCHING=0'])
      enable_size = os.path.getsize('a.out.wasm')
      self.run_process([EMXX, test_file('other/exceptions_modes_symbols_defined.cpp'), '-sDISABLE_EXCEPTION_CATCHING'])
      disable_size = os.path.getsize('a.out.wasm')
      self.run_process([EMXX, test_file('other/exceptions_modes_symbols_defined.cpp'), '-s', '-fignore-exceptions'])
      ignore_size = os.path.getsize('a.out.wasm')
      self.assertGreater(enable_size, disable_size)
      self.assertEqual(disable_size, ignore_size)
  
    @parameterized({
      # exceptions are off by default
      'off':  ([], [], False),
      # enabling exceptions at link and compile works
      'on': (['-fexceptions'], ['-fexceptions'], True),
      # just compile isn't enough as the JS runtime lacks support
      'compile_only': (['-fexceptions'], [], False),
      # just link isn't enough as codegen didn't emit exceptions support
      'link_only': ([], ['-fexceptions'], False),
    })
    def test_f_exception(self, compile_flags, link_flags, expect_caught):
      create_file('src.cpp', r'''
        #include <stdio.h>
        int main () {
          try {
            throw 42;
          } catch (int e) {
            printf("CAUGHT: %d\n", e);
          }
          return 0;
        }
      ''')
      self.run_process([EMXX, 'src.cpp', '-c', '-o', 'src.o'] + compile_flags)
      self.run_process([EMXX, 'src.o'] + link_flags)
      result = self.run_js('a.out.js', assert_returncode=0 if expect_caught else NON_ZERO)
      if not expect_caught:
        self.assertContainedIf('exception catching is disabled, this exception cannot be caught', result, expect_caught)
      self.assertContainedIf('CAUGHT', result, expect_caught)
  
    def test_exceptions_with_closure_and_without_catching(self):
      # using invokes will require setThrew(), and closure will error if it is not
      # defined. this test checks that we define it even without catching any
      # exceptions (if we did catch exceptions, that would include library code
      # that would use setThrew() anyhow)
      create_file('src.cpp', r'''
        #include <stdio.h>
        #include <emscripten.h>
  
        struct A {
          ~A() {
            puts("~A");
          }
        };
  
        int main() {
          // Construct an instance of a class with a destructor, which will cause the
          // use of invokes to ensure its destructor runs.
          A a;
          throw 5;
        }
        ''')
      self.run_process([EMCC, 'src.cpp', '-fexceptions', '--closure=1'])
  
    def test_assertions_on_internal_api_changes(self):
      create_file('src.c', r'''
        #include <emscripten.h>
        int main(int argc, char **argv) {
          EM_ASM({
            try {
              Module['read'];
              out('it should not be there');
            } catch(e) {
              out('error: ' + e);
            }
          });
        }
      ''')
      self.run_process([EMCC, 'src.c', '-sASSERTIONS'])
      self.assertContained('Module.read has been replaced with plain read', self.run_js('a.out.js'))
  
    def test_assertions_on_incoming_module_api_changes(self):
      create_file('pre.js', r'''
        var Module = {
          read: function() {}
        }
      ''')
      self.run_process([EMCC, test_file('hello_world.c'), '-sASSERTIONS', '--pre-js', 'pre.js'])
      self.assertContained('Module.read option was removed', self.run_js('a.out.js', assert_returncode=NON_ZERO))
  
    def test_assertions_on_outgoing_module_api_changes(self):
      create_file('src.cpp', r'''
        #include <emscripten.h>
        int main() {
          EM_ASM({
            console.log();
            function check(name) {
              try {
                Module[name];
                console.log("success: " + name);
              } catch(e) {
              }
            }
            check("read");
            // TODO check("setWindowTitle");
            check("wasmBinary");
            check("arguments");
          });
        }
      ''')
      self.run_process([EMXX, 'src.cpp', '-sASSERTIONS'])
      self.assertContained('''
  Aborted(Module.read has been replaced with plain read_ (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name))
  Aborted(Module.wasmBinary has been replaced with plain wasmBinary (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name))
  Aborted(Module.arguments has been replaced with plain arguments_ (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name))
  ''', self.run_js('a.out.js'))
  
    def test_assertions_on_ready_promise(self):
      # check that when assertions are on we give useful error messages for
      # mistakenly thinking the Promise is an instance. I.e., once you could do
      # Module()._main to get an instance and the main function, but after
      # the breaking change in #10697 Module() now returns a promise, and to get
      # the instance you must use .then() to get a callback with the instance.
      create_file('test.js', r'''
        try {
          Module()._main;
        } catch(e) {
          console.log(e);
        }
        try {
          Module().onRuntimeInitialized = 42;
        } catch(e) {
          console.log(e);
        }
      ''')
      self.run_process([EMCC, test_file('hello_world.c'), '-sMODULARIZE', '-sASSERTIONS', '--extern-post-js', 'test.js'])
      # A return code of 7 is from the unhandled Promise rejection
      out = self.run_js('a.out.js', assert_returncode=7)
      self.assertContained('You are getting _main on the Promise object, instead of the instance. Use .then() to get called back with the instance, see the MODULARIZE docs in src/settings.js', out)
      self.assertContained('You are setting onRuntimeInitialized on the Promise object, instead of the instance. Use .then() to get called back with the instance, see the MODULARIZE docs in src/settings.js', out)
  
    def test_em_asm_duplicate_strings(self):
      # We had a regression where tow different EM_ASM strings from two diffferent
      # object files were de-duplicated in wasm-emscripten-finalize.  This used to
      # work when we used zero-based index for store the JS strings, but once we
      # switched to absolute addresses the string needs to exist twice in the JS
      # file.
      create_file('foo.c', '''
        #include <emscripten.h>
        void foo() {
          EM_ASM({ console.log('Hello, world!'); });
        }
      ''')
      create_file('main.c', '''
        #include <emscripten.h>
  
        void foo();
  
        int main() {
          foo();
          EM_ASM({ console.log('Hello, world!'); });
          return 0;
        }
      ''')
      self.run_process([EMCC, '-c', 'foo.c'])
      self.run_process([EMCC, '-c', 'main.c'])
      self.run_process([EMCC, 'foo.o', 'main.o'])
      self.assertContained('Hello, world!\nHello, world!\n', self.run_js('a.out.js'))
  
    def test_em_asm_c89(self):
      create_file('src.c', '''
        #include <emscripten/em_asm.h>
        int main() {
          EM_ASM({ console.log('hello'); });
        }\n''')
      self.run_process([EMCC, '-c', 'src.c',
                        '-pedantic', '-Wall', '-Werror',
                        '-Wno-gnu-zero-variadic-macro-arguments'])
  
    def test_em_asm_strict_c(self):
      create_file('src.c', '''
        #include <emscripten/em_asm.h>
        int main() {
          EM_ASM({ console.log('Hello, world!'); });
        }
      ''')
      result = self.run_process([EMCC, '-std=c11', 'src.c'], stderr=PIPE, check=False)
      self.assertNotEqual(result.returncode, 0)
      self.assertIn('EM_ASM does not work in -std=c* modes, use -std=gnu* modes instead', result.stderr)
  
    def test_boost_graph(self):
      self.do_smart_test(test_file('test_boost_graph.cpp'),
                         emcc_args=['-sUSE_BOOST_HEADERS'])
  
    def test_setjmp_em_asm(self):
      create_file('src.c', '''
        #include <emscripten.h>
        #include <setjmp.h>
  
        int main() {
          jmp_buf buf;
          setjmp(buf);
          EM_ASM({
            console.log("hello world");
          });
        }
      ''')
      result = self.run_process([EMCC, 'src.c'], stderr=PIPE, check=False)
      self.assertNotEqual(result.returncode, 0)
      self.assertIn('Cannot use EM_ASM* alongside setjmp/longjmp', result.stderr)
      self.assertIn('Please consider using EM_JS, or move the EM_ASM into another function.', result.stderr)
  
    def test_missing_stdlibs(self):
      # Certain standard libraries are expected to be useable via -l flags but
      # don't actually exist in our standard library path.  Make sure we don't
      # error out when linking with these flags.
      self.run_process([EMXX, test_file('hello_world.cpp'), '-lm', '-ldl', '-lrt', '-lpthread'])
  
    def test_supported_linker_flags(self):
      out = self.run_process([EMXX, test_file('hello_world.cpp'), '-Wl,-rpath=foo'], stderr=PIPE).stderr
      self.assertContained('warning: ignoring unsupported linker flag: `-rpath=foo`', out)
  
      out = self.run_process([EMXX, test_file('hello_world.cpp'), '-Wl,-rpath-link,foo'], stderr=PIPE).stderr
      self.assertContained('warning: ignoring unsupported linker flag: `-rpath-link`', out)
  
      out = self.run_process([EMXX, test_file('hello_world.cpp'),
                              '-Wl,--no-check-features,-mllvm,--data-sections'], stderr=PIPE).stderr
      self.assertNotContained('warning: ignoring unsupported linker flag', out)
  
      out = self.run_process([EMXX, test_file('hello_world.cpp'), '-Wl,-allow-shlib-undefined'], stderr=PIPE).stderr
      self.assertContained('warning: ignoring unsupported linker flag: `-allow-shlib-undefined`', out)
  
      out = self.run_process([EMXX, test_file('hello_world.cpp'), '-Wl,--allow-shlib-undefined'], stderr=PIPE).stderr
      self.assertContained('warning: ignoring unsupported linker flag: `--allow-shlib-undefined`', out)
  
      out = self.run_process([EMXX, test_file('hello_world.cpp'), '-Wl,-version-script,foo'], stderr=PIPE).stderr
      self.assertContained('warning: ignoring unsupported linker flag: `-version-script`', out)
  
    def test_supported_linker_flag_skip_next(self):
      # Regression test for a bug where skipping an unsupported linker flag
      # could skip the next unrelated linker flag.
      err = self.expect_fail([EMXX, test_file('hello_world.cpp'), '-Wl,-rpath=foo', '-lbar'])
      self.assertContained('error: unable to find library -lbar', err)
  
    def test_linker_flags_pass_through(self):
      err = self.expect_fail([EMXX, test_file('hello_world.cpp'), '-Wl,--waka'])
      self.assertContained('wasm-ld: error: unknown argument: --waka', err)
  
      err = self.expect_fail([EMXX, test_file('hello_world.cpp'), '-Xlinker', '--waka'])
      self.assertContained('wasm-ld: error: unknown argument: --waka', err)
  
    def test_linker_flags_unused(self):
      err = self.run_process([EMXX, test_file('hello_world.cpp'), '-c', '-lbar'], stderr=PIPE).stderr
      self.assertContained("warning: argument unused during compilation: '-lbar' [-Wunused-command-line-argument]", err)
  
    def test_linker_input_unused(self):
      self.run_process([EMXX, '-c', test_file('hello_world.cpp')])
      err = self.run_process([EMCC, 'hello_world.o', '-c', '-o', 'out.o'], stderr=PIPE).stderr
      self.assertContained("warning: hello_world.o: linker input file unused because linking not done [-Wunused-command-line-argument", err)
      # In this case the compiler does not produce any output file.
      self.assertNotExists('out.o')
  
    def test_non_wasm_without_wasm_in_vm(self):
      # Test that our non-wasm output does not depend on wasm support in the vm.
      self.run_process([EMXX, test_file('hello_world.cpp'), '-sWASM=0', '-sENVIRONMENT=node,shell'])
      js = read_file('a.out.js')
      with open('a.out.js', 'w') as f:
        f.write('var WebAssembly = null;\n' + js)
      for engine in config.JS_ENGINES:
        self.assertContained('hello, world!', self.run_js('a.out.js', engine=engine))
  
    def test_empty_output_extension(self):
      # Default to JS output when no extension is present
      self.run_process([EMXX, test_file('hello_world.cpp'), '-Werror', '-o', 'hello'])
      self.assertContained('hello, world!', self.run_js('hello'))
  
    def test_backwards_deps_in_archive(self):
      # Test that JS dependencies from deps_info.json work for code linked via
      # static archives using -l<name>
      self.run_process([EMCC, '-c', test_file('sockets/test_gethostbyname.c'), '-o', 'a.o'])
      self.run_process([LLVM_AR, 'cr', 'liba.a', 'a.o'])
      create_file('empty.c', 'static int foo = 0;')
      self.run_process([EMCC, 'empty.c', '-la', '-L.'])
      self.assertContained('success', self.run_js('a.out.js'))
  
    def test_warning_flags(self):
      self.run_process([EMCC, '-c', '-o', 'hello.o', test_file('hello_world.c')])
      cmd = [EMCC, 'hello.o', '-o', 'a.js', '-g', '--closure=1']
  
      # warning that is enabled by default
      stderr = self.run_process(cmd, stderr=PIPE).stderr
      self.assertContained('emcc: warning: disabling closure because debug info was requested [-Wemcc]', stderr)
  
      # -w to suppress warnings
      stderr = self.run_process(cmd + ['-w'], stderr=PIPE).stderr
      self.assertNotContained('warning', stderr)
  
      # -Wno-invalid-input to suppress just this one warning
      stderr = self.run_process(cmd + ['-Wno-emcc'], stderr=PIPE).stderr
      self.assertNotContained('warning', stderr)
  
      # with -Werror should fail
      stderr = self.expect_fail(cmd + ['-Werror'])
      self.assertContained('error: disabling closure because debug info was requested [-Wemcc] [-Werror]', stderr)
  
      # with -Werror + -Wno-error=<type> should only warn
      stderr = self.run_process(cmd + ['-Werror', '-Wno-error=emcc'], stderr=PIPE).stderr
      self.assertContained('emcc: warning: disabling closure because debug info was requested [-Wemcc]', stderr)
  
      # check that `-Werror=foo` also enales foo
      stderr = self.expect_fail(cmd + ['-Werror=legacy-settings', '-sTOTAL_MEMORY'])
      self.assertContained('error: use of legacy setting: TOTAL_MEMORY (setting renamed to INITIAL_MEMORY) [-Wlegacy-settings] [-Werror]', stderr)
  
      # check that `-Wno-pthreads-mem` disables USE_PTHREADS + ALLOW_GROWTH_MEMORY warning
      stderr = self.run_process(cmd + ['-Wno-pthreads-mem-growth', '-sUSE_PTHREADS', '-sALLOW_MEMORY_GROWTH'], stderr=PIPE).stderr
      self.assertNotContained('USE_PTHREADS + ALLOW_MEMORY_GROWTH may run non-wasm code slowly, see https://github.com/WebAssembly/design/issues/1271', stderr)
  
    def test_emranlib(self):
      create_file('foo.c', 'int foo = 1;')
      create_file('bar.c', 'int bar = 2;')
      self.run_process([EMCC, '-c', 'foo.c', 'bar.c'])
  
      # Create a library with no archive map
      self.run_process([EMAR, 'crS', 'liba.a', 'foo.o', 'bar.o'])
      output = self.run_process([shared.LLVM_NM, '--print-armap', 'liba.a'], stdout=PIPE).stdout
      self.assertNotContained('Archive map', output)
  
      # Add an archive map
      self.run_process([EMRANLIB, 'liba.a'])
      output = self.run_process([shared.LLVM_NM, '--print-armap', 'liba.a'], stdout=PIPE).stdout
      self.assertContained('Archive map', output)
  
    def test_pthread_stub(self):
      # Verify that programs containing pthread code can still work even
      # without enabling threads.  This is possible becase we link in
      # libpthread_stub.a
      self.do_other_test('test_pthread_stub.c')
  
    @node_pthreads
    def test_main_pthread_join_detach(self):
      # Verify that we're unable to join the main thread
      self.set_setting('EXIT_RUNTIME')
      self.do_run_in_out_file_test('other/test_pthread_self_join_detach.c')
  
    @node_pthreads
    def test_proxy_pthread_join_detach(self):
      # Verify that we're unable to detach or join the proxied main thread
      self.set_setting('PROXY_TO_PTHREAD')
      self.set_setting('EXIT_RUNTIME')
      self.do_run_in_out_file_test('other/test_pthread_self_join_detach.c')
  
    def test_stdin_preprocess(self):
      create_file('temp.h', '#include <string>')
      outputStdin = self.run_process([EMCC, '-x', 'c++', '-dM', '-E', '-'], input="#include <string>", stdout=PIPE).stdout
      outputFile = self.run_process([EMCC, '-x', 'c++', '-dM', '-E', 'temp.h'], stdout=PIPE).stdout
      self.assertTextDataIdentical(outputStdin, outputFile)
  
    def test_stdin_compile_only(self):
      # Should fail without -x lang specifier
      src = read_file(test_file('hello_world.cpp'))
      err = self.expect_fail([EMCC, '-c', '-'], input=src)
      self.assertContained('error: -E or -x required when input is from standard input', err)
  
      self.run_process([EMCC, '-c', '-o', 'out.o', '-x', 'c++', '-'], input=src)
      self.assertExists('out.o')
  
      # Same again but without an explicit output filename
      self.run_process([EMCC, '-c', '-x', 'c++', '-'], input=src)
      self.assertExists('-.o')
  
    def test_stdin_compile_and_link(self):
      self.run_process([EMCC, '-x', 'c++', '-'], input=read_file(test_file('hello_world.cpp')))
      self.assertContained('hello, world!', self.run_js('a.out.js'))
  
    def test_stdout_link(self):
      # linking to stdout `-` doesn't work, we have no way to pass such an output filename
      # through post-link tools such as binaryen.
      err = self.expect_fail([EMXX, '-o', '-', test_file('hello_world.cpp')])
      self.assertContained('invalid output filename: `-`', err)
      self.assertNotExists('-')
  
      err = self.expect_fail([EMXX, '-o', '-foo', test_file('hello_world.cpp')])
      self.assertContained('invalid output filename: `-foo`', err)
      self.assertNotExists('-foo')
  
    def test_immutable_after_link(self):
      # some builds are guaranteed to not require any binaryen work after wasm-ld
      def ok(args, filename='hello_world.cpp', expected='hello, world!'):
        print('ok', args, filename)
        args += ['-sERROR_ON_WASM_CHANGES_AFTER_LINK']
        self.run_process([EMCC, test_file(filename)] + args)
        self.assertContained(expected, self.run_js('a.out.js'))
  
      # -O0 with BigInt support (to avoid the need for legalization)
      required_flags = ['-sWASM_BIGINT']
      ok(required_flags)
      # Same with DWARF
      ok(required_flags + ['-g'])
      # Function pointer calls from JS work too
      ok(required_flags, filename='hello_world_main_loop.cpp')
      # -O1 is ok as we don't run wasm-opt there (but no higher, see below)
      ok(required_flags + ['-O1'])
      # Exception support shouldn't require changes after linking
      ok(required_flags + ['-fexceptions'])
      # Standalone mode should not do anything special to the wasm.
      ok(required_flags + ['-sSTANDALONE_WASM'])
  
      # other builds fail with a standard message + extra details
      def fail(args, details):
        print('fail', args, details)
        args += ['-sERROR_ON_WASM_CHANGES_AFTER_LINK']
        err = self.expect_fail([EMXX, test_file('hello_world.cpp')] + args)
        self.assertContained('changes to the wasm are required after link, but disallowed by ERROR_ON_WASM_CHANGES_AFTER_LINK', err)
        self.assertContained(details, err)
  
      # plain -O0
      legalization_message = 'to disable int64 legalization (which requires changes after link) use -s WASM_BIGINT'
      fail([], legalization_message)
      # optimized builds even without legalization
      optimization_message = '-O2+ optimizations always require changes, build with -O0 or -O1 instead'
      fail(required_flags + ['-O2'], optimization_message)
      fail(required_flags + ['-O3'], optimization_message)
  
    def test_output_to_nowhere(self):
      self.run_process([EMXX, test_file('hello_world.cpp'), '-o', os.devnull, '-c'])
  
    # Test that passing -s MIN_X_VERSION=-1 on the command line will result in browser X being not supported at all.
    # I.e. -s MIN_X_VERSION=-1 is equal to -s MIN_X_VERSION=Infinity
    def test_drop_support_for_browser(self):
      # Test that -1 means "not supported"
      self.run_process([EMCC, test_file('test_html5_core.c'), '-sMIN_IE_VERSION=-1'])
      self.assertContained('allowsDeferredCalls: true', read_file('a.out.js'))
      self.assertNotContained('allowsDeferredCalls: JSEvents.isInternetExplorer()', read_file('a.out.js'))
  
    def test_errno_type(self):
      create_file('errno_type.c', '''
  #include <errno.h>
  
  // Use of these constants in C preprocessor comparisons should work.
  #if EPERM > 0
  #define DAV1D_ERR(e) (-(e))
  #else
  #define DAV1D_ERR(e) (e)
  #endif
  ''')
      self.run_process([EMCC, 'errno_type.c'])
  
    def test_standalone_syscalls(self):
      self.run_process([EMXX, test_file('other/test_standalone_syscalls.cpp'), '-o', 'test.wasm'])
      expected = read_file(test_file('other/test_standalone_syscalls.out'))
      for engine in config.WASM_ENGINES:
        self.assertContained(expected, self.run_js('test.wasm', engine))
  
    @requires_native_clang
    def test_wasm2c_reactor(self):
      # test compiling an unsafe library using wasm2c, then using it from a
      # main program. this shows it is easy to use wasm2c as a sandboxing
      # mechanism.
  
      # first compile the library with emcc, getting a .c and .h
      self.run_process([EMCC,
                        test_file('other/wasm2c/unsafe-library.c'),
                        '-O3', '-o', 'lib.wasm', '-sWASM2C', '--no-entry'])
      # compile the main program natively normally, together with the unsafe
      # library
      self.run_process([CLANG_CC,
                        test_file('other/wasm2c/my-code.c'),
                        'lib.wasm.c', '-O3', '-o', 'program.exe'] +
                       clang_native.get_clang_native_args(),
                       env=clang_native.get_clang_native_env())
      output = self.run_process([os.path.abspath('program.exe')], stdout=PIPE).stdout
      self.assertEqual(output, read_file(test_file('other/wasm2c/output.txt')))
  
    @requires_native_clang
    def test_wasm2c_multi_lib(self):
      # compile two libraries to object files
      for lib in ['a', 'b']:
        self.run_process([EMCC,
                          test_file('other/wasm2c', f'unsafe-library-{lib}.c'),
                          '-O3', '-o', f'lib{lib}.wasm', '-sWASM2C', '--no-entry'])
        # build with a different WASM_RT_MODULE_PREFIX for each library, so that
        # they do not have colliding symbols
        self.run_process([CLANG_CC, f'lib{lib}.wasm.c', '-O3', '-c',
                          f'-DWASM_RT_MODULE_PREFIX={lib}_'] +
                         clang_native.get_clang_native_args(),
                         env=clang_native.get_clang_native_env())
  
      # compile the main program with the wasmboxed libraries
      self.run_process([CLANG_CC,
                        test_file('other/wasm2c/my-code-multi.c'),
                        'liba.wasm.o', 'libb.wasm.o',
                        '-O3', '-o', 'program.exe'] +
                       clang_native.get_clang_native_args(),
                       env=clang_native.get_clang_native_env())
      output = self.run_process([os.path.abspath('program.exe')], stdout=PIPE).stdout
      self.assertEqual(output, read_file(test_file('other/wasm2c/output-multi.txt')))
  
    @parameterized({
      'wasm2js': (['-sWASM=0'], ''),
      'modularize': (['-sMODULARIZE'], 'Module()'),
    })
    def test_promise_polyfill(self, constant_args, extern_post_js):
      def test(args, expect_fail):
        # legacy browsers may lack Promise, which wasm2js depends on. see what
        # happens when we kill the global Promise function.
        create_file('extern-post.js', extern_post_js)
        self.run_process([EMXX, test_file('hello_world.cpp')] + constant_args + args + ['--extern-post-js', 'extern-post.js'])
        js = read_file('a.out.js')
        with open('a.out.js', 'w') as f:
          f.write('Promise = undefined;\n' + js)
        return self.run_js('a.out.js', assert_returncode=NON_ZERO if expect_fail else 0)
  
      # we fail without legacy support
      test([], expect_fail=True)
  
      # but work with it
      output = test(['-sLEGACY_VM_SUPPORT'], expect_fail=False)
      self.assertContained('hello, world!', output)
  
      # unless we explictly disable polyfills
      test(['-sLEGACY_VM_SUPPORT', '-sNO_POLYFILL'], expect_fail=True)
  
    def test_webgpu_compiletest(self):
      for args in [[], ['-sASSERTIONS'], ['-sASSERTIONS', '--closure=1'], ['-sMAIN_MODULE=1']]:
        self.run_process([EMXX, test_file('webgpu_jsvalstore.cpp'), '-sUSE_WEBGPU', '-sASYNCIFY'] + args)
  
    def test_signature_mismatch(self):
      create_file('a.c', 'void foo(); int main() { foo(); return 0; }')
      create_file('b.c', 'int foo() { return 1; }')
      stderr = self.run_process([EMCC, 'a.c', 'b.c'], stderr=PIPE).stderr
      self.assertContained('function signature mismatch: foo', stderr)
      self.expect_fail([EMCC, '-Wl,--fatal-warnings', 'a.c', 'b.c'])
      self.expect_fail([EMCC, '-sSTRICT', 'a.c', 'b.c'])
  
    def test_lld_report_undefined(self):
      create_file('main.c', 'void foo(); int main() { foo(); return 0; }')
      stderr = self.expect_fail([EMCC, '-sLLD_REPORT_UNDEFINED', 'main.c'])
      self.assertContained('wasm-ld: error:', stderr)
      self.assertContained('main_0.o: undefined symbol: foo', stderr)
  
    def test_lld_report_undefined_reverse_deps(self):
      self.run_process([EMCC, '-sLLD_REPORT_UNDEFINED', '-sREVERSE_DEPS=all', test_file('hello_world.c')])
  
    def test_lld_report_undefined_exceptions(self):
      self.run_process([EMXX, '-sLLD_REPORT_UNDEFINED', '-fwasm-exceptions', test_file('hello_libcxx.cpp')])
  
    def test_lld_report_undefined_main_module(self):
      self.run_process([EMCC, '-sLLD_REPORT_UNDEFINED', '-sMAIN_MODULE=2', test_file('hello_world.c')])
  
    # Verifies that warning messages that Closure outputs are recorded to console
    def test_closure_warnings(self):
      proc = self.run_process([EMCC, test_file('test_closure_warning.c'), '-O3', '--closure=1', '-sCLOSURE_WARNINGS=quiet'], stderr=PIPE)
      self.assertNotContained('WARNING', proc.stderr)
  
      proc = self.run_process([EMCC, test_file('test_closure_warning.c'), '-O3', '--closure=1', '-sCLOSURE_WARNINGS=warn'], stderr=PIPE)
      self.assertContained('WARNING - [JSC_REFERENCE_BEFORE_DECLARE] Variable referenced before declaration', proc.stderr)
  
      self.expect_fail([EMCC, test_file('test_closure_warning.c'), '-O3', '--closure=1', '-sCLOSURE_WARNINGS=error'])
  
    def test_bitcode_input(self):
      # Verify that bitcode files are accepted as input
      create_file('main.c', 'void foo(); int main() { return 0; }')
      self.run_process([EMCC, '-emit-llvm', '-c', '-o', 'main.bc', 'main.c'])
      self.assertTrue(building.is_bitcode('main.bc'))
      self.run_process([EMCC, '-c', '-o', 'main.o', 'main.bc'])
      self.assertTrue(building.is_wasm('main.o'))
  
    def test_nostdlib(self):
      # First ensure all the system libs are built
      self.run_process([EMCC, test_file('unistd/close.c')])
  
      err = 'symbol exported via --export not found: __errno_location'
      self.assertContained(err, self.expect_fail([EMCC, test_file('unistd/close.c'), '-nostdlib']))
      self.assertContained(err, self.expect_fail([EMCC, test_file('unistd/close.c'), '-nodefaultlibs']))
  
      # Build again but with explit system libraries
      libs = ['-lc', '-lcompiler_rt']
      self.run_process([EMCC, test_file('unistd/close.c'), '-nostdlib'] + libs)
      self.run_process([EMCC, test_file('unistd/close.c'), '-nodefaultlibs'] + libs)
      self.run_process([EMCC, test_file('unistd/close.c'), '-nolibc', '-lc'])
      self.run_process([EMCC, test_file('unistd/close.c'), '-nostartfiles'])
  
    def test_argument_match(self):
      # Verify that emcc arguments match precisely.  We had a bug where only the prefix
      # was matched
      self.run_process([EMCC, test_file('hello_world.c'), '--minify=0'])
      err = self.expect_fail([EMCC, test_file('hello_world.c'), '--minifyXX'])
      self.assertContained("error: unsupported option '--minifyXX'", err)
  
    def test_argument_missing(self):
      err = self.expect_fail([EMCC, test_file('hello_world.c'), '--minify'])
      self.assertContained("error: option '--minify' requires an argument", err)
  
    def test_argument_missing_file(self):
      err = self.expect_fail([EMCC, test_file('hello_world.c'), '--pre-js', 'foo.js'])
      self.assertContained("emcc: error: '--pre-js': file not found: 'foo.js'", err)
  
    def test_default_to_cxx(self):
      create_file('foo.h', '#include <string.h>')
      create_file('cxxfoo.h', '#include <string>')
  
      # The default bahviour is to default to C++, which means the C++ header can be compiled even
      # with emcc.
      self.run_process([EMCC, '-c', 'cxxfoo.h'])
  
      # But this means that C flags can't be passed (since we are assuming C++)
      err = self.expect_fail([EMCC, '-std=gnu11', '-c', 'foo.h'])
      self.assertContained("'-std=gnu11' not allowed with 'C++'", err)
  
      # If we disable DEFAULT_TO_CXX the emcc can be used with C-only flags (e.g. -std=gnu11),
      self.run_process([EMCC, '-std=gnu11', '-c', 'foo.h', '-sDEFAULT_TO_CXX=0'])
  
      # But can't be used to build C++ headers
      err = self.expect_fail([EMCC, '-c', 'cxxfoo.h', '-sDEFAULT_TO_CXX=0'])
      self.assertContained("'string' file not found", err)
  
      # Check that STRICT also disables DEFAULT_TO_CXX
      err = self.expect_fail([EMCC, '-c', 'cxxfoo.h', '-sSTRICT'])
      self.assertContained("'string' file not found", err)
  
      # Using em++ should alwasy work for C++ headers
      self.run_process([EMXX, '-c', 'cxxfoo.h', '-sDEFAULT_TO_CXX=0'])
      # Or using emcc with `-x c++`
      self.run_process([EMCC, '-c', 'cxxfoo.h', '-sDEFAULT_TO_CXX=0', '-x', 'c++-header'])
  
    @parameterized({
      '': ([],),
      'minimal': (['-sMINIMAL_RUNTIME', '-sSUPPORT_ERRNO'],),
    })
    def test_support_errno(self, args):
      self.emcc_args += args
      src = test_file('core/test_support_errno.c')
      output = test_file('core/test_support_errno.out')
  
      self.do_run_from_file(src, output)
      size_default = os.path.getsize('test_support_errno.js')
  
      # Run the same test again but with SUPPORT_ERRNO disabled.  This time we don't expect errno
      # to be set after the failing syscall.
      self.emcc_args += ['-sSUPPORT_ERRNO=0']
      output = test_file('core/test_support_errno_disabled.out')
      self.do_run_from_file(src, output)
  
      # Verify the JS output was smaller
      self.assertLess(os.path.getsize('test_support_errno.js'), size_default)
  
    def test_assembly(self):
      self.run_process([EMCC, '-c', test_file('other/test_asm.s'), '-o', 'foo.o'])
      self.do_other_test('test_asm.c', libraries=['foo.o'])
  
    def test_assembly_preprocessed(self):
      self.run_process([EMCC, '-c', test_file('other/test_asm_cpp.S'), '-o', 'foo.o'])
      self.do_other_test('test_asm.c', libraries=['foo.o'])
  
    @parameterized({
      '': (['-DUSE_KEEPALIVE'],),
      'minimal': (['-DUSE_KEEPALIVE', '-sMINIMAL_RUNTIME'],),
      'command_line': (['-sEXPORTED_FUNCTIONS=_g_foo,_main'],),
    })
    def test_export_global_address(self, args):
      self.do_other_test('test_export_global_address.c', emcc_args=args)
  
    def test_linker_version(self):
      out = self.run_process([EMCC, '-Wl,--version'], stdout=PIPE).stdout
      self.assertContained('LLD ', out)
  
    # Tests that if a JS library function is missing, the linker will print out which function
    # depended on the missing function.
    def test_chained_js_error_diagnostics(self):
      err = self.expect_fail([EMCC, test_file('test_chained_js_error_diagnostics.c'), '--js-library', test_file('test_chained_js_error_diagnostics.js')])
      self.assertContained("error: undefined symbol: nonexistent_function (referenced by bar__deps: ['nonexistent_function'], referenced by foo__deps: ['bar'], referenced by top-level compiled C/C++ code)", err)
      # Check that we don't recommend LLD_REPORT_UNDEFINED for chained dependencies.
      self.assertNotContained('LLD_REPORT_UNDEFINED', err)
  
      # Test without chaining.  In this case we don't include the JS library at all resulting in `foo`
      # being undefined in the native code and in this case we recommend LLD_REPORT_UNDEFINED.
      err = self.expect_fail([EMCC, test_file('test_chained_js_error_diagnostics.c')])
      self.assertContained('error: undefined symbol: foo (referenced by top-level compiled C/C++ code)', err)
      self.assertContained('Link with `-s LLD_REPORT_UNDEFINED` to get more information on undefined symbols', err)
  
    def test_xclang_flag(self):
      create_file('foo.h', ' ')
      self.run_process([EMCC, '-c', '-o', 'out.o', '-Xclang', '-include', '-Xclang', 'foo.h', test_file('hello_world.c')])
  
    def test_emcc_size_parsing(self):
      create_file('foo.h', ' ')
      err = self.expect_fail([EMCC, '-sTOTAL_MEMORY=X', 'foo.h'])
      self.assertContained('error: invalid byte size `X`.  Valid suffixes are: kb, mb, gb, tb', err)
      err = self.expect_fail([EMCC, '-sTOTAL_MEMORY=11PB', 'foo.h'])
      self.assertContained('error: invalid byte size `11PB`.  Valid suffixes are: kb, mb, gb, tb', err)
  
    def test_native_call_before_init(self):
      self.set_setting('ASSERTIONS')
      self.set_setting('EXPORTED_FUNCTIONS', ['_foo'])
      self.add_pre_run('console.log("calling foo"); Module["_foo"]();')
      create_file('foo.c', '#include <stdio.h>\nint foo() { puts("foo called"); return 3; }')
      self.build('foo.c')
      err = self.expect_fail(config.NODE_JS + ['foo.js'], stdout=PIPE)
      self.assertContained('native function `foo` called before runtime initialization', err)
  
    def test_native_call_after_exit(self):
      self.set_setting('ASSERTIONS')
      self.set_setting('EXIT_RUNTIME')
      self.add_on_exit('console.log("calling main again"); Module["_main"]();')
      create_file('foo.c', '#include <stdio.h>\nint main() { puts("foo called"); return 0; }')
      self.build('foo.c')
      err = self.expect_fail(config.NODE_JS + ['foo.js'], stdout=PIPE)
      self.assertContained('native function `main` called after runtime exit', err)
  
    def test_metadce_wasm2js_i64(self):
      # handling i64 unsigned remainder brings in some i64 support code. metadce
      # must not remove it.
      create_file('src.cpp', r'''
  int main(int argc, char **argv) {
    // Intentionally do not print anything, to not bring in more code than we
    // need to test - this only tests that we do not crash, which we would if
    // metadce broke us.
    unsigned long long x = argc;
    // do some i64 math, but return 0
    return (x % (x - 20)) == 42;
  }''')
      self.run_process([EMXX, 'src.cpp', '-O3', '-sWASM=0'])
      self.run_js('a.out.js')
  
    def test_deterministic(self):
      # test some things that may not be nondeterministic
      create_file('src.cpp', r'''
  #include <emscripten.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <time.h>
  
  int main () {
    timespec now;
    clock_gettime(CLOCK_REALTIME, &now);
    printf("C now: %ld %ld\n", now.tv_sec, now.tv_nsec);
    printf("js now: %f\n", emscripten_get_now());
    printf("C randoms: %d %d %d\n", rand(), rand(), rand());
    printf("JS random: %d\n", EM_ASM_INT({ return Math.random() }));
  }
  ''')
      self.run_process([EMXX, 'src.cpp', '-sDETERMINISTIC'])
      one = self.run_js('a.out.js')
      # ensure even if the time resolution is 1 second, that if we see the real
      # time we'll see a difference
      time.sleep(2)
      two = self.run_js('a.out.js')
      self.assertIdentical(one, two)
  
    def test_err(self):
      self.do_other_test('test_err.cpp')
  
    def test_shared_flag(self):
      # Test that `-shared` flag causes object file generation but gives a warning
      err = self.run_process([EMCC, '-shared', test_file('hello_world.c'), '-o', 'out.foo'], stderr=PIPE).stderr
      self.assertContained('linking a library with `-shared` will emit a static object', err)
      self.assertIsObjectFile('out.foo')
  
      # Test that using an exectuable output name overides the `-shared` flag, but produces a warning.
      err = self.run_process([EMCC, '-shared', test_file('hello_world.c'), '-o', 'out.js'],
                             stderr=PIPE).stderr
      self.assertContained('warning: -shared/-r used with executable output suffix', err)
      self.run_js('out.js')
  
    def test_shared_and_side_module_flag(self):
      # Test that `-shared` and `-s SIDE_MODULE` flag causes wasm dylib generation without a warning.
      err = self.run_process([EMCC, '-shared', '-sSIDE_MODULE=1', test_file('hello_world.c'), '-o', 'out.foo'], stderr=PIPE).stderr
      self.assertNotContained('linking a library with `-shared` will emit a static object', err)
      self.assertIsWasmDylib('out.foo')
  
      # Test that `-shared` and `-s SIDE_MODULE` flag causes wasm dylib generation without a warning even if given exectuable output name.
      err = self.run_process([EMCC, '-shared', '-sSIDE_MODULE=1', test_file('hello_world.c'), '-o', 'out.wasm'],
                             stderr=PIPE).stderr
      self.assertNotContained('warning: -shared/-r used with executable output suffix', err)
      self.assertIsWasmDylib('out.wasm')
  
    @no_windows('windows does not support shbang syntax')
    @with_env_modify({'EMMAKEN_JUST_CONFIGURE': '1'})
    def test_autoconf_mode(self):
      self.run_process([EMCC, test_file('hello_world.c')])
      # Test that output name is just `a.out` and that it is directly executable
      output = self.run_process([os.path.abspath('a.out')], stdout=PIPE).stdout
      self.assertContained('hello, world!', output)
  
    def test_standalone_export_main(self):
      # Tests that explicitly exported `_main` does not fail, even though `_start` is the entry
      # point.
      # We should consider making this a warning since the `_main` export is redundant.
      self.run_process([EMCC, '-sEXPORTED_FUNCTIONS=_main', '-sSTANDALONE_WASM', test_file('core/test_hello_world.c')])
  
    @require_v8
    def test_standalone_wasm_exceptions(self):
      self.set_setting('STANDALONE_WASM')
      self.set_setting('WASM_BIGINT')
      self.wasm_engines = []
      self.emcc_args += ['-fwasm-exceptions']
      self.v8_args.append('--experimental-wasm-eh')
      self.do_run_from_file(test_file('core/test_exceptions.cpp'), test_file('core/test_exceptions_caught.out'))
  
    def test_missing_malloc_export(self):
      # we used to include malloc by default. show a clear error in builds with
      # ASSERTIONS to help with any confusion when the user calls malloc/free
      # directly
      create_file('unincluded_malloc.c', r'''
        #include <emscripten.h>
        int main() {
          EM_ASM({
            try {
              _malloc(1);
            } catch(e) {
              console.log('exception:', e);
            }
            try {
              _free();
            } catch(e) {
              console.log('exception:', e);
            }
          });
        }
      ''')
      self.do_runf('unincluded_malloc.c', (
        "malloc() called but not included in the build - add '_malloc' to EXPORTED_FUNCTIONS",
        "free() called but not included in the build - add '_free' to EXPORTED_FUNCTIONS"))
  
    def test_missing_malloc_export_indirect(self):
      # we used to include malloc by default. show a clear error in builds with
      # ASSERTIONS to help with any confusion when the user calls a JS API that
      # requires malloc
      create_file('unincluded_malloc.c', r'''
        #include <emscripten.h>
        int main() {
          EM_ASM({
            try {
              allocateUTF8("foo");
            } catch(e) {
              console.log('exception:', e);
            }
          });
        }
      ''')
      self.do_runf('unincluded_malloc.c', 'malloc was not included, but is needed in allocateUTF8. Adding "_malloc" to EXPORTED_FUNCTIONS should fix that. This may be a bug in the compiler, please file an issue.')
  
    def test_getrusage(self):
      self.do_runf(test_file('other/test_getrusage.c'))
  
    @with_env_modify({'EMMAKEN_COMPILER': shared.CLANG_CC})
    def test_emmaken_compiler(self):
      stderr = self.run_process([EMCC, '-c', test_file('core/test_hello_world.c')], stderr=PIPE).stderr
      self.assertContained('warning: `EMMAKEN_COMPILER` is deprecated', stderr)
  
    @with_env_modify({'EMMAKEN_CFLAGS': '-O2'})
    def test_emmaken_cflags(self):
      stderr = self.run_process([EMCC, '-c', test_file('core/test_hello_world.c')], stderr=PIPE).stderr
      self.assertContained('warning: `EMMAKEN_CFLAGS` is deprecated', stderr)
  
    @no_windows('relies on a shell script')
    def test_compiler_wrapper(self):
      create_file('wrapper.sh', '''\
  #!/bin/sh
  echo "wrapping compiler call: $@"
  exec "$@"
      ''')
      make_executable('wrapper.sh')
      with env_modify({'EM_COMPILER_WRAPPER': './wrapper.sh'}):
        stdout = self.run_process([EMCC, '-c', test_file('core/test_hello_world.c')], stdout=PIPE).stdout
      self.assertContained('wrapping compiler call: ', stdout)
      self.assertExists('test_hello_world.o')
  
      stdout = self.run_process([EMCC, '-c', test_file('core/test_hello_world.c'), '--compiler-wrapper=./wrapper.sh'], stdout=PIPE).stdout
      self.assertContained('wrapping compiler call: ', stdout)
      self.assertExists('test_hello_world.o')
  
    def test_llvm_option_dash_o(self):
      # emcc used to interpret -mllvm's option value as the output file if it
      # began with -o
      stderr = self.run_process(
        [EMCC, '-v', '-o', 'llvm_option_dash_o_output', '-mllvm',
         '-opt-bisect-limit=1', test_file('hello_world.c')],
        stderr=PIPE).stderr
  
      self.assertExists('llvm_option_dash_o_output')
      self.assertNotExists('pt-bisect-limit=1')
      self.assertContained(' -mllvm -opt-bisect-limit=1 ', stderr)
  
      # Regression test for #12236: the '-mllvm' argument was indexed instead of
      # its value, and the index was out of bounds if the argument was sixth or
      # further on the command line
      self.run_process(
        [EMCC, '-DFOO', '-DBAR', '-DFOOBAR', '-DBARFOO',
         '-o', 'llvm_option_dash_o_output', '-mllvm', '-opt-bisect-limit=1',
         test_file('hello_world.c')])
  
    def test_SYSCALL_DEBUG(self):
      self.set_setting('SYSCALL_DEBUG')
      self.do_runf(test_file('hello_world.c'), 'syscall! fd_write: [1,')
  
    def test_LIBRARY_DEBUG(self):
      self.set_setting('LIBRARY_DEBUG')
      self.do_runf(test_file('hello_world.c'), '[library call:_fd_write: 0x1')
  
    def test_SUPPORT_LONGJMP_executable(self):
      stderr = self.run_process([EMCC, test_file('core/test_longjmp.c'), '-sSUPPORT_LONGJMP=0'], stderr=PIPE, check=False).stderr
      self.assertContained('error: longjmp support was disabled (SUPPORT_LONGJMP=0), but it is required by the code (either set SUPPORT_LONGJMP=1, or remove uses of it in the project)',
                           stderr)
  
    def test_SUPPORT_LONGJMP_object(self):
      # compile the object *with* support, but link without
      self.run_process([EMCC, test_file('core/test_longjmp.c'), '-c', '-sSUPPORT_LONGJMP', '-o', 'a.o'])
      stderr = self.run_process([EMCC, 'a.o', '-sSUPPORT_LONGJMP=0'], stderr=PIPE, check=False).stderr
      self.assertContained('error: longjmp support was disabled (SUPPORT_LONGJMP=0), but it is required by the code (either set SUPPORT_LONGJMP=1, or remove uses of it in the project)',
                           stderr)
  
    def test_SUPPORT_LONGJMP_wasm(self):
      # Tests if -sSUPPORT_LONGJMP=wasm alone is enough to use Wasm SjLj, i.e., it
      # automatically sets DISABLE_EXCEPTION_THROWING to 1, which is 0 by default,
      # because Emscripten EH and Wasm SjLj cannot be used at the same time.
      self.run_process([EMCC, test_file('core/test_longjmp.c'), '-c', '-sSUPPORT_LONGJMP=wasm', '-o', 'a.o'])
  
    def test_pthread_MODULARIZE(self):
      stderr = self.run_process([EMCC, test_file('hello_world.c'), '-pthread', '-sMODULARIZE'], stderr=PIPE, check=False).stderr
      self.assertContained('pthreads + MODULARIZE currently require you to set -s EXPORT_NAME=Something (see settings.js) to Something != Module, so that the .worker.js file can work',
                           stderr)
  
    def test_jslib_clobber_i(self):
      # Regression check for an issue we have where a library clobbering the global `i` variable could
      # prevent processing of further libraries.
      create_file('lib1.js', 'for (var i = 0; i < 100; i++) {}')
      create_file('lib2.js', '''
        mergeInto(LibraryManager.library, {
         foo: function() { }
        });
        ''')
      self.run_process([EMCC, test_file('hello_world.c'),
                        '-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=foo',
                        '--js-library=lib1.js',
                        '--js-library=lib2.js'])
  
    def test_jslib_bad_config(self):
      create_file('lib.js', '''
        mergeInto(LibraryManager.library, {
         foo__sig: 'ii',
        });
        ''')
      err = self.expect_fail([EMCC, test_file('hello_world.c'), '--js-library=lib.js'])
      self.assertContained("error: Missing library element 'foo' for library config 'foo__sig'", err)
  
    def test_jslib_ifdef(self):
      create_file('lib.js', '''
        #ifdef ASSERTIONS
        var foo;
        #endif
        ''')
      proc = self.run_process([EMCC, test_file('hello_world.c'), '--js-library=lib.js'], stderr=PIPE)
      self.assertContained('warning: use of #ifdef in js library.  Use #if instead.', proc.stderr)
  
    def test_jslib_mangling(self):
      create_file('lib.js', '''
        mergeInto(LibraryManager.library, {
         $__foo: function() { return 43; },
        });
        ''')
      self.run_process([EMCC, test_file('hello_world.c'), '--js-library=lib.js', '-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$__foo'])
  
    def test_asan_no_dylink(self):
      for arg in ['-sMAIN_MODULE', '-sSIDE_MODULE', '-sRELOCATABLE', '-sMAIN_MODULE=2', '-sSIDE_MODULE=2']:
        print(arg)
        err = self.expect_fail([EMCC, test_file('hello_world.c'), '-fsanitize=address', arg])
        self.assertContained('ASan does not support dynamic linking', err)
  
    def test_wasm2js_no_dylink(self):
      for arg in ['-sMAIN_MODULE', '-sSIDE_MODULE', '-sRELOCATABLE']:
        print(arg)
        err = self.expect_fail([EMCC, test_file('hello_world.c'), '-sWASM=0', arg])
        self.assertContained('WASM2JS is not compatible with relocatable output', err)
  
    def test_wasm2js_standalone(self):
      self.run_process([EMCC, test_file('hello_world.c'), '-sSTANDALONE_WASM', '-sWASM=0'])
      self.assertContained('hello, world!', self.run_js('a.out.js'))
  
    def test_oformat(self):
      self.run_process([EMCC, test_file('hello_world.c'), '--oformat=wasm', '-o', 'out.foo'])
      self.assertTrue(building.is_wasm('out.foo'))
      self.clear()
  
      self.run_process([EMCC, test_file('hello_world.c'), '--oformat=html', '-o', 'out.foo'])
      self.assertFalse(building.is_wasm('out.foo'))
      self.assertContained('<html ', read_file('out.foo'))
      self.clear()
  
      self.run_process([EMCC, test_file('hello_world.c'), '--oformat=js', '-o', 'out.foo'])
      self.assertFalse(building.is_wasm('out.foo'))
      self.assertContained('new ExitStatus', read_file('out.foo'))
      self.clear()
  
      err = self.expect_fail([EMCC, test_file('hello_world.c'), '--oformat=foo'])
      self.assertContained("error: invalid output format: `foo` (must be one of ['object', 'wasm', 'js', 'mjs', 'html', 'bare']", err)
  
    # Tests that the old format of {{{ makeDynCall('sig') }}}(func, param1) works
    def test_old_makeDynCall_syntax(self):
      err = self.run_process([EMCC, test_file('test_old_dyncall_format.c'), '--js-library', test_file('library_test_old_dyncall_format.js')], stderr=PIPE).stderr
      self.assertContained('syntax for makeDynCall has changed', err)
  
    def test_post_link(self):
      err = self.run_process([EMCC, test_file('hello_world.c'), '--oformat=bare', '-o', 'bare.wasm'], stderr=PIPE).stderr
      self.assertContained('--oformat=bare/--post-link are experimental and subject to change', err)
      err = self.run_process([EMCC, '--post-link', 'bare.wasm'], stderr=PIPE).stderr
      self.assertContained('--oformat=bare/--post-link are experimental and subject to change', err)
      err = self.assertContained('hello, world!', self.run_js('a.out.js'))
  
    def compile_with_wasi_sdk(self, filename, output):
      sysroot = os.environ.get('EMTEST_WASI_SYSROOT')
      if not sysroot:
        self.skipTest('EMTEST_WASI_SYSROOT not found in environment')
      sysroot = os.path.expanduser(sysroot)
      self.run_process([CLANG_CC, '--sysroot=' + sysroot, '--target=wasm32-wasi', filename, '-o', output])
  
    def test_run_wasi_sdk_output(self):
      self.compile_with_wasi_sdk(test_file('hello_world.c'), 'hello.wasm')
      self.run_process([EMCC, '--post-link', '-sPURE_WASI', 'hello.wasm'])
      self.assertContained('hello, world!', self.run_js('a.out.js'))
  
    # Test that Closure prints out clear readable error messages when there are errors.
    def test_closure_errors(self):
      err = self.expect_fail([EMCC, test_file('closure_error.c'), '-O2', '--closure=1'])
      lines = err.split('\n')
  
      def find_substr_index(s):
        for i, line in enumerate(lines):
          if s in line:
            return i
        return -1
  
      idx1 = find_substr_index('[JSC_UNDEFINED_VARIABLE] variable thisVarDoesNotExist is undeclared')
      idx2 = find_substr_index('[JSC_UNDEFINED_VARIABLE] variable thisVarDoesNotExistEither is undeclared')
      self.assertNotEqual(idx1, -1)
      self.assertNotEqual(idx2, -1)
      # The errors must be present on distinct lines.
      self.assertNotEqual(idx1, idx2)
  
    # Make sure that --cpuprofiler compiles with --closure 1
    def test_cpuprofiler_closure(self):
      # TODO: Enable '-sCLOSURE_WARNINGS=error' in the following, but that has currently regressed.
      self.run_process([EMCC, test_file('hello_world.c'), '-O2', '--closure=1', '--cpuprofiler'])
  
    # Make sure that --memoryprofiler compiles with --closure 1
    def test_memoryprofiler_closure(self):
      # TODO: Enable '-sCLOSURE_WARNINGS=error' in the following, but that has currently regressed.
      self.run_process([EMCC, test_file('hello_world.c'), '-O2', '--closure=1', '--memoryprofiler'])
  
    # Make sure that --threadprofiler compiles with --closure 1
    def test_threadprofiler_closure(self):
      # TODO: Enable '-sCLOSURE_WARNINGS=error' in the following, but that has currently regressed.
      self.run_process([EMCC, test_file('hello_world.c'), '-O2', '-sUSE_PTHREADS', '--closure=1', '--threadprofiler', '-sASSERTIONS'])
  
    @node_pthreads
    def test_threadprofiler(self):
      self.run_process([EMCC, test_file('test_threadprofiler.cpp'), '-sUSE_PTHREADS', '-sPROXY_TO_PTHREAD', '-sEXIT_RUNTIME', '--threadprofiler', '-sASSERTIONS'])
      output = self.run_js('a.out.js')
      self.assertRegex(output, r'Thread "Browser main thread" \(0x.*\) now: running.')
      self.assertRegex(output, r'Thread "Application main thread" \(0x.*\) now: waiting for a futex.')
      self.assertRegex(output, r'Thread "test worker" \(0x.*\) now: sleeping.')
  
    def test_syslog(self):
      self.do_other_test('test_syslog.c')
  
    @parameterized({
      '': (False,),
      'custom': (True,),
    })
    def test_split_module(self, customLoader):
      self.set_setting('SPLIT_MODULE')
      self.emcc_args += ['-g', '-Wno-experimental']
      self.emcc_args += ['--post-js', test_file('other/test_split_module.post.js')]
      if customLoader:
        self.emcc_args += ['--pre-js', test_file('other/test_load_split_module.pre.js')]
      self.emcc_args += ['-sEXPORTED_FUNCTIONS=_malloc,_free']
      self.do_other_test('test_split_module.c')
      self.assertExists('test_split_module.wasm')
      self.assertExists('test_split_module.wasm.orig')
      self.assertExists('profile.data')
  
      wasm_split = os.path.join(building.get_binaryen_bin(), 'wasm-split')
      self.run_process([wasm_split, '--enable-mutable-globals', '--export-prefix=%', 'test_split_module.wasm.orig', '-o1', 'primary.wasm', '-o2', 'secondary.wasm', '--profile=profile.data'])
  
      os.remove('test_split_module.wasm')
      os.rename('primary.wasm', 'test_split_module.wasm')
      os.rename('secondary.wasm', 'test_split_module.deferred.wasm')
      result = self.run_js('test_split_module.js')
      self.assertNotIn('profile', result)
      self.assertContainedIf('Custom handler for loading split module.', result, condition=customLoader)
      self.assertIn('Hello! answer: 42', result)
  
    def test_split_main_module(self):
      # Set and reasonably large initial table size to avoid test fragility.
      # The actual number of slots needed is closer to 18 but we don't want
      # this test to fail every time that changes.
      initialTableSize = 100
  
      side_src = test_file('other/lib_hello.c')
      post_js = test_file('other/test_split_module.post.js')
  
      self.run_process([EMCC, side_src, '-sSIDE_MODULE=1', '-g', '-o', 'libhello.wasm'])
  
      self.emcc_args += ['-g']
      self.emcc_args += ['-sMAIN_MODULE=2']
      self.emcc_args += ['-sEXPORTED_FUNCTIONS=_printf']
      self.emcc_args += ['-sSPLIT_MODULE=1', '-Wno-experimental']
      self.emcc_args += ['--embed-file', 'libhello.wasm']
      self.emcc_args += ['--post-js', post_js]
      self.emcc_args += [f'-sINITIAL_TABLE={initialTableSize}']
  
      self.do_other_test('test_split_main_module.c')
  
      self.assertExists('test_split_main_module.wasm')
      self.assertExists('test_split_main_module.wasm.orig')
      self.assertExists('profile.data')
  
      wasm_split = os.path.join(building.get_binaryen_bin(), 'wasm-split')
      self.run_process([wasm_split, '-g',
                        'test_split_main_module.wasm.orig',
                        '--export-prefix=%',
                        f'--initial-table={initialTableSize}',
                        '--profile=profile.data',
                        '-o1', 'primary.wasm',
                        '-o2', 'secondary.wasm',
                        '--enable-mutable-globals'])
  
      os.remove('test_split_main_module.wasm')
      os.rename('primary.wasm', 'test_split_main_module.wasm')
      os.rename('secondary.wasm', 'test_split_main_module.deferred.wasm')
      result = self.run_js('test_split_main_module.js')
      self.assertNotIn('profile', result)
      self.assertIn('Hello from main!', result)
      self.assertIn('Hello from lib!', result)
  
    def test_gen_struct_info(self):
      # This tests is fragile and will need updating any time any of the refereced
      # structs or defines change.   However its easy to rebaseline with
      # --rebaseline and it prevents regressions or unintended changes
      # to the output json.
      self.run_process([PYTHON, path_from_root('tools/gen_struct_info.py'), '-o', 'out.json'])
      self.assertFileContents(test_file('reference_struct_info.json'), read_file('out.json'))
  
    def test_gen_struct_info_env(self):
      # gen_struct_info.py builds C code in a very particlar way.  We don't want EMCC_CFLAGS to
      # be injected which could cause it to fail.
      # For example -O2 causes printf -> iprintf which will fail with undefined symbol iprintf.
      with env_modify({'EMCC_CFLAGS': '-O2 BAD_ARG'}):
        self.run_process([PYTHON, path_from_root('tools/gen_struct_info.py'), '-o', 'out.json'])
  
    def test_relocatable_limited_exports(self):
      # Building with RELOCATABLE should *not* automatically export all sybmols.
      self.run_process([EMCC, test_file('hello_world.c'), '-sRELOCATABLE', '-o', 'out.wasm'])
  
      # Building with RELOCATABLE + LINKABLE should include and export all of the standard library
      self.run_process([EMCC, test_file('hello_world.c'), '-sRELOCATABLE', '-sLINKABLE', '-o', 'out_linkable.wasm'])
  
      exports = parse_wasm('out.wasm')[1]
      exports_linkable = parse_wasm('out_linkable.wasm')[1]
  
      self.assertLess(len(exports), 20)
      self.assertGreater(len(exports_linkable), 1000)
      self.assertIn('sendmsg', exports_linkable)
      self.assertNotIn('sendmsg', exports)
  
    @is_slow_test
    def test_deps_info(self):
      # Verify that for each symbol listed in deps_info all the reverse
      # dependencies are indeed valid.
      # To do this we compile a tiny test program that depends on the address
      # of each function.  Once compiled the resulting JavaScript code should
      # contain a reference to each of the dependencies.
  
      # When debugging set this valud to the function that you want to start
      # with.  All symbols prior will be skipped over.
      start_at = None
      assert not start_at or start_at in deps_info.get_deps_info()
      for function, deps in deps_info.get_deps_info().items():
        if start_at:
          if function == start_at:
            start_at = None
          else:
            print(f'skipping {function}')
            continue
        create_file(function + '.c', '''
        void %s();
        int main() {
          return (int)(long)&%s;
        }
        ''' % (function, function))
        # Compile with -O2 so we get JSDCE run to remove any false positives.  This
        # also makes the string quotes consistent which makes the test below simpler.
        # Including -sREVERSE_DEPS=auto explictly (even though its the default) to
        # be clear this is what we are testing (and in case the default ever changes).
        cmd = [EMCC, function + '.c', '-O2', '--minify=0', '--profiling-funcs', '-Wno-incompatible-library-redeclaration', '-sREVERSE_DEPS=auto']
        print(f'compiling test program for: {function}')
        if 'emscripten_get_compiler_setting' in function:
          cmd.append('-sRETAIN_COMPILER_SETTINGS')
        if 'emscripten_pc_get_function' in function:
          cmd.append('-sUSE_OFFSET_CONVERTER')
        if 'embind' in function:
          cmd.append('-lembind')
        if 'websocket' in function:
          cmd += ['-sPROXY_POSIX_SOCKETS', '-lwebsocket.js']
        if function == 'Mix_LoadWAV_RW':
          cmd += ['-sUSE_SDL=2']
        if 'thread' in function:
          cmd.append('-sUSE_PTHREADS')
        if 'glGetStringi' in function:
          cmd.append('-sUSE_WEBGL2')
        if 'glMapBufferRange' in function:
          cmd.append('-sFULL_ES3')
        if function == 'wgpuDeviceCreateBuffer':
          cmd.append('-sUSE_WEBGPU')
        # dladdr dlsym etc..
        if function.startswith('dl'):
          cmd.append('-sMAIN_MODULE=2')
        if function.startswith('emscripten_idb') or function.startswith('emscripten_wget_'):
          cmd.append('-sASYNCIFY')
        if function.startswith('emscripten_webgl_'):
          cmd.append('-sOFFSCREENCANVAS_SUPPORT')
        if function.startswith('wgpu'):
          cmd.append('-sUSE_WEBGPU')
        if function.startswith('__cxa_'):
          cmd.append('-fexceptions')
        if function.startswith('glfwGetMonitors'):
          cmd.append('-sUSE_GLFW=3')
        # In WebAssemblyLowerEmscriptenEHSjLj pass in the LLVM backend, function
        # calls that exist in the same function with setjmp are converted to some
        # code sequence that includes emscripten_longjmp. emscripten_longjmp is
        # included in deps_info.py because in non-LTO builds setjmp does not exist
        # anymore in the object files. So the mere indirect reference of setjmp or
        # emscripten_longjmp does not generate calls to its dependencies specified
        # in deps_info.py. Also Emscripten EH has a known restriction that setjmp
        # cannot be called or referenced indirectly anyway.
        if function in ['emscripten_longjmp', 'setjmp']:
          continue
        print(shared.shlex_join(cmd))
        self.run_process(cmd)
        js = read_file('a.out.js')
        for dep in deps:
          direct = '_' + dep + '('
          via_module = '"_%s"](' % dep
          assignment = ' = _' + dep
          print(f'  checking for: {dep}')
          if direct not in js and via_module not in js and assignment not in js:
            self.fail(f'use of declared dependency {dep} not found in JS output for {function}')
  
          # Check that the dependency not a JS library function.  Dependencies on JS functions
          # do not need entries in deps_info.py.
          if f'function _{dep}(' in js:
            self.fail(f'dependency {dep} in deps_info.py looks like a JS function (but should be native)')
  
    @require_v8
    def test_shell_Oz(self):
      # regression test for -Oz working on non-web, non-node environments that
      # lack TextDecoder
      self.emcc_args += ['-Oz']
      self.do_runf(test_file('hello_world.c'), 'hello, world!')
  
    def test_runtime_keepalive(self):
      self.uses_es6 = True
      self.set_setting('DEFAULT_LIBRARY_FUNCS_TO_INCLUDE', ['$runtimeKeepalivePush', '$runtimeKeepalivePop', '$callUserCallback'])
      self.set_setting('EXIT_RUNTIME')
      self.do_other_test('test_runtime_keepalive.cpp')
  
    def test_em_asm_side_module(self):
      err = self.expect_fail([EMCC, '-sSIDE_MODULE', test_file('hello_world_em_asm.c')])
      self.assertContained('EM_ASM is not supported in side modules', err)
  
    def test_em_js_side_module(self):
      err = self.expect_fail([EMXX, '-sSIDE_MODULE', test_file('core/test_em_js.cpp')])
      self.assertContained('EM_JS is not supported in side modules', err)
  
    # On Windows maximum command line length is 32767 characters. Create such a long build line by linking together
    # several .o files to test that emcc internally uses response files properly when calling llvmn-nm and wasm-ld.
    @is_slow_test
    def test_windows_long_link_response_file(self):
      decls = ''
      calls = ''
      files = []
  
      def create_o(name, i):
        nonlocal decls, calls, files
        f = name + '.c'
        create_file(f, 'int %s() { return %d; }' % (name, i))
        files += [f]
        decls += 'int %s();' % name
        calls += 'value += %s();' % name
  
      count = 1000
      for i in range(count):
        name = 'a' + str(i)
        for j in range(5):
          name += name
        create_o(name, i)
  
      main = '#include<stdio.h>\n%s int main() { int value = 0; %s printf("%%d\\n", value); }' % (decls, calls)
      open('main.c', 'w').write(main)
  
      assert(sum(len(f) for f in files) > 32767)
  
      self.run_process(building.get_command_with_possible_response_file([EMCC, 'main.c'] + files))
      self.assertContained(str(count * (count - 1) // 2), self.run_js('a.out.js'))
  
    # Tests that the filename suffix of the response files can be used to detect which encoding the file is.
    def test_response_file_encoding(self):
      open('äö.c', 'w').write('int main(){}')
  
      open('a.rsp', 'w', encoding='utf-8').write('äö.c') # Write a response file with unicode contents ...
      self.run_process([EMCC, '@a.rsp']) # ... and test that in the absence of a file suffix, it is autodetected to utf-8.
  
      open('a.rsp.cp437', 'w', encoding='cp437').write('äö.c') # Write a response file with Windows CP-437 encoding ...
      self.run_process([EMCC, '@a.rsp.cp437']) # ... and test that with the explicit suffix present, it is properly decoded
  
      import locale
      preferred_encoding = locale.getpreferredencoding(do_setlocale=False)
      print('Python locale preferredencoding: ' + preferred_encoding)
      open('a.rsp', 'w', encoding=preferred_encoding).write('äö.c') # Write a response file using Python preferred encoding
      self.run_process([EMCC, '@a.rsp']) # ... and test that it is properly autodetected.
  
    def test_output_name_collision(self):
      # Ensure that the seconday filenames never collide with the primary output filename
      # In this case we explcitly ask for JS to be ceated in a file with the `.wasm` suffix.
      # Even though this doesn't make much sense the `--oformat` flag is designed to overide
      # any implict type that we might infer from the output name.
      self.run_process([EMCC, '-o', 'hello.wasm', '--oformat=js', test_file('hello_world.c')])
      self.assertExists('hello.wasm')
      self.assertExists('hello_.wasm')
      self.assertContained('hello, world!', self.run_js('hello.wasm'))
  
    def test_EM_PYTHON_MULTIPROCESSING(self):
      with env_modify({'EM_PYTHON_MULTIPROCESSING': '1'}):
        # wasm2js optimizations use multiprocessing to run multiple node
        # invocations
        self.run_process([EMCC, test_file('hello_world.c'), '-sWASM=0', '-O2'])
  
    def test_main_module_no_undefined(self):
      # Test that ERROR_ON_UNDEFINED_SYMBOLS works with MAIN_MODULE.
      self.run_process([EMCC, '-sMAIN_MODULE', '-sERROR_ON_UNDEFINED_SYMBOLS', test_file('hello_world.c')])
      self.run_js('a.out.js')
  
    @parameterized({
      'relocatable': ('-sRELOCATABLE',),
      'linkable': ('-sLINKABLE',),
      'main_module': ('-sMAIN_MODULE',),
    })
    def test_check_undefined(self, flag):
      # positive case: no undefined symbols
      self.run_process([EMCC, flag, '-sERROR_ON_UNDEFINED_SYMBOLS', test_file('hello_world.c')])
      self.run_js('a.out.js')
  
      # negative case: foo is undefined in test_check_undefined.c
      err = self.expect_fail([EMCC, flag, '-sERROR_ON_UNDEFINED_SYMBOLS', test_file('other/test_check_undefined.c')])
      self.assertContained('undefined symbol: foo', err)
  
    def test_EMMAKEN_NO_SDK(self):
      with env_modify({'EMMAKEN_NO_SDK': '1'}):
        err = self.expect_fail([EMCC, test_file('hello_world.c')])
        self.assertContained("warning: We hope to deprecated EMMAKEN_NO_SDK", err)
        self.assertContained("fatal error: 'stdio.h' file not found", err)
  
    @parameterized({
      'default': ('', '2147483648'),
      '1GB': ('-sMAXIMUM_MEMORY=1GB', '1073741824'),
      # for 4GB we return 1 wasm page less than 4GB, as 4GB cannot fit in a 32bit
      # integer
      '4GB': ('-sMAXIMUM_MEMORY=4GB', '4294901760'),
    })
    def test_emscripten_get_heap_max(self, arg, expected):
      create_file('get.c', r'''
        #include <emscripten/heap.h>
        #include <stdio.h>
        int main() {
          printf("max: |%zu|\n", emscripten_get_heap_max());
        }
      ''')
      self.run_process([EMCC, 'get.c', '-sALLOW_MEMORY_GROWTH', arg])
      self.assertContained(f'max: |{expected}|', self.run_js('a.out.js'))
  
    def test_auto_ptr_cxx17(self):
      # Test that its still possible to use auto_ptr, even in C++17
      self.do_other_test('test_auto_ptr_cxx17.cpp', emcc_args=[
        '-std=c++17',
        '-D_LIBCPP_ENABLE_CXX17_REMOVED_AUTO_PTR',
        '-Wno-deprecated-declarations'])
  
    def test_special_chars_in_arguments(self):
      # We had some regressions where the windows `.bat` files that run the compiler
      # driver were failing to accept certain special characters such as `(`, `)` and `!`.
      # See https://github.com/emscripten-core/emscripten/issues/14063
      create_file('test(file).c', 'int main() { return 0; }')
      create_file('test!.c', 'int main() { return 0; }')
      self.run_process([EMCC, 'test(file).c'])
      self.run_process([EMCC, 'test!.c'])
  
    @no_windows('relies on a shell script')
    def test_report_subprocess_signals(self):
      # Test that when subprocess is killed by signal we report the signal name
      create_file('die.sh', '''\
  #!/bin/sh
  kill -9 $$
      ''')
      make_executable('die.sh')
      with env_modify({'EM_COMPILER_WRAPPER': './die.sh'}):
        err = self.expect_fail([EMCC, test_file('hello_world.c')])
        self.assertContained('failed (received SIGKILL (-9))', err)
  
    def test_concepts(self):
      self.do_runf(test_file('other/test_concepts.cpp'), '', emcc_args=['-std=c++20'])
  
    def test_std_cmp(self):
      self.do_runf(test_file('other/test_std_cmp.cpp'), '', emcc_args=['-std=c++20'])
  
    def test_link_only_setting_warning(self):
      err = self.run_process([EMCC, '-sALLOW_MEMORY_GROWTH', '-c', test_file('hello_world.c')], stderr=PIPE).stderr
      self.assertContained("warning: linker setting ignored during compilation: 'ALLOW_MEMORY_GROWTH' [-Wunused-command-line-argument]", err)
  
    def test_link_only_flag_warning(self):
      err = self.run_process([EMCC, '--embed-file', 'file', '-c', test_file('hello_world.c')], stderr=PIPE).stderr
      self.assertContained("warning: linker flag ignored during compilation: '--embed-file' [-Wunused-command-line-argument]", err)
  
    def test_compile_only_flag_warning(self):
      err = self.run_process([EMCC, '--default-obj-ext', 'foo', test_file('hello_world.c')], stderr=PIPE).stderr
      self.assertContained("warning: compiler flag ignored during linking: '--default-obj-ext' [-Wunused-command-line-argument]", err)
  
    def test_no_deprecated(self):
      # Test that -Wno-deprecated is passed on to clang driver
      create_file('test.c', '''\
          __attribute__((deprecated)) int foo();
          int main() { return foo(); }
      ''')
      err = self.expect_fail([EMCC, '-c', '-Werror', 'test.c'])
      self.assertContained("error: 'foo' is deprecated", err)
      self.run_process([EMCC, '-c', '-Werror', '-Wno-deprecated', 'test.c'])
  
    def test_bad_export_name(self):
      err = self.expect_fail([EMCC, '-sEXPORT_NAME=foo bar', test_file('hello_world.c')])
      self.assertContained('error: EXPORT_NAME is not a valid JS identifier: `foo bar`', err)
  
    def test_offset_convertor_plus_wasm2js(self):
      err = self.expect_fail([EMCC, '-sUSE_OFFSET_CONVERTER', '-sWASM=0', test_file('hello_world.c')])
      self.assertContained('wasm2js is not compatible with USE_OFFSET_CONVERTER', err)
  
    def test_standard_library_mapping(self):
      # Test the `-l` flags on the command line get mapped the correct libraries variant
      self.run_process([EMBUILDER, 'build', 'libc-mt-debug', 'libcompiler_rt-mt', 'libdlmalloc-mt'])
  
      libs = ['-lc', '-lcompiler_rt', '-lmalloc']
      err = self.run_process([EMCC, test_file('hello_world.c'), '-pthread', '-nostdlib', '-v'] + libs, stderr=PIPE).stderr
  
      # Check the the linker was run with `-mt` variants because `-pthread` was passed.
      self.assertContained(' -lc-mt-debug ', err)
      self.assertContained(' -ldlmalloc-mt ', err)
      self.assertContained(' -lcompiler_rt-mt ', err)
  
    def test_explict_gl_linking(self):
      # Test that libGL can be linked explictly via `-lGL` rather than implictly.
      # Here we use NO_AUTO_NATIVE_LIBRARIES to disable the implictly linking that normally
      # includes the native GL library.
      self.run_process([EMCC, test_file('other/test_explict_gl_linking.c'), '-sNO_AUTO_NATIVE_LIBRARIES', '-lGL'])
  
    def test_no_main_with_PROXY_TO_PTHREAD(self):
      create_file('lib.cpp', r'''
  #include <emscripten.h>
  EMSCRIPTEN_KEEPALIVE
  void foo() {}
  ''')
      err = self.expect_fail([EMCC, 'lib.cpp', '-pthread', '-sPROXY_TO_PTHREAD'])
      self.assertContained('emcc: error: PROXY_TO_PTHREAD proxies main() for you, but no main exists', err)
  
    def test_archive_bad_extension(self):
      # Regression test for https://github.com/emscripten-core/emscripten/issues/14012
      # where llvm_nm_multiple would be confused by archives names like object files.
      create_file('main.c', '''
      #include <sys/socket.h>
      int main() {
         return (int)(long)&accept;
      }
      ''')
  
      self.run_process([EMCC, '-c', 'main.c'])
      self.run_process([EMAR, 'crs', 'libtest.bc', 'main.o'])
      self.run_process([EMCC, 'libtest.bc', 'libtest.bc'])
  
    def test_split_dwarf_implicit_compile(self):
      # Verify that the dwo file is generated in the current working directory, even when implicitly
      # compiling (compile+link).
      self.run_process([EMCC, test_file('hello_world.c'), '-g', '-gsplit-dwarf'])
      self.assertExists('hello_world.dwo')
  
    @parameterized({
      '': [[]],
      'strict': [['-sSTRICT']],
      'no_allow': [['-sALLOW_UNIMPLEMENTED_SYSCALLS=0']],
    })
    def test_unimplemented_syscalls(self, args):
      create_file('main.c', '''
      #include <assert.h>
      #include <errno.h>
      #include <sys/mman.h>
  
      int main() {
        assert(mincore(0, 0, 0) == -1);
        assert(errno == ENOSYS);
        return 0;
      }
      ''')
      cmd = [EMCC, 'main.c', '-sASSERTIONS'] + args
      if args:
        err = self.expect_fail(cmd)
        self.assertContained('error: undefined symbol: __syscall_mincore', err)
      else:
        self.run_process(cmd)
        err = self.run_js('a.out.js')
        self.assertContained('warning: unsupported syscall: __syscall_mincore', err)
  
        # Setting ASSERTIONS=0 should avoid the runtime warning
        self.run_process(cmd + ['-sASSERTIONS=0'])
        err = self.run_js('a.out.js')
        self.assertNotContained('warning: unsupported syscall', err)
  
    @require_v8
    def test_missing_shell_support(self):
      # By default shell support is not included
      self.run_process([EMCC, test_file('hello_world.c')])
      err = self.run_js('a.out.js', assert_returncode=NON_ZERO)
      self.assertContained('shell environment detected but not enabled at build time.', err)
  
    def test_removed_runtime_function(self):
      create_file('post.js', 'alignMemory(100, 4);')
      self.run_process([EMCC, test_file('hello_world.c'), '--post-js=post.js'])
      err = self.run_js('a.out.js', assert_returncode=NON_ZERO)
      self.assertContained('`alignMemory` is now a library function and not included by default; add it to your library.js __deps or to DEFAULT_LIBRARY_FUNCS_TO_INCLUDE on the command line', err)
  
    # Tests that it is possible to hook into/override a symbol defined in a system library.
    def test_override_system_js_lib_symbol(self):
      # This test verifies it is possible to override a symbol from WebGL library.
  
      # When WebGL is implicitly linked in, the implicit linking should happen before any user
      # --js-libraries, so that they can adjust the behavior afterwards.
      self.do_run_in_out_file_test(test_file('test_override_system_js_lib_symbol.c'), emcc_args=['--js-library', test_file('test_override_system_js_lib_symbol.js'), '-sMAX_WEBGL_VERSION=2'])
  
      # When WebGL is explicitly linked to in strict mode, the linking order on command line should enable overriding.
      self.emcc_args += ['-sAUTO_JS_LIBRARIES=0', '-sMAX_WEBGL_VERSION=2', '-lwebgl.js', '--js-library', test_file('test_override_system_js_lib_symbol.js')]
      self.do_run_in_out_file_test(test_file('test_override_system_js_lib_symbol.c'))
  
    @node_pthreads
    def test_pthread_lsan_no_leak(self):
      self.set_setting('USE_PTHREADS')
      self.set_setting('PROXY_TO_PTHREAD')
      self.set_setting('EXIT_RUNTIME')
      self.emcc_args += ['-gsource-map']
      self.do_run_in_out_file_test(test_file('pthread/test_pthread_lsan_no_leak.cpp'), emcc_args=['-fsanitize=leak'])
      self.do_run_in_out_file_test(test_file('pthread/test_pthread_lsan_no_leak.cpp'), emcc_args=['-fsanitize=address'])
  
    @node_pthreads
    def test_pthread_lsan_leak(self):
      self.set_setting('USE_PTHREADS')
      self.set_setting('PROXY_TO_PTHREAD')
      self.set_setting('EXIT_RUNTIME')
      self.add_pre_run("Module['LSAN_OPTIONS'] = 'exitcode=0'")
      self.emcc_args += ['-gsource-map']
      expected = [
        'Direct leak of 3432 byte(s) in 1 object(s) allocated from',
        'test_pthread_lsan_leak.cpp:18:17',
        'Direct leak of 2048 byte(s) in 1 object(s) allocated from',
        'test_pthread_lsan_leak.cpp:36:10',
        'Direct leak of 1337 byte(s) in 1 object(s) allocated from',
        'test_pthread_lsan_leak.cpp:30:16',
        'Direct leak of 1234 byte(s) in 1 object(s) allocated from',
        'test_pthread_lsan_leak.cpp:20:13',
        'Direct leak of 420 byte(s) in 1 object(s) allocated from',
        'test_pthread_lsan_leak.cpp:31:13',
        'Direct leak of 42 byte(s) in 1 object(s) allocated from',
        'test_pthread_lsan_leak.cpp:13:21',
        'test_pthread_lsan_leak.cpp:35:3',
        '8513 byte(s) leaked in 6 allocation(s).',
      ]
      self.do_runf(test_file('pthread/test_pthread_lsan_leak.cpp'), expected, assert_all=True, emcc_args=['-fsanitize=leak'])
      self.do_runf(test_file('pthread/test_pthread_lsan_leak.cpp'), expected, assert_all=True, emcc_args=['-fsanitize=address'])
  
    @node_pthreads
    def test_pthread_js_exception(self):
      # Ensure that JS exceptions propagate back to the main main thread and cause node
      # to exit with an error.
      self.set_setting('USE_PTHREADS')
      self.set_setting('PROXY_TO_PTHREAD')
      self.set_setting('EXIT_RUNTIME')
      self.build(test_file('other/test_pthread_js_exception.c'))
      err = self.run_js('test_pthread_js_exception.js', assert_returncode=NON_ZERO)
      self.assertContained('missing is not defined', err)
  
    def test_config_closure_compiler(self):
      self.run_process([EMCC, test_file('hello_world.c'), '--closure=1'])
      with env_modify({'EM_CLOSURE_COMPILER': sys.executable}):
        err = self.expect_fail([EMCC, test_file('hello_world.c'), '--closure=1'])
      self.assertContained('closure compiler', err)
      self.assertContained(sys.executable, err)
      self.assertContained('not execute properly!', err)
  
    def test_node_unhandled_rejection(self):
      create_file('pre.js', '''
      async function foo() {
        var a = missing;
      }
      async function doReject() {
        return foo();
      }
      ''')
      create_file('main.c', '''
      #include <emscripten.h>
  
      int main() {
        EM_ASM(setTimeout(doReject, 0));
        emscripten_exit_with_live_runtime();
        __builtin_trap();
      }
      ''')
  
      # With NODEJS_CATCH_REJECTION we expect the unhandled rejection to cause a non-zero
      # exit code and log the stack trace correctly.
      self.run_process([EMCC, '--pre-js=pre.js', '-sNODEJS_CATCH_REJECTION=1', 'main.c'])
      output = self.run_js('a.out.js', assert_returncode=NON_ZERO)
      self.assertContained('ReferenceError: missing is not defined', output)
      self.assertContained('at foo (', output)
  
      version = self.run_process(config.NODE_JS + ['--version'], stdout=PIPE).stdout.strip()
      version = [int(v) for v in version.replace('v', '').replace('-pre', '').split('.')]
      if version[0] >= 15:
        self.skipTest('old behaviour of node JS cannot be tested on node v15 or above')
  
      # Without NODEJS_CATCH_REJECTION we expect node to log the unhandled rejection
      # but return 0.
      self.node_args = [a for a in self.node_args if '--unhandled-rejections' not in a]
      self.run_process([EMCC, '--pre-js=pre.js', '-sNODEJS_CATCH_REJECTION=0', 'main.c'])
      output = self.run_js('a.out.js')
      self.assertContained('ReferenceError: missing is not defined', output)
      self.assertContained('at foo (', output)
  
    @node_pthreads
    def test_default_pthread_stack_size(self):
      self.do_runf(test_file('other/test_default_pthread_stack_size.c'))
      self.emcc_args.append('-sUSE_PTHREADS')
      self.do_runf(test_file('other/test_default_pthread_stack_size.c'))
  
    def test_emscripten_set_immediate(self):
      self.do_runf(test_file('emscripten_set_immediate.c'))
  
    def test_emscripten_set_immediate_loop(self):
      self.do_runf(test_file('emscripten_set_immediate_loop.c'))
  
    @node_pthreads
    def test_pthread_trap(self):
      # TODO(https://github.com/emscripten-core/emscripten/issues/15161):
      # Make this work without PROXY_TO_PTHREAD
      self.set_setting('PROXY_TO_PTHREAD')
      self.set_setting('USE_PTHREADS')
      self.set_setting('EXIT_RUNTIME')
      self.emcc_args += ['--profiling-funcs']
      output = self.do_runf(test_file('pthread/test_pthread_trap.c'), assert_returncode=NON_ZERO)
      self.assertContained('sent an error!', output)
      self.assertContained('at thread_main', output)
  
    @node_pthreads
    def test_emscripten_set_interval(self):
      self.do_runf(test_file('emscripten_set_interval.c'), args=['-sUSE_PTHREADS', '-sPROXY_TO_PTHREAD'])
  
    # Test emscripten_console_log(), emscripten_console_warn() and emscripten_console_error()
    def test_emscripten_console_log(self):
      self.do_run_in_out_file_test(test_file('emscripten_console_log.c'), emcc_args=['--pre-js', test_file('emscripten_console_log_pre.js')])
  
    # Tests emscripten_unwind_to_js_event_loop() behavior
    def test_emscripten_unwind_to_js_event_loop(self, *args):
      self.do_runf(test_file('test_emscripten_unwind_to_js_event_loop.c'))
  
    @node_pthreads
    def test_emscripten_set_timeout(self):
      self.do_runf(test_file('emscripten_set_timeout.c'), args=['-sUSE_PTHREADS', '-sPROXY_TO_PTHREAD'])
  
    @node_pthreads
    def test_emscripten_set_timeout_loop(self):
      self.do_runf(test_file('emscripten_set_timeout_loop.c'), args=['-sUSE_PTHREADS', '-sPROXY_TO_PTHREAD'])
  
    # Verify that we are able to successfully compile a script when the Windows 7
    # and Python workaround env. vars are enabled.
    # See https://bugs.python.org/issue34780
    @with_env_modify({'EM_WORKAROUND_PYTHON_BUG_34780': '1',
                      'EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG': '1'})
    def test_windows_batch_script_workaround(self):
      self.run_process([EMCC, test_file('hello_world.c')])
      self.assertExists('a.out.js')
  
    @node_pthreads
    def test_pthread_out_err(self):
      self.set_setting('USE_PTHREADS')
      self.set_setting('PROXY_TO_PTHREAD')
      self.set_setting('EXIT_RUNTIME')
      self.do_other_test('test_pthread_out_err.c')
  
    @node_pthreads
    def test_pthread_icu(self):
      self.set_setting('USE_PTHREADS')
      self.set_setting('USE_ICU')
      self.set_setting('PROXY_TO_PTHREAD')
      self.set_setting('EXIT_RUNTIME')
      self.do_other_test('test_pthread_icu.cpp')
  
    # unistd tests
  
    def test_unistd_confstr(self):
      self.do_run_in_out_file_test('unistd/confstr.c')
  
    def test_unistd_ttyname(self):
      self.do_runf(test_file('unistd/ttyname.c'), 'success')
  
    def test_unistd_pathconf(self):
      self.do_run_in_out_file_test('unistd/pathconf.c')
  
    def test_unistd_swab(self):
      self.do_run_in_out_file_test('unistd/swab.c')
  
    def test_unistd_isatty(self):
      self.do_runf(test_file('unistd/isatty.c'), 'success')
  
    def test_unistd_login(self):
      self.do_run_in_out_file_test('unistd/login.c')
  
    def test_unistd_sleep(self):
      self.do_run_in_out_file_test('unistd/sleep.c')
  
    def test_unistd_fstatfs(self):
      self.do_run_in_out_file_test('unistd/fstatfs.c')
  
    # WASMFS tests
  
    # TODO: This test will only work with the new file system.
    # Addresses this issue: https://github.com/emscripten-core/emscripten/issues/4017
    # The new file system also correctly identifies errors that the JS file system missed.
    def test_wasmfs_dup(self):
      self.set_setting('WASMFS')
      self.do_run_in_out_file_test('wasmfs/wasmfs_dup.c')
  
    @also_with_wasmfs
    def test_unistd_open(self):
      self.do_run_in_out_file_test('wasmfs/wasmfs_open.c')
  
    @also_with_wasmfs
    def test_unistd_stat(self):
      self.do_runf(test_file('wasmfs/wasmfs_stat.c'))
  
    @also_with_wasmfs
    def test_unistd_create(self):
      self.set_setting('WASMFS')
      self.do_run_in_out_file_test('wasmfs/wasmfs_create.c')
  
    @also_with_wasmfs
    def test_unistd_seek(self):
      self.do_run_in_out_file_test('wasmfs/wasmfs_seek.c')
  
    @also_with_wasmfs
    def test_unistd_mkdir(self):
      self.do_run_in_out_file_test('wasmfs/wasmfs_mkdir.c')
  
    @also_with_wasmfs
    def test_unistd_cwd(self):
      self.do_run_in_out_file_test('wasmfs/wasmfs_chdir.c')
  
    def test_wasmfs_getdents(self):
      # TODO: update this test when /dev has been filled out.
      # Run only in WASMFS for now.
      self.set_setting('WASMFS')
      self.do_run_in_out_file_test('wasmfs/wasmfs_getdents.c')
  
    def test_wasmfs_readfile(self):
      self.set_setting('WASMFS')
      self.do_run_in_out_file_test(test_file('wasmfs/wasmfs_readfile.c'))
  
    def test_wasmfs_jsfile(self):
      self.set_setting('WASMFS')
      self.do_run_in_out_file_test('wasmfs/wasmfs_jsfile.c')
  
    @node_pthreads
    def test_wasmfs_jsfile_proxying_backend(self):
      self.emcc_args.append('-DPROXYING')
      self.set_setting('USE_PTHREADS')
      self.set_setting('PROXY_TO_PTHREAD')
      self.set_setting('EXIT_RUNTIME')
      self.test_wasmfs_jsfile()
  
    @disabled('Running with initial >2GB heaps is not currently supported on the CI version of Node')
    def test_hello_world_above_2gb(self):
      self.run_process([EMCC, test_file('hello_world.c'), '-sGLOBAL_BASE=2147483648', '-sINITIAL_MEMORY=3GB'])
      self.assertContained('hello, world!', self.run_js('a.out.js'))
  
    def test_hello_function(self):
      # hello_function.cpp is referenced/used in the docs.  This test ensures that it
      # at least compiles.
      # (It seems odd that we ship the entire tests/ directory to all our users and
      # reference them in our docs.  Should we move this file to somewhere else such
      # as `examples/`?)
      self.run_process([EMCC, test_file('hello_function.cpp'), '-o', 'function.html', '-sEXPORTED_FUNCTIONS=_int_sqrt', '-sEXPORTED_RUNTIME_METHODS=ccall,cwrap'])
  
    @parameterized({
      '': ([],),
      'O2': (['-O2'],),
    })
    def test_es5_transpile(self, args):
      self.emcc_args += args
  
      # Create a library file that uses the following ES6 features
      # - let/const
      # - arrow funcs
      # - for..of
      # - object.assign
      create_file('es6_library.js', '''\
      mergeInto(LibraryManager.library, {
        foo: function() {
          // Object.assign + let
          let obj = Object.assign({}, {prop:1});
          err('prop: ' + obj.prop);
  
          // arror funcs + const
          const bar = () => 2;
          err('bar: ' + bar());
        }
      });
      ''')
      create_file('test.c', 'extern void foo(); int main() { foo(); }')
      self.emcc_args += ['--js-library', 'es6_library.js']
      self.uses_es6 = True
  
      def check_for_es6(filename, expect):
        js = read_file(filename)
        if expect:
          self.assertContained(['() => 2', '()=>2'], js)
          self.assertContained('const ', js)
          self.assertContained('let ', js)
        else:
          self.verify_es5(filename)
          self.assertNotContained('() => 2', js)
          self.assertNotContained('()=>2', js)
          self.assertNotContained('const ', js)
          self.assertNotContained('let ', js)
  
      # Check that under normal circumstances none of these features get
      # removed / transpiled.
      print('base case')
      self.do_runf('test.c', 'prop: 1\nbar: 2\n')
      check_for_es6('test.js', True)
  
      # If we select and older browser than closure will kick in by default
      # to traspile.
      print('with old browser')
      self.emcc_args.remove('-Werror')
      self.set_setting('MIN_CHROME_VERSION', '10')
      self.do_runf('test.c', 'prop: 1\nbar: 2\n', output_basename='test2')
      check_for_es6('test2.js', False)
  
      # If we add `--closure=0` that traspiler (closure) is not run at all
      print('with old browser + --closure=0')
      self.do_runf('test.c', 'prop: 1\nbar: 2\n', emcc_args=['--closure=0'], output_basename='test3')
      check_for_es6('test3.js', True)
  
      # If we use `--closure=1` closure will run in full optimization mode
      # and also transpile to ES5
      print('with old browser + --closure=1')
      self.do_runf('test.c', 'prop: 1\nbar: 2\n', emcc_args=['--closure=1'], output_basename='test4')
      check_for_es6('test4.js', False)
  
    def test_gmtime_noleak(self):
      # Confirm that gmtime_r does not leak when called in isolation.
      self.emcc_args.append('-fsanitize=leak')
      self.do_other_test('test_gmtime_noleak.c')
  
    def test_build_fetch_examples(self):
      # We can't run these outside of the browser, but at least we can
      # make sure they build.
      self.set_setting('FETCH')
      self.build(test_file('fetch/example_async_xhr_to_memory.c'))
      self.build(test_file('fetch/example_async_xhr_to_memory_via_indexeddb.c'))
      self.build(test_file('fetch/example_idb_delete.c'))
      self.build(test_file('fetch/example_idb_store.c'))
      self.build(test_file('fetch/example_stream_async_xhr.c'))
      self.build(test_file('fetch/example_synchronous_fetch.c'))
      self.build(test_file('fetch/example_sync_xhr_to_memory.c'))
      self.build(test_file('fetch/example_waitable_xhr_to_memory.c'))
      self.build(test_file('fetch/example_xhr_progress.c'))
  
    # Test that using llvm-nm works when response files are in use, and inputs are linked using relative paths.
    # llvm-nm has a quirk that it does not remove escape chars when printing out filenames.
    @with_env_modify({'EM_FORCE_RESPONSE_FILES': '1'})
    def test_llvm_nm_relative_paths_works_with_response_files(self):
      os.mkdir('foo')
      # Test creating a library file with a relative path in a subdir, so it gets a double backslash "\\" in the generated llvm-nm output.
      # Also add a space to stress space escaping, e.g. "\ ".
      create_file(os.path.join('foo', 'foo bar.c'), r'''
        #include <time.h>
        #include <stdint.h>
        time_t foo()
        {
          int64_t secondsSinceEpoch = 0;
          struct tm* utcTime = gmtime((time_t*)&secondsSinceEpoch);
          return mktime(utcTime);
        }
      ''')
      create_file('main.c', r'''
        #include <stdio.h>
        #include <time.h>
        time_t foo(void);
        int main()
        {
          printf("%d\n", (int)foo());
        }
      ''')
      self.run_process([EMCC, 'main.c', os.path.join('foo', 'foo bar.c')])
  
    def test_tutorial(self):
      # Ensure that files referenced in Tutorial.rst are buildable
      self.run_process([EMCC, test_file('hello_world_file.cpp')])
  
    def test_stdint_limits(self):
      self.do_other_test('test_stdint_limits.c')
  