diff --git a/Assets/Raindrop/Bootstrap/RaindropBootstrapper.cs b/Assets/Raindrop/Bootstrap/RaindropBootstrapper.cs index 1502418..d3ccc6c 100644 --- a/Assets/Raindrop/Bootstrap/RaindropBootstrapper.cs +++ b/Assets/Raindrop/Bootstrap/RaindropBootstrapper.cs @@ -32,10 +32,19 @@ namespace Raindrop.Bootstrap StartUIScene(); } } + + public void OnDestroy() + { + Quit_Application(); + } + #region Bootstrap functions + private void StartUIScene() { - SceneManager.LoadScene("Raindrop/Bootstrap/MainScene"); + SceneManager.LoadScene( + "Raindrop/Bootstrap/MainScene", + LoadSceneMode.Additive); } // prep user file system for LMV's usage, by copying static files. @@ -45,13 +54,18 @@ namespace Raindrop.Bootstrap var copy_result = copier.Work(); if (copy_result == -1) { - Logger.Log("static files copier failed", Helpers.LogLevel.Error); + Logger.Log( + "static files copier failed", + Helpers.LogLevel.Error); } } private static void SendStartupMessageToLogger() { - Logger.Log("RaindropBootstrapper.cs : RaindropInstance Started and succesfully linked to servicelocator.", Helpers.LogLevel.Info); + Logger.Log("RaindropBootstrapper.cs : " + + "RaindropInstance Started and " + + "succesfully linked to servicelocator.", + Helpers.LogLevel.Info); } public static void Start_Raindrop_CoreDependencies() @@ -106,10 +120,6 @@ namespace Raindrop.Bootstrap } - public void OnApplicationQuit() - { - Quit_Application(); - } // globally-accessible quit method. public void Quit_Application() @@ -157,10 +167,14 @@ namespace Raindrop.Bootstrap } frmMain_Disposed(instance); + + Unregister_RaindropInstance(); + Debug.Log("disposed netcom and client! :) This marks the end of the app."); } - //wraps up the netcom and client. + // wraps up the netcom (synchronisation context) , + // followed by the client instance. void frmMain_Disposed(RaindropInstance instance) { RaindropNetcom netcom = instance.Netcom; @@ -182,5 +196,15 @@ namespace Raindrop.Bootstrap instance.CleanUp(); } + + public static void Unregister_RaindropInstance() + { + if (ServiceLocator.Instance.IsRegistered()) + { + ServiceLocator.Instance.Unregister(); + } + } + #endregion + } } diff --git a/Assets/Raindrop/Bootstrap/RaindropLoader.cs b/Assets/Raindrop/Bootstrap/RaindropLoader.cs new file mode 100644 index 0000000..f57fbbe --- /dev/null +++ b/Assets/Raindrop/Bootstrap/RaindropLoader.cs @@ -0,0 +1,24 @@ +using UnityEngine.SceneManagement; + +namespace Raindrop.Bootstrap +{ + // call these methods to load the game. + public static class RaindropLoader + { + public static void Load() + { + SceneManager.LoadScene( + "Raindrop/Bootstrap/BootstrapScene", + LoadSceneMode.Single); + } + + public static void Unload() + { + // calling with LoadSceneMode.Single will call Destroy() on + // RaindropBootstrapper, causing a cascade of teardown, + // similar to what happens in the actual device + var emptyScene = "Scenes/empty"; + SceneManager.LoadScene(emptyScene, LoadSceneMode.Single); + } + } +} \ No newline at end of file diff --git a/Assets/Raindrop/Bootstrap/RaindropLoader.cs.meta b/Assets/Raindrop/Bootstrap/RaindropLoader.cs.meta new file mode 100644 index 0000000..c0add3a --- /dev/null +++ b/Assets/Raindrop/Bootstrap/RaindropLoader.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 37bdc91a50854f16a60547b011c4e1e1 +timeCreated: 1657735609 \ No newline at end of file diff --git a/Assets/Raindrop/Bootstrap/UIBootstrapper.cs b/Assets/Raindrop/Bootstrap/UIBootstrapper.cs index ef4f4c2..e53a479 100644 --- a/Assets/Raindrop/Bootstrap/UIBootstrapper.cs +++ b/Assets/Raindrop/Bootstrap/UIBootstrapper.cs @@ -1,4 +1,5 @@ -using Plugins.CommonDependencies; +using System; +using Plugins.CommonDependencies; using Raindrop.Map.Model; using Raindrop.Netcom; using Raindrop.Services; @@ -20,13 +21,26 @@ namespace Raindrop.Bootstrap // has a funny role; in that it will register itself to the UIservice on start/awake. // this should really be the other way round - that the UIservice // creates/has dependency on the UIrootGO!!! - private void Awake() + private void Start() { //RaindropBootstrapper.Start_Raindrop_CoreDependencies(); // hacky - to ensure that the UI's dependencies are ready. OpenMetaverse.Logger.Log("UI layer of application Started. Logging Started.", OpenMetaverse.Helpers.LogLevel.Info); InitialiseUIVariant(); } + private void OnDestroy() + { + if (ServiceLocator.Instance.IsRegistered()) + { + ServiceLocator.Instance.Unregister(); + } + if (ServiceLocator.Instance.IsRegistered()) + { + ServiceLocator.Instance.Unregister(); + } + + } + //warn: if this init method is called too early, there can be issues. private void InitialiseUIVariant() { @@ -49,6 +63,7 @@ namespace Raindrop.Bootstrap references.ll.Init(); var uisrv = new UIService( + instance, references.sm, references.mm, references.ll, diff --git a/Assets/Raindrop/Services/UIService.cs b/Assets/Raindrop/Services/UIService.cs index f77a252..78b018e 100644 --- a/Assets/Raindrop/Services/UIService.cs +++ b/Assets/Raindrop/Services/UIService.cs @@ -20,7 +20,7 @@ namespace Raindrop.Services // Notification - manages app-wide notifications. // LoadingCanvasPresenter - this particular modal/screen is tricky; it appears only when the scene is loading. - private RaindropInstance instance => ServiceLocator.Instance.Get(); + private RaindropInstance instance; private RaindropNetcom netcom { get { return instance.Netcom; } } private GridClient client { get { return instance.Client; } } @@ -45,9 +45,11 @@ namespace Raindrop.Services * +UIService.showScreen(UIBuilder(CanvasType.Login)) * +UIService.showModal */ - public UIService(ScreenStackManager cm, ModalsManager mm, LoadingView loadingView, + public UIService(RaindropInstance raindropInstance, + ScreenStackManager cm, ModalsManager mm, LoadingView loadingView, ChatPresenter ChatPresenter) { + instance = raindropInstance; ScreenStackManager = cm; ModalsManager = mm; _loadingController = new LoadingController(loadingView); diff --git a/Assets/Raindrop/UI/UpdateCameraBackend.cs b/Assets/Raindrop/UI/UpdateCameraBackend.cs index 0174444..4398bf8 100644 --- a/Assets/Raindrop/UI/UpdateCameraBackend.cs +++ b/Assets/Raindrop/UI/UpdateCameraBackend.cs @@ -12,13 +12,15 @@ using Camera = UnityEngine.Camera; [RequireComponent(typeof(Camera))] public class UpdateCameraBackend : MonoBehaviour { - private RaindropInstance instance { get { return ServiceLocator.Instance.Get(); } } - bool Active => instance.Client.Network.Connected && (instance.Client.Network.CurrentSim != null); + private RaindropInstance instance; + bool Active => instance.Client.Network.Connected && + (instance.Client.Network.CurrentSim != null); public Camera cam; void Start() { cam = GetComponent(); + instance = ServiceLocator.Instance.Get(); } private void Update() diff --git a/Assets/Tests/Raindrop/RaindropFullIntegrationTests/SoundTests.cs b/Assets/Tests/Raindrop/RaindropFullIntegrationTests/SoundTests.cs index 38209b1..703af35 100644 --- a/Assets/Tests/Raindrop/RaindropFullIntegrationTests/SoundTests.cs +++ b/Assets/Tests/Raindrop/RaindropFullIntegrationTests/SoundTests.cs @@ -5,6 +5,7 @@ using NUnit.Framework; using OpenMetaverse; using Plugins.CommonDependencies; using Raindrop; +using Raindrop.Bootstrap; using Raindrop.Media; using UnityEngine; using UnityEngine.SceneManagement; @@ -15,11 +16,19 @@ namespace Tests.Raindrop.SoundTests [TestFixture()] public class SoundTests { - + // TODO: note that current soundtests is not failing on warnings of the type: + // Failed to initialize the sound system: Raindrop.Media.MediaException: FMOD error! ERR_MEMORY - Not enough memory or resources. + [SetUp] public void Setup() { - SceneManager.LoadScene("Raindrop/Bootstrap/BootstrapScene"); + RaindropLoader.Load(); + } + + [TearDown] + public void TearDown() + { + RaindropLoader.Unload(); } // copy sample audio to cache. @@ -63,9 +72,9 @@ namespace Tests.Raindrop.SoundTests { File.Copy(fromPath, toPath); } - catch + catch (Exception e) { - Debug.Log("failed to copy sample sound file for the test."); + Debug.Log("failed to copy sample sound file for the test: " + e.ToString()); } } } @@ -107,7 +116,4 @@ namespace Tests.Raindrop.SoundTests } } - - - } \ No newline at end of file diff --git a/Assets/Tests/Raindrop/RaindropFullIntegrationTests/StabilityTest.cs b/Assets/Tests/Raindrop/RaindropFullIntegrationTests/StabilityTest.cs new file mode 100644 index 0000000..570ac5e --- /dev/null +++ b/Assets/Tests/Raindrop/RaindropFullIntegrationTests/StabilityTest.cs @@ -0,0 +1,39 @@ +using System.Collections; +using NUnit.Framework; +using Raindrop.Bootstrap; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnityEngine.TestTools; + +namespace Tests.Raindrop.RaindropFullIntegrationTests +{ + [TestFixture()] + + public class StabilityTest + { + // Check if we can load and unload game in the test environment, + // consecutively. + // Addresses github issue #10 + [UnityTest] + public IEnumerator StabilityTest_Restart_RaindropApp() + { + int iterations = 3; + for (int i = 0; i < iterations; i++) + { + Debug.Log($"scene iteration {i} start"); + Debug.Log("Load bootstrap scene: "); + + RaindropLoader.Load(); + + yield return new WaitForSeconds(8); + + Debug.Log("unload entire scene: "); + + RaindropLoader.Unload(); + + yield return new WaitForSeconds(2); + Debug.Log($"scene iteration {i} done"); + } + } + } +} \ No newline at end of file diff --git a/Assets/Tests/Raindrop/RaindropFullIntegrationTests/StabilityTest.cs.meta b/Assets/Tests/Raindrop/RaindropFullIntegrationTests/StabilityTest.cs.meta new file mode 100644 index 0000000..826a231 --- /dev/null +++ b/Assets/Tests/Raindrop/RaindropFullIntegrationTests/StabilityTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f103b30862a14c5abd9f2bb68f8223c6 +timeCreated: 1657735870 \ No newline at end of file