TechbloxModdingAPI/GamecraftModdingAPI/Tests/TestRoot.cs
2020-11-13 17:02:27 +01:00

296 lines
8.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Linq; // welcome to the dark side
using Svelto.Tasks;
using Svelto.Tasks.Lean;
using Svelto.Tasks.Enumerators;
using UnityEngine;
using GamecraftModdingAPI.App;
using GamecraftModdingAPI.Tasks;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Tests
{
/// <summary>
/// API test system root class.
/// </summary>
public static class TestRoot
{
public static bool AutoShutdown = true;
public const string ReportFile = "GamecraftModdingAPI_tests.log";
private static bool _testsPassed = false;
private static uint _testsCount = 0;
private static uint _testsCountPassed = 0;
private static uint _testsCountFailed = 0;
private static string state = "StartingUp";
private static Stopwatch timer;
private static List<Type> testTypes = null;
public static bool TestsPassed
{
get => _testsPassed;
set
{
_testsPassed = _testsPassed && value;
_testsCount++;
if (value)
{
_testsCountPassed++;
}
else
{
_testsCountFailed++;
}
}
}
private static void StartUp()
{
// init
timer = Stopwatch.StartNew();
_testsPassed = true;
_testsCount = 0;
_testsCountPassed = 0;
_testsCountFailed = 0;
// flow control
Game.Enter += (sender, args) => { GameTests().RunOn(RobocraftX.Schedulers.Lean.EveryFrameStepRunner_TimeRunningAndStopped); };
Game.Exit += (s, a) => state = "ReturningFromGame";
Client.EnterMenu += (sender, args) =>
{
if (state == "EnteringMenu")
{
MenuTests().RunOn(Scheduler.leanRunner);
state = "EnteringGame";
}
if (state == "ReturningFromGame")
{
TearDown().RunOn(Scheduler.leanRunner);
state = "ShuttingDown";
}
};
// init tests here
foreach (Type t in testTypes)
{
foreach (MethodBase m in t.GetMethods())
{
if (m.GetCustomAttribute<APITestStartUpAttribute>() != null)
{
try
{
m.Invoke(null, new object[0]);
}
catch (Exception e)
{
Assert.Fail($"Start up method '{m}' raised an exception: {e.ToString()}");
}
}
}
}
state = "EnteringMenu";
}
private static IEnumerator<TaskContract> MenuTests()
{
yield return Yield.It;
// menu tests
foreach (Type t in testTypes)
{
foreach (MethodBase m in t.GetMethods())
{
APITestCaseAttribute a = m.GetCustomAttribute<APITestCaseAttribute>();
if (a != null && a.TestType == TestType.Menu)
{
try
{
m.Invoke(null, new object[0]);
}
catch (Exception e)
{
Assert.Fail($"Menu test '{m}' raised an exception: {e.ToString()}");
}
yield return Yield.It;
}
}
}
// load game
yield return GoToGameTests().Continue();
}
private static IEnumerator<TaskContract> GoToGameTests()
{
Client app = new Client();
int oldLength = 0;
while (app.MyGames.Length == 0 || oldLength != app.MyGames.Length)
{
oldLength = app.MyGames.Length;
yield return new WaitForSecondsEnumerator(1).Continue();
}
yield return Yield.It;
app.MyGames[0].EnterGame();
/*Game newGame = Game.NewGame();
yield return new WaitForSecondsEnumerator(5).Continue(); // wait for sync
newGame.EnterGame();*/
}
private static IEnumerator<TaskContract> GameTests()
{
yield return Yield.It;
Game currentGame = Game.CurrentGame();
// in-game tests
yield return new WaitForSecondsEnumerator(5).Continue(); // wait for game to finish loading
foreach (Type t in testTypes)
{
foreach (MethodBase m in t.GetMethods())
{
APITestCaseAttribute a = m.GetCustomAttribute<APITestCaseAttribute>();
if (a != null && a.TestType == TestType.Game)
{
try
{
m.Invoke(null, new object[0]);
}
catch (Exception e)
{
Assert.Fail($"Game test '{m}' raised an exception: {e.ToString()}");
}
yield return Yield.It;
}
}
}
currentGame.ToggleTimeMode();
yield return new WaitForSecondsEnumerator(5).Continue();
// simulation tests
foreach (Type t in testTypes)
{
foreach (MethodBase m in t.GetMethods())
{
APITestCaseAttribute a = m.GetCustomAttribute<APITestCaseAttribute>();
if (a != null && a.TestType == TestType.SimulationMode)
{
try
{
m.Invoke(null, new object[0]);
}
catch (Exception e)
{
Assert.Fail($"Simulation test '{m}' raised an exception: {e.ToString()}");
}
yield return Yield.It;
}
}
}
currentGame.ToggleTimeMode();
yield return new WaitForSecondsEnumerator(5).Continue();
// build tests
foreach (Type t in testTypes)
{
foreach (MethodBase m in t.GetMethods())
{
APITestCaseAttribute a = m.GetCustomAttribute<APITestCaseAttribute>();
if (a != null && a.TestType == TestType.EditMode)
{
try
{
m.Invoke(null, new object[0]);
}
catch (Exception e)
{
Assert.Fail($"Build test '{m}' raised an exception: {e.ToString()}");
}
yield return Yield.It;
}
}
}
// exit game
yield return new WaitForSecondsEnumerator(5).Continue();
yield return ReturnToMenu().Continue();
}
private static IEnumerator<TaskContract> ReturnToMenu()
{
Logging.MetaLog("Returning to main menu");
yield return Yield.It;
Game.CurrentGame().ExitGame();
}
private static IEnumerator<TaskContract> TearDown()
{
yield return new WaitForSecondsEnumerator(5).Continue();
Logging.MetaLog("Tearing down test run");
// dispose tests here
foreach (Type t in testTypes)
{
foreach (MethodBase m in t.GetMethods())
{
if (m.GetCustomAttribute<APITestTearDownAttribute>() != null)
{
try
{
m.Invoke(null, new object[0]);
}
catch (Exception e)
{
Assert.Warn($"Tear down method '{m}' raised an exception: {e.ToString()}");
}
yield return Yield.It;
}
}
}
// finish up
Assert.CallsComplete();
timer.Stop();
string verdict = _testsPassed ? "--- PASSED :) ---" : "--- FAILED :( ---";
Assert.Log($"VERDICT: {verdict} ({_testsCountPassed}/{_testsCountFailed}/{_testsCount} P/F/T in {timer.ElapsedMilliseconds}ms)");
yield return Yield.It;
// end game
Logging.MetaLog("Completed test run: " + verdict);
yield return Yield.It;
Assert.CloseLog();
if (AutoShutdown) Application.Quit();
}
private static void FindTests(Assembly asm)
{
testTypes = new List<Type>();
foreach (Type t in asm.GetTypes())
{
if (t.GetCustomAttribute<APITestClassAttribute>() != null)
{
testTypes.Add(t);
}
}
}
/// <summary>
/// Runs the tests.
/// </summary>
/// <param name="asm">Assembly to search for tests. When set to null, this uses the GamecraftModdingAPI assembly. </param>
public static void RunTests(Assembly asm = null)
{
if (asm == null) asm = Assembly.GetExecutingAssembly();
FindTests(asm);
Logging.MetaLog("Starting test run");
// log metadata
Assert.Log($"Unity {Application.unityVersion}");
Assert.Log($"Gamecraft {Application.version}");
Assert.Log($"GamecraftModdingAPI {Assembly.GetExecutingAssembly().GetName().Version}");
Assert.Log($"Testing {asm.GetName().Name} {asm.GetName().Version}");
Assert.Log($"START: --- {DateTime.Now.ToString()} --- ({testTypes.Count} tests classes detected)");
StartUp();
Logging.MetaLog("Test StartUp complete");
}
}
}