Skip to content

Fix strict test suite #24431

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
May 29, 2025
8 changes: 4 additions & 4 deletions test/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -1147,7 +1147,7 @@ def setup_nodefs_test(self):
if self.get_setting('WASMFS'):
# without this the JS setup code in setup_nodefs.js doesn't work
self.set_setting('FORCE_FILESYSTEM')
self.emcc_args += ['-DNODEFS', '-lnodefs.js', '--pre-js', test_file('setup_nodefs.js')]
self.emcc_args += ['-DNODEFS', '-lnodefs.js', '--pre-js', test_file('setup_nodefs.js'), '-sINCOMING_MODULE_JS_API=[onRuntimeInitialized]']

def setup_noderawfs_test(self):
self.require_node()
Expand Down Expand Up @@ -1336,17 +1336,17 @@ def in_dir(self, *pathelems):
def add_pre_run(self, code):
assert not self.get_setting('MINIMAL_RUNTIME')
create_file('prerun.js', 'Module.preRun = function() { %s }\n' % code)
self.emcc_args += ['--pre-js', 'prerun.js']
self.emcc_args += ['--pre-js', 'prerun.js', '-sINCOMING_MODULE_JS_API=[preRun]']
Copy link
Collaborator

Choose a reason for hiding this comment

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

The problem with doing this here is that then the test doesn't have any way to add more to INCOMING_MODULE_JS_API.

We currently don't have any way to make settings like INCOMING_MODULE_JS_API additive.

I've tried to make them additive by default (which seems like the right behaviour in almost all cases I can think of): #19938. But that PR got hung up with disagreement about how / if to change the default behaviour.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The problem with doing this here is that then the test doesn't have any way to add more to INCOMING_MODULE_JS_API.

Yeah, I was thinking about the additivity as well while writing this. That should definitely be the best behavior to have.

The add_pre_run() is only for tests, and so far it seems that no test needed to concatenate (or that would have shown up in strict run). If/when such a test comes up, that directive can be refactored to occur at the call site.

Copy link
Collaborator Author

@juj juj May 29, 2025

Choose a reason for hiding this comment

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

That is, either that specific test can then be written to duplicate the -sINCOMING_MODULE_JS_API=[preRun,theothersymbol], or -sINCOMING_MODULE_JS_API=[preRun] be hoisted from add_pre_run() to all the tests.

Copy link
Collaborator

Choose a reason for hiding this comment

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

SGTM if the tests pass


def add_post_run(self, code):
assert not self.get_setting('MINIMAL_RUNTIME')
create_file('postrun.js', 'Module.postRun = function() { %s }\n' % code)
self.emcc_args += ['--pre-js', 'postrun.js']
self.emcc_args += ['--pre-js', 'postrun.js', '-sINCOMING_MODULE_JS_API=[postRun]']

def add_on_exit(self, code):
assert not self.get_setting('MINIMAL_RUNTIME')
create_file('onexit.js', 'Module.onExit = function() { %s }\n' % code)
self.emcc_args += ['--pre-js', 'onexit.js']
self.emcc_args += ['--pre-js', 'onexit.js', '-sINCOMING_MODULE_JS_API=[onExit]']

# returns the full list of arguments to pass to emcc
# param @main_file whether this is the main file of the test. some arguments
Expand Down
5 changes: 4 additions & 1 deletion test/core/test_core_types.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@

// We prefer to use __EMSCRIPTEN__, but for compatibility, we define
// EMSCRIPTEN too.
#ifndef EMSCRIPTEN
#if defined(IN_STRICT_MODE) && defined(EMSCRIPTEN)
#error When compiling in -sSTRICT mode, EMSCRIPTEN should not be defined, but it was!
#endif
#if !defined(IN_STRICT_MODE) && !defined(EMSCRIPTEN)
#error EMSCRIPTEN is not defined
#endif

