216 lines
8.3 KiB
C++
216 lines
8.3 KiB
C++
#include <gtest/gtest.h>
|
|
|
|
#include <XCEngine/Core/Asset/AssetDatabase.h>
|
|
#include <XCEngine/Core/Asset/AssetImportService.h>
|
|
#include <XCEngine/Core/Asset/ResourceTypes.h>
|
|
#include <XCEngine/Resources/UI/UIDocumentLoaders.h>
|
|
#include <XCEngine/Resources/UI/UIDocuments.h>
|
|
|
|
#include <chrono>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <thread>
|
|
|
|
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));
|
|
}
|
|
|
|
bool ContainsExtension(const XCEngine::Containers::Array<XCEngine::Containers::String>& values,
|
|
const char* expectedValue) {
|
|
for (const auto& value : values) {
|
|
if (value == expectedValue) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ContainsDependencyFile(const XCEngine::Containers::Array<XCEngine::Containers::String>& 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", "<View name=\"Toolbar\" />\n");
|
|
WriteTextFile(root / "themes" / "editor.xctheme", "<Theme name=\"EditorTheme\" />\n");
|
|
WriteTextFile(root / "schemas" / "entity.xcschema", "<Schema name=\"EntitySchema\" />\n");
|
|
WriteTextFile(
|
|
root / "main.xcui",
|
|
"<!-- root comment -->\n"
|
|
"<View name=\"MainPanel\" theme=\"themes/editor.xctheme\">\n"
|
|
" <Column>\n"
|
|
" <Use view=\"shared/toolbar.xcui\" />\n"
|
|
" <AutoForm schema=\"schemas/entity.xcschema\" />\n"
|
|
" </Column>\n"
|
|
"</View>\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<UIView*>(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", "<View name=\"Toolbar\" />\n");
|
|
WriteTextFile(
|
|
assetsRoot / "UI" / "Main.xcui",
|
|
"<View name=\"Inspector\">\n"
|
|
" <Use view=\"Shared/toolbar.xcui\" />\n"
|
|
"</View>\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()));
|
|
|
|
UIViewLoader loader;
|
|
LoadResult firstLoad = loader.Load(firstResolve.artifactMainPath.CStr());
|
|
ASSERT_TRUE(firstLoad);
|
|
auto* firstView = static_cast<UIView*>(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",
|
|
"<View name=\"Toolbar\">\n"
|
|
" <Button id=\"refresh\" />\n"
|
|
"</View>\n");
|
|
|
|
database.Initialize(projectRoot.string().c_str());
|
|
AssetDatabase::ResolvedAsset secondResolve;
|
|
ASSERT_TRUE(database.EnsureArtifact("Assets/UI/Main.xcui", ResourceType::UIView, secondResolve));
|
|
ASSERT_TRUE(secondResolve.artifactReady);
|
|
EXPECT_NE(firstArtifactPath, secondResolve.artifactMainPath);
|
|
EXPECT_TRUE(fs::exists(secondResolve.artifactMainPath.CStr()));
|
|
database.Shutdown();
|
|
|
|
fs::remove_all(projectRoot);
|
|
}
|
|
|
|
TEST(UIDocumentLoader, AssetImportServiceReportsDetailedDiagnosticsForMissingDependency) {
|
|
namespace fs = std::filesystem;
|
|
|
|
const fs::path projectRoot = fs::temp_directory_path() / "xc_ui_import_error_test";
|
|
const fs::path assetsRoot = projectRoot / "Assets";
|
|
|
|
fs::remove_all(projectRoot);
|
|
WriteTextFile(
|
|
assetsRoot / "UI" / "Broken.xcui",
|
|
"<View>\n"
|
|
" <Use view=\"Shared/missing_toolbar.xcui\" />\n"
|
|
"</View>\n");
|
|
|
|
AssetImportService importService;
|
|
importService.Initialize();
|
|
importService.SetProjectRoot(projectRoot.string().c_str());
|
|
|
|
AssetImportService::ImportedAsset importedAsset;
|
|
EXPECT_FALSE(importService.EnsureArtifact("Assets/UI/Broken.xcui", ResourceType::UIView, importedAsset));
|
|
|
|
const AssetImportService::ImportStatusSnapshot status = importService.GetLastImportStatus();
|
|
EXPECT_TRUE(status.HasValue());
|
|
EXPECT_FALSE(status.success);
|
|
|
|
const std::string message = status.message.CStr();
|
|
EXPECT_NE(message.find("Failed to build asset artifact: Assets/UI/Broken.xcui"), std::string::npos);
|
|
EXPECT_NE(message.find("Referenced UI document was not found"), std::string::npos);
|
|
EXPECT_NE(message.find("missing_toolbar.xcui"), std::string::npos);
|
|
|
|
importService.Shutdown();
|
|
fs::remove_all(projectRoot);
|
|
}
|
|
|
|
} // namespace
|