EBLAN_ENGINE/tests/stability_editor_checks.inl
Игорь Федосеев f42679fe62 fix: stabilize gameplay and IDE source writes
Trace shots against current map geometry instead of stale render depth. Preserve outline counters across volatile pixel calls and freeze gameplay on cinematic, transition and death ticks.

Clamp packed HP before narrowing and match legacy archetype rules. Publish IDE saves atomically; failed saves block file switches and builds. Drain compiler output while hidden.

Verify runtime/editor builds, 112 editor checks, 148 ASM checks and live preview parity. Record remaining stages in docs/STABILIZATION.md.
2026-10-31 21:56:54 +03:00

66 lines
3.7 KiB
C++

static void RunStabilityEditorChecks() {
SpriteEntry sprite{};
sprite.textureId = 13;
Check(GetSpriteEntityType(sprite) == ENTITY_ARCHETYPE_GUARD, "zero flags retain legacy guard fallback");
sprite.flags = 0x06026400;
Check(GetSpriteEntityType(sprite) == ENTITY_ARCHETYPE_PROP, "explicit Prop matches ASM despite guard texture");
for (int health : {1, 100, 255, 256, 300, 500}) {
SetSpriteEntityData(sprite, ENTITY_ARCHETYPE_GUARD, health, 2, 6);
Check(GetSpriteHealth(sprite) == (health > 255 ? 255 : health), "health setter clamps before byte packing");
}
const auto root = fs::absolute("code fixture");
fs::create_directories(root / "include");
const auto first = root / g_ProjectCodeFiles[0].relativePath;
const auto second = root / g_ProjectCodeFiles[1].relativePath;
{ std::ofstream out(first); out << "original"; }
{ std::ofstream out(second); out << "second"; }
CodeEditorState code;
code.LoadFile(0, root.string());
Check(std::string(code.textBuffer.data()) == "original", "IDE opens actual source file");
strcpy_s(code.textBuffer.data(), code.textBuffer.size(), "unsaved source");
code.isModified = true;
HANDLE locked = CreateFileA(first.string().c_str(), GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
Check(locked != INVALID_HANDLE_VALUE, "IDE failure fixture locks destination against replacement");
Check(!code.SaveFile(root.string()) && code.isModified &&
Bytes(first) == std::vector<uint8_t>({'o','r','i','g','i','n','a','l'}),
"failed IDE replacement preserves disk and modified buffer");
code.LoadFile(1, root.string());
Check(code.currentFile == 0 && code.isModified && std::string(code.textBuffer.data()) == "unsaved source",
"IDE refuses file switch after failed autosave");
code.StartBuild(root.string());
Check(!code.isBuilding && code.isModified, "IDE refuses build after failed save");
CloseHandle(locked);
Check(code.SaveFile(root.string()) && !code.isModified &&
Bytes(first) == std::vector<uint8_t>({'u','n','s','a','v','e','d',' ','s','o','u','r','c','e'}),
"IDE publishes complete source atomically");
code.LoadFile(1, root.string());
Check(code.currentFile == 1 && std::string(code.textBuffer.data()) == "second", "IDE switches after successful save");
fs::remove(first);
code.LoadFile(0, root.string());
Check(code.currentFile == 1 && std::string(code.textBuffer.data()) == "second", "failed IDE load preserves current document");
Check(std::distance(fs::directory_iterator(root / "include"), fs::directory_iterator{}) == 1,
"failed IDE saves leave no temporary source files");
{ std::ofstream out(root / "build.bat");
out << "@echo off\nfor /L %%i in (1,1,4000) do echo compiler-output-%%i\nexit /b 7\n"; }
g_CodeEditor.LoadFile(1, root.string());
g_CodeEditor.StartBuild(root.string());
EditorApp hidden;
hidden.showCodeEditorModal = false;
const auto deadline = GetTickCount64() + 10000;
while (g_CodeEditor.isBuilding && GetTickCount64() < deadline) {
hidden.RenderCodeEditorWindow();
if (g_CodeEditor.hBuildProcess) WaitForSingleObject(g_CodeEditor.hBuildProcess, 1);
}
const bool finished = !g_CodeEditor.isBuilding;
if (!finished && g_CodeEditor.hBuildProcess) {
TerminateProcess(g_CodeEditor.hBuildProcess, 99);
WaitForSingleObject(g_CodeEditor.hBuildProcess, 1000);
g_CodeEditor.PollBuild();
}
Check(finished && g_CodeEditor.buildExitCode == 7 &&
g_CodeEditor.buildLog.find("compiler-output-4000") != std::string::npos,
"hidden IDE drains output larger than pipe capacity and retains exit code");
}