using System.IO; using System.Reflection; using NUnit.Framework; using Unity.Pipeline.Compilation; using Unity.Pipeline.CodeReload; namespace Unity.Pipeline.Tests.Editor { /// /// Tests for CodeReloadCompiler's cleanup of versioned code reload assemblies. /// class CodeReloadCompilerTests { private string testAssemblyDir; [SetUp] public void SetUp() { // Keep test files OUTSIDE Assets/ (project-root Temp/, which Unity ignores) so the // editor never imports them — an import triggers a domain reload that tears down the // live pipeline server. testAssemblyDir = Path.GetFullPath(Path.Combine("Temp", "PipelineCodeReloadTests", "CompilerTests")); Directory.CreateDirectory(testAssemblyDir); // Clear code reload state CodeReloadRegistry.ClearAllForTesting(); } [TearDown] public void TearDown() { // Clean up test files if (Directory.Exists(testAssemblyDir)) { Directory.Delete(testAssemblyDir, true); } // Clean up code reload state and temp files CodeReloadRegistry.ClearAllForTesting(); CodeReloadCompiler.CleanupCodeReloadDlls(); } [Test] public void CleanupCodeReloadDlls_WithNoFiles_SucceedsGracefully() { // Act var result = CodeReloadCompiler.CleanupCodeReloadDlls(); // Assert Assert.IsTrue(result.Success, "Cleanup should succeed even when no files exist"); Assert.AreEqual(0, result.DeletedFiles.Count, "Should not delete any files"); Assert.That(result.Message, Does.Contain("No code reload directory").Or.Contain("0 old DLL versions")); } [Test] public void CleanupCodeReloadDlls_WithVersionedDlls_KeepsOnlyLatestVersion() { // Arrange - three versions of one assembly, plus an unversioned DLL cleanup must leave alone var v1 = Path.Combine(testAssemblyDir, "Sample_001.dll"); var v2 = Path.Combine(testAssemblyDir, "Sample_002.dll"); var v3 = Path.Combine(testAssemblyDir, "Sample_003.dll"); var unversioned = Path.Combine(testAssemblyDir, "Plain.dll"); File.WriteAllText(v1, "fake dll v1"); File.WriteAllText(v2, "fake dll v2"); File.WriteAllText(v3, "fake dll v3"); File.WriteAllText(unversioned, "fake dll"); // Act var result = CodeReloadCompiler.CleanupCodeReloadDlls(testAssemblyDir); // Assert Assert.IsTrue(result.Success, $"Cleanup should succeed. Message: {result.Message}"); Assert.GreaterOrEqual(result.ExecutionTimeMs, 0, "Should record execution time (0ms is acceptable for fast cleanup)"); Assert.IsFalse(File.Exists(v1), "Version 001 should be deleted"); Assert.IsFalse(File.Exists(v2), "Version 002 should be deleted"); Assert.IsTrue(File.Exists(v3), "Latest version should be kept"); Assert.IsTrue(File.Exists(unversioned), "Unversioned DLLs should be left alone"); CollectionAssert.AreEquivalent(new[] { "Sample_001.dll", "Sample_002.dll" }, result.DeletedFiles); } [Test] public void CleanupCodeReloadDlls_WithActiveOverrides_ClearsRegistry() { // Arrange - register a reloadable target and an override for it var target = typeof(CleanupTarget).GetMethod(nameof(CleanupTarget.Tick)); CodeReloadRegistry.RegisterReloadableMethod(target, new CodeReloadAttribute { Id = "CleanupTarget.Tick" }); var overrideMethod = typeof(CleanupOverrides).GetMethod(nameof(CleanupOverrides.Tick)); var overrideAttribute = overrideMethod.GetCustomAttribute(); Assert.IsTrue(CodeReloadRegistry.RegisterMethodOverride(overrideMethod, overrideAttribute, typeof(CleanupOverrides)), "Override should register against the reloadable target"); Assert.Greater(CodeReloadRegistry.GetStats().ActiveOverrideCount, 0, "Precondition: an override is active"); // Act var result = CodeReloadCompiler.CleanupCodeReloadDlls(testAssemblyDir); // Assert Assert.IsTrue(result.Success, $"Cleanup should succeed. Message: {result.Message}"); Assert.AreEqual(0, CodeReloadRegistry.GetStats().ActiveOverrideCount, "Registry should be cleared after cleanup"); } /// /// Target whose method the cleanup test registers as reloadable. /// public class CleanupTarget { public int value; public void Tick() { value = 42; } } /// /// Override for , in the shape the reload pipeline generates. /// public static class CleanupOverrides { [CodeReloadOverrideMethod("CleanupTarget.Tick")] public static void Tick(CleanupTarget instance) { instance.value = 99; } } } }