소스 검색

Tests and polish for FileDialog (#2522)

* Add dir selection tests

* Make tests work on windows or linux

* Fixed multi-selection issues

- ".." no longer a valid multi select item
- Pressing Alt+O (or clicking ok) now prefers completing multi
  selection (to completing text box path)
- Pressing Enter in text box still uses Path as selection (ignoring
  any multi selection)

* Allow toggling <space> then <enter> to perform single dir select

* FileDialogState now remembers Path
Makes back button smoother in some nieche situations

* Added Culture property to FileDialog

* Fix bad date/time format

---------

Co-authored-by: Tig <[email protected]>
Thomas Nind 2 년 전
부모
커밋
f622eaf177

+ 5 - 1
Terminal.Gui/FileServices/FileDialogHistory.cs

@@ -19,12 +19,15 @@ namespace Terminal.Gui {
 
 			IDirectoryInfo goTo = null;
 			FileSystemInfoStats restoreSelection = null;
+			string restorePath = null;
 
 			if (this.CanBack ()) {
 
 				var backTo = this.back.Pop ();
 				goTo = backTo.Directory;
 				restoreSelection = backTo.Selected;
+				restorePath = backTo.Path;
+
 			} else if (this.CanUp ()) {
 				goTo = this.dlg.State?.Directory.Parent;
 			}
@@ -35,7 +38,8 @@ namespace Terminal.Gui {
 			}
 
 			this.forward.Push (this.dlg.State);
-			this.dlg.PushState (goTo, false, true, false);
+			this.dlg.PushState (goTo, false, true, false, restorePath);
+
 
 			if (restoreSelection != null) {
 				this.dlg.RestoreSelection (restoreSelection.FileSystemInfo);

+ 11 - 3
Terminal.Gui/FileServices/FileDialogState.cs

@@ -10,10 +10,18 @@ namespace Terminal.Gui {
 
 		public FileSystemInfoStats Selected { get; set; }
 		protected readonly FileDialog Parent;
+
+		/// <summary>
+		/// Gets what was entered in the path text box of the dialog
+		/// when the state was active.
+		/// </summary>
+		public string Path { get; }
+		
 		public FileDialogState (IDirectoryInfo dir, FileDialog parent)
 		{
 			this.Directory = dir;
 			Parent = parent;
+			Path = parent.Path;
 
 			this.RefreshChildren ();
 		}
@@ -36,9 +44,9 @@ namespace Terminal.Gui {
 
 				// if directories only
 				if (Parent.OpenMode == OpenMode.Directory) {
-					children = dir.GetDirectories ().Select (e => new FileSystemInfoStats (e)).ToList ();
+					children = dir.GetDirectories ().Select (e => new FileSystemInfoStats (e, Parent.Style.Culture)).ToList ();
 				} else {
-					children = dir.GetFileSystemInfos ().Select (e => new FileSystemInfoStats (e)).ToList ();
+					children = dir.GetFileSystemInfos ().Select (e => new FileSystemInfoStats (e, Parent.Style.Culture)).ToList ();
 				}
 
 				// if only allowing specific file types
@@ -57,7 +65,7 @@ namespace Terminal.Gui {
 
 				// allow navigating up as '..'
 				if (dir.Parent != null) {
-					children.Add (new FileSystemInfoStats (dir.Parent) { IsParent = true });
+					children.Add (new FileSystemInfoStats (dir.Parent, Parent.Style.Culture) { IsParent = true });
 				}
 
 				return children;

+ 6 - 0
Terminal.Gui/FileServices/FileDialogStyle.cs

@@ -37,6 +37,12 @@ namespace Terminal.Gui {
 		/// </summary>
 		public bool UseColors { get; set; }
 
+		/// <summary>
+		/// Gets or sets the culture to use (e.g. for number formatting).
+		/// Defaults to <see cref="CultureInfo.CurrentUICulture"/>.
+		/// <summary>
+		public CultureInfo Culture {get;set;} = CultureInfo.CurrentUICulture;
+
 		/// <summary>
 		/// Sets a <see cref="ColorScheme"/> to use for directories rows of
 		/// the <see cref="TableView"/>.

+ 7 - 6
Terminal.Gui/FileServices/FileSystemInfoStats.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.IO.Abstractions;
 using System.Linq;
@@ -33,14 +34,15 @@ namespace Terminal.Gui {
 		/// Initializes a new instance of the <see cref="FileSystemInfoStats"/> class.
 		/// </summary>
 		/// <param name="fsi">The directory of path to wrap.</param>
-		public FileSystemInfoStats (IFileSystemInfo fsi)
+		/// <param name="culture"></param>
+		public FileSystemInfoStats (IFileSystemInfo fsi, CultureInfo culture)
 		{
 			this.FileSystemInfo = fsi;
 			this.LastWriteTime = fsi.LastWriteTime;
 
 			if (fsi is IFileInfo fi) {
 				this.MachineReadableLength = fi.Length;
-				this.HumanReadableLength = GetHumanReadableFileSize (this.MachineReadableLength);				
+				this.HumanReadableLength = GetHumanReadableFileSize (this.MachineReadableLength, culture);
 				this.Type = fi.Extension;
 			} else {
 				this.HumanReadableLength = string.Empty;
@@ -112,11 +114,11 @@ namespace Terminal.Gui {
 			return 100;
 		}
 
-		private static string GetHumanReadableFileSize (long value)
+		private static string GetHumanReadableFileSize (long value, CultureInfo culture)
 		{
 
 			if (value < 0) {
-				return "-" + GetHumanReadableFileSize (-value);
+				return "-" + GetHumanReadableFileSize (-value, culture);
 			}
 
 			if (value == 0) {
@@ -125,8 +127,7 @@ namespace Terminal.Gui {
 
 			int mag = (int)Math.Log (value, ByteConversion);
 			double adjustedSize = value / Math.Pow (1000, mag);
-
-			return string.Format ("{0:n2} {1}", adjustedSize, SizeSuffixes [mag]);
+			return string.Format (culture.NumberFormat,"{0:n2} {1}", adjustedSize, SizeSuffixes [mag]);
 		}
 	}
 }

+ 61 - 25
Terminal.Gui/Views/FileDialog.cs

@@ -148,7 +148,7 @@ namespace Terminal.Gui {
 					// TODO: Fiddle factor, seems the Bounds are wrong for someone
 					- 2)
 			};
-			this.btnOk.Clicked += (s, e) => this.Accept ();
+			this.btnOk.Clicked += (s, e) => this.Accept (true);
 			this.btnOk.KeyPress += (s, k) => {
 				this.NavigateIf (k, Key.CursorLeft, this.btnCancel);
 				this.NavigateIf (k, Key.CursorUp, this.tableView);
@@ -772,7 +772,11 @@ namespace Terminal.Gui {
 		{
 			if (!keyEvent.Handled && keyEvent.KeyEvent.Key == isKey) {
 				keyEvent.Handled = true;
-				this.Accept ();
+
+				// User hit Enter in text box so probably wants the
+				// contents of the text box as their selection not
+				// whatever lingering selection is in TableView
+				this.Accept (false);
 			}
 		}
 
@@ -782,7 +786,12 @@ namespace Terminal.Gui {
 				return;
 			}
 
-			this.MultiSelected = toMultiAccept.Select (s => s.FileSystemInfo.FullName).ToList ().AsReadOnly ();
+			// Don't include ".." (IsParent) in multiselections
+			this.MultiSelected = toMultiAccept
+				.Where(s=>!s.IsParent)
+				.Select (s => s.FileSystemInfo.FullName)
+				.ToList ().AsReadOnly ();
+
 			this.tbPath.Text = this.MultiSelected.Count == 1 ? this.MultiSelected [0] : string.Empty;
 
 			FinishAccept ();
@@ -805,8 +814,13 @@ namespace Terminal.Gui {
 			FinishAccept ();
 		}
 
-		private void Accept ()
+		private void Accept (bool allowMulti)
 		{
+			if(allowMulti && TryAcceptMulti())
+			{
+				return;
+			}
+
 			if (!this.IsCompatibleWithOpenMode (this.tbPath.Text.ToString (), out string reason)) {
 				if (reason != null) {
 					feedback = reason;
@@ -1004,20 +1018,9 @@ namespace Terminal.Gui {
 
 		private void CellActivate (object sender, CellActivatedEventArgs obj)
 		{
-			var multi = this.MultiRowToStats ();
-			string reason = null;
-			if (multi.Any ()) {
-				if (multi.All (m => this.IsCompatibleWithOpenMode (m.FileSystemInfo.FullName, out reason))) {
-					this.Accept (multi);
-					return;
-				} else {
-					if (reason != null) {
-						feedback = reason;
-						SetNeedsDisplay ();
-					}
-
-					return;
-				}
+			if(TryAcceptMulti())
+			{
+				return;
 			}
 
 			var stats = this.RowToStats (obj.Row);
@@ -1032,6 +1035,33 @@ namespace Terminal.Gui {
 			}
 		}
 
+		private bool TryAcceptMulti ()
+		{
+			var multi = this.MultiRowToStats ();
+			string reason = null;
+			
+			if (!multi.Any ())
+			{
+				return false;
+			}
+			
+			if (multi.All (m => this.IsCompatibleWithOpenMode (
+				m.FileSystemInfo.FullName, out reason)))
+			{
+				this.Accept (multi);
+				return true;
+			} 
+			else 
+			{
+				if (reason != null) {
+					feedback = reason;
+					SetNeedsDisplay ();
+				}
+
+				return false;
+			}
+		}
+
 		/// <summary>
 		/// Returns true if there are no <see cref="AllowedTypes"/> or one of them agrees
 		/// that <paramref name="file"/> <see cref="IAllowedType.IsAllowed(string)"/>.
@@ -1118,7 +1148,8 @@ namespace Terminal.Gui {
 		/// <param name="addCurrentStateToHistory"></param>
 		/// <param name="setPathText"></param>
 		/// <param name="clearForward"></param>
-		internal void PushState (IDirectoryInfo d, bool addCurrentStateToHistory, bool setPathText = true, bool clearForward = true)
+		/// <param name="pathText">Optional alternate string to set path to.</param>
+		internal void PushState (IDirectoryInfo d, bool addCurrentStateToHistory, bool setPathText = true, bool clearForward = true, string pathText = null)
 		{
 			// no change of state
 			if (d == this.State?.Directory) {
@@ -1128,7 +1159,7 @@ namespace Terminal.Gui {
 				return;
 			}
 
-			PushState (new FileDialogState (d, this), addCurrentStateToHistory, setPathText, clearForward);
+			PushState (new FileDialogState (d, this), addCurrentStateToHistory, setPathText, clearForward, pathText);
 		}
 
 		private void RefreshState ()
@@ -1137,7 +1168,7 @@ namespace Terminal.Gui {
 			PushState (State, false, false, false);
 		}
 
-		private void PushState (FileDialogState newState, bool addCurrentStateToHistory, bool setPathText = true, bool clearForward = true)
+		private void PushState (FileDialogState newState, bool addCurrentStateToHistory, bool setPathText = true, bool clearForward = true, string pathText = null)
 		{
 			if (State is SearchState search) {
 				search.Cancel ();
@@ -1153,6 +1184,12 @@ namespace Terminal.Gui {
 
 				this.tbPath.Autocomplete.ClearSuggestions ();
 
+				if(pathText != null)
+				{
+					this.tbPath.Text = pathText;
+					this.tbPath.MoveEnd ();
+				}
+				else
 				if (setPathText) {
 					this.tbPath.Text = newState.Directory.FullName;
 					this.tbPath.MoveEnd ();
@@ -1228,10 +1265,9 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// If <see cref="TableView.MultiSelect"/> is on and multiple rows are selected
-		/// this returns a union of all <see cref="FileSystemInfoStats"/> in the selection.
+		/// If <see cref="TableView.MultiSelect"/> is this returns a union of all
+		/// <see cref="FileSystemInfoStats"/> in the selection.
 		/// </summary>
-		/// <remarks>Returns an empty collection if there are not at least 2 rows in the selection</remarks>
 		/// <returns></returns>
 		private IEnumerable<FileSystemInfoStats> MultiRowToStats ()
 		{
@@ -1248,7 +1284,7 @@ namespace Terminal.Gui {
 				}
 			}
 
-			return toReturn.Count > 1 ? toReturn : Enumerable.Empty<FileSystemInfoStats> ();
+			return toReturn;
 		}
 		private FileSystemInfoStats RowToStats (int rowIndex)
 		{

+ 324 - 46
UnitTests/FileServices/FileDialogTests.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.IO.Abstractions.TestingHelpers;
 using System.Linq;
@@ -128,40 +129,279 @@ namespace Terminal.Gui.FileServicesTests {
 			Assert.False (dlg.Canceled);
 		}
 
-		[Fact, AutoInitShutdown]
-		public void TestDirectoryContents_Linux ()
+		[Theory, AutoInitShutdown]
+		[InlineData(true,true)]
+		[InlineData(true,false)]
+		[InlineData(false,true)]
+		[InlineData(false,false)]
+		public void PickDirectory_DirectTyping (bool openModeMixed, bool multiple)
 		{
-			if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform (System.Runtime.InteropServices.OSPlatform.Windows)) {
-				// Cannot run test except on linux :( 
-				// See: https://github.com/TestableIO/System.IO.Abstractions/issues/800
-				return;
+			var dlg = GetDialog();
+			dlg.OpenMode = openModeMixed ? OpenMode.Mixed : OpenMode.Directory;
+			dlg.AllowsMultipleSelection = multiple;
+
+			// whe first opening the text field will have select all on
+			// so to add to current path user must press End or right
+			Send ('>', ConsoleKey.RightArrow, false);
+
+			Send("subfolder");
+
+			// Dialog has not yet been confirmed with a choice
+			Assert.True(dlg.Canceled);
+
+			// Now it has
+			Send ('\n', ConsoleKey.Enter, false);
+			Assert.False(dlg.Canceled);
+			AssertIsTheSubfolder(dlg.Path);
+		}
+
+		[Theory, AutoInitShutdown]
+		[InlineData(true,true)]
+		[InlineData(true,false)]
+		[InlineData(false,true)]
+		[InlineData(false,false)]
+		public void PickDirectory_ArrowNavigation (bool openModeMixed, bool multiple)
+		{
+			var dlg = GetDialog();
+			dlg.OpenMode = openModeMixed ? OpenMode.Mixed : OpenMode.Directory;
+			dlg.AllowsMultipleSelection = multiple;
+
+			Assert.IsType<TextField>(dlg.MostFocused);
+			Send ('v', ConsoleKey.DownArrow, false);
+			Assert.IsType<TableView>(dlg.MostFocused);
+
+			// Should be selecting ..
+			Send ('v', ConsoleKey.DownArrow, false);
+
+			// Down to the directory
+			Assert.True(dlg.Canceled);
+			// Alt+O to open (enter would just navigate into the child dir)
+			Send ('o', ConsoleKey.O, false,true);
+			Assert.False(dlg.Canceled);
+
+			AssertIsTheSubfolder(dlg.Path);
+		}
+
+		[Theory, AutoInitShutdown]
+		[InlineData(true)]
+		[InlineData(false)]
+		public void MultiSelectDirectory_CannotToggleDotDot (bool acceptWithEnter)
+		{
+			var dlg = GetDialog();
+			dlg.OpenMode = OpenMode.Directory;
+			dlg.AllowsMultipleSelection = true;
+			IReadOnlyCollection<string> eventMultiSelected = null;
+			dlg.FilesSelected += (s,e)=>
+			{
+				eventMultiSelected  = e.Dialog.MultiSelected;
+			};
+
+			Assert.IsType<TextField>(dlg.MostFocused);
+			Send ('v', ConsoleKey.DownArrow, false);
+			Assert.IsType<TableView>(dlg.MostFocused);
+
+			// Try to toggle '..'
+			Send (' ', ConsoleKey.Spacebar, false);
+			Send ('v', ConsoleKey.DownArrow, false);
+			// Toggle subfolder
+			Send (' ', ConsoleKey.Spacebar, false);
+
+			Assert.True(dlg.Canceled);
+
+			if(acceptWithEnter)
+			{
+				Send ('\n', ConsoleKey.Enter);
+			}
+			else
+			{
+				Send ('o', ConsoleKey.O,false,true);
 			}
+			Assert.False(dlg.Canceled);
+
+			Assert.Multiple(
+				()=>{
+					// Only the subfolder should be selected
+					Assert.Equal(1,dlg.MultiSelected.Count);
+					AssertIsTheSubfolder(dlg.Path);
+					AssertIsTheSubfolder(dlg.MultiSelected.Single());
+				},
+				()=>{
+					// Event should also agree with the final state
+					Assert.NotNull(eventMultiSelected);
+					Assert.Equal(1,eventMultiSelected.Count);
+					AssertIsTheSubfolder(eventMultiSelected.Single());
+				}
+			);
+		}
+		
+		[Fact, AutoInitShutdown]
+		public void DotDot_MovesToRoot_ThenPressBack ()
+		{
+			var dlg = GetDialog();
+			dlg.OpenMode = OpenMode.Directory;
+			dlg.AllowsMultipleSelection = true;
+			bool selected = false;
+			dlg.FilesSelected += (s,e)=>
+			{
+				selected = true;
+			};
 
-			// Arrange
-			var fileSystem = new MockFileSystem (new Dictionary<string, MockFileData> (), "/");
-			fileSystem.MockTime (() => new DateTime (2010, 01, 01, 11, 12, 43));
+			AssertIsTheStartingDirectory(dlg.Path);
 
-			fileSystem.AddFile (@"/myfile.txt", new MockFileData ("Testing is meh.") { LastWriteTime = new DateTime (2001, 01, 01, 11, 12, 11) });
-			fileSystem.AddFile (@"/demo/jQuery.js", new MockFileData ("some js") { LastWriteTime = new DateTime (2001, 01, 01, 11, 44, 42) });
-			fileSystem.AddFile (@"/demo/image.gif", new MockFileData (new byte [] { 0x12, 0x34, 0x56, 0xd2 }) { LastWriteTime = new DateTime (2002, 01, 01, 22, 42, 10) });
+			Assert.IsType<TextField>(dlg.MostFocused);
+			Send ('v', ConsoleKey.DownArrow, false);
+			Assert.IsType<TableView>(dlg.MostFocused);
+			
+			// ".." should be the first thing selected
+			// ".." should not mess with the displayed path
+			AssertIsTheStartingDirectory(dlg.Path);
 
-			var m = (MockDirectoryInfo)fileSystem.DirectoryInfo.New (@"/demo/subfolder");
-			m.Create ();
-			m.LastWriteTime = new DateTime (2002, 01, 01, 22, 42, 10);
+			// Accept navigation up a directory
+			Send ('\n', ConsoleKey.Enter);
 
-			fileSystem.AddFile (@"/demo/subfolder/image2.gif", new MockFileData (new byte [] { 0x12, 0x34, 0x56, 0xd2 }) { LastWriteTime = new DateTime (2002, 01, 01, 22, 42, 10) });
+			AssertIsTheRootDirectory(dlg.Path);
+			
+			Assert.True(dlg.Canceled);
+			Assert.False(selected);
 
-			var fd = new FileDialog (fileSystem) {
-				Height = 15
+			// Now press the back button (in table view)
+			Send ('<', ConsoleKey.Backspace);
+
+			// Should move us back to the root
+			AssertIsTheStartingDirectory(dlg.Path);
+
+			Assert.True(dlg.Canceled);
+			Assert.False(selected);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void MultiSelectDirectory_EnterOpensFolder ()
+		{
+			var dlg = GetDialog();
+			dlg.OpenMode = OpenMode.Directory;
+			dlg.AllowsMultipleSelection = true;
+			IReadOnlyCollection<string> eventMultiSelected = null;
+			dlg.FilesSelected += (s,e)=>
+			{
+				eventMultiSelected  = e.Dialog.MultiSelected;
 			};
-			fd.Path = @"/demo/";
-			Begin (fd);
-			fd.Title = string.Empty;
 
-			fd.Redraw (fd.Bounds);
+			Assert.IsType<TextField>(dlg.MostFocused);
+			Send ('v', ConsoleKey.DownArrow, false);
+			Assert.IsType<TableView>(dlg.MostFocused);
+			// Move selection to subfolder
+			Send ('v', ConsoleKey.DownArrow, false);
+
+			Send ('\n', ConsoleKey.Enter);
+
+			// Path should update to the newly opened folder
+			AssertIsTheSubfolder(dlg.Path);
+
+			// No selection will have been confirmed
+			Assert.True(dlg.Canceled);
+			Assert.Empty(dlg.MultiSelected);
+			Assert.Null(eventMultiSelected);
+		}
+
+		[Theory, AutoInitShutdown]
+		[InlineData(true)]
+		[InlineData(false)]
+		public void MultiSelectDirectory_CanToggleThenAccept (bool acceptWithEnter)
+		{
+			var dlg = GetDialog();
+			dlg.OpenMode = OpenMode.Directory;
+			dlg.AllowsMultipleSelection = true;
+			IReadOnlyCollection<string> eventMultiSelected = null;
+			dlg.FilesSelected += (s,e)=>
+			{
+				eventMultiSelected  = e.Dialog.MultiSelected;
+			};
+
+			Assert.IsType<TextField>(dlg.MostFocused);
+			Send ('v', ConsoleKey.DownArrow, false);
+			Assert.IsType<TableView>(dlg.MostFocused);
+			// Move selection to subfolder
+			Send ('v', ConsoleKey.DownArrow, false);
+			// Toggle subfolder
+			Send (' ', ConsoleKey.Spacebar, false);
+
+			Assert.True(dlg.Canceled);
+
+			if(acceptWithEnter)
+			{
+				Send ('\n', ConsoleKey.Enter);
+			}
+			else
+			{
+				Send ('o', ConsoleKey.O,false,true);
+			}
+			Assert.False(dlg.Canceled);
+
+			Assert.Multiple(
+				()=>{
+					// Only the subfolder should be selected
+					Assert.Equal(1,dlg.MultiSelected.Count);
+					AssertIsTheSubfolder(dlg.Path);
+					AssertIsTheSubfolder(dlg.MultiSelected.Single());
+				},
+				()=>{
+					// Event should also agree with the final state
+					Assert.NotNull(eventMultiSelected);
+					Assert.Equal(1,eventMultiSelected.Count);
+					AssertIsTheSubfolder(eventMultiSelected.Single());
+				}
+			);
+		}
+
+		private void AssertIsTheStartingDirectory (string path)
+		{
+			if(IsWindows())
+			{
+				Assert.Equal (@"c:\demo\",path);
+			}
+			else
+			{
+				Assert.Equal ("/demo/",path);
+			}
+		}
+
+		private void AssertIsTheRootDirectory (string path)
+		{
+			if(IsWindows())
+			{
+				Assert.Equal (@"c:\",path);
+			}
+			else
+			{
+				Assert.Equal ("/",path);
+			}
+		}
+
+		private void AssertIsTheSubfolder (string path)
+		{
+			if(IsWindows())
+			{
+				Assert.Equal (@"c:\demo\subfolder",path);
+			}
+			else
+			{
+				Assert.Equal ("/demo/subfolder",path);
+			}
+		}
+
+		[Fact, AutoInitShutdown]
+		public void TestDirectoryContents_Linux ()
+		{
+			if (IsWindows()) {
+				return;
+			}
+			var fd = GetLinuxDialog();
+			fd.Title = string.Empty;
 
-			fd.Style.DateFormat = "yyyy-MM-dd hh:mm:ss";
+			fd.Style.Culture = new CultureInfo("en-US");
 
+			fd.Redraw (fd.Bounds);
+			
 			string expected =
 			@"
  ┌──────────────────────────────────────────────────────────────────┐
@@ -186,35 +426,17 @@ namespace Terminal.Gui.FileServicesTests {
 		[Fact, AutoInitShutdown]
 		public void TestDirectoryContents_Windows ()
 		{
-			if (!System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform (System.Runtime.InteropServices.OSPlatform.Windows)) {
-				// Can only run this test on windows :( 
-				// See: https://github.com/TestableIO/System.IO.Abstractions/issues/800
+			if (!IsWindows()) {
 				return;
 			}
-			// Arrange
-			var fileSystem = new MockFileSystem (new Dictionary<string, MockFileData> (), @"c:\");
-			fileSystem.MockTime (() => new DateTime (2010, 01, 01, 11, 12, 43));
-
-			fileSystem.AddFile (@"c:\myfile.txt", new MockFileData ("Testing is meh.") { LastWriteTime = new DateTime (2001, 01, 01, 11, 12, 11) });
-			fileSystem.AddFile (@"c:\demo\jQuery.js", new MockFileData ("some js") { LastWriteTime = new DateTime (2001, 01, 01, 11, 44, 42) });
-			fileSystem.AddFile (@"c:\demo\image.gif", new MockFileData (new byte [] { 0x12, 0x34, 0x56, 0xd2 }) { LastWriteTime = new DateTime (2002, 01, 01, 22, 42, 10) });
 
-			var m = (MockDirectoryInfo)fileSystem.DirectoryInfo.New (@"c:\demo\subfolder");
-			m.Create ();
-			m.LastWriteTime = new DateTime (2002, 01, 01, 22, 42, 10);
-
-			fileSystem.AddFile (@"c:\demo\subfolder\image2.gif", new MockFileData (new byte [] { 0x12, 0x34, 0x56, 0xd2 }) { LastWriteTime = new DateTime (2002, 01, 01, 22, 42, 10) });
-
-			var fd = new FileDialog (fileSystem) {
-				Height = 15
-			};
-			fd.Path = @"c:\demo\";
-			Begin (fd);
+			var fd = GetWindowsDialog();
 			fd.Title = string.Empty;
 
+			fd.Style.Culture = new CultureInfo("en-US");
+
 			fd.Redraw (fd.Bounds);
 
-			fd.Style.DateFormat = "yyyy-MM-dd hh:mm:ss";
 
 			string expected =
 			@"
@@ -265,7 +487,6 @@ namespace Terminal.Gui.FileServicesTests {
 			foreach (var ch in chars) {
 				Application.Driver.SendKeys (ch, ConsoleKey.NoName, false, false, false);
 			}
-
 		}
 		/*
 				[Fact, AutoInitShutdown]
@@ -297,6 +518,63 @@ namespace Terminal.Gui.FileServicesTests {
 					Assert.Equal (@"/bob/fish", tb.Text);
 				}*/
 
+		private bool IsWindows()
+		{
+			return System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform (System.Runtime.InteropServices.OSPlatform.Windows);
+		}
+
+		private FileDialog GetDialog()
+		{
+			return IsWindows() ? GetWindowsDialog() : GetLinuxDialog();			
+		}
+
+		private FileDialog GetWindowsDialog()
+		{
+			// Arrange
+			var fileSystem = new MockFileSystem (new Dictionary<string, MockFileData> (), @"c:\");
+			fileSystem.MockTime (() => new DateTime (2010, 01, 01, 11, 12, 43));
+
+			fileSystem.AddFile (@"c:\myfile.txt", new MockFileData ("Testing is meh.") { LastWriteTime = new DateTime (2001, 01, 01, 11, 12, 11) });
+			fileSystem.AddFile (@"c:\demo\jQuery.js", new MockFileData ("some js") { LastWriteTime = new DateTime (2001, 01, 01, 11, 44, 42) });
+			fileSystem.AddFile (@"c:\demo\image.gif", new MockFileData (new byte [] { 0x12, 0x34, 0x56, 0xd2 }) { LastWriteTime = new DateTime (2002, 01, 01, 22, 42, 10) });
+
+			var m = (MockDirectoryInfo)fileSystem.DirectoryInfo.New (@"c:\demo\subfolder");
+			m.Create ();
+			m.LastWriteTime = new DateTime (2002, 01, 01, 22, 42, 10);
+
+			fileSystem.AddFile (@"c:\demo\subfolder\image2.gif", new MockFileData (new byte [] { 0x12, 0x34, 0x56, 0xd2 }) { LastWriteTime = new DateTime (2002, 01, 01, 22, 42, 10) });
+
+			var fd = new FileDialog (fileSystem) {
+				Height = 15
+			};
+			fd.Path = @"c:\demo\";
+			Begin (fd);
+			return fd;
+		}
+
+		private FileDialog GetLinuxDialog()
+		{
+			// Arrange
+			var fileSystem = new MockFileSystem (new Dictionary<string, MockFileData> (), "/");
+			fileSystem.MockTime (() => new DateTime (2010, 01, 01, 11, 12, 43));
+
+			fileSystem.AddFile (@"/myfile.txt", new MockFileData ("Testing is meh.") { LastWriteTime = new DateTime (2001, 01, 01, 11, 12, 11) });
+			fileSystem.AddFile (@"/demo/jQuery.js", new MockFileData ("some js") { LastWriteTime = new DateTime (2001, 01, 01, 11, 44, 42) });
+			fileSystem.AddFile (@"/demo/image.gif", new MockFileData (new byte [] { 0x12, 0x34, 0x56, 0xd2 }) { LastWriteTime = new DateTime (2002, 01, 01, 22, 42, 10) });
+
+			var m = (MockDirectoryInfo)fileSystem.DirectoryInfo.New (@"/demo/subfolder");
+			m.Create ();
+			m.LastWriteTime = new DateTime (2002, 01, 01, 22, 42, 10);
+
+			fileSystem.AddFile (@"/demo/subfolder/image2.gif", new MockFileData (new byte [] { 0x12, 0x34, 0x56, 0xd2 }) { LastWriteTime = new DateTime (2002, 01, 01, 22, 42, 10) });
+
+			var fd = new FileDialog (fileSystem) {
+				Height = 15
+			};
+			fd.Path = @"/demo/";
+			Begin (fd);
+			return fd;
+		}
 		private FileDialog GetInitializedFileDialog ()
 		{
 			var dlg = new FileDialog ();