Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:19:28 +08:00
commit 1da7b24c8e
254 changed files with 43797 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2c29ec05dc5459f4b915f5753f44a028
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,297 @@
/**
* Unity Test Framework Tests for GameObject Caching
*
* Performance optimization testing for BaseHandler's WeakReference caching.
*/
using NUnit.Framework;
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using UnityEditorToolkit.Handlers;
namespace UnityEditorToolkit.Tests
{
public class GameObjectCachingTests
{
private GameObject testGameObject;
private GameObjectHandler handler;
[SetUp]
public void Setup()
{
handler = new GameObjectHandler();
testGameObject = new GameObject("TestCacheObject");
}
[TearDown]
public void Teardown()
{
// Clean up all test GameObjects
var allTestObjects = Object.FindObjectsOfType<GameObject>()
.Where(go => go.name.StartsWith("TestCache") || go.name.StartsWith("CacheTest"));
foreach (var obj in allTestObjects)
{
Object.DestroyImmediate(obj);
}
testGameObject = null;
handler = null;
}
[Test]
public void FindGameObject_Should_FindExistingObject()
{
// Arrange
var expectedName = "TestCacheObject";
// Act
var result = handler.FindGameObject(expectedName);
// Assert
Assert.IsNotNull(result, "Should find existing GameObject");
Assert.AreEqual(expectedName, result.name);
}
[Test]
public void FindGameObject_Should_ReturnNull_WhenNotFound()
{
// Arrange
var nonExistentName = "NonExistent_GameObject_12345";
// Act
var result = handler.FindGameObject(nonExistentName);
// Assert
Assert.IsNull(result, "Should return null for non-existent GameObject");
}
[Test]
public void FindGameObject_Should_UseCaching_OnSecondCall()
{
// Arrange
var objectName = "TestCacheObject";
// Act: First call (cache miss)
var start1 = System.DateTime.Now;
var result1 = handler.FindGameObject(objectName);
var time1 = (System.DateTime.Now - start1).TotalMilliseconds;
// Second call (cache hit)
var start2 = System.DateTime.Now;
var result2 = handler.FindGameObject(objectName);
var time2 = (System.DateTime.Now - start2).TotalMilliseconds;
// Assert
Assert.AreEqual(result1, result2, "Should return same GameObject instance");
Assert.Less(time2, time1 * 0.5, "Second call should be significantly faster (cache hit)");
}
[Test]
public void FindGameObject_Should_HandleMultipleObjects()
{
// Arrange
var objects = new List<GameObject>();
for (int i = 0; i < 5; i++)
{
objects.Add(new GameObject($"CacheTest_{i}"));
}
// Act: Find all objects
var results = new List<GameObject>();
foreach (var obj in objects)
{
results.Add(handler.FindGameObject(obj.name));
}
// Assert
Assert.AreEqual(5, results.Count, "Should find all 5 objects");
for (int i = 0; i < 5; i++)
{
Assert.IsNotNull(results[i], $"Object {i} should be found");
Assert.AreEqual(objects[i], results[i], $"Object {i} should match");
}
// Cleanup
foreach (var obj in objects)
{
Object.DestroyImmediate(obj);
}
}
[Test]
public void Cache_Should_Invalidate_WhenGameObjectDestroyed()
{
// Arrange
var objectName = "TestCacheObject";
handler.FindGameObject(objectName); // Cache it
// Act: Destroy GameObject
Object.DestroyImmediate(testGameObject);
testGameObject = null;
// Create new GameObject with same name
testGameObject = new GameObject(objectName);
var result = handler.FindGameObject(objectName);
// Assert
Assert.IsNotNull(result, "Should find newly created GameObject");
Assert.AreEqual(objectName, result.name);
}
[Test]
public void Cache_Should_HandleInactiveGameObjects()
{
// Arrange
testGameObject.SetActive(false);
var objectName = testGameObject.name;
// Act
var result = handler.FindGameObject(objectName);
// Assert
Assert.IsNotNull(result, "Should find inactive GameObject");
Assert.AreEqual(objectName, result.name);
Assert.IsFalse(result.activeSelf, "GameObject should be inactive");
}
[Test]
public void Cache_Should_HandleNestedGameObjects()
{
// Arrange
var parent = new GameObject("CacheTest_Parent");
var child = new GameObject("CacheTest_Child");
child.transform.SetParent(parent.transform);
// Act
var foundParent = handler.FindGameObject("CacheTest_Parent");
var foundChild = handler.FindGameObject("CacheTest_Child");
// Assert
Assert.IsNotNull(foundParent, "Should find parent");
Assert.IsNotNull(foundChild, "Should find child");
Assert.AreEqual(parent, foundParent);
Assert.AreEqual(child, foundChild);
// Cleanup
Object.DestroyImmediate(parent);
}
[Test]
public void Cache_Should_HandleDuplicateNames()
{
// Arrange
var obj1 = new GameObject("CacheTest_Duplicate");
var obj2 = new GameObject("CacheTest_Duplicate");
// Act
var result = handler.FindGameObject("CacheTest_Duplicate");
// Assert
Assert.IsNotNull(result, "Should find one of the duplicate objects");
Assert.AreEqual("CacheTest_Duplicate", result.name);
// Cleanup
Object.DestroyImmediate(obj1);
Object.DestroyImmediate(obj2);
}
[Test]
public void FindGameObject_Should_HandleEmptyString()
{
// Arrange
var emptyName = "";
// Act
var result = handler.FindGameObject(emptyName);
// Assert
Assert.IsNull(result, "Should return null for empty name");
}
[Test]
public void FindGameObject_Should_HandleNullString()
{
// Arrange
string nullName = null;
// Act
var result = handler.FindGameObject(nullName);
// Assert
Assert.IsNull(result, "Should return null for null name");
}
[Test]
public void Cache_Should_WorkAcrossMultipleCalls()
{
// Arrange
var obj1 = new GameObject("CacheTest_Multi_1");
var obj2 = new GameObject("CacheTest_Multi_2");
var obj3 = new GameObject("CacheTest_Multi_3");
// Act: Interleave calls to different objects
var result1a = handler.FindGameObject("CacheTest_Multi_1");
var result2a = handler.FindGameObject("CacheTest_Multi_2");
var result1b = handler.FindGameObject("CacheTest_Multi_1"); // From cache
var result3a = handler.FindGameObject("CacheTest_Multi_3");
var result2b = handler.FindGameObject("CacheTest_Multi_2"); // From cache
// Assert
Assert.AreEqual(obj1, result1a);
Assert.AreEqual(obj1, result1b);
Assert.AreEqual(obj2, result2a);
Assert.AreEqual(obj2, result2b);
Assert.AreEqual(obj3, result3a);
// Cleanup
Object.DestroyImmediate(obj1);
Object.DestroyImmediate(obj2);
Object.DestroyImmediate(obj3);
}
[Test]
public void Cache_Should_HandleLargeNumberOfObjects()
{
// Arrange: Create 100 GameObjects
var objects = new List<GameObject>();
for (int i = 0; i < 100; i++)
{
objects.Add(new GameObject($"CacheTest_Large_{i}"));
}
// Act: Find all objects (should populate cache)
var results = new List<GameObject>();
foreach (var obj in objects)
{
results.Add(handler.FindGameObject(obj.name));
}
// Assert: All objects found
Assert.AreEqual(100, results.Count);
for (int i = 0; i < 100; i++)
{
Assert.IsNotNull(results[i], $"Object {i} should be found");
}
// Act: Find all again (should use cache)
var cachedResults = new List<GameObject>();
var start = System.DateTime.Now;
foreach (var obj in objects)
{
cachedResults.Add(handler.FindGameObject(obj.name));
}
var cacheTime = (System.DateTime.Now - start).TotalMilliseconds;
// Assert: Cached access should be fast
Assert.Less(cacheTime, 50, "Cached access for 100 objects should be < 50ms");
// Cleanup
foreach (var obj in objects)
{
Object.DestroyImmediate(obj);
}
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c5412e9c9d2c86740a38bd913dd0efca

View File

@@ -0,0 +1,363 @@
/**
* Unity Test Framework Tests for JSON-RPC Protocol
*
* Protocol compliance and serialization testing.
*/
using NUnit.Framework;
using Newtonsoft.Json.Linq;
namespace UnityEditorToolkit.Tests
{
public class JsonRpcProtocolTests
{
[Test]
public void JsonRpcRequest_Should_SerializeCorrectly()
{
// Arrange
var request = new JsonRpcRequest
{
JsonRpc = "2.0",
Id = "test_123",
Method = "GameObject.Find",
Params = JToken.FromObject(new { name = "Player" })
};
// Act
var json = request.ToJson();
// Assert
Assert.IsNotEmpty(json);
Assert.IsTrue(json.Contains("\"jsonrpc\":\"2.0\""));
Assert.IsTrue(json.Contains("\"id\":\"test_123\""));
Assert.IsTrue(json.Contains("\"method\":\"GameObject.Find\""));
}
[Test]
public void JsonRpcRequest_Should_DeserializeCorrectly()
{
// Arrange
var json = @"{
""jsonrpc"": ""2.0"",
""id"": ""req_456"",
""method"": ""Transform.SetPosition"",
""params"": { ""name"": ""Cube"", ""position"": { ""x"": 1, ""y"": 2, ""z"": 3 } }
}";
// Act
var request = JsonRpcRequest.FromJson(json);
// Assert
Assert.IsNotNull(request);
Assert.AreEqual("2.0", request.JsonRpc);
Assert.AreEqual("req_456", request.Id);
Assert.AreEqual("Transform.SetPosition", request.Method);
Assert.IsNotNull(request.Params);
}
[Test]
public void JsonRpcResponse_Should_SerializeSuccessResponse()
{
// Arrange
var response = new JsonRpcResponse("req_789", new
{
success = true,
name = "Player",
active = true
});
// Act
var json = response.ToJson();
// Assert
Assert.IsNotEmpty(json);
Assert.IsTrue(json.Contains("\"jsonrpc\":\"2.0\""));
Assert.IsTrue(json.Contains("\"id\":\"req_789\""));
Assert.IsTrue(json.Contains("\"result\""));
Assert.IsFalse(json.Contains("\"error\""));
}
[Test]
public void JsonRpcErrorResponse_Should_SerializeErrorResponse()
{
// Arrange
var error = JsonRpcError.InternalError("Test error message");
var response = new JsonRpcErrorResponse("req_error", error);
// Act
var json = response.ToJson();
// Assert
Assert.IsNotEmpty(json);
Assert.IsTrue(json.Contains("\"jsonrpc\":\"2.0\""));
Assert.IsTrue(json.Contains("\"id\":\"req_error\""));
Assert.IsTrue(json.Contains("\"error\""));
Assert.IsTrue(json.Contains("\"code\":-32603"));
Assert.IsTrue(json.Contains("Test error message"));
}
[Test]
public void JsonRpcErrorResponse_Should_PreserveRequestId()
{
// Arrange
var requestId = "important_request_123";
var error = JsonRpcError.MethodNotFound("GameObject.InvalidMethod");
// Act
var response = new JsonRpcErrorResponse(requestId, error);
var json = response.ToJson();
// Assert
Assert.IsTrue(json.Contains($"\"id\":\"{requestId}\""));
}
[Test]
public void JsonRpcErrorResponse_Should_HandleNullRequestId()
{
// Arrange
var error = JsonRpcError.ParseError();
// Act
var response = new JsonRpcErrorResponse(null, error);
var json = response.ToJson();
// Assert
Assert.IsTrue(json.Contains("\"id\":null"));
}
[Test]
public void JsonRpcError_InternalError_Should_HaveCorrectCode()
{
// Arrange & Act
var error = JsonRpcError.InternalError("Test message");
// Assert
Assert.AreEqual(-32603, error.Code);
Assert.AreEqual("Internal error", error.Message);
Assert.IsNotNull(error.Data);
}
[Test]
public void JsonRpcError_MethodNotFound_Should_HaveCorrectCode()
{
// Arrange & Act
var error = JsonRpcError.MethodNotFound("Unknown.Method");
// Assert
Assert.AreEqual(-32601, error.Code);
Assert.AreEqual("Method not found", error.Message);
}
[Test]
public void JsonRpcError_InvalidParams_Should_HaveCorrectCode()
{
// Arrange & Act
var error = JsonRpcError.InvalidParams("Missing 'name' parameter");
// Assert
Assert.AreEqual(-32602, error.Code);
Assert.AreEqual("Invalid params", error.Message);
}
[Test]
public void JsonRpcError_ParseError_Should_HaveCorrectCode()
{
// Arrange & Act
var error = JsonRpcError.ParseError();
// Assert
Assert.AreEqual(-32700, error.Code);
Assert.AreEqual("Parse error", error.Message);
}
[Test]
public void JsonRpcError_InvalidRequest_Should_HaveCorrectCode()
{
// Arrange & Act
var error = JsonRpcError.InvalidRequest();
// Assert
Assert.AreEqual(-32600, error.Code);
Assert.AreEqual("Invalid Request", error.Message);
}
[Test]
public void GetParams_Should_DeserializeCorrectly()
{
// Arrange
var json = @"{
""jsonrpc"": ""2.0"",
""id"": 1,
""method"": ""GameObject.Find"",
""params"": { ""name"": ""TestObject"" }
}";
var request = JsonRpcRequest.FromJson(json);
// Act
var param = request.GetParams<FindParams>();
// Assert
Assert.IsNotNull(param);
Assert.AreEqual("TestObject", param.name);
}
[Test]
public void GetParams_Should_HandleUnknownFields()
{
// Arrange
var json = @"{
""jsonrpc"": ""2.0"",
""id"": 1,
""method"": ""GameObject.Find"",
""params"": { ""wrongField"": ""value"" }
}";
var request = JsonRpcRequest.FromJson(json);
// Act
var param = request.GetParams<FindParams>();
// Assert: JsonConvert ignores unknown fields, so param is not null but name is null
Assert.IsNotNull(param);
Assert.IsNull(param.name);
}
[Test]
public void GetParams_Should_ReturnNullForNullParams()
{
// Arrange
var request = new JsonRpcRequest
{
JsonRpc = "2.0",
Id = 1,
Method = "ping",
Params = null
};
// Act
var param = request.GetParams<FindParams>();
// Assert
Assert.IsNull(param);
}
[Test]
public void Request_Should_HandleComplexParameters()
{
// Arrange
var json = @"{
""jsonrpc"": ""2.0"",
""id"": ""complex_1"",
""method"": ""Transform.SetPosition"",
""params"": {
""name"": ""Player"",
""position"": { ""x"": 1.5, ""y"": 2.7, ""z"": -3.9 }
}
}";
// Act
var request = JsonRpcRequest.FromJson(json);
var param = request.GetParams<SetPositionParams>();
// Assert
Assert.IsNotNull(param);
Assert.AreEqual("Player", param.name);
Assert.IsNotNull(param.position);
Assert.AreEqual(1.5f, param.position.x, 0.0001f);
Assert.AreEqual(2.7f, param.position.y, 0.0001f);
Assert.AreEqual(-3.9f, param.position.z, 0.0001f);
}
[Test]
public void Request_Should_HandleNumericId()
{
// Arrange
var request = new JsonRpcRequest
{
JsonRpc = "2.0",
Id = 42,
Method = "GameObject.Find"
};
// Act
var json = request.ToJson();
// Assert
Assert.IsTrue(json.Contains("\"id\":42"));
}
[Test]
public void Request_Should_HandleStringId()
{
// Arrange
var request = new JsonRpcRequest
{
JsonRpc = "2.0",
Id = "string_id_123",
Method = "GameObject.Find"
};
// Act
var json = request.ToJson();
// Assert
Assert.IsTrue(json.Contains("\"id\":\"string_id_123\""));
}
[Test]
public void Response_Should_MatchRequestId()
{
// Arrange
var requestId = "match_test_789";
var result = new { success = true };
// Act
var response = new JsonRpcResponse(requestId, result);
var json = response.ToJson();
// Assert
Assert.IsTrue(json.Contains($"\"id\":\"{requestId}\""));
}
[Test]
public void Protocol_Should_Comply_WithJsonRpc20Spec()
{
// Arrange
var request = new JsonRpcRequest
{
JsonRpc = "2.0",
Id = "spec_test",
Method = "GameObject.Find",
Params = JToken.FromObject(new { name = "Test" })
};
// Act
var json = request.ToJson();
var parsed = JObject.Parse(json);
// Assert: JSON-RPC 2.0 required fields
Assert.IsTrue(parsed.ContainsKey("jsonrpc"));
Assert.IsTrue(parsed.ContainsKey("method"));
Assert.IsTrue(parsed.ContainsKey("id"));
Assert.AreEqual("2.0", parsed["jsonrpc"].ToString());
}
// Helper classes for testing
private class FindParams
{
public string name { get; set; }
}
private class SetPositionParams
{
public string name { get; set; }
public Vector3Param position { get; set; }
}
private class Vector3Param
{
public float x { get; set; }
public float y { get; set; }
public float z { get; set; }
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c7d883732abd0f24c86ce9c7e3a7480d

View File

@@ -0,0 +1,26 @@
{
"name": "UnityEditorToolkit.Editor.Tests",
"rootNamespace": "UnityEditorToolkit.Tests",
"references": [
"UnityEditorToolkit",
"UnityEditorToolkit.Editor",
"Unity.Nuget.Newtonsoft-Json",
"UnityEngine.TestRunner",
"UnityEditor.TestRunner"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 085f6286143df3442b7938a709f17559
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,251 @@
/**
* Unity Test Framework Tests for UnityMainThreadDispatcher
*
* Critical thread safety component testing.
*/
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using System.Collections;
using System.Threading;
using UnityEditorToolkit.Utils;
namespace UnityEditorToolkit.Tests
{
public class UnityMainThreadDispatcherTests
{
private UnityMainThreadDispatcher dispatcher;
[SetUp]
public void Setup()
{
// Ensure fresh dispatcher instance for each test
var existingDispatcher = Object.FindObjectOfType<UnityMainThreadDispatcher>();
if (existingDispatcher != null)
{
Object.DestroyImmediate(existingDispatcher.gameObject);
}
dispatcher = UnityMainThreadDispatcher.Instance();
}
[TearDown]
public void Teardown()
{
if (dispatcher != null && dispatcher.gameObject != null)
{
Object.DestroyImmediate(dispatcher.gameObject);
}
}
[Test]
public void Instance_Should_CreateSingleton()
{
// Arrange & Act
var instance1 = UnityMainThreadDispatcher.Instance();
var instance2 = UnityMainThreadDispatcher.Instance();
// Assert
Assert.IsNotNull(instance1);
Assert.IsNotNull(instance2);
Assert.AreEqual(instance1, instance2, "Instance should be singleton");
}
[Test]
public void Instance_Should_CreateGameObjectWithCorrectName()
{
// Arrange & Act
var instance = UnityMainThreadDispatcher.Instance();
// Assert
Assert.AreEqual("UnityMainThreadDispatcher", instance.gameObject.name);
}
[UnityTest]
public IEnumerator Enqueue_Should_ExecuteAction_OnMainThread()
{
// Arrange
bool actionExecuted = false;
int executionThreadId = 0;
int mainThreadId = Thread.CurrentThread.ManagedThreadId;
// Act: Enqueue from background thread
var backgroundThread = new Thread(() =>
{
dispatcher.Enqueue(() =>
{
executionThreadId = Thread.CurrentThread.ManagedThreadId;
actionExecuted = true;
});
});
backgroundThread.Start();
backgroundThread.Join(); // Wait for thread to complete
// Wait one frame for Update to process queue
yield return null;
// Assert
Assert.IsTrue(actionExecuted, "Action should have been executed");
Assert.AreEqual(mainThreadId, executionThreadId, "Action should execute on main thread");
}
[UnityTest]
public IEnumerator Enqueue_Should_ExecuteMultipleActions_InOrder()
{
// Arrange
var executionOrder = new System.Collections.Generic.List<int>();
// Act
for (int i = 0; i < 10; i++)
{
int index = i; // Capture loop variable
dispatcher.Enqueue(() => executionOrder.Add(index));
}
// Wait one frame for Update to process queue
yield return null;
// Assert
Assert.AreEqual(10, executionOrder.Count, "All actions should be executed");
for (int i = 0; i < 10; i++)
{
Assert.AreEqual(i, executionOrder[i], $"Action {i} should execute in order");
}
}
[UnityTest]
public IEnumerator Enqueue_Should_HandleExceptions_Gracefully()
{
// Arrange
bool firstActionExecuted = false;
bool secondActionExecuted = false;
bool thirdActionExecuted = false;
// Act: First action executes, second throws exception, third should still execute
dispatcher.Enqueue(() => firstActionExecuted = true);
dispatcher.Enqueue(() => throw new System.Exception("Test exception"));
dispatcher.Enqueue(() => thirdActionExecuted = true);
// Expect error log from exception
LogAssert.Expect(LogType.Error, new System.Text.RegularExpressions.Regex(".*Test exception.*"));
// Wait one frame for Update to process queue
yield return null;
// Assert
Assert.IsTrue(firstActionExecuted, "First action should execute");
Assert.IsTrue(thirdActionExecuted, "Third action should execute despite exception in second");
}
[Test]
public void Enqueue_Should_ThrowException_WhenActionIsNull()
{
// Arrange & Act & Assert
Assert.Throws<System.ArgumentNullException>(() =>
{
dispatcher.Enqueue(null);
});
}
[UnityTest]
public IEnumerator Enqueue_Should_HandleConcurrentAccess()
{
// Arrange
int executionCount = 0;
var threads = new Thread[5];
// Act: Multiple threads enqueueing simultaneously
for (int i = 0; i < 5; i++)
{
threads[i] = new Thread(() =>
{
for (int j = 0; j < 10; j++)
{
dispatcher.Enqueue(() => Interlocked.Increment(ref executionCount));
}
});
threads[i].Start();
}
// Wait for all threads to complete
for (int i = 0; i < 5; i++)
{
threads[i].Join();
}
// Wait one frame for Update to process queue
yield return null;
// Assert
Assert.AreEqual(50, executionCount, "All 50 actions should execute (5 threads × 10 actions)");
}
[UnityTest]
public IEnumerator Update_Should_ClearQueue_AfterExecution()
{
// Arrange
int executionCount = 0;
dispatcher.Enqueue(() => executionCount++);
// Act: Wait for first Update
yield return null;
Assert.AreEqual(1, executionCount);
// Wait for second Update (queue should be empty)
yield return null;
// Assert: No additional executions
Assert.AreEqual(1, executionCount, "Queue should be cleared after execution");
}
[UnityTest]
public IEnumerator Dispatcher_Should_SurviveSceneLoad()
{
// Arrange
var instance = UnityMainThreadDispatcher.Instance();
bool actionExecuted = false;
// Act: Enqueue action
dispatcher.Enqueue(() => actionExecuted = true);
// Wait one frame
yield return null;
// Assert: Dispatcher still exists and works
Assert.IsNotNull(Object.FindObjectOfType<UnityMainThreadDispatcher>());
Assert.IsTrue(actionExecuted);
}
[UnityTest]
public IEnumerator Enqueue_Should_WorkWith_UnityAPIcalls()
{
// Arrange
GameObject testObject = null;
string objectName = "TestObject_FromBackgroundThread";
// Act: Create GameObject from background thread via dispatcher
var thread = new Thread(() =>
{
dispatcher.Enqueue(() =>
{
testObject = new GameObject(objectName);
});
});
thread.Start();
thread.Join();
// Wait for Update to process
yield return null;
// Assert
Assert.IsNotNull(testObject);
Assert.AreEqual(objectName, testObject.name);
// Cleanup
Object.DestroyImmediate(testObject);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 2404c3c8ae4d1804b9344e65740fa83f

View File

@@ -0,0 +1,336 @@
/**
* Unity Test Framework Tests for Vector3 Validation
*
* Security testing for Vector3Data's NaN/Infinity validation.
*/
using NUnit.Framework;
using UnityEngine;
namespace UnityEditorToolkit.Tests
{
public class Vector3ValidationTests
{
[Test]
public void ToVector3_Should_Throw_On_NaN_X()
{
// Arrange
var data = new TransformHandler.Vector3Data
{
x = float.NaN,
y = 0f,
z = 0f
};
// Act & Assert
var exception = Assert.Throws<System.ArgumentException>(() => data.ToVector3());
Assert.IsTrue(exception.Message.Contains("x"), "Error message should mention x coordinate");
}
[Test]
public void ToVector3_Should_Throw_On_NaN_Y()
{
// Arrange
var data = new TransformHandler.Vector3Data
{
x = 0f,
y = float.NaN,
z = 0f
};
// Act & Assert
var exception = Assert.Throws<System.ArgumentException>(() => data.ToVector3());
Assert.IsTrue(exception.Message.Contains("y"), "Error message should mention y coordinate");
}
[Test]
public void ToVector3_Should_Throw_On_NaN_Z()
{
// Arrange
var data = new TransformHandler.Vector3Data
{
x = 0f,
y = 0f,
z = float.NaN
};
// Act & Assert
var exception = Assert.Throws<System.ArgumentException>(() => data.ToVector3());
Assert.IsTrue(exception.Message.Contains("z"), "Error message should mention z coordinate");
}
[Test]
public void ToVector3_Should_Throw_On_PositiveInfinity_X()
{
// Arrange
var data = new TransformHandler.Vector3Data
{
x = float.PositiveInfinity,
y = 0f,
z = 0f
};
// Act & Assert
var exception = Assert.Throws<System.ArgumentException>(() => data.ToVector3());
Assert.IsTrue(exception.Message.Contains("x"), "Error message should mention x coordinate");
}
[Test]
public void ToVector3_Should_Throw_On_PositiveInfinity_Y()
{
// Arrange
var data = new TransformHandler.Vector3Data
{
x = 0f,
y = float.PositiveInfinity,
z = 0f
};
// Act & Assert
var exception = Assert.Throws<System.ArgumentException>(() => data.ToVector3());
Assert.IsTrue(exception.Message.Contains("y"), "Error message should mention y coordinate");
}
[Test]
public void ToVector3_Should_Throw_On_PositiveInfinity_Z()
{
// Arrange
var data = new TransformHandler.Vector3Data
{
x = 0f,
y = 0f,
z = float.PositiveInfinity
};
// Act & Assert
var exception = Assert.Throws<System.ArgumentException>(() => data.ToVector3());
Assert.IsTrue(exception.Message.Contains("z"), "Error message should mention z coordinate");
}
[Test]
public void ToVector3_Should_Throw_On_NegativeInfinity_X()
{
// Arrange
var data = new TransformHandler.Vector3Data
{
x = float.NegativeInfinity,
y = 0f,
z = 0f
};
// Act & Assert
var exception = Assert.Throws<System.ArgumentException>(() => data.ToVector3());
Assert.IsTrue(exception.Message.Contains("x"), "Error message should mention x coordinate");
}
[Test]
public void ToVector3_Should_Throw_On_NegativeInfinity_Y()
{
// Arrange
var data = new TransformHandler.Vector3Data
{
x = 0f,
y = float.NegativeInfinity,
z = 0f
};
// Act & Assert
var exception = Assert.Throws<System.ArgumentException>(() => data.ToVector3());
Assert.IsTrue(exception.Message.Contains("y"), "Error message should mention y coordinate");
}
[Test]
public void ToVector3_Should_Throw_On_NegativeInfinity_Z()
{
// Arrange
var data = new TransformHandler.Vector3Data
{
x = 0f,
y = 0f,
z = float.NegativeInfinity
};
// Act & Assert
var exception = Assert.Throws<System.ArgumentException>(() => data.ToVector3());
Assert.IsTrue(exception.Message.Contains("z"), "Error message should mention z coordinate");
}
[Test]
public void ToVector3_Should_Accept_Zero()
{
// Arrange
var data = new TransformHandler.Vector3Data
{
x = 0f,
y = 0f,
z = 0f
};
// Act
var result = data.ToVector3();
// Assert
Assert.AreEqual(Vector3.zero, result);
}
[Test]
public void ToVector3_Should_Accept_PositiveValues()
{
// Arrange
var data = new TransformHandler.Vector3Data
{
x = 1.5f,
y = 2.7f,
z = 3.9f
};
// Act
var result = data.ToVector3();
// Assert
Assert.AreEqual(1.5f, result.x, 0.0001f);
Assert.AreEqual(2.7f, result.y, 0.0001f);
Assert.AreEqual(3.9f, result.z, 0.0001f);
}
[Test]
public void ToVector3_Should_Accept_NegativeValues()
{
// Arrange
var data = new TransformHandler.Vector3Data
{
x = -1.5f,
y = -2.7f,
z = -3.9f
};
// Act
var result = data.ToVector3();
// Assert
Assert.AreEqual(-1.5f, result.x, 0.0001f);
Assert.AreEqual(-2.7f, result.y, 0.0001f);
Assert.AreEqual(-3.9f, result.z, 0.0001f);
}
[Test]
public void ToVector3_Should_Accept_MixedValues()
{
// Arrange
var data = new TransformHandler.Vector3Data
{
x = -5.5f,
y = 0f,
z = 10.25f
};
// Act
var result = data.ToVector3();
// Assert
Assert.AreEqual(-5.5f, result.x, 0.0001f);
Assert.AreEqual(0f, result.y, 0.0001f);
Assert.AreEqual(10.25f, result.z, 0.0001f);
}
[Test]
public void ToVector3_Should_Accept_VerySmallValues()
{
// Arrange
var data = new TransformHandler.Vector3Data
{
x = 0.0001f,
y = -0.0001f,
z = 0.00001f
};
// Act
var result = data.ToVector3();
// Assert
Assert.AreEqual(0.0001f, result.x, 0.000001f);
Assert.AreEqual(-0.0001f, result.y, 0.000001f);
Assert.AreEqual(0.00001f, result.z, 0.000001f);
}
[Test]
public void ToVector3_Should_Accept_VeryLargeValues()
{
// Arrange
var data = new TransformHandler.Vector3Data
{
x = 1000000f,
y = -1000000f,
z = 999999.99f
};
// Act
var result = data.ToVector3();
// Assert
Assert.AreEqual(1000000f, result.x, 0.01f);
Assert.AreEqual(-1000000f, result.y, 0.01f);
Assert.AreEqual(999999.99f, result.z, 0.01f);
}
[Test]
public void ToVector3_Should_Accept_MaxValue()
{
// Arrange
var data = new TransformHandler.Vector3Data
{
x = float.MaxValue,
y = float.MaxValue,
z = float.MaxValue
};
// Act
var result = data.ToVector3();
// Assert
Assert.AreEqual(float.MaxValue, result.x);
Assert.AreEqual(float.MaxValue, result.y);
Assert.AreEqual(float.MaxValue, result.z);
}
[Test]
public void ToVector3_Should_Accept_MinValue()
{
// Arrange
var data = new TransformHandler.Vector3Data
{
x = float.MinValue,
y = float.MinValue,
z = float.MinValue
};
// Act
var result = data.ToVector3();
// Assert
Assert.AreEqual(float.MinValue, result.x);
Assert.AreEqual(float.MinValue, result.y);
Assert.AreEqual(float.MinValue, result.z);
}
[Test]
public void ToVector3_Should_PreserveFloatPrecision()
{
// Arrange
var data = new TransformHandler.Vector3Data
{
x = 1.23456789f,
y = -9.87654321f,
z = 0.11111111f
};
// Act
var result = data.ToVector3();
// Assert: Float precision is ~7 significant digits
Assert.AreEqual(1.23456789f, result.x, 0.0000001f);
Assert.AreEqual(-9.87654321f, result.y, 0.0000001f);
Assert.AreEqual(0.11111111f, result.z, 0.0000001f);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 2c991ed74ecfc7749872d96a419169bb

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3e180254c3076cb45a6a5876c015ef54
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,23 @@
{
"name": "UnityEditorToolkit.Tests",
"rootNamespace": "UnityEditorToolkit.Tests",
"references": [
"UnityEditorToolkit",
"Unity.Nuget.Newtonsoft-Json",
"UnityEngine.TestRunner",
"UnityEditor.TestRunner"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e3eb7ed55a5e07a4ebec896b5c390f6d
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: