From 78122ee445917a8d0cc87cb67c21db024cd6e57b Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Wed, 17 Jun 2020 21:04:40 -0400 Subject: [PATCH] Add automatic testing functionality --- GamecraftModdingAPI.sln | 3 + .../GamecraftModdingAPI.csproj | 406 +++++++++++++++++- .../Tests/APITestAttributes.cs | 45 ++ GamecraftModdingAPI/Tests/Assert.cs | 64 +++ .../Tests/GamecraftModdingAPIPluginTest.cs | 150 ++++--- GamecraftModdingAPI/Tests/TestRoot.cs | 247 +++++++++++ GamecraftModdingAPI/Tests/TestTest.cs | 52 +++ 7 files changed, 903 insertions(+), 64 deletions(-) create mode 100644 GamecraftModdingAPI/Tests/APITestAttributes.cs create mode 100644 GamecraftModdingAPI/Tests/Assert.cs create mode 100644 GamecraftModdingAPI/Tests/TestRoot.cs create mode 100644 GamecraftModdingAPI/Tests/TestTest.cs diff --git a/GamecraftModdingAPI.sln b/GamecraftModdingAPI.sln index ff9fc7f..6482776 100644 --- a/GamecraftModdingAPI.sln +++ b/GamecraftModdingAPI.sln @@ -9,12 +9,15 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU + Test|Any CPU = Test|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Debug|Any CPU.Build.0 = Debug|Any CPU {7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Release|Any CPU.ActiveCfg = Release|Any CPU {7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Release|Any CPU.Build.0 = Release|Any CPU + {7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Test|Any CPU.ActiveCfg = Test|Any CPU + {7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Test|Any CPU.Build.0 = Test|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/GamecraftModdingAPI/GamecraftModdingAPI.csproj b/GamecraftModdingAPI/GamecraftModdingAPI.csproj index 0fdbcfd..c78272a 100644 --- a/GamecraftModdingAPI/GamecraftModdingAPI.csproj +++ b/GamecraftModdingAPI/GamecraftModdingAPI.csproj @@ -1,5 +1,4 @@ - - + net472 true @@ -14,19 +13,403 @@ + + DEBUG;TEST;TRACE + + + ..\..\ref\Gamecraft_Data\Managed\Analytics.dll + + + ..\..\ref\Gamecraft_Data\Managed\Assembly-CSharp-firstpass.dll + + + ..\..\ref\Gamecraft_Data\Managed\Assembly-CSharp.dll + + + ..\..\ref\Gamecraft_Data\Managed\Authentication.dll + + + ..\..\ref\Gamecraft_Data\Managed\BlockEntityFactory.dll + + + ..\..\ref\Gamecraft_Data\Managed\Blocks.HUDFeedbackBlocks.dll + + + ..\..\ref\Gamecraft_Data\Managed\ClusterToWireConversion.Mock.dll + + + ..\..\ref\Gamecraft_Data\Managed\CommandLine.dll + + + ..\..\ref\Gamecraft_Data\Managed\DataLoader.dll + + + ..\..\ref\Gamecraft_Data\Managed\DDNA.dll + + + ..\..\ref\Gamecraft_Data\Managed\FMOD.dll + + + ..\..\ref\Gamecraft_Data\Managed\FullGame.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.AudioBlocks.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.ConsoleBlock.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.DamagingSurfaceBlock.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.GenericPhysicsBlocks.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.LogicBlock.dll + + + ..\..\ref\Gamecraft_Data\Managed\GameCraft.Blocks.ProjectileBlock.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.TimerBlock.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.CharacterVulnerability.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.CharacterVulnerabilityGui.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.Effects.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.ConsoleBlock.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.GraphicsScreen.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.HUDFeedbackBlocks.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.Tweaks.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.Wires.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.Wires.Mockup.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.WorldSpaceGuis.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.Music.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.PerformanceWarnings.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.Tweaks.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.Tweaks.Mockup.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.VisualEffects.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.Wires.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.Wires.Input.dll + + + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.Wires.Mockup.dll + + + ..\..\ref\Gamecraft_Data\Managed\GameState.dll + + + ..\..\ref\Gamecraft_Data\Managed\GPUInstancer.dll + + + ..\..\ref\Gamecraft_Data\Managed\Havok.Physics.dll + + + ..\..\ref\Gamecraft_Data\Managed\Havok.Physics.Hybrid.dll + + + ..\..\ref\Gamecraft_Data\Managed\LZ4.dll + + + ..\..\ref\Gamecraft_Data\Managed\MultiplayerNetworking.dll + + + ..\..\ref\Gamecraft_Data\Managed\MultiplayerTest.dll + + + ..\..\ref\Gamecraft_Data\Managed\RCX.ScreenshotTaker.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftECS.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.AccountPreferences.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Blocks.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Blocks.Ghost.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Blocks.Triggers.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Building.BoxSelect.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Building.Jobs.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Character.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.ClusterToWireConversion.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Common.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.ControlsScreen.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Crosshair.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.FrontEnd.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.BlockLabel.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.DebugDisplay.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.RemoveBlock.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.ScaleGhost.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.GUIs.WorkshopPrefabs.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Input.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.MachineEditor.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.MainGame.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.MainSimulation.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.MockCharacter.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Multiplayer.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Multiplayer.NetworkEntityStream.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.MultiplayerInput.dll + + + ..\..\ref\Gamecraft_Data\Managed\Robocraftx.ObjectIdBlocks.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Party.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.PartyGui.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Physics.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.PilotSeat.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Player.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Rendering.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Rendering.Mock.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.SaveAndLoad.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.SaveGameDialog.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Serializers.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Services.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.SignalHandling.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.StateSync.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX_SpawnPoints.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocraftX_TextBlock.dll + + + ..\..\ref\Gamecraft_Data\Managed\RobocratX.SimulationCompositionRoot.dll + + + ..\..\ref\Gamecraft_Data\Managed\StringFormatter.dll + + + ..\..\ref\Gamecraft_Data\Managed\Svelto.Common_3.dll + + + ..\..\ref\Gamecraft_Data\Managed\Svelto.ECS.dll + + + ..\..\ref\Gamecraft_Data\Managed\Svelto.Services.dll + + + ..\..\ref\Gamecraft_Data\Managed\Svelto.Tasks.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Addressables.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Build.SlimPlayerRuntime.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Burst.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Collections.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Deformations.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Entities.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Entities.Hybrid.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Jobs.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Mathematics.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Mathematics.Extensions.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Mathematics.Extensions.Hybrid.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Physics.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Physics.Hybrid.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Platforms.Common.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Postprocessing.Runtime.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Properties.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Properties.Reflection.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Properties.UI.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.RenderPipeline.Universal.ShaderLibrary.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.RenderPipelines.Core.Runtime.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.RenderPipelines.Core.ShaderLibrary.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.RenderPipelines.Universal.Runtime.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.RenderPipelines.Universal.Shaders.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.ResourceManager.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Scenes.Hybrid.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.ScriptableBuildPipeline.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Serialization.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.TextMeshPro.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Timeline.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Transforms.dll + + + ..\..\ref\Gamecraft_Data\Managed\Unity.Transforms.Hybrid.dll + + + ..\..\ref\Gamecraft_Data\Managed\UnityEngine.UI.dll + + + ..\..\ref\Gamecraft_Data\Managed\uREPL.dll + + + ..\..\ref\Gamecraft_Data\Managed\VisualProfiler.dll + - - - - - - - + ..\ref\Gamecraft_Data\Managed\IllusionInjector.dll @@ -833,6 +1216,5 @@ ..\..\ref\Gamecraft_Data\Managed\VisualProfiler.dll - - - + + \ No newline at end of file diff --git a/GamecraftModdingAPI/Tests/APITestAttributes.cs b/GamecraftModdingAPI/Tests/APITestAttributes.cs new file mode 100644 index 0000000..8506a56 --- /dev/null +++ b/GamecraftModdingAPI/Tests/APITestAttributes.cs @@ -0,0 +1,45 @@ +using System; +namespace GamecraftModdingAPI.Tests +{ + public enum TestType + { + Menu, + Game, + SimulationMode, + EditMode, + } + + [AttributeUsage(AttributeTargets.Class)] + public class APITestClassAttribute : Attribute + { + internal string Name; + + public APITestClassAttribute(string name = "") + { + this.Name = name; + } + } + + [AttributeUsage(AttributeTargets.Method)] + public class APITestCaseAttribute : Attribute + { + internal TestType TestType; + + public APITestCaseAttribute(TestType testType) + { + this.TestType = testType; + } + } + + [AttributeUsage(AttributeTargets.Method)] + public class APITestStartUpAttribute : Attribute + { + + } + + [AttributeUsage(AttributeTargets.Method)] + public class APITestTearDownAttribute : Attribute + { + + } +} diff --git a/GamecraftModdingAPI/Tests/Assert.cs b/GamecraftModdingAPI/Tests/Assert.cs new file mode 100644 index 0000000..fb03013 --- /dev/null +++ b/GamecraftModdingAPI/Tests/Assert.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Concurrent; +using System.IO; +using System.Runtime.CompilerServices; + +namespace GamecraftModdingAPI.Tests +{ + public static class Assert + { + private static StreamWriter logFile = null; + + private static ConcurrentDictionary callbacks = new ConcurrentDictionary(); + + private const string PASS = "SUCCESS: "; + + private const string FAIL = "FAILURE: "; + + private const string WARN = "WARNING: "; + + private const string INFO = "DEBUG: "; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Log(string msg, string end = "\n") + { + if (logFile == null) openTestLog(); + logFile.Write(msg + end); + logFile.Flush(); + } + + public static EventHandler CallsBack(string eventName, string eventMsg = null) + { + if (eventMsg == null) eventMsg = $"expected callback to {eventName} but it never occurred..."; + callbacks[eventName] = eventMsg; + + return (sender, args) => + { + string value = null; + if (!callbacks.TryRemove(eventName, out value)) { Log(WARN + $"callback to {eventName} occurred again or a related error occurred... (Received '{args.ToString()}' from '{(sender == null ? (string)sender : sender.ToString())}')"); } + Log(PASS + $"callback to {eventName} occurred... (Received '{args.ToString()}' from '{(sender == null ? (string)sender : sender.ToString())}')"); + TestRoot.TestsPassed = true; + }; + } + + internal static void CallsComplete() + { + foreach(string key in callbacks.Keys) + { + Log(FAIL + callbacks[key]); + TestRoot.TestsPassed = false; + } + } + + internal static void CloseLog() + { + if (logFile != null) logFile.Close(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void openTestLog() + { + logFile = File.CreateText(TestRoot.ReportFile); + } + } +} diff --git a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs index dc07e1e..fa65ebb 100644 --- a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs +++ b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs @@ -48,11 +48,11 @@ namespace GamecraftModdingAPI.Tests GamecraftModdingAPI.Main.Shutdown(); } - public void OnApplicationStart() - { + public void OnApplicationStart() + { FileLog.Reset(); Harmony.DEBUG = true; - GamecraftModdingAPI.Main.Init(); + GamecraftModdingAPI.Main.Init(); Logging.MetaDebugLog($"Version group id {(uint)ApiExclusiveGroups.versionGroup}"); // in case Steam is not installed/running // this will crash the game slightly later during startup @@ -62,7 +62,7 @@ namespace GamecraftModdingAPI.Tests // disable some Gamecraft analytics //AnalyticsDisablerPatch.DisableAnalytics = true; // disable background music - Logging.MetaDebugLog("Audio Mixers: "+string.Join(",", AudioTools.GetMixers())); + Logging.MetaDebugLog("Audio Mixers: " + string.Join(",", AudioTools.GetMixers())); //AudioTools.SetVolume(0.0f, "Music"); // The game now sets this from settings again after this is called :( //Utility.VersionTracking.Enable();//(very) unstable @@ -76,60 +76,68 @@ namespace GamecraftModdingAPI.Tests HandlerBuilder.Builder("menuact API debug") .Handle(EventType.Menu) - .OnActivation(() => { Logging.Log("Menu Activated event!"); }) + .OnActivation(() => { Logging.Log("Menu Activated event!"); }) .OnDestruction(() => { Logging.Log("Menu Destroyed event!"); }) .Build(); HandlerBuilder.Builder("menuswitch API debug") - .Handle(EventType.MenuSwitchedTo) - .OnActivation(() => { Logging.Log("Menu Switched To event!"); }) - .Build(); + .Handle(EventType.MenuSwitchedTo) + .OnActivation(() => { Logging.Log("Menu Switched To event!"); }) + .Build(); HandlerBuilder.Builder("gameact API debug") - .Handle(EventType.Menu) - .OnActivation(() => { Logging.Log("Game Activated event!"); }) - .OnDestruction(() => { Logging.Log("Game Destroyed event!"); }) - .Build(); + .Handle(EventType.Menu) + .OnActivation(() => { Logging.Log("Game Activated event!"); }) + .OnDestruction(() => { Logging.Log("Game Destroyed event!"); }) + .Build(); HandlerBuilder.Builder("gamerel API debug") - .Handle(EventType.GameReloaded) - .OnActivation(() => { Logging.Log("Game Reloaded event!"); }) - .Build(); + .Handle(EventType.GameReloaded) + .OnActivation(() => { Logging.Log("Game Reloaded event!"); }) + .Build(); HandlerBuilder.Builder("gameswitch API debug") - .Handle(EventType.GameSwitchedTo) - .OnActivation(() => { Logging.Log("Game Switched To event!"); }) - .Build(); + .Handle(EventType.GameSwitchedTo) + .OnActivation(() => { Logging.Log("Game Switched To event!"); }) + .Build(); HandlerBuilder.Builder("simulationswitch API debug") - .Handle(EventType.SimulationSwitchedTo) - .OnActivation(() => { Logging.Log("Game Mode Simulation Switched To event!"); }) - .Build(); + .Handle(EventType.SimulationSwitchedTo) + .OnActivation(() => { Logging.Log("Game Mode Simulation Switched To event!"); }) + .Build(); HandlerBuilder.Builder("buildswitch API debug") - .Handle(EventType.BuildSwitchedTo) - .OnActivation(() => { Logging.Log("Game Mode Build Switched To event!"); }) - .Build(); + .Handle(EventType.BuildSwitchedTo) + .OnActivation(() => { Logging.Log("Game Mode Build Switched To event!"); }) + .Build(); HandlerBuilder.Builder("menu activated API error thrower test") .Handle(EventType.Menu) .OnActivation(() => { throw new Exception("Event Handler always throws an exception!"); }) .Build(); - // debug/test commands + /*HandlerBuilder.Builder("enter game from menu test") + .Handle(EventType.Menu) + .OnActivation(() => + { + Tasks.Scheduler.Schedule(new Tasks.Repeatable(enterGame, shouldRetry, 0.2f)); + }) + .Build();*/ + + // debug/test commands if (Dependency.Hell("ExtraCommands")) { CommandBuilder.Builder() - .Name("Exit") - .Description("Close Gamecraft immediately, without any prompts") - .Action(() => { UnityEngine.Application.Quit(); }) - .Build(); - + .Name("Exit") + .Description("Close Gamecraft immediately, without any prompts") + .Action(() => { UnityEngine.Application.Quit(); }) + .Build(); + CommandBuilder.Builder() - .Name("SetFOV") - .Description("Set the player camera's field of view") - .Action((float d) => { UnityEngine.Camera.main.fieldOfView = d; }) - .Build(); + .Name("SetFOV") + .Description("Set the player camera's field of view") + .Action((float d) => { UnityEngine.Camera.main.fieldOfView = d; }) + .Build(); CommandBuilder.Builder() .Name("MoveLastBlock") @@ -144,14 +152,14 @@ namespace GamecraftModdingAPI.Tests }).Build(); CommandBuilder.Builder() - .Name("PlaceAluminium") - .Description("Place a block of aluminium at the given coordinates") - .Action((float x, float y, float z) => - { - var block = Block.PlaceNew(BlockIDs.AluminiumCube, new float3(x, y, z)); - Logging.CommandLog("Block placed with type: " + block.Type); - }) - .Build(); + .Name("PlaceAluminium") + .Description("Place a block of aluminium at the given coordinates") + .Action((float x, float y, float z) => + { + var block = Block.PlaceNew(BlockIDs.AluminiumCube, new float3(x, y, z)); + Logging.CommandLog("Block placed with type: " + block.Type); + }) + .Build(); CommandBuilder.Builder() .Name("PlaceAluminiumLots") @@ -161,8 +169,8 @@ namespace GamecraftModdingAPI.Tests Logging.CommandLog("Starting..."); var sw = Stopwatch.StartNew(); for (int i = 0; i < 100; i++) - for (int j = 0; j < 100; j++) - Block.PlaceNew(BlockIDs.AluminiumCube, new float3(x + i, y, z + j)); + for (int j = 0; j < 100; j++) + Block.PlaceNew(BlockIDs.AluminiumCube, new float3(x + i, y, z + j)); //Block.Sync(); sw.Stop(); Logging.CommandLog("Finished in " + sw.ElapsedMilliseconds + "ms"); @@ -171,10 +179,10 @@ namespace GamecraftModdingAPI.Tests //With Sync(): 1135ms //Without Sync(): 134ms //Async: 348 794ms, doesn't freeze game - //Without Sync() but wait for submission: 530ms - //With Sync() at the end: 380ms + //Without Sync() but wait for submission: 530ms + //With Sync() at the end: 380ms - Block b = null; + Block b = null; CommandBuilder.Builder("moveBlockInSim", "Run in build mode first while looking at a block, then in sim to move it up") .Action(() => { @@ -204,7 +212,7 @@ namespace GamecraftModdingAPI.Tests return; } new Player(PlayerType.Local).GetBlockLookedAt().Color = - new BlockColor {Color = color}; + new BlockColor { Color = color }; Logging.CommandLog("Colored block to " + color); }).Build(); @@ -230,7 +238,7 @@ namespace GamecraftModdingAPI.Tests Block.Removed += (sender, args) => Logging.MetaDebugLog("Removed block " + args.Type + " with ID " + args.ID); - /* + /* CommandManager.AddCommand(new SimpleCustomCommandEngine((float d) => { UnityEngine.Camera.main.fieldOfView = d; }, "SetFOV", "Set the player camera's field of view")); CommandManager.AddCommand(new SimpleCustomCommandEngine( @@ -274,8 +282,8 @@ namespace GamecraftModdingAPI.Tests */ } - // dependency test - if (Dependency.Hell("GamecraftScripting", new Version("0.0.1.0"))) + // dependency test + if (Dependency.Hell("GamecraftScripting", new Version("0.0.1.0"))) { Logging.LogWarning("You're in GamecraftScripting dependency hell"); } @@ -283,7 +291,11 @@ namespace GamecraftModdingAPI.Tests { Logging.Log("Compatible GamecraftScripting detected"); } - } + +#if TEST + TestRoot.RunTests(); +#endif + } private string modsString; private string InstalledMods() @@ -295,6 +307,40 @@ namespace GamecraftModdingAPI.Tests return modsString = sb.ToString(); } + private bool retry = true; + + private bool shouldRetry() + { + return retry; + } + + private void enterGame() + { + App.Client app = new App.Client(); + App.Game[] myGames = app.MyGames; + Logging.MetaDebugLog($"MyGames count {myGames.Length}"); + if (myGames.Length != 0) + { + Logging.MetaDebugLog($"MyGames[0] EGID {myGames[0].EGID}"); + retry = false; + try + { + //myGames[0].Description = "test msg pls ignore"; // make sure game exists first + Logging.MetaDebugLog($"Entering game {myGames[0].Name}"); + myGames[0].EnterGame(); + } + catch (Exception e) + { + Logging.MetaDebugLog($"Failed to enter game; exception: {e}"); + retry = true; + } + } + else + { + Logging.MetaDebugLog("MyGames not populated yet :("); + } + } + public void OnFixedUpdate() { } public void OnLateUpdate() { } diff --git a/GamecraftModdingAPI/Tests/TestRoot.cs b/GamecraftModdingAPI/Tests/TestRoot.cs new file mode 100644 index 0000000..167173f --- /dev/null +++ b/GamecraftModdingAPI/Tests/TestRoot.cs @@ -0,0 +1,247 @@ +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 +{ + 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 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_RUNS_IN_TIME_STOPPED_AND_RUNNING); }; + 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() != null) + { + m.Invoke(null, new object[0]); + } + } + } + state = "EnteringMenu"; + } + + private static IEnumerator MenuTests() + { + yield return Yield.It; + // menu tests + foreach (Type t in testTypes) + { + foreach (MethodBase m in t.GetMethods()) + { + APITestCaseAttribute a = m.GetCustomAttribute(); + if (a != null && a.TestType == TestType.Menu) + { + m.Invoke(null, new object[0]); + yield return Yield.It; + } + } + } + // load game + yield return GoToGameTests().Continue(); + } + + private static IEnumerator 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(); + // returning from a new game without saving will hard lock GC (it's an invalid state) + //Game newGame = Game.NewGame(); + //yield return new WaitForSecondsEnumerator(5).Continue(); // wait for sync + //newGame.EnterGame(); + } + + private static IEnumerator 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(); + if (a != null && a.TestType == TestType.Game) + { + m.Invoke(null, new object[0]); + 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(); + if (a != null && a.TestType == TestType.SimulationMode) + { + m.Invoke(null, new object[0]); + 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(); + if (a != null && a.TestType == TestType.EditMode) + { + m.Invoke(null, new object[0]); + yield return Yield.It; + } + } + } + // exit game + yield return new WaitForSecondsEnumerator(5).Continue(); + yield return ReturnToMenu().Continue(); + } + + private static IEnumerator ReturnToMenu() + { + Logging.MetaLog("Returning to main menu"); + yield return Yield.It; + Game.CurrentGame().ExitGame(); + } + + private static IEnumerator 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() != null) + { + m.Invoke(null, new object[0]); + 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(); + foreach (Type t in asm.GetTypes()) + { + if (t.GetCustomAttribute() != null) + { + testTypes.Add(t); + } + } + } + + 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"); + } + } +} diff --git a/GamecraftModdingAPI/Tests/TestTest.cs b/GamecraftModdingAPI/Tests/TestTest.cs new file mode 100644 index 0000000..02eeda0 --- /dev/null +++ b/GamecraftModdingAPI/Tests/TestTest.cs @@ -0,0 +1,52 @@ +using System; + +using System.Reflection; + +using HarmonyLib; + +namespace GamecraftModdingAPI.Tests +{ +#if TEST + /// + /// Test test test. + /// + [APITestClass] + public static class TestTest + { + public static event EventHandler StartUp; + + public static event EventHandler Test; + + public static event EventHandler TearDown; + + [APITestStartUp] + public static void Init() + { + StartUp += Assert.CallsBack("TestStartUp"); + Test += Assert.CallsBack("TestCase"); + TearDown += Assert.CallsBack("TestTearDown"); + StartUp(null, default(TestEventArgs)); + } + + [APITestCase(TestType.Menu)] + public static void RunTest() + { + Test(null, default(TestEventArgs)); + } + + [APITestTearDown] + public static void End() + { + TearDown(null, default(TestEventArgs)); + } + } + + public struct TestEventArgs + { + public override string ToString() + { + return "TestEventArgs{}"; + } + } +#endif +}