Expand Down
8 changes: 4 additions & 4 deletions test/core/test_getValue_setValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,20 @@ int main() {
setValue($0, 1234, 'i32');
out('i32: ' + getValue($0, 'i32'));
#ifdef WASM_BIGINT
i64 = getValue($1, 'i64');
var i64 = getValue($1, 'i64');
out('i64: 0x' + i64.toString(16) + ' ' + typeof(i64));
#endif
ptr = getValue($1, '*');
var ptr = getValue($1, '*');
out('ptr: 0x' + ptr.toString(16) + ' ' + typeof(ptr));
#else
out('i32: ' + getValue($0, 'i32'));
Module['setValue']($0, 1234, 'i32');
out('i32: ' + Module['getValue']($0, 'i32'));
#ifdef WASM_BIGINT
i64 = Module['getValue']($1, 'i64');
var i64 = Module['getValue']($1, 'i64');
out('i64: 0x' + i64.toString(16) + ' ' + typeof(i64));
#endif
ptr = Module['getValue']($1, '*');
var ptr = Module['getValue']($1, '*');
out('ptr: 0x' + ptr.toString(16) + ' ' + typeof(ptr));
#endif

Expand Down
69 changes: 46 additions & 23 deletions test/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ def decorated(self, textdecoder, *args, **kwargs):

no_minimal_runtime = make_no_decorator_for_setting('MINIMAL_RUNTIME')
no_safe_heap = make_no_decorator_for_setting('SAFE_HEAP')
no_strict = make_no_decorator_for_setting('STRICT')


def is_sanitizing(args):
Expand Down Expand Up @@ -646,6 +647,8 @@ def test_sha1(self):
self.do_runf('third_party/sha1.c', 'SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6')

def test_core_types(self):
if self.get_setting('STRICT'):
self.emcc_args += ['-DIN_STRICT_MODE=1']
self.do_runf('core/test_core_types.c')

def test_cube2md5(self):
Expand Down Expand Up @@ -2649,6 +2652,7 @@ def test_pthread_abort(self):
# handler will only be present in the main thread (much like it would if it
# was passed in by pre-populating the module object on prior to loading).
self.add_pre_run("Module.onAbort = () => console.log('onAbort called');")
self.emcc_args += ['-sINCOMING_MODULE_JS_API=[preRun,onAbort]']
self.do_run_in_out_file_test('pthread/test_pthread_abort.c', assert_returncode=NON_ZERO)

@node_pthreads
Expand Down Expand Up @@ -4159,7 +4163,7 @@ def test_dylink_locate_file(self):
}
};
''' % (so_name, so_dir))
self.do_basic_dylink_test(so_dir=so_dir, so_name=so_name, main_emcc_args=['--pre-js', 'pre.js'])
self.do_basic_dylink_test(so_dir=so_dir, so_name=so_name, main_emcc_args=['--pre-js', 'pre.js', '-sINCOMING_MODULE_JS_API=[locateFile]'])

@with_dylink_reversed
def test_dylink_function_pointer_equality(self):
Expand Down Expand Up @@ -5209,7 +5213,7 @@ class Bar : public Foo {
@needs_dylink
def test_dylink_argv_argc(self):
# Verify that argc and argv can be sent to main when main is in a side module
self.emcc_args += ['--pre-js', 'pre.js']
self.emcc_args += ['--pre-js', 'pre.js', '--no-entry', '-sINCOMING_MODULE_JS_API=[arguments]']
create_file('pre.js', "Module['arguments'] = ['hello', 'world!']")
self.dylink_test(
'', # main module is empty.
Expand Down Expand Up @@ -5424,6 +5428,7 @@ def test_langinfo(self):
self.do_core_test('test_langinfo.c')

@no_modularize_instance('uses Module object directly')
@no_strict('TODO: Fails in -sSTRICT mode due to an unknown reason.')
def test_files(self):
# Use closure here, to test we don't break FS stuff
if '-O3' in self.emcc_args and self.is_wasm2js():
Expand All @@ -5435,7 +5440,7 @@ def test_files(self):
else:
self.maybe_closure()

self.emcc_args += ['--pre-js', 'pre.js']
self.emcc_args += ['--pre-js', 'pre.js', '-sINCOMING_MODULE_JS_API=[preRun]']
self.set_setting('FORCE_FILESYSTEM')

create_file('pre.js', '''
Expand Down Expand Up @@ -5467,7 +5472,7 @@ def test_module_stdin(self):
stdout: (x) => out('got: ' + x)
};
''')
self.emcc_args += ['--pre-js', 'pre.js']
self.emcc_args += ['--pre-js', 'pre.js', '-sINCOMING_MODULE_JS_API=[stdin,stdout]']

src = r'''
#include <stdio.h>
Expand Down Expand Up @@ -5916,7 +5921,7 @@ def test_fs_no_main(self):
''')
self.set_setting('EXPORTED_FUNCTIONS', '_foo')
self.set_setting('FORCE_FILESYSTEM')
self.emcc_args += ['--pre-js', 'pre.js']
self.emcc_args += ['--pre-js', 'pre.js', '-sINCOMING_MODULE_JS_API=[preRun, onRuntimeInitialized]']
self.do_run('int foo() { return 42; }', '', force_c=True)

@also_with_noderawfs
Expand Down Expand Up @@ -6159,6 +6164,8 @@ def test_posixtime(self):
self.do_core_test('test_posixtime.c')

def test_uname(self):
if self.get_setting('STRICT'):
self.emcc_args += ['-lstubs']
self.do_core_test('test_uname.c', regex=True)

def test_unary_literal(self):
Expand Down Expand Up @@ -6188,6 +6195,8 @@ def test_stddef(self):
self.do_core_test('test_stddef.cpp', force_c=True)

def test_getloadavg(self):
if self.get_setting('STRICT'):
self.emcc_args += ['-lstubs']
self.do_core_test('test_getloadavg.c')

def test_nl_types(self):
Expand Down Expand Up @@ -6717,17 +6726,17 @@ def test_gcc_unmangler(self):
@needs_make('configure script')
@is_slow_test
def test_freetype(self):
# Not needed for js, but useful for debugging
shutil.copy(test_file('freetype/LiberationSansBold.ttf'), 'font.ttf')
ftlib = self.get_freetype_library()

if self.get_setting('WASMFS'):
self.emcc_args += ['-sFORCE_FILESYSTEM']

self.add_pre_run("FS.createDataFile('/', 'font.ttf', %s, true, false, false);" % str(
list(bytearray(read_binary(test_file('freetype/LiberationSansBold.ttf')))),
))

# Not needed for js, but useful for debugging
shutil.copy(test_file('freetype/LiberationSansBold.ttf'), 'font.ttf')
ftlib = self.get_freetype_library()

# Main
self.do_run_in_out_file_test('freetype/main.c',
args=['font.ttf', 'test!', '150', '120', '25'],
Expand Down Expand Up @@ -6763,6 +6772,8 @@ def test_freetype(self):
'pthreads': (True,),
})
def test_sqlite(self, use_pthreads):
if self.get_setting('STRICT'):
self.emcc_args += ['-lstubs']
if use_pthreads:
self.emcc_args.append('-pthread')
self.setup_node_pthreads()
Expand Down Expand Up @@ -6847,7 +6858,7 @@ def test_poppler(self):
out("Data: " + JSON.stringify(FileData.map(function(x) { return unSign(x, 8) })));
};
''')
self.emcc_args += ['--pre-js', 'pre.js', '-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$unSign']
self.emcc_args += ['--pre-js', 'pre.js', '-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$unSign', '-sINCOMING_MODULE_JS_API=[preRun, postRun]']

