Browse Source

Merge pull request #2187 from tig/fixes_2185

Fixes #2185. PR #2176 broke Run<T>
Tig 2 years ago
parent
commit
cf41b910f6

+ 65 - 50
Example/Example.cs

@@ -5,53 +5,68 @@
 
 using Terminal.Gui;
 
-// Initialize the console
-Application.Init();
-
-// Creates the top-level window with border and title
-var win = new Window("Example App (Ctrl+Q to quit)");
-
-// Create input components and labels
-
-var usernameLabel = new Label("Username:");
-var usernameText = new TextField("")
-{
-    // Position text field adjacent to label
-    X = Pos.Right(usernameLabel) + 1,
-
-    // Fill remaining horizontal space with a margin of 1
-    Width = Dim.Fill(1),
-};
-
-var passwordLabel = new Label(0,2,"Password:");
-var passwordText = new TextField("")
-{
-    Secret = true,
-    // align with the text box above
-    X = Pos.Left(usernameText),
-    Y = 2,
-    Width = Dim.Fill(1),
-};
-
-// Create login button
-var btnLogin = new Button("Login")
-{
-    Y = 4,
-    // center the login button horizontally
-    X = Pos.Center(),
-    IsDefault = true,
-};
-
-// When login button is clicked display a message popup
-btnLogin.Clicked += () => MessageBox.Query("Logging In", "Login Successful", "Ok");
-
-// Add all the views to the window
-win.Add(
-    usernameLabel, usernameText, passwordLabel, passwordText,btnLogin
-);
-
-// Show the application
-Application.Run(win);
-
-// After the application exits, release and reset console for clean shutdown
-Application.Shutdown();
+Application.Run<ExampleWindow> ();
+
+System.Console.WriteLine ($"Username: {((ExampleWindow)Application.Top).usernameText.Text}");
+
+// Before the application exits, reset Terminal.Gui for clean shutdown
+Application.Shutdown ();
+
+// Defines a top-level window with border and title
+public class ExampleWindow : Window {
+	public TextField usernameText;
+	
+	public ExampleWindow ()
+	{
+		Title = "Example App (Ctrl+Q to quit)";
+
+		// Create input components and labels
+		var usernameLabel = new Label () { 
+			Text = "Username:" 
+		};
+
+		usernameText = new TextField ("") {
+			// Position text field adjacent to the label
+			X = Pos.Right (usernameLabel) + 1,
+
+			// Fill remaining horizontal space
+			Width = Dim.Fill (),
+		};
+
+		var passwordLabel = new Label () {
+			Text = "Password:",
+			X = Pos.Left (usernameLabel),
+			Y = Pos.Bottom (usernameLabel) + 1
+		};
+
+		var passwordText = new TextField ("") {
+			Secret = true,
+			// align with the text box above
+			X = Pos.Left (usernameText),
+			Y = Pos.Top (passwordLabel),
+			Width = Dim.Fill (),
+		};
+
+		// Create login button
+		var btnLogin = new Button () {
+			Text = "Login",
+			Y = Pos.Bottom(passwordLabel) + 1,
+			// center the login button horizontally
+			X = Pos.Center (),
+			IsDefault = true,
+		};
+
+		// When login button is clicked display a message popup
+		btnLogin.Clicked += () => {
+			if (usernameText.Text == "admin" && passwordText.Text == "password") {
+				MessageBox.Query ("Logging In", "Login Successful", "Ok");
+				Application.RequestStop ();
+			} else {
+				MessageBox.ErrorQuery ("Logging In", "Incorrect username or password", "Ok");
+			}
+		};
+
+		// Add the views to the Window
+		Add (usernameLabel, usernameText, passwordLabel, passwordText, btnLogin);
+	}
+}

+ 65 - 50
README.md

@@ -68,56 +68,71 @@ The following example shows a basic Terminal.Gui application written in C#:
 
 using Terminal.Gui;
 
