using NUnit.Framework; using System; using System.Collections; using System.Linq; using UnityEngine; using UnityEngine.Pool; namespace UnityEditor.Rendering.Tests { class AnalyticsUtilsTests { private TestScriptableObject m_SO; private TestObject m_Current; [SetUp] public void SetUp() { m_Current = new TestObject(); m_SO = ScriptableObject.CreateInstance(typeof(TestScriptableObject)) as TestScriptableObject; } [TearDown] public void TearDown() { ScriptableObject.DestroyImmediate(m_SO); } [Serializable] public class TestObject { public int integer; public float floatNumber; public double doubleNumber; public bool boolean; public string test; public int[] array; public enum TestDiffEnum { a, b, c } public TestDiffEnum diffEnum; [Serializable] public struct InnerTestStruct { public int myValue; } public InnerTestStruct innerStruct; } public class TestScriptableObject : ScriptableObject { public TestObject myData; public TestScriptableObject() { myData = new TestObject(); } } static TestCaseData[] s_TestCaseDatas = { new TestCaseData("integer", 1) .Returns(new string[] { "{\"myData.integer\":\"1\"}" }) .SetName("Given an int field, the nested column is generated correctly"), new TestCaseData("floatNumber", 1.5f) .Returns(new string[] { "{\"myData.floatNumber\":\"1.5\"}" }) .SetName("Given a float field, the nested column is generated correctly"), new TestCaseData("doubleNumber", 1.2) .Returns(new string[] { "{\"myData.doubleNumber\":\"1.2\"}" }) .SetName("Given a double field, the nested column is generated correctly"), new TestCaseData("boolean", true) .Returns(new string[] { "{\"myData.boolean\":\"True\"}" }) .SetName("Given a bool field, the nested column is generated correctly"), new TestCaseData("test", "Hello World") .Returns(new string[] { "{\"myData.test\":\"Hello World\"}" }) .SetName("Given a string field, the nested column is generated correctly"), new TestCaseData("array", new int[] { 1,2,3} ) .Returns(new string[] { "{\"myData.array\":\"[1,2,3]\"}" }) .SetName("Given an array of ints field, the nested column is generated correctly"), new TestCaseData("diffEnum", TestObject.TestDiffEnum.b) .Returns(new string[] { "{\"myData.diffEnum\":\"b\"}" }) .SetName("Given an enum field, the nested column is generated correctly"), new TestCaseData("innerStruct", new TestObject.InnerTestStruct{ myValue = 5 } ) .Returns(new string[] { "{\"myData.innerStruct.myValue\":\"5\"}" }) .SetName("Given an inner struct field, the nested column is generated correctly"), }; private void SetValue(TestObject current, string fieldName, object value) { current.GetType().GetField(fieldName).SetValue(current, value); } [Test, TestCaseSource(nameof(s_TestCaseDatas))] public string[] ToNestedColumnUnityObject(string fieldName, object value) { SetValue(m_Current, fieldName, value); m_SO.myData = m_Current; return m_SO.ToNestedColumn(true); } static TestCaseData[] s_TestCaseDatasWithDefault = { new TestCaseData("integer", 1) .Returns(new string[] { "{\"integer\":\"1\"}" }) .SetName("Given an int field, the nested column is generated correctly"), new TestCaseData("floatNumber", 1.5f) .Returns(new string[] { "{\"floatNumber\":\"1.5\"}" }) .SetName("Given a float field, the nested column is generated correctly"), new TestCaseData("doubleNumber", 1.2) .Returns(new string[] { "{\"doubleNumber\":\"1.2\"}" }) .SetName("Given a double field, the nested column is generated correctly"), new TestCaseData("boolean", true) .Returns(new string[] { "{\"boolean\":\"True\"}" }) .SetName("Given a bool field, the nested column is generated correctly"), new TestCaseData("test", "Hello World") .Returns(new string[] { "{\"test\":\"Hello World\"}" }) .SetName("Given a string field, the nested column is generated correctly"), new TestCaseData("array", new int[] { 1,2,3} ) .Returns(new string[] { "{\"array\":\"[1,2,3]\"}" }) .SetName("Given an array of ints field, the nested column is generated correctly"), new TestCaseData("diffEnum", TestObject.TestDiffEnum.b) .Returns(new string[] { "{\"diffEnum\":\"b\"}" }) .SetName("Given an enum field, the nested column is generated correctly"), new TestCaseData("innerStruct", new TestObject.InnerTestStruct{ myValue = 5 } ) .Returns(new string[] { "{\"innerStruct.myValue\":\"5\"}" }) .SetName("Given an inner struct field, the nested column is generated correctly"), }; [Test, TestCaseSource(nameof(s_TestCaseDatasWithDefault))] public string[] ToNestedColumnWithDefault(string fieldName, object value) { SetValue(m_Current, fieldName, value); m_SO.myData = m_Current; return m_Current.ToNestedColumnWithDefault(new TestObject(), true); } [Test] public void GivenAnObjectWithSerializableFields_WhenAllFieldsAreRequested_AllOfThemAreReturnedCorrectly() { using (ListPool.Get(out var tmp)) { foreach (var testCase in s_TestCaseDatas) { SetValue(m_Current, (string)testCase.Arguments[0], testCase.Arguments[1]); tmp.Add(((string[])testCase.ExpectedResult)[0]); } m_SO.myData = m_Current; Assert.AreEqual(m_SO.ToNestedColumn(false), tmp.ToArray()); } } [Test] public void CheckEditorAnalyticsAreDisabledOnCI() { var ci = Environment.GetEnvironmentVariable("CI"); if (!string.IsNullOrWhiteSpace(ci) && bool.TryParse(ci, out var result) && result) { Assert.IsFalse(EditorAnalytics.enabled, "Analytics must be only enabled for user projects not on CI"); Debug.Log("Assert for CheckEditorAnalyticsAreDisabledOnCI was executed."); } } [Serializable] class ClassWithPublicInt { public int myPublicInt; } [Serializable] class ClassWithPrivateBool { private bool myPrivateBoolNotSerializable; } [Serializable] class ClassWithPrivateIntWithSerializableFieldAttribute { [SerializeField] private int myPrivateIntSerializable; } [Serializable] class ClassWithPublicIntWithNonSerializableFieldAttribute { [NonSerialized] public int myPublicNonSerializedInt; } [Serializable] class ClassWithPublicPropertytWithSerializableFieldAttributeForTheBackingField { [field: SerializeField] public int myProperty { get; private set; } } [Serializable] class ClassWithObsoletePublicField { [Obsolete("this is obsolete")] public int myPublicInt; } [Serializable] class BaseClassWithField { public int myPublicInt; } [Serializable] class ChildClass : BaseClassWithField { public bool myPublicBool; } static TestCaseData[] s_ListTestsCaseDatasSerializableFields = { new TestCaseData(typeof(ClassWithPublicInt), false) .SetName("Given a class with a public field, the field is returned") .Returns(new string[] { "myPublicInt" }), new TestCaseData(typeof(ClassWithPrivateBool), false) .SetName("Given a class with a private field, nothing is returned") .Returns(new string[] { }), new TestCaseData(typeof(ClassWithPrivateIntWithSerializableFieldAttribute), false) .SetName("Given a class with a private field with [SerializeField] attribute, the field is returned") .Returns(new string[] {"myPrivateIntSerializable" }), new TestCaseData(typeof(ClassWithPublicIntWithNonSerializableFieldAttribute), false) .SetName("Given a class with public field with [NonSerialized] attribute, nothing is returned") .Returns(new string[] { }), new TestCaseData(typeof(ClassWithPublicPropertytWithSerializableFieldAttributeForTheBackingField), false) .SetName("Given a class with public property with [field: SerializeField] attribute, the backing field is returned") .Returns(new string[] {"k__BackingField" }), new TestCaseData(typeof(ClassWithObsoletePublicField), false) .SetName("Given a class with public field with [Obsolete] attribute and not removing obsolete fields, the obsolete is returned") .Returns(new string[] {"myPublicInt" }), new TestCaseData(typeof(ClassWithObsoletePublicField), true) .SetName("Given a class with public field with [Obsolete] attribute, nothing is returned when obsolete fields must be removed") .Returns(new string[] { }), new TestCaseData(typeof(ChildClass), false) .SetName("Given a class with inheriting from another with a public field, the parent field is returned") .Returns(new string[] { "myPublicInt", "myPublicBool" }), }; [Test, TestCaseSource(nameof(s_ListTestsCaseDatasSerializableFields))] public string[] GetSerializableFields(Type type, bool removeObsolete) { return type .GetSerializableFields(removeObsolete) .Select(f => f.Name) .ToArray(); } private static IEnumerable ValueComparisonTestsCases { get { // String test cases yield return new TestCaseData(typeof(string), "test", "test", true).SetName("StringEqual"); yield return new TestCaseData(typeof(string), "test1", "test2", false).SetName("StringNotEqual"); // Primitive and enum test cases yield return new TestCaseData(typeof(int), 1, 1, true).SetName("IntEqual"); yield return new TestCaseData(typeof(int), 1, 2, false).SetName("IntNotEqual"); yield return new TestCaseData(typeof(DayOfWeek), DayOfWeek.Monday, DayOfWeek.Monday, true).SetName("EnumEqual"); yield return new TestCaseData(typeof(DayOfWeek), DayOfWeek.Monday, DayOfWeek.Tuesday, false).SetName("EnumNotEqual"); // Array test cases yield return new TestCaseData(typeof(int[]), new int[] { 1, 2, 3 }, new int[] { 1, 2, 3 }, true).SetName("ArrayEqual"); yield return new TestCaseData(typeof(int[]), new int[] { 1, 2, 3 }, new int[] { 1, 2, 4 }, false).SetName("ArrayNotEqual"); yield return new TestCaseData(typeof(int[]), new int[] { 1, 2, 3 }, new int[] { 1, 2 }, false).SetName("ArrayDifferentLength"); // Null test cases yield return new TestCaseData(typeof(object), null, null, true).SetName("BothNull"); yield return new TestCaseData(typeof(object), new object(), null, false).SetName("OneNull"); yield return new TestCaseData(typeof(object), null, new object(), false).SetName("OneNullOtherWay"); } } [Test, TestCaseSource(nameof(ValueComparisonTestsCases))] public void TestAreValuesEqual(Type fieldType, object valueCurrent, object valueDefault, bool expected) { bool result = AnalyticsUtils.AreValuesEqual(fieldType, valueCurrent, valueDefault); Assert.AreEqual(expected, result); } private class TestClass { } private static IEnumerable TypeComplexityTestCases { get { // Primitive types test cases yield return new TestCaseData(typeof(int), false).SetName("IntIsNotComplex"); yield return new TestCaseData(typeof(double), false).SetName("DoubleIsNotComplex"); // Enum test cases yield return new TestCaseData(typeof(DayOfWeek), false).SetName("EnumIsNotComplex"); // String test cases yield return new TestCaseData(typeof(string), false).SetName("StringIsNotComplex"); // Array test cases yield return new TestCaseData(typeof(int[]), false).SetName("IntArrayIsNotComplex"); yield return new TestCaseData(typeof(string[]), false).SetName("StringArrayIsNotComplex"); // Struct test cases yield return new TestCaseData(typeof(DateTime), true).SetName("DateTimeIsComplex"); // Class test cases yield return new TestCaseData(typeof(object), true).SetName("ObjectIsComplex"); yield return new TestCaseData(typeof(TestClass), true).SetName("CustomClassIsComplex"); } } [Test, TestCaseSource(nameof(TypeComplexityTestCases))] public void TestIsComplexType(Type fieldType, bool expected) { bool result = AnalyticsUtils.IsComplexType(fieldType); Assert.AreEqual(expected, result); } } }