#include #include "Scripting/EditorScriptAssemblyBuilder.h" #ifdef XCENGINE_ENABLE_MONO_SCRIPTING #include #endif #include #include #include #include #include namespace XCEngine::Editor::Scripting { namespace { class EditorScriptAssemblyBuilderTest : public ::testing::Test { protected: static void WriteTextFile(const std::filesystem::path& path, const std::string& content) { std::ofstream output(path, std::ios::out | std::ios::trunc); ASSERT_TRUE(output.is_open()); output << content; output.close(); ASSERT_TRUE(output.good()); } void SetUp() override { const auto stamp = std::chrono::steady_clock::now().time_since_epoch().count(); m_projectRoot = std::filesystem::temp_directory_path() / ("xc_script_builder_" + std::to_string(stamp)); std::filesystem::create_directories(m_projectRoot / "Assets" / "Scripts"); } void TearDown() override { std::error_code ec; std::filesystem::remove_all(m_projectRoot, ec); } std::filesystem::path m_projectRoot; }; TEST_F(EditorScriptAssemblyBuilderTest, RebuildsProjectScriptAssembliesIntoLibraryDirectory) { const std::filesystem::path scriptPath = m_projectRoot / "Assets" / "Scripts" / "BuilderProbe.cs"; WriteTextFile( scriptPath, "using XCEngine;\n" "namespace BuilderTests {\n" " public sealed class BuilderProbe : MonoBehaviour {\n" " public float Speed = 4.0f;\n" " }\n" "}\n"); const EditorScriptAssemblyBuildResult result = EditorScriptAssemblyBuilder::RebuildProjectAssemblies(m_projectRoot.string()); ASSERT_TRUE(result.succeeded) << result.message; EXPECT_TRUE(std::filesystem::exists(m_projectRoot / "Library" / "ScriptAssemblies" / "XCEngine.ScriptCore.dll")); EXPECT_TRUE(std::filesystem::exists(m_projectRoot / "Library" / "ScriptAssemblies" / "GameScripts.dll")); EXPECT_TRUE(std::filesystem::exists(m_projectRoot / "Library" / "ScriptAssemblies" / "mscorlib.dll")); } #ifdef XCENGINE_ENABLE_MONO_SCRIPTING TEST_F(EditorScriptAssemblyBuilderTest, RebuildFailsWhileLoadedAssemblyIsStillHeldByMonoRuntime) { const std::filesystem::path initialScriptPath = m_projectRoot / "Assets" / "Scripts" / "BuilderProbe.cs"; WriteTextFile( initialScriptPath, "using XCEngine;\n" "namespace BuilderTests {\n" " public sealed class BuilderProbe : MonoBehaviour {\n" " public float Speed = 4.0f;\n" " }\n" "}\n"); EditorScriptAssemblyBuildResult result = EditorScriptAssemblyBuilder::RebuildProjectAssemblies(m_projectRoot.string()); ASSERT_TRUE(result.succeeded) << result.message; XCEngine::Scripting::MonoScriptRuntime::Settings settings; settings.assemblyDirectory = m_projectRoot / "Library" / "ScriptAssemblies"; settings.corlibDirectory = settings.assemblyDirectory; settings.coreAssemblyPath = settings.assemblyDirectory / "XCEngine.ScriptCore.dll"; settings.appAssemblyPath = settings.assemblyDirectory / "GameScripts.dll"; auto runtime = std::make_unique(settings); ASSERT_TRUE(runtime->Initialize()) << runtime->GetLastError(); const std::filesystem::path addedScriptPath = m_projectRoot / "Assets" / "Scripts" / "TickLogProbe.cs"; WriteTextFile( addedScriptPath, "using XCEngine;\n" "namespace BuilderTests {\n" " public sealed class TickLogProbe : MonoBehaviour {\n" " public int TickCount;\n" " }\n" "}\n"); result = EditorScriptAssemblyBuilder::RebuildProjectAssemblies(m_projectRoot.string()); EXPECT_FALSE(result.succeeded); runtime.reset(); result = EditorScriptAssemblyBuilder::RebuildProjectAssemblies(m_projectRoot.string()); ASSERT_TRUE(result.succeeded) << result.message; runtime = std::make_unique(settings); ASSERT_TRUE(runtime->Initialize()) << runtime->GetLastError(); EXPECT_TRUE(runtime->IsClassAvailable("GameScripts", "BuilderTests", "BuilderProbe")); EXPECT_TRUE(runtime->IsClassAvailable("GameScripts", "BuilderTests", "TickLogProbe")); } #endif } // namespace } // namespace XCEngine::Editor::Scripting