From 55fb855626065d920662b08e633e953d983a4fa4 Mon Sep 17 00:00:00 2001
From: Sam Clegg <sbc@chromium.org>
Date: Tue, 25 Feb 2025 14:02:28 -0800
Subject: [PATCH] Cleanup the emscripten tests relating to the event loop. NFC

The browser tests were only being run in `PROXY_TO_PTHREAD` mode
for some reason even though these tests should pass on the main thread.

In test_other the `PROXY_TO_PTHREAD` flags were being passed as args
(rather then emcc_args).  Likely a copy/paste bug.
---
 test/emscripten_performance_now.c  |  5 +-
 test/emscripten_set_interval.c     |  3 +-
 test/emscripten_set_timeout.c      | 19 +++---
 test/emscripten_set_timeout_loop.c | 10 ++--
 test/test_browser.py               | 95 +++++++++++++++---------------
 test/test_other.py                 |  9 +--
 6 files changed, 71 insertions(+), 70 deletions(-)

diff --git a/test/emscripten_performance_now.c b/test/emscripten_performance_now.c
index 626b4f4d2772e..bc3f95b13c5f2 100644
--- a/test/emscripten_performance_now.c
+++ b/test/emscripten_performance_now.c
@@ -12,9 +12,7 @@ void test(void *userData) {
   double now3 = emscripten_date_now();
   assert(now3 >= dateNow + 100);
 
-#ifdef REPORT_RESULT
-  REPORT_RESULT(0);
-#endif
+  exit(0);
 }
 
 int main() {
@@ -30,4 +28,5 @@ int main() {
 #else
   emscripten_set_timeout(test, 200, 0);
 #endif
+  return 99;
 }
diff --git a/test/emscripten_set_interval.c b/test/emscripten_set_interval.c
index 103877c6eca43..63ebe64886672 100644
--- a/test/emscripten_set_interval.c
+++ b/test/emscripten_set_interval.c
@@ -7,6 +7,7 @@
 int funcExecuted = 0;
 
 void testDone(void *userData) {
+  printf("testDone\n");
   assert((long)userData == 2);
   assert(funcExecuted == 10);
   exit(0);
@@ -15,6 +16,7 @@ void testDone(void *userData) {
 long intervalId = 0;
 
 void tick(void *userData) {
+  printf("tick: %d\n", funcExecuted);
   assert((long)userData == 1);
   ++funcExecuted;
   if (funcExecuted == 10) {
@@ -28,6 +30,5 @@ void tick(void *userData) {
 
 int main() {
   intervalId = emscripten_set_interval(tick, 100, (void*)1);
-  emscripten_exit_with_live_runtime();
   return 99;
 }
diff --git a/test/emscripten_set_timeout.c b/test/emscripten_set_timeout.c
index cf1e8521df68d..7ef638fbb5beb 100644
--- a/test/emscripten_set_timeout.c
+++ b/test/emscripten_set_timeout.c
@@ -1,8 +1,8 @@
 #include <emscripten/emscripten.h>
 #include <emscripten/html5.h>
-#include <emscripten/em_asm.h>
 #include <assert.h>
 #include <stdlib.h>
+#include <stdio.h>
 
 int func1Executed = 0;
 int func2Executed = 0;
@@ -10,25 +10,31 @@ int func2Executed = 0;
 void func1(void *userData);
 
 void func2(void *userData) {
-  assert((long)userData == 2);
   ++func2Executed;
+  printf("func2: %d\n", func2Executed);
+  assert((long)userData == 2);
 
-  if (func2Executed == 1)
-  {
+  if (func2Executed == 1) {
     // Test canceling a setTimeout: register a callback but then cancel it immediately
     long id = emscripten_set_timeout(func1, 10, (void*)2);
     emscripten_clear_timeout(id);
 
+    // Without this, the test will not exit correctly
+    // https://github.com/emscripten-core/emscripten/issues/23763
+    emscripten_runtime_keepalive_pop();
+
     emscripten_set_timeout(func2, 100, (void*)2);
   }
-  if (func2Executed == 2)
-  {
+
+  if (func2Executed == 2) {
     assert(func1Executed == 1);
+    printf("done\n");
     exit(0);
   }
 }
 
 void func1(void *userData) {
+  printf("func1\n");
   assert((long)userData == 1);
   ++func1Executed;
 
@@ -39,6 +45,5 @@ void func1(void *userData) {
 
 int main() {
   emscripten_set_timeout(func1, 100, (void*)1);
-  emscripten_exit_with_live_runtime();
   return 99;
 }
diff --git a/test/emscripten_set_timeout_loop.c b/test/emscripten_set_timeout_loop.c
index 1ddf79c21ecdc..5fdf08498590e 100644
--- a/test/emscripten_set_timeout_loop.c
+++ b/test/emscripten_set_timeout_loop.c
@@ -1,25 +1,27 @@
 #include <emscripten/emscripten.h>
 #include <emscripten/html5.h>
-#include <emscripten/em_asm.h>
 #include <assert.h>
 #include <stdlib.h>
+#include <stdio.h>
 
 double previousSetTimeouTime = 0;
 int funcExecuted = 0;
 
 void testDone(void *userData) {
+  printf("testDone\n");
   assert((long)userData == 2);
   assert(funcExecuted == 10);
+  emscripten_runtime_keepalive_pop();
   exit(0);
 }
 
 bool tick(double time, void *userData) {
+  printf("tick: %d\n", funcExecuted);
   assert(time >= previousSetTimeouTime);
   previousSetTimeouTime = time;
   assert((long)userData == 1);
   ++funcExecuted;
-  if (funcExecuted == 10)
-  {
+  if (funcExecuted == 10) {
     emscripten_set_timeout(testDone, 300, (void*)2);
   }
   return funcExecuted < 10;
@@ -27,6 +29,6 @@ bool tick(double time, void *userData) {
 
 int main() {
   emscripten_set_timeout_loop(tick, 100, (void*)1);
-  emscripten_exit_with_live_runtime();
+  emscripten_runtime_keepalive_push();
   return 99;
 }
diff --git a/test/test_browser.py b/test/test_browser.py
index 7219f8fe8aa1b..aed6f819f3173 100644
--- a/test/test_browser.py
+++ b/test/test_browser.py
@@ -199,7 +199,22 @@ def decorated(self, threads, *args, **kwargs):
     f(self, *args, **kwargs)
 
   parameterize(decorated, {'': (False,),
-                           'pthreads': (True,)})
+                           'pthread': (True,)})
+
+  return decorated
+
+
+def also_with_proxy_to_pthread(f):
+  assert callable(f)
+
+  @wraps(f)
+  def decorated(self, threads, *args, **kwargs):
+    if threads:
+      self.emcc_args += ['-pthread', '-sPROXY_TO_PTHREAD']
+    f(self, *args, **kwargs)
+
+  parameterize(decorated, {'': (False,),
+                           'pthread': (True,)})
 
   return decorated
 
@@ -495,14 +510,11 @@ def make_main_two_files(path1, path2, nonexistingpath):
     self.btest_exit('main.c', emcc_args=['--pre-js', 'pre.js', '--use-preload-plugins'])
 
   # Tests that user .html shell files can manually download .data files created with --preload-file cmdline.
-  @parameterized({
-    '': ([],),
-    'pthreads': (['-pthread', '-sPROXY_TO_PTHREAD', '-sEXIT_RUNTIME'],),
-  })
-  def test_preload_file_with_manual_data_download(self, args):
+  @also_with_proxy_to_pthread
+  def test_preload_file_with_manual_data_download(self):
     create_file('file.txt', 'Hello!')
 
-    self.compile_btest('browser/test_manual_download_data.c', ['-sEXIT_RUNTIME', '-o', 'out.js', '--preload-file', 'file.txt@/file.txt'] + args)
+    self.compile_btest('browser/test_manual_download_data.c', ['-sEXIT_RUNTIME', '-o', 'out.js', '--preload-file', 'file.txt@/file.txt'])
     shutil.copy(test_file('browser/test_manual_download_data.html'), '.')
 
     # Move .data file out of server root to ensure that getPreloadedPackage is actually used
@@ -1602,12 +1614,9 @@ def test_glfw_time(self):
   def test_egl(self, args):
     self.btest_exit('test_egl.c', emcc_args=['-O2', '-lEGL', '-lGL', '-sGL_ENABLE_GET_PROC_ADDRESS'] + args)
 
-  @parameterized({
-    '': ([],),
-    'proxy_to_pthread': (['-pthread', '-sPROXY_TO_PTHREAD'],),
-  })
-  def test_egl_width_height(self, args):
-    self.btest_exit('test_egl_width_height.c', emcc_args=['-O2', '-lEGL', '-lGL'] + args)
+  @also_with_proxy_to_pthread
+  def test_egl_width_height(self):
+    self.btest_exit('test_egl_width_height.c', emcc_args=['-O2', '-lEGL', '-lGL'])
 
   @requires_graphics_hardware
   def test_egl_createcontext_error(self):
@@ -1891,27 +1900,17 @@ def setup():
   def test_emscripten_api_infloop(self):
     self.btest_exit('emscripten_api_browser_infloop.cpp')
 
-  @parameterized({
-    '': ([],),
-    'pthreads': (['-pthread', '-sPROXY_TO_PTHREAD', '-sEXIT_RUNTIME'],),
-  })
-  def test_emscripten_main_loop(self, args):
-    self.btest_exit('test_emscripten_main_loop.c', emcc_args=args)
+  @also_with_proxy_to_pthread
+  def test_emscripten_main_loop(self):
+    self.btest_exit('test_emscripten_main_loop.c')
 
-  @parameterized({
-    '': ([],),
-    # test pthreads + AUTO_JS_LIBRARIES mode as well
-    'pthreads': (['-pthread', '-sPROXY_TO_PTHREAD', '-sAUTO_JS_LIBRARIES=0'],),
-  })
-  def test_emscripten_main_loop_settimeout(self, args):
-    self.btest_exit('test_emscripten_main_loop_settimeout.c', emcc_args=args)
+  @also_with_proxy_to_pthread
+  def test_emscripten_main_loop_settimeout(self):
+    self.btest_exit('test_emscripten_main_loop_settimeout.c', emcc_args=['-sAUTO_JS_LIBRARIES=0'])
 
-  @parameterized({
-    '': ([],),
-    'pthreads': (['-pthread', '-sPROXY_TO_PTHREAD'],),
-  })
-  def test_emscripten_main_loop_and_blocker(self, args):
-    self.btest_exit('test_emscripten_main_loop_and_blocker.c', emcc_args=args)
+  @also_with_proxy_to_pthread
+  def test_emscripten_main_loop_and_blocker(self):
+    self.btest_exit('test_emscripten_main_loop_and_blocker.c')
 
   def test_emscripten_main_loop_and_blocker_exit(self):
     # Same as above but tests that EXIT_RUNTIME works with emscripten_main_loop.  The
@@ -1967,13 +1966,10 @@ def test_sdl_glshader2(self):
   def test_gl_glteximage(self):
     self.btest('gl_teximage.c', '1', emcc_args=['-lGL', '-lSDL'])
 
-  @parameterized({
-    '': ([],),
-    'pthreads': (['-pthread', '-sPROXY_TO_PTHREAD', '-sOFFSCREEN_FRAMEBUFFER'],),
-  })
   @requires_graphics_hardware
-  def test_gl_textures(self, args):
-    self.btest_exit('gl_textures.c', emcc_args=['-lGL', '-g', '-sSTACK_SIZE=1MB'] + args)
+  @also_with_proxy_to_pthread
+  def test_gl_textures(self):
+    self.btest_exit('gl_textures.c', emcc_args=['-lGL', '-g', '-sSTACK_SIZE=1MB'])
 
   @requires_graphics_hardware
   def test_gl_ps(self):
@@ -2632,10 +2628,10 @@ def test_html5_core(self, opts):
       self.emcc_args.append('--pre-js=pre.js')
     self.btest_exit('test_html5_core.c', emcc_args=opts)
 
+  @also_with_proxy_to_pthread
   @parameterized({
     '': ([],),
     'closure': (['-O2', '-g1', '--closure=1'],),
-    'pthread': (['-pthread', '-sPROXY_TO_PTHREAD'],),
   })
   def test_html5_gamepad(self, args):
     self.btest_exit('test_gamepad.c', emcc_args=args)
@@ -4969,11 +4965,13 @@ def test_emscripten_request_animation_frame_loop(self):
   def test_request_animation_frame(self):
     self.btest_exit('test_request_animation_frame.c')
 
+  @also_with_proxy_to_pthread
   def test_emscripten_set_timeout(self):
-    self.btest_exit('emscripten_set_timeout.c', emcc_args=['-pthread', '-sPROXY_TO_PTHREAD'])
+    self.btest_exit('emscripten_set_timeout.c')
 
+  @also_with_proxy_to_pthread
   def test_emscripten_set_timeout_loop(self):
-    self.btest_exit('emscripten_set_timeout_loop.c', emcc_args=['-pthread', '-sPROXY_TO_PTHREAD'])
+    self.btest_exit('emscripten_set_timeout_loop.c')
 
   def test_emscripten_set_immediate(self):
     self.btest_exit('emscripten_set_immediate.c')
@@ -4981,12 +4979,14 @@ def test_emscripten_set_immediate(self):
   def test_emscripten_set_immediate_loop(self):
     self.btest_exit('emscripten_set_immediate_loop.c')
 
+  @also_with_proxy_to_pthread
   def test_emscripten_set_interval(self):
-    self.btest_exit('emscripten_set_interval.c', emcc_args=['-pthread', '-sPROXY_TO_PTHREAD'])
+    self.btest_exit('emscripten_set_interval.c')
 
   # Test emscripten_performance_now() and emscripten_date_now()
+  @also_with_proxy_to_pthread
   def test_emscripten_performance_now(self):
-    self.btest('emscripten_performance_now.c', '0', emcc_args=['-pthread', '-sPROXY_TO_PTHREAD'])
+    self.btest_exit('emscripten_performance_now.c')
 
   def test_embind_with_pthreads(self):
     self.btest_exit('embind/test_pthreads.cpp', emcc_args=['-lembind', '-pthread', '-sPTHREAD_POOL_SIZE=2'])
@@ -5058,12 +5058,9 @@ def test_minimal_runtime_loader_shell(self, args):
   def test_minimal_runtime_hello_world(self, args):
     self.btest_exit('small_hello_world.c', emcc_args=args + ['-sMINIMAL_RUNTIME'])
 
-  @parameterized({
-    '': ([],),
-    'pthread': (['-sPROXY_TO_PTHREAD', '-pthread'],),
-  })
-  def test_offset_converter(self, args):
-    self.btest_exit('test_offset_converter.c', emcc_args=['-sUSE_OFFSET_CONVERTER', '-gsource-map'] + args)
+  @also_with_proxy_to_pthread
+  def test_offset_converter(self):
+    self.btest_exit('test_offset_converter.c', emcc_args=['-sUSE_OFFSET_CONVERTER', '-gsource-map'])
 
   # Tests emscripten_unwind_to_js_event_loop() behavior
   def test_emscripten_unwind_to_js_event_loop(self):
diff --git a/test/test_other.py b/test/test_other.py
index 3a15bd213a62a..2d81aa221a563 100644
--- a/test/test_other.py
+++ b/test/test_other.py
@@ -14197,9 +14197,8 @@ def test_pthread_trap(self):
   def test_pthread_kill(self):
     self.do_run_in_out_file_test('pthread/test_pthread_kill.c')
 
-  @node_pthreads
   def test_emscripten_set_interval(self):
-    self.do_runf('emscripten_set_interval.c', args=['-pthread', '-sPROXY_TO_PTHREAD'])
+    self.do_runf('emscripten_set_interval.c')
 
   # Test emscripten_console_log(), emscripten_console_warn() and emscripten_console_error()
   def test_emscripten_console_log(self):
@@ -14209,13 +14208,11 @@ def test_emscripten_console_log(self):
   def test_emscripten_unwind_to_js_event_loop(self):
     self.do_runf('test_emscripten_unwind_to_js_event_loop.c')
 
-  @node_pthreads
   def test_emscripten_set_timeout(self):
-    self.do_runf('emscripten_set_timeout.c', args=['-pthread', '-sPROXY_TO_PTHREAD'])
+    self.do_runf('emscripten_set_timeout.c', emcc_args=['-sEXIT_RUNTIME'])
 
-  @node_pthreads
   def test_emscripten_set_timeout_loop(self):
-    self.do_runf('emscripten_set_timeout_loop.c', args=['-pthread', '-sPROXY_TO_PTHREAD'])
+    self.do_runf('emscripten_set_timeout_loop.c', emcc_args=['-sEXIT_RUNTIME'])
 
   # Verify that we are able to successfully compile a script when the Windows 7
   # and Python workaround env. vars are enabled.