Skip to content

Commit 8735060

Browse files
authored
Add edit mode tests for AssemblyExtensions, SystemType, and SerializableDictionary (#1122)
* Add new tests * Update AssemblyExtensions.cs * Update CHANGELOG.md
1 parent bd016cc commit 8735060

8 files changed

Lines changed: 284 additions & 1 deletion

File tree

org.mixedrealitytoolkit.core/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
88

99
* Updated code style in `HandsSubsystemDescriptor`, `MRTKSubsystemDescriptor`, `DictationSubsystemDescriptor`, and `XRSubsystemHelpers`. [PR #1109](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1109)
1010
* Updated `PackageValidator` to only be valid if `UNITY_EDITOR` is true. [PR #1125](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1125)
11+
* `AssemblyExtensions.GetLoadableTypes` now throws `ArgumentNullException` when called on a null assembly instead of `NullReferenceException`. [PR #1122](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1122)
12+
13+
### Added
14+
15+
* Added edit mode tests for `AssemblyExtensions`, `SystemType`, and `SerializableDictionary`. [PR #1122](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1122)
1116

1217
### Fixed
1318

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright (c) Mixed Reality Toolkit Contributors
2+
// Licensed under the BSD 3-Clause
3+
4+
using NUnit.Framework;
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using System.Reflection;
9+
10+
namespace MixedReality.Toolkit.Core.Tests.EditMode
11+
{
12+
/// <summary>
13+
/// Unit tests for AssemblyExtensions.
14+
/// These run outside of PlayMode and do not require Unity engine initialization.
15+
/// </summary>
16+
public class AssemblyExtensionsTests
17+
{
18+
[Test]
19+
public void GetLoadableTypes_ReturnsTypes_ForValidAssembly()
20+
{
21+
Assembly currentAssembly = Assembly.GetExecutingAssembly();
22+
23+
IEnumerable<Type> types = currentAssembly.GetLoadableTypes();
24+
25+
Assert.IsNotNull(types, "GetLoadableTypes should never return null.");
26+
Assert.Greater(types.Count(), 0, "GetLoadableTypes should return the types within the executing assembly.");
27+
Assert.IsTrue(types.Contains(typeof(AssemblyExtensionsTests)), "GetLoadableTypes failed to return known loadable types.");
28+
}
29+
30+
[Test]
31+
public void GetLoadableTypes_HandlesNullAssembly_Safely()
32+
{
33+
Assembly nullAssembly = null;
34+
Assert.Throws<ArgumentNullException>(() => nullAssembly.GetLoadableTypes());
35+
}
36+
}
37+
}

org.mixedrealitytoolkit.core/Tests/Editor/AssemblyExtensionsTests.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright (c) Mixed Reality Toolkit Contributors
2+
// Licensed under the BSD 3-Clause
3+
4+
using NUnit.Framework;
5+
using System.Collections.Generic;
6+
using UnityEngine;
7+
8+
namespace MixedReality.Toolkit.Core.Tests.EditMode
9+
{
10+
/// <summary>
11+
/// Unit tests for SerializableDictionary.
12+
/// These run outside of PlayMode and do not require Unity engine initialization.
13+
/// </summary>
14+
public class SerializableDictionaryTests
15+
{
16+
[Test]
17+
public void Serialization_RestoresDictionary_FromInternalEntries()
18+
{
19+
var dict = new SerializableDictionary<string, int>();
20+
dict.Add("Key1", 100);
21+
dict.Add("Key2", 200);
22+
23+
ISerializationCallbackReceiver receiver = dict;
24+
25+
// Simulate Unity preparing to serialize the object (populates the internal 'entries' list)
26+
receiver.OnBeforeSerialize();
27+
28+
// Clear the base dictionary to simulate starting fresh after deserialization
29+
((Dictionary<string, int>)dict).Clear();
30+
Assert.AreEqual(0, dict.Count, "Base dictionary should be empty before deserialization.");
31+
32+
// Simulate Unity finishing deserialization (repopulates the dictionary from 'entries')
33+
receiver.OnAfterDeserialize();
34+
35+
Assert.AreEqual(2, dict.Count, "Dictionary should have restored 2 items.");
36+
Assert.AreEqual(100, dict["Key1"]);
37+
Assert.AreEqual(200, dict["Key2"]);
38+
}
39+
40+
[Test]
41+
public void EditorOverride_Clear_RemovesAllSerializedEntries()
42+
{
43+
var dict = new SerializableDictionary<string, int>();
44+
dict.Add("Key1", 100);
45+
46+
ISerializationCallbackReceiver receiver = dict;
47+
receiver.OnBeforeSerialize(); // Populate internal list
48+
49+
// Call the custom overridden Clear()
50+
dict.Clear();
51+
52+
// Attempt to deserialize (which would normally restore Key1 if the internal list wasn't cleared)
53+
receiver.OnAfterDeserialize();
54+
55+
Assert.AreEqual(0, dict.Count, "Dictionary should remain empty because the internal serialized entries were cleared.");
56+
}
57+
58+
[Test]
59+
public void EditorOverride_Remove_RemovesSpecificSerializedEntry()
60+
{
61+
var dict = new SerializableDictionary<string, int>();
62+
dict.Add("A", 1);
63+
dict.Add("B", 2);
64+
dict.Add("C", 3);
65+
66+
ISerializationCallbackReceiver receiver = dict;
67+
receiver.OnBeforeSerialize();
68+
69+
// Use the overridden remove method which should also remove from the internal list
70+
bool removedB = dict.Remove("B");
71+
bool removedC = dict.Remove("C", out int valC);
72+
73+
// Clear the dictionary and restore from the serialized list to verify they are gone
74+
((Dictionary<string, int>)dict).Clear();
75+
receiver.OnAfterDeserialize();
76+
77+
Assert.IsTrue(removedB, "Remove(key) should return true for existing key.");
78+
Assert.IsTrue(removedC, "Remove(key, out val) should return true for existing key.");
79+
Assert.AreEqual(3, valC, "Remove(key, out val) should output the correct value.");
80+
81+
Assert.AreEqual(1, dict.Count, "Only 1 item should remain after deserialization.");
82+
Assert.IsTrue(dict.ContainsKey("A"), "Key 'A' should have been preserved.");
83+
Assert.IsFalse(dict.ContainsKey("B"), "Key 'B' should have been permanently removed.");
84+
Assert.IsFalse(dict.ContainsKey("C"), "Key 'C' should have been permanently removed.");
85+
}
86+
}
87+
}

org.mixedrealitytoolkit.core/Tests/Editor/SerializableDictionaryTests.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// Copyright (c) Mixed Reality Toolkit Contributors
2+
// Licensed under the BSD 3-Clause
3+
4+
using NUnit.Framework;
5+
using System;
6+
using UnityEngine;
7+
using UnityEngine.TestTools;
8+
9+
namespace MixedReality.Toolkit.Core.Tests.EditMode
10+
{
11+
/// <summary>
12+
/// Unit tests for SystemType.
13+
/// These run outside of PlayMode and do not require Unity engine initialization.
14+
/// </summary>
15+
public class SystemTypeTests
16+
{
17+
[Test]
18+
public void GetReference_FromType_ReturnsValidString()
19+
{
20+
string reference = SystemType.GetReference(typeof(Vector3));
21+
22+
Assert.IsFalse(string.IsNullOrEmpty(reference));
23+
Assert.IsTrue(reference.Contains("UnityEngine.Vector3"));
24+
Assert.IsTrue(reference.Contains("UnityEngine.CoreModule"));
25+
}
26+
27+
[Test]
28+
public void GetReference_NullOrEmpty_ReturnsEmptyString()
29+
{
30+
Assert.AreEqual(string.Empty, SystemType.GetReference((Type)null));
31+
Assert.AreEqual(string.Empty, SystemType.GetReference((string)null));
32+
Assert.AreEqual(string.Empty, SystemType.GetReference(string.Empty));
33+
}
34+
35+
[Test]
36+
public void Constructors_SetPropertiesCorrectly()
37+
{
38+
Type targetType = typeof(int);
39+
40+
// Initialize from Type
41+
SystemType fromType = new SystemType(targetType);
42+
Assert.AreEqual(targetType, fromType.Type);
43+
Assert.AreEqual(SystemType.GetReference(targetType), (string)fromType);
44+
45+
// Initialize from AssemblyQualifiedName
46+
SystemType fromString = new SystemType(targetType.AssemblyQualifiedName);
47+
Assert.AreEqual(targetType, fromString.Type);
48+
}
49+
50+
[Test]
51+
public void Constructor_AbstractType_SetsTypeToNull()
52+
{
53+
// SystemType is intentionally designed to nullify abstract types when initialized via string
54+
SystemType fromString = new SystemType(typeof(Array).AssemblyQualifiedName);
55+
Assert.IsNull(fromString.Type, "SystemType should not allow abstract types when initialized from an assembly string.");
56+
}
57+
58+
[Test]
59+
public void InvalidTypeAssignment_LogsError_ButSetsType()
60+
{
61+
SystemType sysType = new SystemType(typeof(int));
62+
63+
// Enums violate the ValidConstraint. SystemType logs an error but still completes the assignment.
64+
LogAssert.Expect(LogType.Error, $"'{typeof(DayOfWeek).FullName}' is not a valid class or struct type.");
65+
66+
sysType.Type = typeof(DayOfWeek);
67+
68+
Assert.AreEqual(typeof(DayOfWeek), sysType.Type);
69+
}
70+
71+
[Test]
72+
public void ImplicitConversions_WorkCorrectly()
73+
{
74+
Type originalType = typeof(string);
75+
76+
// Type -> SystemType
77+
SystemType sysType = originalType;
78+
Assert.IsNotNull(sysType);
79+
80+
// SystemType -> Type
81+
Type convertedType = sysType;
82+
Assert.AreEqual(originalType, convertedType);
83+
84+
// SystemType -> string
85+
string reference = sysType;
86+
Assert.AreEqual(SystemType.GetReference(originalType), reference);
87+
}
88+
89+
[Test]
90+
public void Equality_MatchesSameTypes()
91+
{
92+
SystemType type1 = new SystemType(typeof(float));
93+
SystemType type2 = new SystemType(typeof(float));
94+
SystemType type3 = new SystemType(typeof(double));
95+
96+
Assert.IsTrue(type1.Equals(type2));
97+
Assert.IsFalse(type1.Equals(type3));
98+
Assert.IsFalse(type1.Equals(null));
99+
100+
// HashCodes should match for identical references
101+
Assert.AreEqual(type1.GetHashCode(), type2.GetHashCode());
102+
}
103+
104+
[Test]
105+
public void Serialization_RestoresType()
106+
{
107+
var sysType = new SystemType(typeof(int));
108+
ISerializationCallbackReceiver receiver = sysType;
109+
110+
// SystemType uses OnAfterDeserialize to re-establish the `type` mapping from the string `reference`
111+
receiver.OnAfterDeserialize();
112+
113+
Assert.AreEqual(typeof(int), sysType.Type);
114+
}
115+
}
116+
}

org.mixedrealitytoolkit.core/Tests/Editor/SystemTypeTests.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

org.mixedrealitytoolkit.core/Utilities/Extensions/AssemblyExtensions.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,15 @@ namespace MixedReality.Toolkit
1414
public static class AssemblyExtensions
1515
{
1616
/// <summary>
17-
/// Assembly.GetTypes() can throw in some cases. This extension will catch that exception and return only the types which were successfully loaded from the assembly.
17+
/// Assembly.GetTypes() can throw in some cases. This extension will catch that exception and return only the types which were successfully loaded from the assembly.
1818
/// </summary>
1919
public static IEnumerable<Type> GetLoadableTypes(this Assembly @this)
2020
{
21+
if (@this == null)
22+
{
23+
throw new ArgumentNullException(nameof(@this), "Assembly cannot be null.");
24+
}
25+
2126
try
2227
{
2328
return @this.GetTypes();

0 commit comments

Comments
 (0)