ppm_data = str(list(bytearray(read_binary(test_file('poppler/ref.ppm')))))
self.do_run('', ppm_data.replace(' ', ''),
Expand Down Expand Up @@ -6935,7 +6946,7 @@ def image_compare(output):
assert diff_mean < 0.01, diff_mean

self.emcc_args += ['--minify=0'] # to compare the versions
self.emcc_args += ['--pre-js', 'pre.js']
self.emcc_args += ['--pre-js', 'pre.js', '-sINCOMING_MODULE_JS_API=[preRun,postRun]']

output = self.do_runf('third_party/openjpeg/codec/j2k_to_image.c',
'Successfully generated', # The real test for valid output is in image_compare
Expand Down Expand Up @@ -7135,6 +7146,7 @@ def test(output_prefix='', args=None, assert_returncode=0):
test(args=['-sFORCE_FILESYSTEM'])

@no_modularize_instance('uses Module object directly')
@no_strict('This test verifies legacy behavior that does not apply to -sSTRICT builds.')
def test_legacy_exported_runtime_numbers(self):
# these used to be exported, but no longer are by default
def test(expected, args=None, assert_returncode=0):
Expand Down Expand Up @@ -7573,7 +7585,7 @@ def test_embind_val_coro(self):
create_file('pre.js', r'''Module.onRuntimeInitialized = () => {
Module.asyncCoro().then(console.log);
}''')
self.emcc_args += ['-std=c++20', '--bind', '--pre-js=pre.js']
self.emcc_args += ['-std=c++20', '--bind', '--pre-js=pre.js', '-sINCOMING_MODULE_JS_API=[onRuntimeInitialized]', '--no-entry']
self.do_runf('embind/test_val_coro.cpp', '34\n')

def test_embind_val_coro_caught(self):
Expand All @@ -7584,7 +7596,7 @@ def test_embind_val_coro_caught(self):
err => console.error(`rejected with: ${err.stack}`)
);
}''')
self.emcc_args += ['-std=c++20', '--bind', '--pre-js=pre.js', '-fexceptions']
self.emcc_args += ['-std=c++20', '--bind', '--pre-js=pre.js', '-fexceptions', '-sINCOMING_MODULE_JS_API=[onRuntimeInitialized]', '--no-entry']
self.do_runf('embind/test_val_coro.cpp', 'rejected with: std::runtime_error: bang from throwingCoro!\n')

def test_embind_dynamic_initialization(self):
Expand Down Expand Up @@ -7688,6 +7700,7 @@ def test_embind_wasm_workers(self):
'all_growth': ('ALL', True),
})
@no_modularize_instance('uses Module global')
@no_strict('TODO: Fails in -sSTRICT mode due to an unknown reason.')
def test_webidl(self, mode, allow_memory_growth):
self.set_setting('WASM_ASYNC_COMPILATION', 0)
if self.maybe_closure():
Expand Down Expand Up @@ -8055,7 +8068,7 @@ def test_exit_status(self):
assert(status == EXITSTATUS);
};
''')
self.emcc_args += ['--pre-js', 'pre.js']
self.emcc_args += ['--pre-js', 'pre.js', '-sINCOMING_MODULE_JS_API=[onExit]']
print('.. exit')
self.do_runf('exit.c', 'hello, world!\ncleanup\nI see exit status: 117', assert_returncode=117, emcc_args=['-DNORMAL_EXIT'])
print('.. _exit')
Expand Down Expand Up @@ -8172,7 +8185,7 @@ def test_async_ccall_bad(self):
}
};
''')
self.emcc_args += ['--pre-js', 'pre.js']
self.emcc_args += ['--pre-js', 'pre.js', '-sINCOMING_MODULE_JS_API=[onRuntimeInitialized]']
self.do_runf('main.c', 'The call to main is running asynchronously.')

@with_asyncify_and_jspi
Expand All @@ -8197,7 +8210,7 @@ def test_async_ccall_good(self):
ccall('main', null, ['number', 'string'], [2, 'waka'], { async: true });
};
''')
self.emcc_args += ['--pre-js', 'pre.js']
self.emcc_args += ['--pre-js', 'pre.js', '-sINCOMING_MODULE_JS_API=[onRuntimeInitialized]']
self.do_runf('main.c', 'HelloWorld')

@parameterized({
Expand Down Expand Up @@ -8242,7 +8255,7 @@ def test_async_ccall_promise(self, exit_runtime):
});
};
''')
self.emcc_args += ['--pre-js', 'pre.js']
self.emcc_args += ['--pre-js', 'pre.js', '-sINCOMING_MODULE_JS_API=[onRuntimeInitialized]']
self.do_runf('main.c', 'stringf: first\nsecond\n6.4')

@no_esm_integration('WASM_ESM_INTEGRATION is not compatible with ASYNCIFY=1')
Expand Down Expand Up @@ -8582,7 +8595,7 @@ def test_fs_dict(self):
out(typeof NODEFS);
};
''')
self.emcc_args += ['--pre-js', 'pre.js']
self.emcc_args += ['--pre-js', 'pre.js', '-sINCOMING_MODULE_JS_API=[preRun]']
self.do_run('int main() { return 0; }', 'object\nobject\nobject\nobject\nobject\nobject')

def test_fs_dict_none(self):
Expand All @@ -8606,7 +8619,7 @@ def test_fs_dict_none(self):
}
};
''')
self.emcc_args += ['--pre-js', 'pre.js']
self.emcc_args += ['--pre-js', 'pre.js', '-sINCOMING_MODULE_JS_API=[preRun]']
expected = '''\
object
undefined
Expand Down Expand Up @@ -8715,7 +8728,7 @@ def test_postrun_exception(self):
def test_postrun_exit_runtime(self):
create_file('pre.js', "Module['postRun'] = () => err('post run\\n');")
self.set_setting('EXIT_RUNTIME')
self.emcc_args.append('--pre-js=pre.js')
self.emcc_args += ['--pre-js=pre.js', '-sINCOMING_MODULE_JS_API=[postRun]']
self.do_runf('hello_world.c', 'post run')

