Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Assets/Tests/Demo/uLoopMCP.Tests.Demo.asmdef
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
"GUID:c956a21f824994ef087b6de566690b3d",
"GUID:4307f53044263cf4b835bd812fc161a4"
],
"includePlatforms": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
Expand Down
8 changes: 8 additions & 0 deletions Assets/Tests/Editor/DomainReloadRecoveryUseCaseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ public void ExecuteBeforeDomainReload_ShouldPreferInstanceState_WhenInstanceIsRu
// Assert
Assert.IsTrue(result.Success, "ExecuteBeforeDomainReload should succeed");
Assert.IsFalse(server.IsRunning, "Running server instance should be stopped before domain reload");
Assert.That(server.StopCallCount, Is.EqualTo(1));
Assert.That(server.DisposeCallCount, Is.EqualTo(0));
}

[Test]
Expand Down Expand Up @@ -183,6 +185,10 @@ private sealed class TestServerInstance : IUnityCliLoopServerInstance
{
public bool IsRunning { get; private set; }

public int StopCallCount { get; private set; }

public int DisposeCallCount { get; private set; }

public string Endpoint => "test";

public void StartServer()
Expand All @@ -192,11 +198,13 @@ public void StartServer()

public void StopServer()
{
StopCallCount++;
IsRunning = false;
}

public void Dispose()
{
DisposeCallCount++;
IsRunning = false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,23 @@ await facade.ExecuteAsync(
Assert.That(provider.CreatedExecutors[0].DisposeCallCount, Is.EqualTo(1));
}

[Test]
public void ResetServerScopedServicesBeforeDomainReload_ShouldSignalShutdownWithoutWaitingForRuntimeDrain()
{
// Tests that domain reload reset does not leave a pending drain task that can block Unity teardown.
DynamicCodeServicesRegistry registry = new();
FakeShutdownAwareRuntime runtime = new();
registry.SetRuntimeFacadeForTests(runtime);

registry.ResetServerScopedServicesBeforeDomainReload();

Assert.That(runtime.ShutdownCallCount, Is.EqualTo(1));
Assert.That(runtime.DisposeCallCount, Is.EqualTo(0));
Assert.That(registry.GetServerScopedDrainTaskForTests().IsCompleted, Is.True);

runtime.CompleteShutdown();
}

private static DynamicCodeExecutionRequest CreateRequest(
DynamicCodeSecurityLevel securityLevel,
string code)
Expand Down Expand Up @@ -138,5 +155,47 @@ public void Dispose()
}
}

