#include #include #include #include #include #include #include #include #include #include #include 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(output)); } bool ContainsExtension(const XCEngine::Containers::Array& values, const char* expectedValue) { for (const auto& value : values) { if (value == expectedValue) { return true; } } return false; } bool ContainsDependencyFile(const XCEngine::Containers::Array& dependencies, const char* expectedFileName) { namespace fs = std::filesystem; for (const auto& dependency : dependencies) { if (fs::path(dependency.CStr()).filename().string() == expectedFileName) { return true; } } return false; } TEST(UIDocumentLoader, LoadersExposeExpectedTypesAndExtensions) { UIViewLoader viewLoader; UIThemeLoader themeLoader; UISchemaLoader schemaLoader; EXPECT_EQ(viewLoader.GetResourceType(), ResourceType::UIView); EXPECT_EQ(themeLoader.GetResourceType(), ResourceType::UITheme); EXPECT_EQ(schemaLoader.GetResourceType(), ResourceType::UISchema); const auto viewExtensions = viewLoader.GetSupportedExtensions(); EXPECT_TRUE(ContainsExtension(viewExtensions, "xcui")); EXPECT_TRUE(ContainsExtension(viewExtensions, "xcuiasset")); EXPECT_TRUE(viewLoader.CanLoad("panel.xcui")); EXPECT_TRUE(viewLoader.CanLoad("panel.xcuiasset")); EXPECT_FALSE(viewLoader.CanLoad("panel.txt")); const auto themeExtensions = themeLoader.GetSupportedExtensions(); EXPECT_TRUE(ContainsExtension(themeExtensions, "xctheme")); EXPECT_TRUE(ContainsExtension(themeExtensions, "xcthemeasset")); EXPECT_TRUE(themeLoader.CanLoad("editor.xctheme")); EXPECT_TRUE(themeLoader.CanLoad("editor.xcthemeasset")); const auto schemaExtensions = schemaLoader.GetSupportedExtensions(); EXPECT_TRUE(ContainsExtension(schemaExtensions, "xcschema")); EXPECT_TRUE(ContainsExtension(schemaExtensions, "xcschemaasset")); EXPECT_TRUE(schemaLoader.CanLoad("entity.xcschema")); EXPECT_TRUE(schemaLoader.CanLoad("entity.xcschemaasset")); } TEST(UIDocumentLoader, CompileAndLoadViewTracksDependencies) { namespace fs = std::filesystem; const fs::path root = fs::temp_directory_path() / "xc_ui_document_compile_test"; fs::remove_all(root); WriteTextFile(root / "shared" / "toolbar.xcui", "\n"); WriteTextFile(root / "themes" / "editor.xctheme", "\n"); WriteTextFile(root / "schemas" / "entity.xcschema", "\n"); WriteTextFile( root / "main.xcui", "\n" "\n" " \n" " \n" " \n" " \n" "\n"); UIViewLoader loader; UIDocumentCompileResult compileResult = {}; ASSERT_TRUE(loader.CompileDocument((root / "main.xcui").string().c_str(), compileResult)); ASSERT_TRUE(compileResult.succeeded); ASSERT_TRUE(compileResult.document.valid); EXPECT_EQ(compileResult.document.rootNode.tagName, "View"); EXPECT_EQ(compileResult.document.rootNode.children.Size(), 1u); EXPECT_EQ(compileResult.document.rootNode.children[0].tagName, "Column"); EXPECT_EQ(compileResult.document.dependencies.Size(), 3u); EXPECT_TRUE(ContainsDependencyFile(compileResult.document.dependencies, "toolbar.xcui")); EXPECT_TRUE(ContainsDependencyFile(compileResult.document.dependencies, "editor.xctheme")); EXPECT_TRUE(ContainsDependencyFile(compileResult.document.dependencies, "entity.xcschema")); LoadResult loadResult = loader.Load((root / "main.xcui").string().c_str()); ASSERT_TRUE(loadResult); ASSERT_NE(loadResult.resource, nullptr); auto* view = static_cast(loadResult.resource); ASSERT_NE(view, nullptr); EXPECT_TRUE(view->IsValid()); EXPECT_EQ(view->GetName(), "MainPanel"); EXPECT_EQ(view->GetRootNode().tagName, "View"); EXPECT_EQ(view->GetDependencies().Size(), 3u); delete view; fs::remove_all(root); } TEST(UIDocumentLoader, AssetDatabaseImportsViewArtifactAndReimportsWhenDependencyChanges) { namespace fs = std::filesystem; using namespace std::chrono_literals; const fs::path projectRoot = fs::temp_directory_path() / "xc_ui_artifact_reimport_test"; const fs::path assetsRoot = projectRoot / "Assets"; fs::remove_all(projectRoot); WriteTextFile(assetsRoot / "UI" / "Shared" / "toolbar.xcui", "\n"); WriteTextFile( assetsRoot / "UI" / "Main.xcui", "\n" " \n" "\n"); AssetDatabase database; database.Initialize(projectRoot.string().c_str()); AssetDatabase::ResolvedAsset firstResolve; ASSERT_TRUE(database.EnsureArtifact("Assets/UI/Main.xcui", ResourceType::UIView, firstResolve)); ASSERT_TRUE(firstResolve.artifactReady); EXPECT_EQ(fs::path(firstResolve.artifactMainPath.CStr()).extension().string(), ".xcuiasset"); EXPECT_TRUE(fs::exists(firstResolve.artifactMainPath.CStr())); EXPECT_TRUE(IsArtifactContainerFile(firstResolve.artifactMainPath)); EXPECT_FALSE(firstResolve.artifactMainEntryPath.Empty()); EXPECT_NE(firstResolve.artifactMainEntryPath, firstResolve.artifactMainPath); XCEngine::Containers::Array artifactPayload; ASSERT_TRUE(ReadArtifactContainerMainEntryPayload( firstResolve.artifactMainPath, ResourceType::UIView, artifactPayload)); EXPECT_FALSE(artifactPayload.Empty()); UIViewLoader loader; LoadResult firstLoad = loader.Load(firstResolve.artifactMainEntryPath.CStr()); ASSERT_TRUE(firstLoad); auto* firstView = static_cast(firstLoad.resource); ASSERT_NE(firstView, nullptr); EXPECT_EQ(firstView->GetRootNode().tagName, "View"); EXPECT_EQ(firstView->GetDependencies().Size(), 1u); EXPECT_TRUE(ContainsDependencyFile(firstView->GetDependencies(), "toolbar.xcui")); delete firstView; const XCEngine::Containers::String firstArtifactPath = firstResolve.artifactMainPath; database.Shutdown(); std::this_thread::sleep_for(50ms); WriteTextFile( assetsRoot / "UI" / "Shared" / "toolbar.xcui", "\n" "