# Tests that building with -sDECLARE_ASM_MODULE_EXPORTS=0 works
Expand Down Expand Up @@ -9233,7 +9246,7 @@ def test_pthread_exceptions(self):
def test_pthread_exit_process(self):
self.set_setting('PROXY_TO_PTHREAD')
self.set_setting('EXIT_RUNTIME')
self.emcc_args += ['-DEXIT_RUNTIME', '--pre-js', test_file('core/pthread/test_pthread_exit_runtime.pre.js')]
self.emcc_args += ['-DEXIT_RUNTIME', '--pre-js', test_file('core/pthread/test_pthread_exit_runtime.pre.js'), '-sINCOMING_MODULE_JS_API=[onRuntimeInitialized, onExit]']
self.do_run_in_out_file_test('core/pthread/test_pthread_exit_runtime.c', assert_returncode=42)

@node_pthreads
Expand Down Expand Up @@ -9401,6 +9414,10 @@ def test_pthread_dylink_longjmp(self):
@needs_dylink
@node_pthreads
def test_pthread_dylink_main_module_1(self):
# TODO: For some reason, -lhtml5 must be passed in -sSTRICT mode, but can NOT
# be passed when not compiling in -sSTRICT mode. That does not seem intentional?
Copy link
Collaborator

Choose a reason for hiding this comment

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

This certainly sounds like a bug. Could you open one an link it here (and below).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

if self.get_setting('STRICT'):
self.emcc_args += ['-lhtml5']
self.emcc_args += ['-Wno-experimental', '-pthread']
self.set_setting('MAIN_MODULE')
self.do_runf('hello_world.c')
Expand All @@ -9413,7 +9430,7 @@ def test_pthread_dylink_main_module_1(self):
def test_Module_dynamicLibraries(self, args):
# test that Module.dynamicLibraries works with pthreads
self.emcc_args += args
self.emcc_args += ['--pre-js', 'pre.js']
self.emcc_args += ['--pre-js', 'pre.js', '-sINCOMING_MODULE_JS_API=[dynamicLibraries]']
self.emcc_args += ['--js-library', 'lib.js']
# This test is for setting dynamicLibraries at runtime, so we don't
# want emscripten loading `liblib.so` automatically (which it would
Expand Down Expand Up @@ -9534,7 +9551,7 @@ def test_abort_on_exceptions(self):
self.set_setting('ABORT_ON_WASM_EXCEPTIONS')
self.set_setting('ALLOW_TABLE_GROWTH')
self.set_setting('EXPORTED_RUNTIME_METHODS', ['ccall', 'cwrap'])
self.set_setting('DEFAULT_LIBRARY_FUNCS_TO_INCLUDE', ['$addFunction'])
self.set_setting('DEFAULT_LIBRARY_FUNCS_TO_INCLUDE', ['$addFunction', '$addOnPostRun'])
self.emcc_args += ['-lembind', '--post-js', test_file('core/test_abort_on_exceptions_post.js')]
self.do_core_test('test_abort_on_exceptions.cpp', interleaved_output=False)

Expand All @@ -9561,6 +9578,10 @@ def test_abort_on_exceptions_pthreads(self):

@needs_dylink
def test_gl_main_module(self):
# TODO: For some reason, -lGL must be passed in -sSTRICT mode, but can NOT
# be passed when not compiling in -sSTRICT mode. That does not seem intentional?
if self.get_setting('STRICT'):
self.emcc_args += ['-lGL']
self.set_setting('MAIN_MODULE')
self.emcc_args += ['-sGL_ENABLE_GET_PROC_ADDRESS']
self.do_runf('core/test_gl_get_proc_address.c')
Expand Down Expand Up @@ -9589,6 +9610,8 @@ def test_embind_lib_with_asyncify(self, args):
'-sASYNCIFY_IMPORTS=sleep_and_return',
'-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$ASSERTIONS',
'--post-js', test_file('core/embind_lib_with_asyncify.test.js'),
'--no-entry',
'-sINCOMING_MODULE_JS_API=[onRuntimeInitialized]',
]
self.emcc_args += args
self.do_core_test('embind_lib_with_asyncify.cpp')
Expand Down