/// <summary>
/// Test support type used by editor and play mode fixtures.
/// </summary>
private sealed class FakeShutdownAwareRuntime : IShutdownAwareDynamicCodeExecutionRuntime, System.IDisposable
{
private readonly TaskCompletionSource<bool> _shutdownCompletionSource = new(TaskCreationOptions.RunContinuationsAsynchronously);

public int ShutdownCallCount { get; private set; }

public int DisposeCallCount { get; private set; }

public Task<ExecutionResult> ExecuteAsync(
DynamicCodeExecutionRequest request,
CancellationToken cancellationToken = default)
{
throw new System.NotSupportedException();
}

public Task<(bool Entered, ExecutionResult Result)> TryExecuteIfIdleAsync(
DynamicCodeExecutionRequest request,
CancellationToken cancellationToken = default)
{
throw new System.NotSupportedException();
}

public Task ShutdownAsync()
{
ShutdownCallCount++;
return _shutdownCompletionSource.Task;
}

public void CompleteShutdown()
{
_shutdownCompletionSource.SetResult(true);
}

public void Dispose()
{
DisposeCallCount++;
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public void ResolveScriptingRootPath_WhenLegacyLayoutExists_ShouldReturnContents
string contentsPath = CreateDirectory("Contents");
CreateDirectory(Path.Combine("Contents", "NetCoreRuntime"));
CreateDirectory(Path.Combine("Contents", "DotNetSdkRoslyn"));
CreateFile(Path.Combine("Contents", "DotNetSdkRoslyn", "csc.dll"));

string resolvedScriptingRootPath = ExternalCompilerPathResolver.ResolveScriptingRootPath(contentsPath);

Expand All @@ -82,10 +83,26 @@ public void ResolveScriptingRootPath_WhenLegacyLayoutExists_ShouldReturnContents
[Test]
public void ResolveScriptingRootPath_WhenResourcesScriptingLayoutExists_ShouldReturnResourcesScriptingPath()
{
// Verifies Unity's Resources/Scripting compiler layout is preferred when present.
string contentsPath = CreateDirectory("Contents");
string expectedScriptingRootPath = CreateDirectory(Path.Combine("Contents", "Resources", "Scripting"));
CreateDirectory(Path.Combine("Contents", "Resources", "Scripting", "NetCoreRuntime"));
CreateDirectory(Path.Combine("Contents", "Resources", "Scripting", "DotNetSdkRoslyn"));
CreateFile(Path.Combine("Contents", "Resources", "Scripting", "DotNetSdkRoslyn", "csc.dll"));

string resolvedScriptingRootPath = ExternalCompilerPathResolver.ResolveScriptingRootPath(contentsPath);

Assert.That(resolvedScriptingRootPath, Is.EqualTo(expectedScriptingRootPath));
}

[Test]
public void ResolveScriptingRootPath_WhenResourcesScriptingDotNetSdkLayoutExists_ShouldReturnResourcesScriptingPath()
{
// Verifies Unity 6.5 DotNetSdk compiler layouts are accepted under Resources/Scripting.
string contentsPath = CreateDirectory("Contents");
string expectedScriptingRootPath = CreateDirectory(Path.Combine("Contents", "Resources", "Scripting"));
CreateDirectory(Path.Combine("Contents", "Resources", "Scripting", "NetCoreRuntime"));
CreateDirectory(Path.Combine("Contents", "Resources", "Scripting", "DotNetSdk", "sdk", "8.0.318", "Roslyn", "bincore"));

string resolvedScriptingRootPath = ExternalCompilerPathResolver.ResolveScriptingRootPath(contentsPath);

Expand All @@ -95,12 +112,15 @@ public void ResolveScriptingRootPath_WhenResourcesScriptingLayoutExists_ShouldRe
[Test]
public void ResolveScriptingRootPath_WhenBothLayoutsExist_ShouldPreferResourcesScriptingLayout()
{
// Verifies the current Resources/Scripting layout wins over the legacy contents-root layout.
string contentsPath = CreateDirectory("Contents");
CreateDirectory(Path.Combine("Contents", "NetCoreRuntime"));
CreateDirectory(Path.Combine("Contents", "DotNetSdkRoslyn"));
CreateFile(Path.Combine("Contents", "DotNetSdkRoslyn", "csc.dll"));
string expectedScriptingRootPath = CreateDirectory(Path.Combine("Contents", "Resources", "Scripting"));
CreateDirectory(Path.Combine("Contents", "Resources", "Scripting", "NetCoreRuntime"));
CreateDirectory(Path.Combine("Contents", "Resources", "Scripting", "DotNetSdkRoslyn"));
CreateFile(Path.Combine("Contents", "Resources", "Scripting", "DotNetSdkRoslyn", "csc.dll"));

string resolvedScriptingRootPath = ExternalCompilerPathResolver.ResolveScriptingRootPath(contentsPath);

Expand All @@ -114,17 +134,66 @@ public void ResolveScriptingRootPath_WhenKnownLayoutsAreMissing_ShouldDiscoverNe
string expectedScriptingRootPath = CreateDirectory(Path.Combine("Contents", "PlaybackEngines", "Custom", "Scripting"));
CreateDirectory(Path.Combine("Contents", "PlaybackEngines", "Custom", "Scripting", "NetCoreRuntime"));
CreateDirectory(Path.Combine("Contents", "PlaybackEngines", "Custom", "Scripting", "DotNetSdkRoslyn"));
CreateFile(Path.Combine("Contents", "PlaybackEngines", "Custom", "Scripting", "DotNetSdkRoslyn", "csc.dll"));

string resolvedScriptingRootPath = ExternalCompilerPathResolver.ResolveScriptingRootPath(contentsPath);

Assert.That(resolvedScriptingRootPath, Is.EqualTo(expectedScriptingRootPath));
}

[Test]
public void ResolveCompilerDirectoryPath_WhenLegacyLayoutExists_ShouldReturnDotNetSdkRoslynPath()
{
// Verifies legacy compiler roots keep resolving to DotNetSdkRoslyn.
string scriptingRootPath = CreateDirectory("Scripting");
string expectedCompilerDirectoryPath = CreateDirectory(Path.Combine("Scripting", "DotNetSdkRoslyn"));
CreateFile(Path.Combine("Scripting", "DotNetSdkRoslyn", "csc.dll"));

string resolvedCompilerDirectoryPath = ExternalCompilerPathResolver.ResolveCompilerDirectoryPath(scriptingRootPath);

Assert.That(resolvedCompilerDirectoryPath, Is.EqualTo(expectedCompilerDirectoryPath));
}

[Test]
public void ResolveCompilerDirectoryPath_WhenLegacyLayoutIsIncomplete_ShouldUseDotNetSdkLayout()
{
// Verifies stale legacy compiler roots fall back to the versioned DotNetSdk layout.
string scriptingRootPath = CreateDirectory("Scripting");
CreateDirectory(Path.Combine("Scripting", "DotNetSdkRoslyn"));
string expectedCompilerDirectoryPath = CreateDirectory(Path.Combine("Scripting", "DotNetSdk", "sdk", "8.0.318", "Roslyn", "bincore"));

string resolvedCompilerDirectoryPath = ExternalCompilerPathResolver.ResolveCompilerDirectoryPath(scriptingRootPath);

Assert.That(resolvedCompilerDirectoryPath, Is.EqualTo(expectedCompilerDirectoryPath));
}

[Test]
public void ResolveCompilerDirectoryPath_WhenDotNetSdkLayoutHasMultipleSdkVersions_ShouldChooseHighestSdkRoslynBincorePath()
{
// Verifies Unity 6.5 SDK layouts choose the newest versioned Roslyn compiler directory.
string scriptingRootPath = CreateDirectory("Scripting");
CreateDirectory(Path.Combine("Scripting", "DotNetSdk", "sdk", "8.0.100", "Roslyn", "bincore"));
string expectedCompilerDirectoryPath = CreateDirectory(Path.Combine("Scripting", "DotNetSdk", "sdk", "8.0.318", "Roslyn", "bincore"));
CreateDirectory(Path.Combine("Scripting", "DotNetSdk", "sdk", "current", "Roslyn", "bincore"));

string resolvedCompilerDirectoryPath = ExternalCompilerPathResolver.ResolveCompilerDirectoryPath(scriptingRootPath);

Assert.That(resolvedCompilerDirectoryPath, Is.EqualTo(expectedCompilerDirectoryPath));
}

private string CreateDirectory(string relativePath)
{
string directoryPath = Path.Combine(_tempDirectoryPath, relativePath);
Directory.CreateDirectory(directoryPath);
return directoryPath;
}

private string CreateFile(string relativePath)
{
string filePath = Path.Combine(_tempDirectoryPath, relativePath);
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
File.WriteAllText(filePath, string.Empty);
return filePath;
}
}
}
21 changes: 18 additions & 3 deletions Assets/Tests/Editor/FindGameObjectsToolTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Globalization;
using System.IO;
using System.Threading.Tasks;
using NUnit.Framework;
Expand Down Expand Up @@ -640,11 +641,13 @@ public async Task ExecuteAsync_ReturnsObjectReferenceProperties()
Assert.That(probeAnchor, Is.Not.Null, "MeshRenderer should have Probe Anchor property");
Assert.That(probeAnchor.type, Is.EqualTo("ObjectReference"));

// Value should be a structured object with name, type, instanceId
string expectedEntityId = GetExpectedObjectId(anchorTarget.transform);

// Value should be a structured object with name, type, entityId
JObject valueObj = JObject.FromObject(probeAnchor.value);
Assert.That(valueObj["name"].ToString(), Is.EqualTo("AnchorTarget"));
Assert.That(valueObj["type"].ToString(), Is.EqualTo("Transform"));
Assert.That(valueObj["instanceId"].Value<int>(), Is.EqualTo(anchorTarget.transform.GetInstanceID()));
Assert.That(valueObj["entityId"].ToString(), Is.EqualTo(expectedEntityId));
}
finally
{
Expand Down Expand Up @@ -682,7 +685,19 @@ public async Task ExecuteAsync_ReturnsNoneForUnsetObjectReference()
JObject valueObj = JObject.FromObject(probeAnchor.value);
Assert.That(valueObj["name"].ToString(), Is.EqualTo("None"));
Assert.That(valueObj["type"].ToString(), Is.EqualTo("None"));
Assert.That(valueObj["instanceId"].Value<int>(), Is.EqualTo(0));
Assert.That(valueObj["entityId"].ToString(), Is.EqualTo("0"));
}