-// Initialize the console
-Application.Init();
-
-// Creates the top-level window with border and title
-var win = new Window("Example App (Ctrl+Q to quit)");
-
-// Create input components and labels
-
-var usernameLabel = new Label("Username:");
-var usernameText = new TextField("")
-{
-    // Position text field adjacent to label
-    X = Pos.Right(usernameLabel) + 1,
-
-    // Fill remaining horizontal space with a margin of 1
-    Width = Dim.Fill(1),
-};
-
-var passwordLabel = new Label(0,2,"Password:");
-var passwordText = new TextField("")
-{
-    Secret = true,
-    // align with the text box above
-    X = Pos.Left(usernameText),
-    Y = 2,
-    Width = Dim.Fill(1),
-};
-
-// Create login button
-var btnLogin = new Button("Login")
-{
-    Y = 4,
-    // center the login button horizontally
-    X = Pos.Center(),
-    IsDefault = true,
-};
-
-// When login button is clicked display a message popup
-btnLogin.Clicked += () => MessageBox.Query("Logging In", "Login Successful", "Ok");
-
-// Add all the views to the window
-win.Add(
-    usernameLabel, usernameText, passwordLabel, passwordText,btnLogin
-);
-
-// Show the application
-Application.Run(win);
-
-// After the application exits, release and reset console for clean shutdown
-Application.Shutdown();
+Application.Run<ExampleWindow> ();
+
+System.Console.WriteLine ($"Username: {((ExampleWindow)Application.Top).usernameText.Text}");
+
+// Before the application exits, reset Terminal.Gui for clean shutdown
+Application.Shutdown ();
+
+// Defines a top-level window with border and title
+public class ExampleWindow : Window {
+	public TextField usernameText;
+	
+	public ExampleWindow ()
+	{
+		Title = "Example App (Ctrl+Q to quit)";
+
+		// Create input components and labels
+		var usernameLabel = new Label () { 
+			Text = "Username:" 
+		};
+
+		usernameText = new TextField ("") {
+			// Position text field adjacent to the label
+			X = Pos.Right (usernameLabel) + 1,
+
+			// Fill remaining horizontal space
+			Width = Dim.Fill (),
+		};
+
+		var passwordLabel = new Label () {
+			Text = "Password:",
+			X = Pos.Left (usernameLabel),
+			Y = Pos.Bottom (usernameLabel) + 1
+		};
+
+		var passwordText = new TextField ("") {
+			Secret = true,
+			// align with the text box above
+			X = Pos.Left (usernameText),
+			Y = Pos.Top (passwordLabel),
+			Width = Dim.Fill (),
+		};
+
+		// Create login button
+		var btnLogin = new Button () {
+			Text = "Login",
+			Y = Pos.Bottom(passwordLabel) + 1,
+			// center the login button horizontally
+			X = Pos.Center (),
+			IsDefault = true,
+		};
+
+		// When login button is clicked display a message popup
+		btnLogin.Clicked += () => {
+			if (usernameText.Text == "admin" && passwordText.Text == "password") {
+				MessageBox.Query ("Logging In", "Login Successful", "Ok");
+				Application.RequestStop ();
+			} else {
+				MessageBox.ErrorQuery ("Logging In", "Incorrect username or password", "Ok");
+			}
+		};
+
+		// Add the views to the Window
+		Add (usernameLabel, usernameText, passwordLabel, passwordText, btnLogin);
+	}
+}
 ```
 
 When run the application looks as follows:

+ 11 - 7
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -1451,16 +1451,20 @@ namespace Terminal.Gui {
 		{
 			TerminalResized = terminalResized;
 
-			var winSize = WinConsole.GetConsoleOutputWindow (out Point pos);
-			cols = winSize.Width;
-			rows = winSize.Height;
+			try {
+				var winSize = WinConsole.GetConsoleOutputWindow (out Point pos);
+				cols = winSize.Width;
+				rows = winSize.Height;
 
-			WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
+				WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
 
-			ResizeScreen ();
-			UpdateOffScreen ();
+				ResizeScreen ();
+				UpdateOffScreen ();
 
-			CreateColors ();
+				CreateColors ();
+			} catch (Win32Exception e) {
+				throw new InvalidOperationException ("The Windows Console output window is not available.", e);
+			}
 		}
 
 		public override void ResizeScreen ()

+ 64 - 42
Terminal.Gui/Core/Application.cs

@@ -309,6 +309,9 @@ namespace Terminal.Gui {
 		/// </summary>
 		public static bool UseSystemConsole;
 
+		// For Unit testing - ignores UseSystemConsole
+		internal static bool ForceFakeConsole;
+
 		/// <summary>
 		/// Initializes a new instance of <see cref="Terminal.Gui"/> Application. 
 		/// </summary>
@@ -329,29 +332,28 @@ namespace Terminal.Gui {
 		/// into a single call. An applciation cam use <see cref="Run{T}(Func{Exception, bool}, ConsoleDriver, IMainLoopDriver)"/> 
 		/// without explicitly calling <see cref="Init(ConsoleDriver, IMainLoopDriver)"/>.
 		/// </para>
-		/// <param name="driver">The <see cref="ConsoleDriver"/> to use. If not specified the default driver for the
+		/// <param name="driver">
+		/// The <see cref="ConsoleDriver"/> to use. If not specified the default driver for the
 		/// platform will be used (see <see cref="WindowsDriver"/>, <see cref="CursesDriver"/>, and <see cref="NetDriver"/>).</param>
-		/// <param name="mainLoopDriver">Specifies the <see cref="MainLoop"/> to use.</param>
-		public static void Init (ConsoleDriver driver = null, IMainLoopDriver mainLoopDriver = null) => Init (() => Toplevel.Create (), driver, mainLoopDriver, resetState: true);
+		/// <param name="mainLoopDriver">
+		/// Specifies the <see cref="MainLoop"/> to use. 
+		/// Must not be <see langword="null"/> if <paramref name="driver"/> is not <see langword="null"/>.
+		/// </param>
+		public static void Init (ConsoleDriver driver = null, IMainLoopDriver mainLoopDriver = null) => InternalInit (() => Toplevel.Create (), driver, mainLoopDriver);
 
 		internal static bool _initialized = false;
 		internal static int _mainThreadId = -1;
 
-		/// <summary>
-		/// Internal function for initializing a Terminal.Gui application with a <see cref="Toplevel"/> factory object, 
-		/// a <see cref="ConsoleDriver"/>, and <see cref="MainLoop"/>.
-		/// <para>
-		/// This is a low-level function; most applications will use <see cref="Init(ConsoleDriver, IMainLoopDriver)"/> as it is simpler.</para>
-		/// </summary>
-		/// <param name="topLevelFactory">Specifies the <see cref="Toplevel"> factory funtion.</see>/></param>
-		/// <param name="driver">The <see cref="ConsoleDriver"/> to use. If not specified the default driver for the
-		/// platform will be used (see <see cref="WindowsDriver"/>, <see cref="CursesDriver"/>, and <see cref="NetDriver"/>).</param>
-		/// <param name="mainLoopDriver">Specifies the <see cref="MainLoop"/> to use.</param>
-		/// <param name="resetState">If <see langword="true"/> (default) all <see cref="Application"/> state will be reset. 
-		/// Set to <see langword="false"/> to not reset the state (for when this function is called via 
-		/// <see cref="Run{T}(Func{Exception, bool}, ConsoleDriver, IMainLoopDriver)"/> when <see cref="Init(ConsoleDriver, IMainLoopDriver)"/>
-		/// has not already been called. f</param>
-		internal static void Init (Func<Toplevel> topLevelFactory, ConsoleDriver driver = null, IMainLoopDriver mainLoopDriver = null, bool resetState = true)
+		// INTERNAL function for initializing an app with a Toplevel factory object, driver, and mainloop.
+		//
+		// Called from:
+		// 
+		// Init() - When the user wants to use the default Toplevel. calledViaRunT will be false, causing all state to be reset.
+		// Run<T>() - When the user wants to use a custom Toplevel. calledViaRunT will be true, enabling Run<T>() to be called without calling Init first.
+		// Unit Tests - To initialize the app with a custom Toplevel, using the FakeDriver. calledViaRunT will be false, causing all state to be reset.
+		// 
+		// calledViaRunT: If false (default) all state will be reset. If true the state will not be reset.
+		internal static void InternalInit (Func<Toplevel> topLevelFactory, ConsoleDriver driver = null, IMainLoopDriver mainLoopDriver = null, bool calledViaRunT = false)
 		{
 			if (_initialized && driver == null) return;
 
@@ -359,6 +361,7 @@ namespace Terminal.Gui {
 				throw new InvalidOperationException ("Init has already been called and must be bracketed by Shutdown.");
 			}
 
+			// Note in this case, we don't verify the type of the Toplevel created by new T(). 
 			// Used only for start debugging on Unix.
 			//#if DEBUG
 			//			while (!System.Diagnostics.Debugger.IsAttached) {
@@ -367,22 +370,29 @@ namespace Terminal.Gui {
 			//			System.Diagnostics.Debugger.Break ();
 			//#endif
 
-			// Reset all class variables (Application is a singleton).
-			if (resetState) {
+			if (!calledViaRunT) {
+				// Reset all class variables (Application is a singleton).
 				ResetState ();
 			}
 
-			// This supports Unit Tests and the passing of a mock driver/loopdriver
+			// FakeDriver (for UnitTests)
 			if (driver != null) {
 				if (mainLoopDriver == null) {
-					throw new ArgumentNullException ("mainLoopDriver cannot be null if driver is provided.");
+					throw new ArgumentNullException ("InternalInit mainLoopDriver cannot be null if driver is provided.");
+				}
+				if (!(driver is FakeDriver)) {
+					throw new InvalidOperationException ("InternalInit can only be called with FakeDriver.");
 				}
 				Driver = driver;
 			}
 
 			if (Driver == null) {
 				var p = Environment.OSVersion.Platform;
-				if (UseSystemConsole) {
+				if (ForceFakeConsole) {
+					// For Unit Testing only
+					Driver = new FakeDriver ();
+					mainLoopDriver = new FakeMainLoop (() => FakeConsole.ReadKey (true));
+				} else if (UseSystemConsole) {
 					Driver = new NetDriver ();
 					mainLoopDriver = new NetMainLoop (Driver);
 				} else if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) {
@@ -395,7 +405,16 @@ namespace Terminal.Gui {
 			}
 			MainLoop = new MainLoop (mainLoopDriver);
 
-			Driver.Init (TerminalResized);
+			try {
+				Driver.Init (TerminalResized);
+			} catch (InvalidOperationException ex) {
+				// This is a case where the driver is unable to initialize the console.
+				// This can happen if the console is already in use by another process or
+				// if running in unit tests.
+				// In this case, we want to throw a more specific exception.
+				throw new InvalidOperationException ("Unable to initialize the console. This can happen if the console is already in use by another process or in unit tests.", ex);
+			}
+			
 			SynchronizationContext.SetSynchronizationContext (new MainLoopSyncContext (MainLoop));
 
 			Top = topLevelFactory ();
@@ -1250,8 +1269,7 @@ namespace Terminal.Gui {
 		/// Runs the application by calling <see cref="Run(Toplevel, Func{Exception, bool})"/> 
 		/// with a new instance of the specified <see cref="Toplevel"/>-derived class.
 		/// <para>
-		/// If <see cref="Init(ConsoleDriver, IMainLoopDriver)"/> has not arleady been called, this function will
-		/// call <see cref="Init(Func{Toplevel}, ConsoleDriver, IMainLoopDriver, bool)"/>.
+		/// Calling <see cref="Init(ConsoleDriver, IMainLoopDriver)"/> first is not needed as this function will initialze the application.
 		/// </para>
 		/// <para>
 		/// <see cref="Shutdown"/> must be called when the application is closing (typically after Run> has 
@@ -1263,27 +1281,31 @@ namespace Terminal.Gui {
 		/// </remarks>
 		/// <param name="errorHandler"></param>
 		/// <param name="driver">The <see cref="ConsoleDriver"/> to use. If not specified the default driver for the
-		/// platform will be used (see <see cref="WindowsDriver"/>, <see cref="CursesDriver"/>, and <see cref="NetDriver"/>).</param>
+		/// platform will be used (<see cref="WindowsDriver"/>, <see cref="CursesDriver"/>, or <see cref="NetDriver"/>).
+		/// This parameteter must be <see langword="null"/> if <see cref="Init(ConsoleDriver, IMainLoopDriver)"/> has already been called. 
+		/// </param>
 		/// <param name="mainLoopDriver">Specifies the <see cref="MainLoop"/> to use.</param>
 		public static void Run<T> (Func<Exception, bool> errorHandler = null, ConsoleDriver driver = null, IMainLoopDriver mainLoopDriver = null) where T : Toplevel, new()
 		{
-			if (_initialized && Driver != null) {
-				var top = new T ();
-				var type = top.GetType ().BaseType;
-				while (type != typeof (Toplevel) && type != typeof (object)) {
-					type = type.BaseType;
-				}
-				if (type != typeof (Toplevel)) {
-					throw new ArgumentException ($"{top.GetType ().Name} must be derived from TopLevel");
+			if (_initialized) {
+				if (Driver != null) {
+					// Init() has been called and we have a driver, so just run the app.
+					var top = new T ();
+					var type = top.GetType ().BaseType;
+					while (type != typeof (Toplevel) && type != typeof (object)) {
+						type = type.BaseType;
+					}
+					if (type != typeof (Toplevel)) {
+						throw new ArgumentException ($"{top.GetType ().Name} must be derived from TopLevel");
+					}
+					Run (top, errorHandler);
+				} else {
+					// This codepath should be impossible because Init(null, null) will select the platform default driver
+					throw new InvalidOperationException ("Init() completed without a driver being set (this should be impossible); Run<T>() cannot be called.");
 				}
-				// Run() will eventually cause Application.Top to be set, via Begin() and SetCurrentAsTop()
-				Run (top, errorHandler);
 			} else {
-				if (!_initialized && driver == null) {
-					throw new ArgumentException ("Init has not been called; a valid driver and mainloop must be provided");
-				}
-				// Note in this case, we don't verify the type of the Toplevel created by new T(). 
-				Init (() => new T (), Driver == null ? driver : Driver, Driver == null ? mainLoopDriver : null, resetState: false);
+				// Init() has NOT been called.
+				InternalInit (() => new T (), driver, mainLoopDriver, calledViaRunT: true);
 				Run (Top, errorHandler);
 			}
 		}

+ 0 - 4
UICatalog/Scenarios/BackgroundWorkerCollection.cs

@@ -12,10 +12,6 @@ namespace UICatalog.Scenarios {
 	[ScenarioCategory ("Dialogs")]
 	[ScenarioCategory ("Controls")]
 	public class BackgroundWorkerCollection : Scenario {
-		public override void Init (ColorScheme colorScheme)
-		{
-			// Do not call Init as Application.Run<T> will do it
-		}
 
 		public override void Run ()
 		{

+ 76 - 0
UICatalog/Scenarios/Generic - Copy.cs

@@ -0,0 +1,76 @@
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios {
+	[ScenarioMetadata (Name: "Run<T> Example", Description: "Illustrates using Application.Run<T> to run a custom class")]
+	[ScenarioCategory ("Top Level Windows")]
+	public class RunTExample : Scenario {
+		public override void Setup ()
+		{
+			// No need to call Init if Application.Run<T> is used
+		}
+
+		public override void Run ()
+		{
+			Application.Run<ExampleWindow> ();
+		}
+
+		public class ExampleWindow : Window {
+			public TextField usernameText;
+
+			public ExampleWindow ()
+			{
+				Title = "Example App (Ctrl+Q to quit)";
+
+				// Create input components and labels
+				var usernameLabel = new Label () {
+					Text = "Username:"
+				};
+
+				usernameText = new TextField ("") {
+					// Position text field adjacent to the label
+					X = Pos.Right (usernameLabel) + 1,
+
+					// Fill remaining horizontal space
+					Width = Dim.Fill (),
+				};
+
+				var passwordLabel = new Label () {
+					Text = "Password:",
+					X = Pos.Left (usernameLabel),
+					Y = Pos.Bottom (usernameLabel) + 1
+				};
+
+				var passwordText = new TextField ("") {
+					Secret = true,
+					// align with the text box above
+					X = Pos.Left (usernameText),
+					Y = Pos.Top (passwordLabel),
+					Width = Dim.Fill (),
+				};
+
+				// Create login button
+				var btnLogin = new Button () {
+					Text = "Login",
+					Y = Pos.Bottom (passwordLabel) + 1,
+					// center the login button horizontally
+					X = Pos.Center (),
+					IsDefault = true,
+				};
+
+				// When login button is clicked display a message popup
+				btnLogin.Clicked += () => {
+					if (usernameText.Text == "admin" && passwordText.Text == "password") {
+						MessageBox.Query ("Login Successful", $"Username: {usernameText.Text}", "Ok");
+						Application.RequestStop ();
+					} else {
+						MessageBox.ErrorQuery ("Error Logging In", "Incorrect username or password (hint: admin/password)", "Ok");
+					}
+				};
+
+				// Add the views to the Window
+				Add (usernameLabel, usernameText, passwordLabel, passwordText, btnLogin);
+			}
+		}
+
+	}
+}

+ 1 - 0
UICatalog/UICatalog.cs

@@ -117,6 +117,7 @@ namespace UICatalog {
 
 			// Run UI Catalog UI. When it exits, if _selectedScenario is != null then
 			// a Scenario was selected. Otherwise, the user wants to exit UI Catalog.
+			Application.Init ();
 			Application.Run<UICatalogTopLevel> ();
 			Application.Shutdown ();
 

+ 61 - 12
UnitTests/ApplicationTests.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Diagnostics;
 using System.Linq;
+using System.Runtime.InteropServices;
 using System.Threading;
 using System.Threading.Tasks;
 using Xunit;
@@ -100,7 +101,7 @@ namespace Terminal.Gui.Core {
 			Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
 
 			Toplevel topLevel = null;
-			Assert.Throws<InvalidOperationException> (() => Application.Init (() => topLevel = new TestToplevel (), new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))));
+			Assert.Throws<InvalidOperationException> (() => Application.InternalInit (() => topLevel = new TestToplevel (), new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))));
 			Shutdown ();
 
 			Assert.Null (Application.Top);
@@ -109,7 +110,7 @@ namespace Terminal.Gui.Core {
 
 			// Now try the other way
 			topLevel = null;
-			Application.Init (() => topLevel = new TestToplevel (), new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
+			Application.InternalInit (() => topLevel = new TestToplevel (), new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
 
 			Assert.Throws<InvalidOperationException> (() => Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))));
 			Shutdown ();
@@ -118,7 +119,7 @@ namespace Terminal.Gui.Core {
 			Assert.Null (Application.MainLoop);
 			Assert.Null (Application.Driver);
 		}
-	
+
 
 		class TestToplevel : Toplevel {
 			public TestToplevel ()
@@ -131,7 +132,7 @@ namespace Terminal.Gui.Core {
 		public void Init_Begin_End_Cleans_Up ()
 		{
 			Init ();
-			
+
 			// Begin will cause Run() to be called, which will call Begin(). Thus will block the tests
 			// if we don't stop
 			Application.Iteration = () => {
@@ -145,7 +146,7 @@ namespace Terminal.Gui.Core {
 			};
 			Application.NotifyNewRunState += NewRunStateFn;
 
-			Toplevel topLevel = new Toplevel();
+			Toplevel topLevel = new Toplevel ();
 			var rs = Application.Begin (topLevel);
 			Assert.NotNull (rs);
 			Assert.NotNull (runstate);
@@ -181,7 +182,7 @@ namespace Terminal.Gui.Core {
 			// NOTE: Run<T>, when called after Init has been called behaves differently than
 			// when called if Init has not been called.
 			Toplevel topLevel = null;
-			Application.Init (() => topLevel = new TestToplevel (), new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
+			Application.InternalInit (() => topLevel = new TestToplevel (), new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
 
 			Application.RunState runstate = null;
 			Action<Application.RunState> NewRunStateFn = (rs) => {
@@ -272,9 +273,9 @@ namespace Terminal.Gui.Core {
 			Application.Iteration = () => {
 				Application.RequestStop ();
 			};
-			
+
 			// Init has been called and we're passing no driver to Run<TestTopLevel>. This is ok.
-			Application.Run<TestToplevel> (errorHandler: null);
+			Application.Run<TestToplevel> ();
 
 			Shutdown ();
 
@@ -284,14 +285,59 @@ namespace Terminal.Gui.Core {
 		}
 
 		[Fact]
-		public void Run_T_NoInit_Throws ()
+		public void Run_T_After_InitNullDriver_with_TestTopLevel_Throws ()
 		{
+			Application.ForceFakeConsole = true;
+
+			Application.Init (null, null);
+			Assert.Equal (typeof (FakeDriver), Application.Driver.GetType ());
+
 			Application.Iteration = () => {
 				Application.RequestStop ();
 			};
 
-			// Init has NOT been called and we're passing no driver to Run<TestToplevel>. This is an error.
-			Assert.Throws<ArgumentException> (() => Application.Run<TestToplevel> (errorHandler: null, driver: null, mainLoopDriver: null));
+			// Init has been called without selecting a driver and we're passing no driver to Run<TestTopLevel>. Bad
+			Application.Run<TestToplevel> ();
+
+			Shutdown ();
+
+			Assert.Null (Application.Top);
+			Assert.Null (Application.MainLoop);
+			Assert.Null (Application.Driver);
+		}
+
+		[Fact]
+		public void Run_T_Init_Driver_Cleared_with_TestTopLevel_Throws ()
+		{
+			Init ();
+			
+			Application.Driver = null;
+
+			Application.Iteration = () => {
+				Application.RequestStop ();
+			};
+
+			// Init has been called, but Driver has been set to null. Bad.
+			Assert.Throws<InvalidOperationException> (() => Application.Run<TestToplevel> ());
+
+			Shutdown ();
+
+			Assert.Null (Application.Top);
+			Assert.Null (Application.MainLoop);
+			Assert.Null (Application.Driver);
+		}
+
+		[Fact]
+		public void Run_T_NoInit_DoesNotThrow ()
+		{
+			Application.ForceFakeConsole = true; 
+			
+			Application.Iteration = () => {
+				Application.RequestStop ();
+			};
+
+			Application.Run<TestToplevel> ();
+			Assert.Equal (typeof (FakeDriver), Application.Driver.GetType ());
 
 			Shutdown ();
 
@@ -379,6 +425,9 @@ namespace Terminal.Gui.Core {
 			Application.Shutdown ();
 			Assert.Equal (3, count);
 		}
+
+		// TODO: Add tests for Run that test errorHandler
+		
 		#endregion
 
 		#region ShutdownTests
@@ -407,7 +456,7 @@ namespace Terminal.Gui.Core {
 			Assert.Null (SynchronizationContext.Current);
 		}
 		#endregion
-		
+
 		[Fact]
 		[AutoInitShutdown]
 		public void SetCurrentAsTop_Run_A_Not_Modal_Toplevel_Make_It_The_Current_Application_Top ()