Skip to content

Commit e2ecc19

Browse files
committed
Merge branch 'master' into dev
2 parents b19a244 + c98f883 commit e2ecc19

File tree

5 files changed

+154
-31
lines changed

5 files changed

+154
-31
lines changed

Source/Editor/ExampleClass.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#if !FLAX_PLUGIN
2+
using System.Collections.Generic;
23
using FlaxCommunity.UnitTesting.Editor;
4+
using FlaxEngine;
35

46
namespace UnitTests.Editor
57
{
@@ -75,6 +77,19 @@ public int ExpectedResultsTests(int a, int b)
7577
{
7678
return a + b;
7779
}
80+
81+
public IEnumerable<TestCaseData> AddVectorsTestCases()
82+
{
83+
yield return new TestCaseData(new Vector2(0, 0), new Vector2(0, 0)).Returns(new Vector2(0, 0));
84+
yield return new TestCaseData(new Vector2(3, 5), new Vector2(1, 1)).Returns(new Vector2(4, 6));
85+
yield return new TestCaseData(new Vector2(1, 2), new Vector2(-1, -3)).Returns(new Vector2(0, -1));
86+
}
87+
88+
[TestCaseSource(nameof(AddVectorsTestCases))]
89+
public Vector2 AddVectorsTests(Vector2 a, Vector2 b)
90+
{
91+
return a + b;
92+
}
7893
}
7994
}
8095
#endif

Source/Editor/TestAttributes.cs

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,41 +8,64 @@ namespace FlaxCommunity.UnitTesting.Editor
88
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
99
public sealed class TestCase : Attribute
1010
{
11-
public readonly object[] Attributes;
12-
public object ExpectedResult { get; set; }
11+
public TestCaseData TestCaseData { get; } = new TestCaseData(null);
12+
public object ExpectedResult
13+
{
14+
get => TestCaseData.ExpectedResult;
15+
set => TestCaseData.ExpectedResult = value;
16+
}
1317
public TestCase(object T1)
1418
{
15-
Attributes = new object[] { T1 };
19+
TestCaseData.Attributes = new object[] { T1 };
1620
}
1721

1822
public TestCase(object T1, object T2)
1923
{
20-
Attributes = new object[] { T1, T2 };
24+
TestCaseData.Attributes = new object[] { T1, T2 };
2125
}
2226

2327
public TestCase(object T1, object T2, object T3)
2428
{
25-
Attributes = new object[] { T1, T2, T3 };
29+
TestCaseData.Attributes = new object[] { T1, T2, T3 };
2630
}
2731

2832
public TestCase(object T1, object T2, object T3, object T4)
2933
{
30-
Attributes = new object[] { T1, T2, T3, T4 };
34+
TestCaseData.Attributes = new object[] { T1, T2, T3, T4 };
3135
}
3236

3337
public TestCase(object T1, object T2, object T3, object T4, object T5)
3438
{
35-
Attributes = new object[] { T1, T2, T3, T4, T5 };
39+
TestCaseData.Attributes = new object[] { T1, T2, T3, T4, T5 };
3640
}
3741

3842
public TestCase(object T1, object T2, object T3, object T4, object T5, object T6)
3943
{
40-
Attributes = new object[] { T1, T2, T3, T4, T5, T6 };
44+
TestCaseData.Attributes = new object[] { T1, T2, T3, T4, T5, T6 };
4145
}
4246

4347
public TestCase(object T1, object T2, object T3, object T4, object T5, object T6, object T7)
4448
{
45-
Attributes = new object[] { T1, T2, T3, T4, T5, T6, T7 };
49+
TestCaseData.Attributes = new object[] { T1, T2, T3, T4, T5, T6, T7 };
50+
}
51+
}
52+
53+
/// <summary>
54+
/// Specifies a test case source method
55+
/// </summary>
56+
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field)]
57+
public sealed class TestCaseSource : Attribute
58+
{
59+
public readonly string MemberName;
60+
public readonly Type SourceClassType;
61+
public TestCaseSource(string memberName) : this(null, memberName)
62+
{
63+
}
64+
65+
public TestCaseSource(Type sourceClassType, string memberName)
66+
{
67+
SourceClassType = sourceClassType;
68+
MemberName = memberName;
4669
}
4770
}
4871

Source/Editor/TestCaseData.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace FlaxCommunity.UnitTesting.Editor
8+
{
9+
public class TestCaseData
10+
{
11+
public TestCaseData(params object[] attributes)
12+
{
13+
Attributes = attributes;
14+
}
15+
16+
public object[] Attributes { get; set; }
17+
18+
public object ExpectedResult { get; set; }
19+
20+
public TestCaseData Returns(object expectedResult)
21+
{
22+
ExpectedResult = expectedResult;
23+
return this;
24+
}
25+
}
26+
}

Source/Editor/TestRunner.cs

Lines changed: 80 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,18 @@ private static void GatherTests()
6363
_suites.Add(type);
6464
}
6565

66+
private static bool TryGetAttribute<T>(MemberInfo memberInfo, out T attribute) where T : Attribute
67+
{
68+
attribute = memberInfo.GetCustomAttribute<T>();
69+
return attribute != null;
70+
}
71+
72+
private static bool TryGetAttributes<T>(MemberInfo memberInfo, out List<T> attributes) where T : Attribute
73+
{
74+
attributes = memberInfo.GetCustomAttributes<T>().ToList();
75+
return attributes.Count > 0;
76+
}
77+
6678
public static void RunTests()
6779
{
6880
GatherTests();
@@ -71,7 +83,6 @@ public static void RunTests()
7183
{
7284
var suiteMethods = suite.GetMethods();
7385

74-
var tests = suiteMethods.Where(m => m.GetCustomAttributes<Test>().Count() > 0 || m.GetCustomAttributes<TestCase>().Count() > 0).ToArray();
7586
var setup = suiteMethods.Where(m => m.GetCustomAttributes<OneTimeSetUp>().Count() > 0).FirstOrDefault();
7687
var disposer = suiteMethods.Where(m => m.GetCustomAttributes<OneTimeTearDown>().Count() > 0).FirstOrDefault();
7788
var beforeEach = suiteMethods.Where(m => m.GetCustomAttributes<SetUp>().Count() > 0).FirstOrDefault();
@@ -81,9 +92,12 @@ public static void RunTests()
8192

8293
setup?.Invoke(instance, null);
8394

84-
foreach (var testMethod in tests)
95+
foreach (var testMethod in suiteMethods)
8596
{
86-
if (testMethod.GetCustomAttributes<Test>().Count() > 0)
97+
bool hasTestCases = TryGetAttributes(testMethod, out List<TestCase> testCasesAttribute);
98+
bool hasTestCaseSource = TryGetAttribute(testMethod, out TestCaseSource testCaseSourceAttribute);
99+
100+
if (TryGetAttribute(testMethod, out Test testAttribute))
87101
{
88102
bool failed = false;
89103
beforeEach?.Invoke(instance, null);
@@ -102,20 +116,45 @@ public static void RunTests()
102116
finally
103117
{
104118
afterEach?.Invoke(instance, null);
105-
string message = $"Test '{suite.Name} {testMethod.Name}' finished with " + (failed ? "Error" : "Success");
106-
if (failed)
119+
OutputResults(suite, testMethod, failed);
120+
}
121+
}
122+
else if (hasTestCases || hasTestCaseSource)
123+
{
124+
IEnumerable<TestCaseData> testCases = Enumerable.Empty<TestCaseData>();
125+
126+
if (hasTestCases)
127+
{
128+
testCases = testCases.Concat(testCasesAttribute.Select(a => a.TestCaseData));
129+
}
130+
if (hasTestCaseSource)
131+
{
132+
var sourceClassType = testCaseSourceAttribute.SourceClassType ?? suite;
133+
object sourceObjectInstance = null;
134+
if (sourceClassType == suite || sourceClassType == null)
107135
{
108-
Debug.LogError(message);
136+
sourceObjectInstance = instance;
109137
}
110-
else
138+
else if (!sourceClassType.IsAbstract)
111139
{
112-
Debug.Log(message);
140+
sourceObjectInstance = Activator.CreateInstance(sourceClassType);
113141
}
142+
143+
var sourceTestCases = (IEnumerable<TestCaseData>)(
144+
sourceClassType
145+
.GetProperty(testCaseSourceAttribute.MemberName, typeof(IEnumerable<TestCaseData>))
146+
?.GetValue(sourceObjectInstance) ??
147+
sourceClassType
148+
.GetField(testCaseSourceAttribute.MemberName)
149+
?.GetValue(sourceObjectInstance) ??
150+
sourceClassType
151+
.GetMethod(testCaseSourceAttribute.MemberName)
152+
?.Invoke(sourceObjectInstance, null));
153+
154+
testCases = testCases.Concat(sourceTestCases);
114155
}
115-
}
116-
else
117-
{
118-
var testCases = testMethod.GetCustomAttributes<TestCase>();
156+
157+
int testCaseCount = 0;
119158
int successCount = 0;
120159
foreach (var testCase in testCases)
121160
{
@@ -137,27 +176,46 @@ public static void RunTests()
137176
finally
138177
{
139178
afterEach?.Invoke(instance, null);
179+
testCaseCount++;
140180

141181
if (!failed)
142182
successCount++;
143183
}
144184
}
145185

146-
int testCount = testCases.Count();
147-
string message = $"Test '{suite.Name} {testMethod.Name}' finished with {successCount}/{testCount} successfull test cases.";
148-
if (successCount < testCount)
149-
{
150-
Debug.LogError(message);
151-
}
152-
else
153-
{
154-
Debug.Log(message);
155-
}
186+
OutputResults(suite, testMethod, successCount, testCaseCount);
187+
156188
}
157189
}
158190

159191
disposer?.Invoke(instance, null);
160192
}
161193
}
194+
195+
private static void OutputResults(Type suite, MethodInfo testMethod, int successCount, int testCount)
196+
{
197+
string message = $"Test '{suite.Name} {testMethod.Name}' finished with {successCount}/{testCount} successfull test cases.";
198+
if (successCount < testCount)
199+
{
200+
Debug.LogError(message);
201+
}
202+
else
203+
{
204+
Debug.Log(message);
205+
}
206+
}
207+
208+
private static void OutputResults(Type suite, MethodInfo testMethod, bool failed)
209+
{
210+
string message = $"Test '{suite.Name} {testMethod.Name}' finished with " + (failed ? "Error" : "Success");
211+
if (failed)
212+
{
213+
Debug.LogError(message);
214+
}
215+
else
216+
{
217+
Debug.Log(message);
218+
}
219+
}
162220
}
163221
}

UnitTests.Editor.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
<Compile Include="Source\Editor\Assert.cs" />
6363
<Compile Include="Source\Editor\ExampleClass.cs" />
6464
<Compile Include="Source\Editor\TestAttributes.cs" />
65+
<Compile Include="Source\Editor\TestCaseData.cs" />
6566
<Compile Include="Source\Editor\TestRunner.cs" />
6667
</ItemGroup>
6768
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

0 commit comments

Comments
 (0)