private static string GetExpectedObjectId(Object obj)
{
UnityEngine.Debug.Assert(obj != null, "Unity Object must exist before reading its identifier.");

#if UNITY_6000_4_OR_NEWER
return obj.GetEntityId().ToString();
#else
int instanceId = obj.GetInstanceID();
return instanceId.ToString(CultureInfo.InvariantCulture);
#endif
}

[Test]
Expand Down
14 changes: 7 additions & 7 deletions Assets/Tests/Editor/HierarchySerializerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ public void BuildGroups_WithValidNodes_ReturnsCorrectGroups()
// Arrange
List<HierarchyNode> nodes = new()
{
new(1, "Root", null, 0, true, new[] { "Transform" }, "SceneA"),
new(2, "Child", 1, 1, true, new[] { "Transform", "MeshRenderer" }, "SceneA")
new("1", "Root", null, 0, true, new[] { "Transform" }, "SceneA"),
new("2", "Child", "1", 1, true, new[] { "Transform", "MeshRenderer" }, "SceneA")
};

HierarchyContext context = new("editor", "TestScene", 0, 0);
Expand Down Expand Up @@ -77,10 +77,10 @@ public void BuildGroups_CalculatesCorrectMaxDepth()
// Arrange
List<HierarchyNode> nodes = new()
{
new(1, "Root", null, 0, true, new string[0]),
new(2, "Level1", 1, 1, true, new string[0]),
new(3, "Level2", 2, 2, true, new string[0]),
new(4, "Level3", 3, 3, true, new string[0])
new("1", "Root", null, 0, true, new string[0]),
new("2", "Level1", "1", 1, true, new string[0]),
new("3", "Level2", "2", 2, true, new string[0]),
new("4", "Level3", "3", 3, true, new string[0])
};

