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/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 84c62e2d9..d0a576e03 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") @@ -35,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", @@ -49,6 +60,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)