Files
XCEngine/tests/Resources/UI/test_ui_schema_document.cpp

231 lines
8.5 KiB
C++

#include <gtest/gtest.h>
#include <XCEngine/Core/Asset/ArtifactContainer.h>
#include <XCEngine/Resources/UI/UIDocumentCompiler.h>
#include <XCEngine/Resources/UI/UIDocumentLoaders.h>
#include <filesystem>
#include <fstream>
using namespace XCEngine::Resources;
namespace {
void WriteTextFile(const std::filesystem::path& path, const std::string& contents) {
std::filesystem::create_directories(path.parent_path());
std::ofstream output(path, std::ios::binary | std::ios::trunc);
ASSERT_TRUE(output.is_open());
output << contents;
ASSERT_TRUE(static_cast<bool>(output));
}
void ExpectSchemaCompileFailure(
const char* testFolderName,
const char* markup,
const char* expectedMessageFragment) {
namespace fs = std::filesystem;
const fs::path root = fs::temp_directory_path() / testFolderName;
const fs::path schemaPath = root / "invalid.xcschema";
fs::remove_all(root);
WriteTextFile(schemaPath, markup);
UISchemaLoader loader;
UIDocumentCompileResult compileResult = {};
EXPECT_FALSE(loader.CompileDocument(schemaPath.string().c_str(), compileResult));
EXPECT_FALSE(compileResult.succeeded);
EXPECT_FALSE(compileResult.errorMessage.Empty());
ASSERT_FALSE(compileResult.document.diagnostics.Empty());
const std::string errorMessage = compileResult.errorMessage.CStr();
EXPECT_NE(errorMessage.find(expectedMessageFragment), std::string::npos);
fs::remove_all(root);
}
TEST(UISchemaDocument, CompileAndArtifactLoadPopulateSchemaDefinition) {
namespace fs = std::filesystem;
const fs::path root = fs::temp_directory_path() / "xc_ui_schema_compile_test";
const fs::path schemaPath = root / "markup.xcschema";
const fs::path artifactPath = root / "markup.xcschemaasset";
fs::remove_all(root);
WriteTextFile(
schemaPath,
"<Schema name=\"EditorMarkup\">\n"
" <Element tag=\"View\" allowUnknownChildren=\"true\">\n"
" <Attribute name=\"theme\" type=\"document\" kind=\"theme\" />\n"
" <Attribute name=\"mode\" type=\"enum\" values=\"compact, cozy\" />\n"
" <Element tag=\"Column\">\n"
" <Attribute name=\"gap\" type=\"number\" />\n"
" </Element>\n"
" </Element>\n"
"</Schema>\n");
UISchemaLoader loader;
UIDocumentCompileResult compileResult = {};
ASSERT_TRUE(loader.CompileDocument(schemaPath.string().c_str(), compileResult));
ASSERT_TRUE(compileResult.succeeded);
ASSERT_TRUE(compileResult.document.valid);
ASSERT_TRUE(compileResult.document.schemaDefinition.valid);
EXPECT_EQ(compileResult.document.schemaDefinition.name, "EditorMarkup");
const UISchemaElementDefinition* viewElement =
compileResult.document.schemaDefinition.FindElement("View");
ASSERT_NE(viewElement, nullptr);
EXPECT_TRUE(viewElement->allowUnknownChildren);
const UISchemaAttributeDefinition* themeAttribute = viewElement->FindAttribute("theme");
ASSERT_NE(themeAttribute, nullptr);
EXPECT_EQ(themeAttribute->valueType, UISchemaValueType::Document);
EXPECT_TRUE(themeAttribute->restrictDocumentKind);
EXPECT_EQ(themeAttribute->documentKind, UIDocumentKind::Theme);
const UISchemaAttributeDefinition* modeAttribute = viewElement->FindAttribute("mode");
ASSERT_NE(modeAttribute, nullptr);
EXPECT_EQ(modeAttribute->valueType, UISchemaValueType::Enum);
ASSERT_EQ(modeAttribute->allowedValues.Size(), 2u);
EXPECT_EQ(modeAttribute->allowedValues[0], "compact");
EXPECT_EQ(modeAttribute->allowedValues[1], "cozy");
LoadResult loadResult = loader.Load(schemaPath.string().c_str());
ASSERT_TRUE(loadResult);
ASSERT_NE(loadResult.resource, nullptr);
auto* schemaResource = static_cast<UISchema*>(loadResult.resource);
ASSERT_NE(schemaResource, nullptr);
ASSERT_TRUE(schemaResource->GetSchemaDefinition().valid);
ASSERT_NE(schemaResource->GetSchemaDefinition().FindElement("View"), nullptr);
delete schemaResource;
XCEngine::Containers::String artifactWriteError;
ASSERT_TRUE(
WriteUIDocumentArtifact(artifactPath.string().c_str(), compileResult, &artifactWriteError))
<< artifactWriteError.CStr();
XCEngine::Containers::Array<XCEngine::Core::uint8> artifactPayload;
ASSERT_TRUE(ReadArtifactContainerMainEntryPayload(
artifactPath.string().c_str(),
ResourceType::UISchema,
artifactPayload));
EXPECT_FALSE(artifactPayload.Empty());
UIDocumentCompileResult artifactResult = {};
ASSERT_TRUE(LoadUIDocumentArtifact(
artifactPath.string().c_str(),
UIDocumentKind::Schema,
artifactResult));
ASSERT_TRUE(artifactResult.succeeded);
ASSERT_TRUE(artifactResult.document.valid);
ASSERT_TRUE(artifactResult.document.schemaDefinition.valid);
const UISchemaElementDefinition* artifactViewElement =
artifactResult.document.schemaDefinition.FindElement("View");
ASSERT_NE(artifactViewElement, nullptr);
ASSERT_NE(artifactViewElement->FindChild("Column"), nullptr);
fs::remove_all(root);
}
TEST(UISchemaDocument, CompileRejectsInvalidSchemaFlags) {
namespace fs = std::filesystem;
const fs::path root = fs::temp_directory_path() / "xc_ui_schema_invalid_flag_test";
const fs::path schemaPath = root / "broken.xcschema";
fs::remove_all(root);
WriteTextFile(
schemaPath,
"<Schema>\n"
" <Element tag=\"View\" allowUnknownChildren=\"sometimes\" />\n"
"</Schema>\n");
UISchemaLoader loader;
UIDocumentCompileResult compileResult = {};
EXPECT_FALSE(loader.CompileDocument(schemaPath.string().c_str(), compileResult));
EXPECT_FALSE(compileResult.succeeded);
EXPECT_FALSE(compileResult.errorMessage.Empty());
ASSERT_FALSE(compileResult.document.diagnostics.Empty());
const std::string errorMessage = compileResult.errorMessage.CStr();
EXPECT_NE(errorMessage.find("allowUnknownChildren"), std::string::npos);
fs::remove_all(root);
}
TEST(UISchemaDocument, CompileRejectsDuplicateAttributeDefinitions) {
namespace fs = std::filesystem;
const fs::path root = fs::temp_directory_path() / "xc_ui_schema_duplicate_attribute_test";
const fs::path schemaPath = root / "duplicate.xcschema";
fs::remove_all(root);
WriteTextFile(
schemaPath,
"<Schema>\n"
" <Element tag=\"View\">\n"
" <Attribute name=\"id\" type=\"string\" />\n"
" <Attribute name=\"id\" type=\"string\" />\n"
" </Element>\n"
"</Schema>\n");
UISchemaLoader loader;
UIDocumentCompileResult compileResult = {};
EXPECT_FALSE(loader.CompileDocument(schemaPath.string().c_str(), compileResult));
EXPECT_FALSE(compileResult.succeeded);
EXPECT_FALSE(compileResult.errorMessage.Empty());
ASSERT_FALSE(compileResult.document.diagnostics.Empty());
const std::string errorMessage = compileResult.errorMessage.CStr();
EXPECT_NE(errorMessage.find("Duplicate schema attribute definition"), std::string::npos);
fs::remove_all(root);
}
TEST(UISchemaDocument, CompileRejectsAllowedValuesOnNonEnumAttribute) {
ExpectSchemaCompileFailure(
"xc_ui_schema_allowed_values_non_enum_test",
"<Schema>\n"
" <Element tag=\"View\">\n"
" <Attribute name=\"gap\" type=\"number\" allowedValues=\"8,12\" />\n"
" </Element>\n"
"</Schema>\n",
"allowedValues");
}
TEST(UISchemaDocument, CompileRejectsDocumentKindOnNonDocumentAttribute) {
ExpectSchemaCompileFailure(
"xc_ui_schema_document_kind_non_document_test",
"<Schema>\n"
" <Element tag=\"View\">\n"
" <Attribute name=\"id\" type=\"string\" documentKind=\"theme\" />\n"
" </Element>\n"
"</Schema>\n",
"documentKind");
}
TEST(UISchemaDocument, CompileRejectsRestrictDocumentKindOnNonDocumentAttribute) {
ExpectSchemaCompileFailure(
"xc_ui_schema_restrict_non_document_test",
"<Schema>\n"
" <Element tag=\"View\">\n"
" <Attribute name=\"id\" type=\"string\" restrictDocumentKind=\"true\" />\n"
" </Element>\n"
"</Schema>\n",
"restrictDocumentKind");
}
TEST(UISchemaDocument, CompileRejectsRestrictDocumentKindWithoutExplicitDocumentKind) {
ExpectSchemaCompileFailure(
"xc_ui_schema_restrict_without_kind_test",
"<Schema>\n"
" <Element tag=\"View\">\n"
" <Attribute name=\"theme\" type=\"document\" restrictDocumentKind=\"true\" />\n"
" </Element>\n"
"</Schema>\n",
"must declare 'documentKind'");
}
} // namespace