HierarchyContext context = new("editor", "DeepScene", 0, 0);
Expand All @@ -92,4 +92,4 @@ public void BuildGroups_CalculatesCorrectMaxDepth()
Assert.That(result.Context.maxDepth, Is.EqualTo(3));
}
}
}
}
18 changes: 17 additions & 1 deletion Assets/Tests/Editor/HierarchyServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,22 @@ public void GetHierarchyNodes_WithComponents_IncludesComponentNames()
Assert.That(rootNode.components, Contains.Item("Rigidbody"));
Assert.That(rootNode.components, Contains.Item("Transform"));
}

#if UNITY_6000_4_OR_NEWER
[Test]
public void GetHierarchyNodes_WithEntityId_UsesUnityEntityIdString()
{
// Verifies Unity 6.0.4+ hierarchy IDs use the EntityId public string representation.
HierarchyOptions options = new();
string expectedObjectId = testRoot.GetEntityId().ToString();

List<HierarchyNode> nodes = service.GetHierarchyNodes(options);

HierarchyNode rootNode = nodes.Find(n => n.name == testRoot.name);
Assert.That(rootNode, Is.Not.Null);
Assert.That(rootNode.id, Is.EqualTo(expectedObjectId));
}
#endif

[Test]
public void GetHierarchyNodes_WithRootPathIncludingRootName_ReturnsChild()
Expand Down Expand Up @@ -219,4 +235,4 @@ public void GetHierarchyNodes_WithUseSelectionAndParentChildSelection_FiltersDes
Assert.That(childNode.parent, Is.EqualTo(rootNode.id), "Child should be traversed as descendant of root, not as separate root");
}
}
}
}
Loading
Loading