From 4f69596c4781bdb66058286fb79f0bd99ace6b5b Mon Sep 17 00:00:00 2001 From: xxxsen Date: Mon, 31 Aug 2026 00:55:16 +0800 Subject: [PATCH 1/2] fix(web): clear checkpoint blocker after ini close --- src/vm_builtins.c | 2 ++ tests/test_retrom_web_host.py | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/src/vm_builtins.c b/src/vm_builtins.c index 013f7818d..4351d2269 100644 --- a/src/vm_builtins.c +++ b/src/vm_builtins.c @@ -7215,6 +7215,7 @@ static RValue builtin_ini_close(VMContext* ctx, MAYBE_UNUSED RValue* args, MAYBE if (runner->currentIni == nullptr) { free(runner->currentIniPath); runner->currentIniPath = nullptr; + runner->currentIniDirty = false; // No ini open = empty return RValue_makeOwnedString(safeStrdup("")); } @@ -7227,6 +7228,7 @@ static RValue builtin_ini_close(VMContext* ctx, MAYBE_UNUSED RValue* args, MAYBE FileSystem* fs = runner->fileSystem; fs->vtable->writeFileText(fs, runner->currentIniPath, serialized); } + runner->currentIniDirty = false; // Move to cache instead of freeing discardIniCache(runner); diff --git a/tests/test_retrom_web_host.py b/tests/test_retrom_web_host.py index 84c62e2d9..f6db7f667 100755 --- a/tests/test_retrom_web_host.py +++ b/tests/test_retrom_web_host.py @@ -13,6 +13,7 @@ class RetromWebHostTests(unittest.TestCase): def setUp(self) -> None: self.source = (ROOT / "src/web/main.c").read_text(encoding="utf-8") + self.builtins = (ROOT / "src/vm_builtins.c").read_text(encoding="utf-8") self.cmake = (ROOT / "CMakeLists.txt").read_text(encoding="utf-8") self.release_workflow = (ROOT / ".github/workflows/rpg-runtime-release.yml").read_text(encoding="utf-8") self.build_script = (ROOT / ".github/rpg-runtime/build-web.sh").read_text(encoding="utf-8") @@ -49,6 +50,13 @@ def test_checkpoint_is_bounded_and_requires_a_paused_frame_boundary(self) -> Non self.assertIn('memcmp(bytes, "BSCP", 4) != 0', self.source) self.assertIn("Runner_restoreStateJson(gRunner, json)", self.source) + def test_closing_a_dirty_ini_clears_the_transient_checkpoint_blocker(self) -> None: + start = self.builtins.index("static RValue builtin_ini_close") + end = self.builtins.index("static RValue builtin_ini_read_string", start) + close_body = self.builtins[start:end] + cache_move = close_body.index("runner->cachedIni = runner->currentIni;") + self.assertLess(close_body.index("runner->currentIniDirty = false;"), cache_move) + def test_release_workflow_supports_immutable_integration_candidates(self) -> None: self.assertIn("(-rc\\.[1-9][0-9]*)?", self.release_workflow) self.assertIn("prerelease+=(--prerelease)", self.release_workflow) From 2891f96d2ddac910179c4fff3abf637a589c9741 Mon Sep 17 00:00:00 2001 From: xxxsen Date: Mon, 31 Aug 2026 01:07:07 +0800 Subject: [PATCH 2/2] fix(web): keep runtime warnings out of error console --- src/web/main.c | 1 + tests/test_retrom_web_host.py | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/src/web/main.c b/src/web/main.c index 6de5de0f5..e5cd7ae1e 100644 --- a/src/web/main.c +++ b/src/web/main.c @@ -38,6 +38,7 @@ void platformLog(const logType type, const char *format, va_list va) { out = stdout; break; case LOG_TYPE_WARNING: + out = stdout; fputs("Warning: ", out); break; case LOG_TYPE_ERROR: diff --git a/tests/test_retrom_web_host.py b/tests/test_retrom_web_host.py index f6db7f667..d0a576e03 100755 --- a/tests/test_retrom_web_host.py +++ b/tests/test_retrom_web_host.py @@ -36,6 +36,16 @@ def test_ready_and_exit_are_explicit_host_events(self) -> None: self.assertEqual(self.source.count("type: 'runnerReady'"), 1) self.assertEqual(self.source.count("type: 'runnerExit'"), 1) + def test_web_warnings_do_not_use_the_browser_error_stream(self) -> None: + start = self.source.index("void platformLog") + end = self.source.index("// Configures the sample rate", start) + logger = self.source[start:end] + warning = logger.index("case LOG_TYPE_WARNING:") + error = logger.index("case LOG_TYPE_ERROR:") + warning_body = logger[warning:error] + self.assertIn("out = stdout;", warning_body) + self.assertIn('fputs("Warning: ", out);', warning_body) + def test_web_exports_are_stable(self) -> None: for name in ( "_setRunnerPaused", "_setGamepadConnected",