using System.Diagnostics; using System.Globalization; using System.Reflection; using Xunit.Sdk; namespace UnitTests; /// /// Enables test functions annotated with the [AutoInitShutdown] attribute to /// automatically call Application.Init at start of the test and Application.Shutdown after the /// test exits. /// This is necessary because a) Application is a singleton and Init/Shutdown must be called /// as a pair, and b) all unit test functions should be atomic.. /// [AttributeUsage (AttributeTargets.Class | AttributeTargets.Method)] public class AutoInitShutdownAttribute : BeforeAfterTestAttribute { /// /// Initializes a [AutoInitShutdown] attribute, which determines if/how Application.Init and Application.Shutdown /// are automatically called Before/After a test runs. /// /// If true, Application.Init will be called Before the test runs. /// /// Forces the specified driver ("windows", "dotnet", "unix", or "fake") to /// be used when Application.Init is called. If not specified FakeDriver will be used. Only valid if /// is true. /// public AutoInitShutdownAttribute ( bool autoInit = true, string forceDriver = null ) { _autoInit = autoInit; CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US"); _forceDriver = forceDriver; } private readonly string _forceDriver; private IDisposable _v2Cleanup; public override void After (MethodInfo methodUnderTest) { Debug.WriteLine ($"After: {methodUnderTest?.Name ?? "Unknown Test"}"); // Turn off diagnostic flags in case some test left them on View.Diagnostics = ViewDiagnosticFlags.Off; _v2Cleanup?.Dispose (); if (_autoInit) { try { Application.Shutdown (); #if DEBUG_IDISPOSABLE if (View.Instances.Count == 0) { Assert.Empty (View.Instances); } else { View.Instances.Clear (); } #endif } //catch (Exception e) //{ // Debug.WriteLine ($"Application.Shutdown threw an exception after the test exited: {e}"); //} finally { #if DEBUG_IDISPOSABLE View.Instances.Clear (); Application.ResetState (true); #endif ApplicationImpl.SetInstance (null); } } Debug.Assert (!CM.IsEnabled, "This test left ConfigurationManager enabled!"); // Force the ConfigurationManager to reset to its hardcoded defaults CM.Disable (true); } public override void Before (MethodInfo methodUnderTest) { Debug.WriteLine ($"Before: {methodUnderTest?.Name ?? "Unknown Test"}"); Debug.Assert (!CM.IsEnabled, "A previous test left ConfigurationManager enabled!"); // Disable & force the ConfigurationManager to reset to its hardcoded defaults CM.Disable (true); //Debug.Assert(!CM.IsEnabled, "Some other test left ConfigurationManager enabled."); if (_autoInit) { #if DEBUG_IDISPOSABLE View.EnableDebugIDisposableAsserts = true; // Clear out any lingering Responder instances from previous tests if (View.Instances.Count == 0) { Assert.Empty (View.Instances); } else { View.Instances.Clear (); } #endif if (string.IsNullOrEmpty (_forceDriver) || _forceDriver.ToLowerInvariant () == "fake") { var fa = new FakeApplicationFactory (); _v2Cleanup = fa.SetupFakeApplication (); } else { Assert.Fail ("Specifying driver name not yet supported"); //Application.Init ((IDriver)Activator.CreateInstance (_forceDriver)); } } } private bool _autoInit { get; } /// /// Runs a single iteration of the main loop (layout, draw, run timed events etc.) /// public static void RunIteration () { ApplicationImpl a = (ApplicationImpl)ApplicationImpl.Instance; a.Coordinator?.RunIteration (); } }