EBLAN_ENGINE/tests/stability_editor_checks.inl
Игорь Федосеев 527d92411d chore: integrate main before stage 2
Preserve stage-1 stability fixes alongside v5 lighting, IPC v2, weapons and studio updates. Fix barrel AOE pointer corruption and extend modal gameplay gating.

Verify 137 editor checks, 191 ASM checks, lighting bake and preview parity. Document stage-2 scope without implementing it.
2026-10-31 22:10:07 +03:00

71 lines
4 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");
sprite.textureId = 11;
sprite.flags = 0;
Check(GetSpriteEntityType(sprite) == ENTITY_ARCHETYPE_BARREL, "zero flags retain new legacy barrel fallback");
sprite.flags = 0x06026400;
Check(GetSpriteEntityType(sprite) == ENTITY_ARCHETYPE_PROP, "explicit Prop overrides barrel artwork");
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");
}