From f7febcb9357976efc0d7bea3674ecb8761106162 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Wed, 25 Nov 2020 10:11:52 -0800 Subject: [PATCH] Enable browser tests to be run under node In some cases, specifically thread tests, it can be useful to expriment with running browser tests under node. This change add a new special value to EMTEST_BROSWER that will cause `btest` to attempt to run the test under node. I've also adding since browser test to the wasm2-test suite in circle CI so this features gets tested. --- .circleci/config.yml | 6 +++-- tests/browser_reporting.js | 30 +++++++++++++-------- tests/common.py | 24 +++++++++++++---- tests/pthread/test_pthread_create.cpp | 5 ++-- tests/report_result.cpp | 38 ++++++++++++++++++++------- tests/report_result.h | 20 ++------------ tests/sdl2_net_client.c | 2 +- tests/test_browser.py | 9 ++++--- 8 files changed, 82 insertions(+), 52 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2aa19545da2ad..250ba761bcccc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -364,10 +364,12 @@ jobs: test_targets: "wasm0" test-wasm2: executor: bionic + environment: + EMTEST_BROWSER: "node" steps: - run-tests: - # also add a few asan tests - test_targets: "wasm2 asan.test_embind* asan.test_abort_on_exceptions asan.test_ubsan_full_left_shift_fsanitize_integer asan.test_pthread* asan.test_dyncall_specific_minimal_runtime asan.test_async_hello lsan.test_stdio_locking lsan.test_pthread_create" + # also add a few asan tests and a single test of EMTEST_BROWSER=node + test_targets: "wasm2 asan.test_embind* asan.test_abort_on_exceptions asan.test_ubsan_full_left_shift_fsanitize_integer asan.test_pthread* asan.test_dyncall_specific_minimal_runtime asan.test_async_hello lsan.test_stdio_locking lsan.test_pthread_create browser.test_pthread_join" test-wasm3: executor: bionic steps: diff --git a/tests/browser_reporting.js b/tests/browser_reporting.js index ef9acefc062e8..c96fa7c3112ac 100644 --- a/tests/browser_reporting.js +++ b/tests/browser_reporting.js @@ -9,15 +9,19 @@ function reportResultToServer(result, sync, port) { reportErrorToServer("excessive reported results, sending " + result + ", test will fail"); } reportResultToServer.reported = true; - var xhr = new XMLHttpRequest(); - if (hasModule && Module['pageThrewException']) { - result = 'pageThrewException'; - } - xhr.open('GET', 'http://localhost:' + port + '/report_result?' + result, !sync); - xhr.send(); - if (typeof window === 'object' && window && hasModule && !Module['pageThrewException']) { - /* for easy debugging, don't close window on failure */ - setTimeout(function() { window.close() }, 1000); + if (typeof ENVIRONMENT_IS_NODE !== 'undefined' && ENVIRONMENT_IS_NODE) { + out('RESULT: ' + result); + } else { + var xhr = new XMLHttpRequest(); + if (hasModule && Module['pageThrewException']) { + result = 'pageThrewException'; + } + xhr.open('GET', 'http://localhost:' + port + '/report_result?' + result, !sync); + xhr.send(); + if (typeof window === 'object' && window && hasModule && !Module['pageThrewException']) { + /* for easy debugging, don't close window on failure */ + setTimeout(function() { window.close() }, 1000); + } } } @@ -30,8 +34,12 @@ function maybeReportResultToServer(result, sync, port) { function reportErrorToServer(message) { var xhr = new XMLHttpRequest(); - xhr.open('GET', encodeURI('http://localhost:8888?stderr=' + message)); - xhr.send(); + if (typeof ENVIRONMENT_IS_NODE !== 'undefined' && ENVIRONMENT_IS_NODE) { + err(message); + } else { + xhr.open('GET', encodeURI('http://localhost:8888?stderr=' + message)); + xhr.send(); + } } if (typeof window === 'object' && window) { diff --git a/tests/common.py b/tests/common.py index f88540165d7a2..b1c65ec8bf374 100644 --- a/tests/common.py +++ b/tests/common.py @@ -39,8 +39,14 @@ # User can specify an environment variable EMTEST_BROWSER to force the browser # test suite to run using another browser command line than the default system -# browser. Setting '0' as the browser disables running a browser (but we still -# see tests compile) +# browser. +# There are two special value that can be used here if running in an actual +# browser is not desired: +# EMTEST_BROWSER=0 : This will disable the actual running of the test and simply +# verify that it compiles and links. +# EMTEST_BROWSER=node : This will attempt to run the browser test under node. +# For most browser tests this does not work, but it can +# be useful for running pthread tests under node. EMTEST_BROWSER = None EMTEST_DETECT_TEMPFILE_LEAKS = None EMTEST_SAVE_DIR = None @@ -1326,7 +1332,7 @@ def setUpClass(cls): super().setUpClass() cls.also_asmjs = int(os.getenv('EMTEST_BROWSER_ALSO_ASMJS', '0')) == 1 cls.port = int(os.getenv('EMTEST_BROWSER_PORT', '8888')) - if not has_browser(): + if not has_browser() or EMTEST_BROWSER == 'node': return cls.browser_timeout = 60 cls.harness_in_queue = multiprocessing.Queue() @@ -1339,7 +1345,7 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): super().tearDownClass() - if not has_browser(): + if not has_browser() or EMTEST_BROWSER == 'node': return cls.harness_server.terminate() print('[Browser harness server terminated]') @@ -1538,6 +1544,8 @@ def compile_btest(self, args, reporting=Reporting.FULL): args += ['-I' + TEST_ROOT, '-include', test_file('report_result.h'), test_file('report_result.cpp')] + if EMTEST_BROWSER == 'node': + args.append('-DEMTEST_NODE') self.run_process([EMCC] + self.get_emcc_args() + args) def btest_exit(self, filename, assert_returncode=0, *args, **kwargs): @@ -1581,7 +1589,13 @@ def btest(self, filename, expected=None, reference=None, post_build() if not isinstance(expected, list): expected = [expected] - self.run_browser(outfile + url_suffix, message, ['/report_result?' + e for e in expected], timeout=timeout, extra_tries=extra_tries) + if EMTEST_BROWSER == 'node': + self.js_engines = [config.NODE_JS] + self.node_args += ['--experimental-wasm-threads', '--experimental-wasm-bulk-memory'] + output = self.run_js('test.js') + self.assertContained('RESULT: ' + expected[0], output) + else: + self.run_browser(outfile + url_suffix, message, ['/report_result?' + e for e in expected], timeout=timeout, extra_tries=extra_tries) # Tests can opt into being run under asmjs as well if 'WASM=0' not in original_args and (also_asmjs or self.also_asmjs): diff --git a/tests/pthread/test_pthread_create.cpp b/tests/pthread/test_pthread_create.cpp index d5c49b31a3434..86cd1bb1ea311 100644 --- a/tests/pthread/test_pthread_create.cpp +++ b/tests/pthread/test_pthread_create.cpp @@ -74,8 +74,8 @@ int main() CreateThread(i); // Join all threads and create more. - while (numThreadsToCreate > 0) - { + while (numThreadsToCreate > 0) + { for(int i = 0; i < NUM_THREADS; ++i) { if (thread[i]) @@ -94,5 +94,6 @@ int main() } } } + printf("All threads joined.\n"); return 0; } diff --git a/tests/report_result.cpp b/tests/report_result.cpp index 630c5c5986298..fe1ff610f25be 100644 --- a/tests/report_result.cpp +++ b/tests/report_result.cpp @@ -6,35 +6,55 @@ */ #include +#include -#ifdef __EMSCRIPTEN__ +#include "report_result.h" +#if defined __EMSCRIPTEN__ && !defined EMTEST_NODE #include - -#ifndef EMTEST_PORT_NUMBER -#error "EMTEST_PORT_NUMBER not defined" #endif #ifdef __cplusplus extern "C" { #endif -void EMSCRIPTEN_KEEPALIVE _ReportResult(int result, int sync) -{ +#if defined __EMSCRIPTEN__ && !defined EMTEST_NODE +#ifndef EMTEST_PORT_NUMBER +#error "EMTEST_PORT_NUMBER not defined" +#endif + +void EMSCRIPTEN_KEEPALIVE _ReportResult(int result, int sync) { EM_ASM({ reportResultToServer($0, $1, $2); }, result, sync, EMTEST_PORT_NUMBER); } -void EMSCRIPTEN_KEEPALIVE _MaybeReportResult(int result, int sync) -{ +void EMSCRIPTEN_KEEPALIVE _MaybeReportResult(int result, int sync) { EM_ASM({ maybeReportResultToServer($0, $1, $2); }, result, sync, EMTEST_PORT_NUMBER); } +#else + +static bool reported = false; + +void _ReportResult(int result, int sync) { + if (reported) { + printf("ERROR: result already reported\n"); + exit(1); + } + reported = true; + printf("RESULT: %d\n", result); +} + +void _MaybeReportResult(int result, int sync) { + if (!reported) _ReportResult(result, sync); +} + +#endif // __EMSCRIPTEN__ && !defined EMTEST_NODE + #ifdef __cplusplus } #endif -#endif // __EMSCRIPTEN__ diff --git a/tests/report_result.h b/tests/report_result.h index 2012bdf16b072..cd0ceca233dcf 100644 --- a/tests/report_result.h +++ b/tests/report_result.h @@ -10,8 +10,6 @@ #ifndef REPORT_RESULT_H_ #define REPORT_RESULT_H_ -#ifdef __EMSCRIPTEN__ - #ifdef __cplusplus extern "C" { #endif @@ -23,7 +21,8 @@ void _MaybeReportResult(int result, int sync); } #endif -#if __EMSCRIPTEN_PTHREADS__ +#if defined __EMSCRIPTEN__ && defined __EMSCRIPTEN_PTHREADS__ + #include #include #define REPORT_RESULT(result) emscripten_async_run_in_main_runtime_thread(EM_FUNC_SIG_VII, _ReportResult, (result), 0) #define REPORT_RESULT_SYNC(result) emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VII, _ReportResult, (result), 1) @@ -36,19 +35,4 @@ void _MaybeReportResult(int result, int sync); #define MAYBE_REPORT_RESULT_SYNC(result) _MaybeReportResult((result), 1) #endif -#else - -#include -#include - -#define REPORT_RESULT(result) \ - do { \ - printf("result: %d\n", result); \ - exit(result); \ - } - -#define REPORT_RESULT_SYNC REPORT_RESULT - -#endif // __EMSCRIPTEN__ - #endif // REPORT_RESULT_H_ diff --git a/tests/sdl2_net_client.c b/tests/sdl2_net_client.c index 90293ca61be21..46108147600c5 100644 --- a/tests/sdl2_net_client.c +++ b/tests/sdl2_net_client.c @@ -24,7 +24,7 @@ #include "SDL_net.h" #ifdef __EMSCRIPTEN__ -#include +#include #endif typedef enum { diff --git a/tests/test_browser.py b/tests/test_browser.py index da00ae169afe5..593794241966a 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -175,9 +175,10 @@ class browser(BrowserCore): def setUpClass(cls): super().setUpClass() cls.browser_timeout = 60 - print() - print('Running the browser tests. Make sure the browser allows popups from localhost.') - print() + if EMTEST_BROWSER != 'node': + print() + print('Running the browser tests. Make sure the browser allows popups from localhost.') + print() def setUp(self): super().setUp() @@ -4333,7 +4334,7 @@ def test_small_js_flags(self): size = os.path.getsize('test.js') print('size:', size) # Note that this size includes test harness additions (for reporting the result, etc.). - self.assertLess(abs(size - 5629), 100) + self.assertLess(abs(size - 5787), 100) # Tests that it is possible to initialize and render WebGL content in a pthread by using OffscreenCanvas. # -DTEST_CHAINED_WEBGL_CONTEXT_PASSING: Tests that it is possible to transfer WebGL canvas in a chain from main thread -> thread 1 -> thread 2 and then init and render WebGL content there.