MetaCore/tests/MetaCoreRuntimeUiTests.cpp
2026-07-27 10:36:59 +08:00

118 lines
5.5 KiB
C++

#include "MetaCoreRuntimeUi/MetaCoreRuntimeUiSystem.h"
#include "MetaCoreFoundation/MetaCoreGeneratedReflection.h"
#include "MetaCoreScene/MetaCoreSceneSerializer.h"
#include <cstdlib>
#include <filesystem>
#include <iostream>
#include <string>
namespace {
void Expect(bool value, const char* message) {
if (!value) {
std::cerr << "MetaCoreRuntimeUiTests: " << message << '\n';
std::exit(1);
}
}
MetaCore::MetaCoreUiDocument BuildDocument() {
using namespace MetaCore;
MetaCoreUiDocument document;
document.Name = "Controls";
document.ReferenceWidth = 1920;
document.ReferenceHeight = 1080;
document.ScaleMode = MetaCoreUiCanvasScaleMode::ScaleWithScreenSize;
document.MatchWidthOrHeight = 0.5F;
MetaCoreUiNodeDocument root;
root.Id = "Root";
root.Name = "Root";
root.Type = MetaCoreUiNodeType::Panel;
root.RectTransform.AnchorMin = {0.0F, 0.0F, 0.0F};
root.RectTransform.AnchorMax = {1.0F, 1.0F, 0.0F};
root.RectTransform.Size = {0.0F, 0.0F, 0.0F};
const std::vector<MetaCoreUiNodeType> types{
MetaCoreUiNodeType::Text, MetaCoreUiNodeType::Image, MetaCoreUiNodeType::Button,
MetaCoreUiNodeType::Input, MetaCoreUiNodeType::List, MetaCoreUiNodeType::Scroll
};
for (std::size_t index = 0; index < types.size(); ++index) {
MetaCoreUiNodeDocument node;
node.Id = "Node" + std::to_string(index);
node.Name = node.Id;
node.ParentId = root.Id;
node.Type = types[index];
node.Text = "Safe <text>";
node.Interactable = types[index] == MetaCoreUiNodeType::Button || types[index] == MetaCoreUiNodeType::Input;
node.NavigationOrder = static_cast<std::int32_t>(index);
node.RectTransform.AnchorMin = {0.25F, 0.25F, 0.0F};
node.RectTransform.AnchorMax = {0.75F, 0.25F, 0.0F};
node.RectTransform.Position = {10.0F, 20.0F + static_cast<float>(index) * 50.0F, 0.0F};
if (types[index] == MetaCoreUiNodeType::Input) {
node.Placeholder = "Value";
node.MaxLength = 32;
node.Bindings.push_back({"script.form.value", MetaCoreUiBindingTarget::Value,
MetaCoreUiBindingMode::TwoWay, {}, {}, "default"});
}
if (types[index] == MetaCoreUiNodeType::List) {
node.Bindings.push_back({"script.rows", MetaCoreUiBindingTarget::Items,
MetaCoreUiBindingMode::OneWay, {}, {}, {}});
}
root.Children.push_back(node.Id);
document.Nodes.push_back(std::move(node));
}
document.Nodes.insert(document.Nodes.begin(), root);
document.RootNodeIds.push_back(root.Id);
return document;
}
} // namespace
int main() {
using namespace MetaCore;
const MetaCoreUiDocument document = BuildDocument();
Expect(MetaCoreValidateUiDocument(document).empty(), "valid document was rejected");
const MetaCoreCompiledUiDocument first = MetaCoreCompileUiDocument(document);
const MetaCoreCompiledUiDocument second = MetaCoreCompileUiDocument(document);
Expect(first.Succeeded, "document compilation failed");
Expect(first.Rml == second.Rml && first.Rcss == second.Rcss, "compiler output is not deterministic");
Expect(first.Rml.find("<input") != std::string::npos, "Input control was not compiled");
Expect(first.Rml.find("role=\"listbox\"") != std::string::npos, "List control was not compiled");
Expect(first.Rml.find("mcui-scroll") != std::string::npos, "Scroll control was not compiled");
Expect(first.Rml.find("Safe &lt;text&gt;") != std::string::npos, "text was not escaped");
Expect(first.Rcss.find("left:calc(25") != std::string::npos, "anchors were not compiled");
Expect(first.Rml.find("data-metacore-bind-mode-value=\"two-way\"") != std::string::npos, "two-way binding was not compiled");
MetaCoreUiDocument invalid = document;
invalid.Nodes.back().Id = invalid.Nodes.front().Id;
Expect(!MetaCoreValidateUiDocument(invalid).empty(), "duplicate stable ID was accepted");
invalid = document;
invalid.Nodes[1].Bindings.push_back({"unknown.path", MetaCoreUiBindingTarget::Text,
MetaCoreUiBindingMode::OneWay, {}, {}, {}});
Expect(!MetaCoreValidateUiDocument(invalid).empty(), "invalid binding namespace was accepted");
MetaCoreUiDocument legacy = document;
legacy.SchemaVersion = 1;
legacy.Nodes[1].Bindings.clear();
legacy.Nodes[1].DataBinding = "runtime.temperature";
legacy.Nodes[1].DataFormat = "1";
const MetaCoreUiDocument migrated = MetaCoreMigrateUiDocument(legacy);
Expect(migrated.SchemaVersion == METACORE_UI_SCHEMA_VERSION, "legacy schema was not migrated");
Expect(migrated.Nodes[1].Bindings.size() == 1, "legacy binding was not migrated");
const std::filesystem::path jsonPath = std::filesystem::temp_directory_path() / "MetaCoreRuntimeUiV2.mcui.json";
MetaCoreTypeRegistry registry;
MetaCoreRegisterFoundationGeneratedTypes(registry);
MetaCoreRegisterSceneGeneratedTypes(registry);
Expect(MetaCoreSerializeToBytes(document, registry).has_value(), "v2 binary serialization failed");
Expect(MetaCoreSceneSerializer::SaveUiToJson(jsonPath, document, registry), "v2 JSON write failed");
const auto loaded = MetaCoreSceneSerializer::LoadUiFromJson(jsonPath, registry);
Expect(loaded.has_value() && loaded->SchemaVersion == 2, "v2 JSON schema did not round-trip");
Expect(loaded->Nodes[4].Placeholder == "Value", "Input settings did not round-trip");
std::filesystem::remove(jsonPath);
std::cout << "MetaCoreRuntimeUiTests passed\n";
return 0;
}