Skip to content

Commit

Permalink
Merge branch 'master' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
stefnotch committed Nov 4, 2019
2 parents b19a244 + c98f883 commit e2ecc19
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 31 deletions.
15 changes: 15 additions & 0 deletions Source/Editor/ExampleClass.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#if !FLAX_PLUGIN
using System.Collections.Generic;
using FlaxCommunity.UnitTesting.Editor;
using FlaxEngine;

namespace UnitTests.Editor
{
Expand Down Expand Up @@ -75,6 +77,19 @@ public int ExpectedResultsTests(int a, int b)
{
return a + b;
}

public IEnumerable<TestCaseData> AddVectorsTestCases()
{
yield return new TestCaseData(new Vector2(0, 0), new Vector2(0, 0)).Returns(new Vector2(0, 0));
yield return new TestCaseData(new Vector2(3, 5), new Vector2(1, 1)).Returns(new Vector2(4, 6));
yield return new TestCaseData(new Vector2(1, 2), new Vector2(-1, -3)).Returns(new Vector2(0, -1));
}

[TestCaseSource(nameof(AddVectorsTestCases))]
public Vector2 AddVectorsTests(Vector2 a, Vector2 b)
{
return a + b;
}
}
}
#endif
41 changes: 32 additions & 9 deletions Source/Editor/TestAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,64 @@ namespace FlaxCommunity.UnitTesting.Editor
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class TestCase : Attribute
{
public readonly object[] Attributes;
public object ExpectedResult { get; set; }
public TestCaseData TestCaseData { get; } = new TestCaseData(null);
public object ExpectedResult
{
get => TestCaseData.ExpectedResult;
set => TestCaseData.ExpectedResult = value;
}
public TestCase(object T1)
{
Attributes = new object[] { T1 };
TestCaseData.Attributes = new object[] { T1 };
}

public TestCase(object T1, object T2)
{
Attributes = new object[] { T1, T2 };
TestCaseData.Attributes = new object[] { T1, T2 };
}

public TestCase(object T1, object T2, object T3)
{
Attributes = new object[] { T1, T2, T3 };
TestCaseData.Attributes = new object[] { T1, T2, T3 };
}

public TestCase(object T1, object T2, object T3, object T4)
{
Attributes = new object[] { T1, T2, T3, T4 };
TestCaseData.Attributes = new object[] { T1, T2, T3, T4 };
}

public TestCase(object T1, object T2, object T3, object T4, object T5)
{
Attributes = new object[] { T1, T2, T3, T4, T5 };
TestCaseData.Attributes = new object[] { T1, T2, T3, T4, T5 };
}

public TestCase(object T1, object T2, object T3, object T4, object T5, object T6)
{
Attributes = new object[] { T1, T2, T3, T4, T5, T6 };
TestCaseData.Attributes = new object[] { T1, T2, T3, T4, T5, T6 };
}

public TestCase(object T1, object T2, object T3, object T4, object T5, object T6, object T7)
{
Attributes = new object[] { T1, T2, T3, T4, T5, T6, T7 };
TestCaseData.Attributes = new object[] { T1, T2, T3, T4, T5, T6, T7 };
}
}

/// <summary>
/// Specifies a test case source method
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field)]
public sealed class TestCaseSource : Attribute
{
public readonly string MemberName;
public readonly Type SourceClassType;
public TestCaseSource(string memberName) : this(null, memberName)
{
}

public TestCaseSource(Type sourceClassType, string memberName)
{
SourceClassType = sourceClassType;
MemberName = memberName;
}
}

Expand Down
26 changes: 26 additions & 0 deletions Source/Editor/TestCaseData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FlaxCommunity.UnitTesting.Editor
{
public class TestCaseData
{
public TestCaseData(params object[] attributes)
{
Attributes = attributes;
}

public object[] Attributes { get; set; }

public object ExpectedResult { get; set; }

public TestCaseData Returns(object expectedResult)
{
ExpectedResult = expectedResult;
return this;
}
}
}
102 changes: 80 additions & 22 deletions Source/Editor/TestRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,18 @@ private static void GatherTests()
_suites.Add(type);
}

private static bool TryGetAttribute<T>(MemberInfo memberInfo, out T attribute) where T : Attribute
{
attribute = memberInfo.GetCustomAttribute<T>();
return attribute != null;
}

private static bool TryGetAttributes<T>(MemberInfo memberInfo, out List<T> attributes) where T : Attribute
{
attributes = memberInfo.GetCustomAttributes<T>().ToList();
return attributes.Count > 0;
}

public static void RunTests()
{
GatherTests();
Expand All @@ -71,7 +83,6 @@ public static void RunTests()
{
var suiteMethods = suite.GetMethods();

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

setup?.Invoke(instance, null);

foreach (var testMethod in tests)
foreach (var testMethod in suiteMethods)
{
if (testMethod.GetCustomAttributes<Test>().Count() > 0)
bool hasTestCases = TryGetAttributes(testMethod, out List<TestCase> testCasesAttribute);
bool hasTestCaseSource = TryGetAttribute(testMethod, out TestCaseSource testCaseSourceAttribute);

if (TryGetAttribute(testMethod, out Test testAttribute))
{
bool failed = false;
beforeEach?.Invoke(instance, null);
Expand All @@ -102,20 +116,45 @@ public static void RunTests()
finally
{
afterEach?.Invoke(instance, null);
string message = $"Test '{suite.Name} {testMethod.Name}' finished with " + (failed ? "Error" : "Success");
if (failed)
OutputResults(suite, testMethod, failed);
}
}
else if (hasTestCases || hasTestCaseSource)
{
IEnumerable<TestCaseData> testCases = Enumerable.Empty<TestCaseData>();

if (hasTestCases)
{
testCases = testCases.Concat(testCasesAttribute.Select(a => a.TestCaseData));
}
if (hasTestCaseSource)
{
var sourceClassType = testCaseSourceAttribute.SourceClassType ?? suite;
object sourceObjectInstance = null;
if (sourceClassType == suite || sourceClassType == null)
{
Debug.LogError(message);
sourceObjectInstance = instance;
}
else
else if (!sourceClassType.IsAbstract)
{
Debug.Log(message);
sourceObjectInstance = Activator.CreateInstance(sourceClassType);
}

var sourceTestCases = (IEnumerable<TestCaseData>)(
sourceClassType
.GetProperty(testCaseSourceAttribute.MemberName, typeof(IEnumerable<TestCaseData>))
?.GetValue(sourceObjectInstance) ??
sourceClassType
.GetField(testCaseSourceAttribute.MemberName)
?.GetValue(sourceObjectInstance) ??
sourceClassType
.GetMethod(testCaseSourceAttribute.MemberName)
?.Invoke(sourceObjectInstance, null));

testCases = testCases.Concat(sourceTestCases);
}
}
else
{
var testCases = testMethod.GetCustomAttributes<TestCase>();

int testCaseCount = 0;
int successCount = 0;
foreach (var testCase in testCases)
{
Expand All @@ -137,27 +176,46 @@ public static void RunTests()
finally
{
afterEach?.Invoke(instance, null);
testCaseCount++;

if (!failed)
successCount++;
}
}

int testCount = testCases.Count();
string message = $"Test '{suite.Name} {testMethod.Name}' finished with {successCount}/{testCount} successfull test cases.";
if (successCount < testCount)
{
Debug.LogError(message);
}
else
{
Debug.Log(message);
}
OutputResults(suite, testMethod, successCount, testCaseCount);

}
}

disposer?.Invoke(instance, null);
}
}

private static void OutputResults(Type suite, MethodInfo testMethod, int successCount, int testCount)
{
string message = $"Test '{suite.Name} {testMethod.Name}' finished with {successCount}/{testCount} successfull test cases.";
if (successCount < testCount)
{
Debug.LogError(message);
}
else
{
Debug.Log(message);
}
}

private static void OutputResults(Type suite, MethodInfo testMethod, bool failed)
{
string message = $"Test '{suite.Name} {testMethod.Name}' finished with " + (failed ? "Error" : "Success");
if (failed)
{
Debug.LogError(message);
}
else
{
Debug.Log(message);
}
}
}
}
1 change: 1 addition & 0 deletions UnitTests.Editor.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
<Compile Include="Source\Editor\Assert.cs" />
<Compile Include="Source\Editor\ExampleClass.cs" />
<Compile Include="Source\Editor\TestAttributes.cs" />
<Compile Include="Source\Editor\TestCaseData.cs" />
<Compile Include="Source\Editor\TestRunner.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
Expand Down

0 comments on commit e2ecc19

Please sign in to comment.