[Monodevelop-patches-list] r2641 - in trunk/MonoDevelop/Extras/NUnit: . Commands Gui Project Services templates

Lluis Sanchez <lluis@ximian.com> lluis at mono-cvs.ximian.com
Wed Jul 13 12:55:25 EDT 2005


Author: lluis
Date: 2005-07-13 12:55:25 -0400 (Wed, 13 Jul 2005)
New Revision: 2641

Added:
   trunk/MonoDevelop/Extras/NUnit/Gui/NUnit.Failed.png
   trunk/MonoDevelop/Extras/NUnit/Gui/NUnit.Loading.png
   trunk/MonoDevelop/Extras/NUnit/Gui/NUnit.None.png
   trunk/MonoDevelop/Extras/NUnit/Gui/NUnit.NotRun.png
   trunk/MonoDevelop/Extras/NUnit/Gui/NUnit.Running.png
   trunk/MonoDevelop/Extras/NUnit/Gui/NUnit.Success.png
   trunk/MonoDevelop/Extras/NUnit/Gui/NUnit.SuccessAndFailed.png
   trunk/MonoDevelop/Extras/NUnit/Gui/NUnitAssemblyGroupConfigurationNodeBuilder.cs
   trunk/MonoDevelop/Extras/NUnit/Gui/NUnitAssemblyGroupNodeBuilder.cs
   trunk/MonoDevelop/Extras/NUnit/Gui/NUnitOptionsPanel.cs
   trunk/MonoDevelop/Extras/NUnit/Gui/TestAssemblyNodeBuilder.cs
   trunk/MonoDevelop/Extras/NUnit/Gui/TestChart.cs
   trunk/MonoDevelop/Extras/NUnit/Gui/TestNodeBuilder.cs
   trunk/MonoDevelop/Extras/NUnit/Gui/TestResultsPad.cs
   trunk/MonoDevelop/Extras/NUnit/Gui/UnitTestOptionsDialog.cs
   trunk/MonoDevelop/Extras/NUnit/Project/
   trunk/MonoDevelop/Extras/NUnit/Project/NUnitAssemblyGroupFileFormat.cs
   trunk/MonoDevelop/Extras/NUnit/Project/NUnitAssemblyGroupProject.cs
   trunk/MonoDevelop/Extras/NUnit/Project/TestAssembly.cs
   trunk/MonoDevelop/Extras/NUnit/Project/TestAssemblyCollection.cs
   trunk/MonoDevelop/Extras/NUnit/Services/CombineTestGroup.cs
   trunk/MonoDevelop/Extras/NUnit/Services/ExternalTestRunner.cs
   trunk/MonoDevelop/Extras/NUnit/Services/GeneralTestOptions.cs
   trunk/MonoDevelop/Extras/NUnit/Services/IResultsStore.cs
   trunk/MonoDevelop/Extras/NUnit/Services/ITestProgressMonitor.cs
   trunk/MonoDevelop/Extras/NUnit/Services/ITestProvider.cs
   trunk/MonoDevelop/Extras/NUnit/Services/NUnitAssemblyTestSuite.cs
   trunk/MonoDevelop/Extras/NUnit/Services/NUnitOptions.cs
   trunk/MonoDevelop/Extras/NUnit/Services/NUnitProjectTestSuite.cs
   trunk/MonoDevelop/Extras/NUnit/Services/NUnitTestCase.cs
   trunk/MonoDevelop/Extras/NUnit/Services/NUnitTestSuite.cs
   trunk/MonoDevelop/Extras/NUnit/Services/SystemTestProvider.cs
   trunk/MonoDevelop/Extras/NUnit/Services/TestContext.cs
   trunk/MonoDevelop/Extras/NUnit/Services/UnitTest.cs
   trunk/MonoDevelop/Extras/NUnit/Services/UnitTestCollection.cs
   trunk/MonoDevelop/Extras/NUnit/Services/UnitTestGroup.cs
   trunk/MonoDevelop/Extras/NUnit/Services/UnitTestResult.cs
   trunk/MonoDevelop/Extras/NUnit/Services/UnitTestResultsStore.cs
   trunk/MonoDevelop/Extras/NUnit/Services/UnitTestStatus.cs
   trunk/MonoDevelop/Extras/NUnit/Services/XmlResultsStore.cs
   trunk/MonoDevelop/Extras/NUnit/nunit.glade
   trunk/MonoDevelop/Extras/NUnit/templates/
   trunk/MonoDevelop/Extras/NUnit/templates/NUnitAssemblyGroup.xpt.xml
Removed:
   trunk/MonoDevelop/Extras/NUnit/FixtureAddedEventHandler.cs
   trunk/MonoDevelop/Extras/NUnit/FixtureLoadedErrorEventHandler.cs
   trunk/MonoDevelop/Extras/NUnit/Gui/ResultsView.cs
Modified:
   trunk/MonoDevelop/Extras/NUnit/ChangeLog
   trunk/MonoDevelop/Extras/NUnit/Commands/NUnitCommands.cs
   trunk/MonoDevelop/Extras/NUnit/Gui/CircleImage.cs
   trunk/MonoDevelop/Extras/NUnit/Gui/TestPad.cs
   trunk/MonoDevelop/Extras/NUnit/Makefile
   trunk/MonoDevelop/Extras/NUnit/MonoDevelopNUnit.addin.xml
   trunk/MonoDevelop/Extras/NUnit/Services/NUnitService.cs
Log:
2005-07-13  Lluis Sanchez Gual  <lluis at novell.com> 

	* New addin implementation.



Modified: trunk/MonoDevelop/Extras/NUnit/ChangeLog
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/ChangeLog	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/ChangeLog	2005-07-13 16:55:25 UTC (rev 2641)
@@ -1,3 +1,7 @@
+2005-07-13  Lluis Sanchez Gual  <lluis at novell.com> 
+
+	* New addin implementation.
+
 2005-03-16  John Luke  <john.luke at gmail.com>
 
 	* Redo the nunit addin based on gnunit

Modified: trunk/MonoDevelop/Extras/NUnit/Commands/NUnitCommands.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Commands/NUnitCommands.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Commands/NUnitCommands.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -1,3 +1,31 @@
+//
+// TestChart.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
 using System;
 using System.IO;
 using Gtk;
@@ -9,32 +37,27 @@
 
 namespace MonoDevelop.Commands
 {
-	public class NUnitLoadAssembly : AbstractMenuCommand
+	public enum TestCommands
 	{
-		NUnitService nunitService = (NUnitService) ServiceManager.GetService (typeof (NUnitService));
-
-		public override void Run ()
-		{
-			using (FileSelector fs = new FileSelector ("Load test assembly")) {
-				//fs.DefaultPath = Path.Combine (Environment.GetEnvironmentVariable ("HOME"), "Projects");
-
-				if (fs.Run () == (int) Gtk.ResponseType.Ok)
-				{
-					nunitService.LoadAssembly (fs.Filename);
-				}
-
-				fs.Hide ();
-			}
-		}
+		RunTest,
+		ShowTestCode,
+		SelectTestInTree,
+		ShowTestDetails
 	}
-
-	public class NUnitRunTests : AbstractMenuCommand
+	
+	public enum TestChartCommands
 	{
-		NUnitService nunitService = (NUnitService) ServiceManager.GetService (typeof (NUnitService));
-
-		public override void Run ()
-		{
-			nunitService.RunTests ();
-		}
+		ShowResults,
+		ShowTime,
+		UseTimeScale,
+		SingleDayResult,
+		ShowSuccessfulTests,
+		ShowFailedTests,
+		ShowIgnoredTests
 	}
+	
+	public enum NUnitProjectCommands
+	{
+		AddAssembly
+	}
 }

Deleted: trunk/MonoDevelop/Extras/NUnit/FixtureAddedEventHandler.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/FixtureAddedEventHandler.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/FixtureAddedEventHandler.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -1,26 +0,0 @@
-using System;
-
-namespace MonoDevelop.NUnit
-{
-	delegate void FixtureAddedEventHandler (object sender, FixtureAddedEventArgs args);
-
-	public class FixtureAddedEventArgs : EventArgs
-	{
-		int total, current;
-
-		public FixtureAddedEventArgs (int current, int total)
-		{
-			this.total = total;
-			this.current = current;
-		}
-
-		public int Current {
-			get { return current; }
-		}
-
-		public int Total {
-			get { return total; }
-		}
-	}
-}
-

Deleted: trunk/MonoDevelop/Extras/NUnit/FixtureLoadedErrorEventHandler.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/FixtureLoadedErrorEventHandler.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/FixtureLoadedErrorEventHandler.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -1,26 +0,0 @@
-using System;
-
-namespace MonoDevelop.NUnit
-{
-	public delegate void FixtureLoadedErrorEventHandler (object sender, FixtureLoadedErrorEventArgs args);
-
-	public class FixtureLoadedErrorEventArgs : EventArgs
-	{
-		string message, filename;
-
-		public FixtureLoadedErrorEventArgs (string filename, Exception e)
-		{
-			this.filename = filename;
-			this.message = e.Message;
-		}
-
-		public string Filename {
-			get { return filename; }
-		}
-
-		public string Message {
-			get { return message; }
-		}
-	}
-}
-

Modified: trunk/MonoDevelop/Extras/NUnit/Gui/CircleImage.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Gui/CircleImage.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Gui/CircleImage.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -1,3 +1,31 @@
+//
+// CircleImage.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
 using System;
 using Gdk;
 
@@ -7,42 +35,17 @@
 
 namespace MonoDevelop.NUnit
 {
-	static class CircleImage
+	abstract class CircleImage
 	{
-		static Pixbuf none, failure, success, notrun;
-		static ResourceService res = ServiceManager.GetService (typeof (ResourceService)) as ResourceService;
+		CircleImage () {}
 
-		internal static Pixbuf Failure {
-			get {
-				if (failure == null)
-					failure = res.GetIcon (Stock.NunitFailed);
-				return failure;
-			}
-		}
-
-		internal static Pixbuf None {
-			get {
-				if (none == null)
-					none = res.GetIcon (Stock.NunitNone);
-				return none;
-			}
-		}
-
-		internal static Pixbuf NotRun {
-			get {
-				if (notrun == null)
-					notrun = res.GetIcon (Stock.NunitNotRun);
-				return notrun;
-			}
-		}
-
-		internal static Pixbuf Success {
-			get {
-				if (success == null)
-					success = res.GetIcon (Stock.NunitSuccess);
-				return success;
-			}
-		}
+		internal static Gdk.Pixbuf Running = Gdk.Pixbuf.LoadFromResource("NUnit.Running.png");
+		internal static Gdk.Pixbuf Failure = Gdk.Pixbuf.LoadFromResource("NUnit.Failed.png");
+		internal static Gdk.Pixbuf None = Gdk.Pixbuf.LoadFromResource("NUnit.None.png");
+		internal static Gdk.Pixbuf NotRun = Gdk.Pixbuf.LoadFromResource("NUnit.NotRun.png");
+		internal static Gdk.Pixbuf Success = Gdk.Pixbuf.LoadFromResource("NUnit.Success.png");
+		internal static Gdk.Pixbuf SuccessAndFailure = Gdk.Pixbuf.LoadFromResource("NUnit.SuccessAndFailed.png");
+		internal static Gdk.Pixbuf Loading = Gdk.Pixbuf.LoadFromResource("NUnit.Loading.png");
 	}
 }
 

Added: trunk/MonoDevelop/Extras/NUnit/Gui/NUnit.Failed.png
===================================================================
(Binary files differ)


Property changes on: trunk/MonoDevelop/Extras/NUnit/Gui/NUnit.Failed.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/MonoDevelop/Extras/NUnit/Gui/NUnit.Loading.png
===================================================================
(Binary files differ)


Property changes on: trunk/MonoDevelop/Extras/NUnit/Gui/NUnit.Loading.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/MonoDevelop/Extras/NUnit/Gui/NUnit.None.png
===================================================================
(Binary files differ)


Property changes on: trunk/MonoDevelop/Extras/NUnit/Gui/NUnit.None.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/MonoDevelop/Extras/NUnit/Gui/NUnit.NotRun.png
===================================================================
(Binary files differ)


Property changes on: trunk/MonoDevelop/Extras/NUnit/Gui/NUnit.NotRun.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/MonoDevelop/Extras/NUnit/Gui/NUnit.Running.png
===================================================================
(Binary files differ)


Property changes on: trunk/MonoDevelop/Extras/NUnit/Gui/NUnit.Running.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/MonoDevelop/Extras/NUnit/Gui/NUnit.Success.png
===================================================================
(Binary files differ)


Property changes on: trunk/MonoDevelop/Extras/NUnit/Gui/NUnit.Success.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/MonoDevelop/Extras/NUnit/Gui/NUnit.SuccessAndFailed.png
===================================================================
(Binary files differ)


Property changes on: trunk/MonoDevelop/Extras/NUnit/Gui/NUnit.SuccessAndFailed.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/MonoDevelop/Extras/NUnit/Gui/NUnitAssemblyGroupConfigurationNodeBuilder.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Gui/NUnitAssemblyGroupConfigurationNodeBuilder.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Gui/NUnitAssemblyGroupConfigurationNodeBuilder.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,133 @@
+//
+// NUnitAssemblyGroupConfigurationNodeBuilder.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+
+using MonoDevelop.Internal.Project;
+using MonoDevelop.Services;
+using MonoDevelop.Gui.Pads;
+using MonoDevelop.Commands;
+using MonoDevelop.Gui;
+using MonoDevelop.Gui.Widgets;
+
+namespace MonoDevelop.NUnit
+{
+	public class NUnitAssemblyGroupConfigurationNodeBuilder: TypeNodeBuilder
+	{
+		EventHandler assembliesChanged;
+		
+		public NUnitAssemblyGroupConfigurationNodeBuilder ()
+		{
+			assembliesChanged = (EventHandler) Runtime.DispatchService.GuiDispatch (new EventHandler (OnAssembliesChanged));
+		}
+		
+		public override Type CommandHandlerType {
+			get { return typeof(NUnitAssemblyGroupConfigurationNodeCommandHandler); }
+		}
+		
+		public override string ContextMenuAddinPath {
+			get { return "/SharpDevelop/Views/ProjectBrowser/ContextMenu/NUnitAssemblyGroupConfiguration"; }
+		}
+
+		public override Type NodeDataType {
+			get { return typeof(NUnitAssemblyGroupProjectConfiguration); }
+		}
+		
+		public override string GetNodeName (ITreeNavigator thisNode, object dataObject)
+		{
+			return ((IConfiguration)dataObject).Name;
+		}
+		
+		public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, ref string label, ref Gdk.Pixbuf icon, ref Gdk.Pixbuf closedIcon)
+		{
+			IConfiguration conf = dataObject as IConfiguration;
+			label = conf.Name;
+			icon = Context.GetIcon (Stock.ClosedFolderBitmap);
+		}
+
+		public override void BuildChildNodes (ITreeBuilder builder, object dataObject)
+		{
+			NUnitAssemblyGroupProjectConfiguration config = dataObject as NUnitAssemblyGroupProjectConfiguration;
+				
+			foreach (TestAssembly ta in config.Assemblies)
+				builder.AddChild (ta);
+		}
+
+		public override bool HasChildNodes (ITreeBuilder builder, object dataObject)
+		{
+			NUnitAssemblyGroupProjectConfiguration config = dataObject as NUnitAssemblyGroupProjectConfiguration;
+			return config.Assemblies.Count > 0;
+		}
+		
+		public override void OnNodeAdded (object dataObject)
+		{
+			NUnitAssemblyGroupProjectConfiguration config = dataObject as NUnitAssemblyGroupProjectConfiguration;
+			config.AssembliesChanged += assembliesChanged;
+		}
+		
+		public override void OnNodeRemoved (object dataObject)
+		{
+			NUnitAssemblyGroupProjectConfiguration config = dataObject as NUnitAssemblyGroupProjectConfiguration;
+			config.AssembliesChanged -= assembliesChanged;
+		}
+		
+		public void OnAssembliesChanged (object sender, EventArgs args)
+		{
+			ITreeBuilder tb = Context.GetTreeBuilder (sender);
+			if (tb != null) tb.UpdateAll ();
+		}
+	}
+	
+	class NUnitAssemblyGroupConfigurationNodeCommandHandler: NodeCommandHandler
+	{
+		[CommandHandler (NUnitProjectCommands.AddAssembly)]
+		protected void OnAddAssembly ()
+		{
+			NUnitAssemblyGroupProjectConfiguration config = (NUnitAssemblyGroupProjectConfiguration) CurrentNode.DataItem;
+			
+			FileSelector fdiag  = new FileSelector (GettextCatalog.GetString ("Add files"));
+			try
+			{
+				fdiag.SelectMultiple = true;
+				
+				int result = fdiag.Run ();
+				if (result != (int) Gtk.ResponseType.Ok)
+					return;
+
+				foreach (string file in fdiag.Filenames)
+					config.Assemblies.Add (new TestAssembly (file));
+				
+				Runtime.ProjectService.SaveCombine();
+			}
+			finally {
+				fdiag.Destroy ();
+			}
+		}
+	}
+}

Added: trunk/MonoDevelop/Extras/NUnit/Gui/NUnitAssemblyGroupNodeBuilder.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Gui/NUnitAssemblyGroupNodeBuilder.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Gui/NUnitAssemblyGroupNodeBuilder.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,115 @@
+//
+// NUnitAssemblyGroupNodeBuilder.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+
+using MonoDevelop.Internal.Project;
+using MonoDevelop.Services;
+using MonoDevelop.Gui.Pads;
+using MonoDevelop.Commands;
+using MonoDevelop.Gui;
+
+namespace MonoDevelop.NUnit
+{
+	public class NUnitAssemblyGroupNodeBuilder: TypeNodeBuilder
+	{
+		ConfigurationEventHandler configsChanged;
+		
+		public NUnitAssemblyGroupNodeBuilder ()
+		{
+			configsChanged = (ConfigurationEventHandler) Runtime.DispatchService.GuiDispatch (new ConfigurationEventHandler (OnConfigurationsChanged));
+		}
+		
+		public override Type CommandHandlerType {
+			get { return typeof(NUnitAssemblyGroupNodeCommandHandler); }
+		}
+		
+		public override string ContextMenuAddinPath {
+			get { return "/SharpDevelop/Views/ProjectBrowser/ContextMenu/NUnitAssemblyGroup"; }
+		}
+
+		public override Type NodeDataType {
+			get { return typeof(NUnitAssemblyGroupProject); }
+		}
+		
+		public override string GetNodeName (ITreeNavigator thisNode, object dataObject)
+		{
+			return ((NUnitAssemblyGroupProject)dataObject).Name;
+		}
+		
+		public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, ref string label, ref Gdk.Pixbuf icon, ref Gdk.Pixbuf closedIcon)
+		{
+			NUnitAssemblyGroupProject project = dataObject as NUnitAssemblyGroupProject;
+			label = project.Name;
+			icon = Context.GetIcon (Stock.EmptyProjectIcon);
+		}
+
+		public override void BuildChildNodes (ITreeBuilder builder, object dataObject)
+		{
+			NUnitAssemblyGroupProject project = dataObject as NUnitAssemblyGroupProject;
+				
+			foreach (NUnitAssemblyGroupProjectConfiguration c in project.Configurations)
+				builder.AddChild (c);
+		}
+
+		public override bool HasChildNodes (ITreeBuilder builder, object dataObject)
+		{
+			NUnitAssemblyGroupProject project = dataObject as NUnitAssemblyGroupProject;
+			return project.Configurations.Count > 0;
+		}
+		
+		public override void OnNodeAdded (object dataObject)
+		{
+			NUnitAssemblyGroupProject project = dataObject as NUnitAssemblyGroupProject;
+			project.ConfigurationAdded += configsChanged;
+			project.ConfigurationRemoved += configsChanged;
+		}
+		
+		public override void OnNodeRemoved (object dataObject)
+		{
+			NUnitAssemblyGroupProject project = dataObject as NUnitAssemblyGroupProject;
+			project.ConfigurationAdded -= configsChanged;
+			project.ConfigurationRemoved -= configsChanged;
+		}
+		
+		public void OnConfigurationsChanged (object sender, ConfigurationEventArgs args)
+		{
+			ITreeBuilder tb = Context.GetTreeBuilder (sender);
+			if (tb != null) tb.UpdateAll ();
+		}
+	}
+	
+	class NUnitAssemblyGroupNodeCommandHandler: NodeCommandHandler
+	{
+		[CommandHandler (NUnitProjectCommands.AddAssembly)]
+		protected void OnShowTest ()
+		{
+		}
+	}
+}

Added: trunk/MonoDevelop/Extras/NUnit/Gui/NUnitOptionsPanel.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Gui/NUnitOptionsPanel.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Gui/NUnitOptionsPanel.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,206 @@
+//
+// NUnitOptionsPanel.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+using System;
+using System.IO;
+using System.Collections;
+
+using MonoDevelop.Core.Properties;
+using MonoDevelop.Core.Services;
+
+using MonoDevelop.Gui.Components;
+using MonoDevelop.Gui.Widgets;
+using MonoDevelop.Gui.Dialogs;
+using Gtk;
+
+namespace MonoDevelop.NUnit
+{
+	public class NUnitOptionsPanel : AbstractOptionPanel
+	{
+		class NUnitOptionsWidget : GladeWidgetExtract
+		{
+			// Gtk Controls
+			[Glade.Widget] Gtk.TreeView categoryTree;
+			[Glade.Widget] CheckButton useParentCheck;
+			[Glade.Widget] RadioButton noFilterRadio;
+			[Glade.Widget] RadioButton includeRadio;
+			[Glade.Widget] RadioButton excludeRadio;
+			[Glade.Widget] Button addButton;
+			[Glade.Widget] Button removeButton;
+			TreeStore store;
+			TreeViewColumn textColumn;
+			
+			UnitTest test;
+			string config;
+			NUnitCategoryOptions options;
+			NUnitCategoryOptions localOptions;
+
+			public NUnitOptionsWidget (IProperties customizationObject) : base ("nunit.glade", "NUnitOptions")
+			{
+				test = (UnitTest) ((IProperties)customizationObject).GetProperty ("UnitTest");
+				config = (string) ((IProperties)customizationObject).GetProperty ("Config");
+				options = localOptions = (NUnitCategoryOptions) test.GetOptions (typeof(NUnitCategoryOptions), config);
+				
+				store = new TreeStore (typeof(string));
+				categoryTree.Model = store;
+				categoryTree.HeadersVisible = false;
+				
+				CellRendererText tr = new CellRendererText ();
+				tr.Editable = true;
+				tr.Edited += new EditedHandler (OnCategoryEdited);
+				textColumn = new TreeViewColumn ();
+				textColumn.Title = "Category";
+				textColumn.PackStart (tr, false);
+				textColumn.AddAttribute (tr, "text", 0);
+				textColumn.Expand = true;
+				categoryTree.AppendColumn (textColumn);
+				
+				if (test.Parent != null)
+					useParentCheck.Active = !test.HasOptions (typeof(NUnitCategoryOptions), config);
+				else {
+					useParentCheck.Active = false;
+					useParentCheck.Sensitive = false;
+				}
+				
+				if (!options.EnableFilter)
+					noFilterRadio.Active = true;
+				else if (options.Exclude)
+					excludeRadio.Active = true;
+				else
+					includeRadio.Active = true;
+
+				Fill ();
+				
+				noFilterRadio.Toggled += new EventHandler (OnFilterToggled);
+				includeRadio.Toggled += new EventHandler (OnFilterToggled);
+				excludeRadio.Toggled += new EventHandler (OnFilterToggled);
+				useParentCheck.Toggled += new EventHandler (OnToggledUseParent);
+				addButton.Clicked += new EventHandler (OnAddCategory);
+				removeButton.Clicked += new EventHandler (OnRemoveCategory);
+			}
+			
+			void Fill ()
+			{
+				noFilterRadio.Sensitive = !useParentCheck.Active;
+				includeRadio.Sensitive = !useParentCheck.Active;
+				excludeRadio.Sensitive = !useParentCheck.Active;
+				categoryTree.Sensitive = !useParentCheck.Active && !noFilterRadio.Active;
+				removeButton.Sensitive = !useParentCheck.Active && !noFilterRadio.Active;
+				addButton.Sensitive = !useParentCheck.Active && !noFilterRadio.Active;
+				
+				store.Clear ();
+				foreach (string cat in options.Categories)
+					store.AppendValues (cat);
+			}
+			
+			void OnToggledUseParent (object sender, EventArgs args)
+			{
+				if (useParentCheck.Active)
+					options = (NUnitCategoryOptions) test.Parent.GetOptions (typeof(NUnitCategoryOptions), config);
+				else
+					options = localOptions;
+
+				if (!options.EnableFilter)
+					noFilterRadio.Active = true;
+				else if (options.Exclude)
+					excludeRadio.Active = true;
+				else
+					includeRadio.Active = true;
+
+				Fill ();
+			}
+			
+			void OnFilterToggled (object sender, EventArgs args)
+			{
+				options.EnableFilter = !noFilterRadio.Active;
+				options.Exclude = excludeRadio.Active;
+				Fill ();
+			}
+
+			void OnAddCategory (object sender, EventArgs args)
+			{
+				TreeIter it = store.AppendValues ("");
+				categoryTree.SetCursor (store.GetPath (it), textColumn, true);
+			}
+
+			void OnRemoveCategory (object sender, EventArgs args)
+			{
+				Gtk.TreeModel foo;
+				Gtk.TreeIter iter;
+				if (!categoryTree.Selection.GetSelected (out foo, out iter))
+					return;
+				string old = (string) store.GetValue (iter, 0);
+				options.Categories.Remove (old);
+				store.Remove (ref iter);
+			}
+
+			void OnCategoryEdited (object sender, EditedArgs args)
+			{
+				TreeIter iter;
+				if (!store.GetIter (out iter, new TreePath (args.Path)))
+					return;
+				
+				string old = (string) store.GetValue (iter, 0);
+				if (args.NewText.Length == 0) {
+					options.Categories.Remove (old);
+					store.Remove (ref iter);
+				} else {
+					int i = options.Categories.IndexOf (old);
+					if (i == -1)
+						options.Categories.Add (args.NewText);
+					else
+						options.Categories [i] = args.NewText;
+					store.SetValue (iter, 0, args.NewText);
+				}
+			}
+
+			public void Store (IProperties customizationObject)
+			{
+				if (useParentCheck.Active)
+					test.ResetOptions (typeof(NUnitCategoryOptions), config);
+				else
+					test.SetOptions (options, config);
+			}
+		}
+		
+		NUnitOptionsWidget widget;
+
+		public override void LoadPanelContents()
+		{
+			Add (widget = new NUnitOptionsWidget ((IProperties) CustomizationObject));
+		}
+		
+		public override bool StorePanelContents()
+		{
+			widget.Store ((IProperties) CustomizationObject);
+ 			return true;
+		}
+	}
+}
+

Deleted: trunk/MonoDevelop/Extras/NUnit/Gui/ResultsView.cs
===================================================================

Added: trunk/MonoDevelop/Extras/NUnit/Gui/TestAssemblyNodeBuilder.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Gui/TestAssemblyNodeBuilder.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Gui/TestAssemblyNodeBuilder.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,78 @@
+//
+// TestAssemblyNodeBuilder.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.IO;
+using System.Collections;
+
+using MonoDevelop.Internal.Project;
+using MonoDevelop.Services;
+using MonoDevelop.Gui.Pads;
+using MonoDevelop.Commands;
+using MonoDevelop.Gui;
+
+namespace MonoDevelop.NUnit
+{
+	public class TestAssemblyNodeBuilder: TypeNodeBuilder
+	{
+		public override Type CommandHandlerType {
+			get { return typeof(TestAssemblyNodeCommandHandler); }
+		}
+		
+		public override string ContextMenuAddinPath {
+			get { return "/SharpDevelop/Views/ProjectBrowser/ContextMenu/TestAssembly"; }
+		}
+
+		public override Type NodeDataType {
+			get { return typeof(TestAssembly); }
+		}
+		
+		public override string GetNodeName (ITreeNavigator thisNode, object dataObject)
+		{
+			return Path.GetFileName (((TestAssembly)dataObject).Path);
+		}
+		
+		public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, ref string label, ref Gdk.Pixbuf icon, ref Gdk.Pixbuf closedIcon)
+		{
+			TestAssembly asm = dataObject as TestAssembly;
+			label = Path.GetFileName (asm.Path);
+			icon = Context.GetIcon (Stock.Reference);
+		}
+	}
+	
+	class TestAssemblyNodeCommandHandler: NodeCommandHandler
+	{
+		[CommandHandler (EditCommands.Delete)]
+		protected void OnDeleteAssembly ()
+		{
+			TestAssembly asm = CurrentNode.DataItem as TestAssembly;
+			NUnitAssemblyGroupProjectConfiguration config = (NUnitAssemblyGroupProjectConfiguration) CurrentNode.GetParentDataItem (typeof(NUnitAssemblyGroupProjectConfiguration), false);
+			config.Assemblies.Remove (asm);
+		}
+	}
+}

Added: trunk/MonoDevelop/Extras/NUnit/Gui/TestChart.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Gui/TestChart.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Gui/TestChart.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,406 @@
+//
+// TestChart.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+using Gtk;
+using Gdk;
+using MonoDevelop.Gui.Widgets.Chart;
+
+namespace MonoDevelop.NUnit
+{
+	public enum TestChartType {
+		Results,
+		Time
+	}
+	
+	class TestRunAxis: IntegerAxis
+	{
+		public UnitTestResult[] CurrentResults;
+		
+		public TestRunAxis (bool showLabel): base (showLabel)
+		{
+		}
+		
+		public override string GetValueLabel (double value)
+		{
+			if (CurrentResults == null)
+				return "";
+
+			int val = (int) value;
+			if (val >= CurrentResults.Length)
+				return "";
+				
+			UnitTestResult res = CurrentResults [CurrentResults.Length - val - 1];
+			return string.Format ("{0}/{1}", res.TestDate.Day, res.TestDate.Month);
+		}
+	}
+	
+	public class TestChart: BasicChart
+	{
+		Serie serieFailed;
+		Serie serieSuccess;
+		Serie serieIgnored;
+		
+		Serie serieTime;
+		
+		bool timeScale = false;
+		bool singleDayResult = false;
+		TestChartType type;
+		
+		TimeSpan currentSpan = TimeSpan.FromDays (5);
+		int testCount = 20;
+		UnitTest test;
+		bool showLastTest = true;
+		bool resetCursors = true;
+		double lastDateValue;
+		double lastTestNumber;
+		UnitTestResult[] currentResults;
+		TestRunAxis testRunAxis;
+		
+		public TestChart ()
+		{
+			AllowSelection = true;
+			SetAutoScale (AxisDimension.Y, false, true);
+			StartY = 0;
+			
+			serieFailed = new Serie ("Failed tests");
+			serieFailed.Color = new Color (255, 0, 0);
+			serieSuccess = new Serie ("Successful tests");
+			serieSuccess.Color = new Color (0, 164, 0);
+			serieIgnored = new Serie ("Ignored tests");
+			serieIgnored.Color = new Color (206, 206, 0);
+			
+			serieTime = new Serie ("Time");
+			serieTime.Color = new Color (0, 0, 255);
+			
+			UpdateMode ();
+			
+/*			EndX = DateTime.Now.Ticks;
+			StartX = EndX - currentSpan.Ticks;
+			*/
+			EndX = 5;
+			StartX = 0;
+		}
+		
+		public bool ShowSuccessfulTests {
+			get { return serieSuccess.Visible; }
+			set { serieSuccess.Visible = value; }
+		}
+		
+		public bool ShowFailedTests {
+			get { return serieFailed.Visible; }
+			set { serieFailed.Visible = value; }
+		}
+		
+		public bool ShowIgnoredTests {
+			get { return serieIgnored.Visible; }
+			set { serieIgnored.Visible = value; }
+		}
+		
+		public bool UseTimeScale {
+			get { return timeScale; }
+			set { timeScale = value; UpdateMode (); }
+		}
+		
+		public bool SingleDayResult {
+			get { return singleDayResult; }
+			set { singleDayResult = value; UpdateMode (); }
+		}
+		
+		public TestChartType Type {
+			get { return type; }
+			set { type = value; UpdateMode (); }
+		}
+		
+		public DateTime CurrentDate {
+			get {
+				if (timeScale)
+					return new DateTime ((long) SelectionEnd.Value);
+				else {
+					int n = (int) SelectionStart.Value;
+					if (currentResults != null && n >= 0 && n < currentResults.Length)
+						return currentResults [currentResults.Length - n - 1].TestDate;
+					else
+						return DateTime.MinValue;
+				}
+			}
+		}
+		
+		public DateTime ReferenceDate {
+			get {
+				if (timeScale)
+					return new DateTime ((long) SelectionStart.Value);
+				else {
+					int n = (int) SelectionEnd.Value;
+					if (currentResults != null && n >= 0 && n < currentResults.Length)
+						return currentResults [currentResults.Length - n - 1].TestDate;
+					else
+						return DateTime.MinValue;
+				}
+			}
+		}
+		
+		void UpdateMode ()
+		{
+			AllowSelection = false;
+			
+			Reset ();
+
+			if (type == TestChartType.Results) {
+				AddSerie (serieIgnored);
+				AddSerie (serieFailed);
+				AddSerie (serieSuccess);
+			} else {
+				AddSerie (serieTime);
+			}
+			
+			if (timeScale) {
+				ReverseXAxis = false;
+				Axis ax = new DateTimeAxis (true);
+				AddAxis (new DateTimeAxis (false), AxisPosition.Top);
+				AddAxis (ax, AxisPosition.Bottom);
+				SelectionEnd.Value = SelectionStart.Value = DateTime.Now.Ticks;
+				SelectionStart.LabelAxis = ax;
+				SelectionEnd.LabelAxis = ax;
+			} else {
+				ReverseXAxis = true;
+				AddAxis (new TestRunAxis (false), AxisPosition.Top);
+				testRunAxis = new TestRunAxis (true);
+				AddAxis (testRunAxis, AxisPosition.Bottom);
+				SelectionEnd.Value = SelectionStart.Value = 0;
+				SelectionStart.LabelAxis = testRunAxis;
+				SelectionEnd.LabelAxis = testRunAxis;
+			}
+			showLastTest = true;
+			resetCursors = true;
+			
+			AddAxis (new IntegerAxis (true), AxisPosition.Left);
+			AddAxis (new IntegerAxis (true), AxisPosition.Right);
+			
+			if (test != null)
+				Fill (test);
+			
+			AllowSelection = true;
+		}
+		
+		public new void Clear ()
+		{
+			base.Clear ();
+			test = null;
+		}
+		
+		public void ZoomIn ()
+		{
+			if (test == null)
+				return;
+			if (timeScale) {
+				currentSpan = new TimeSpan (currentSpan.Ticks / 2);
+				if (currentSpan.TotalSeconds < 60)
+					currentSpan = TimeSpan.FromSeconds (60);
+			} else {
+				testCount = testCount / 2;
+				if (testCount < 5)
+					testCount = 5;
+			}
+			Fill (test);
+		}
+		
+		public void ZoomOut ()
+		{
+			if (test == null)
+				return;
+			if (timeScale) {
+				currentSpan = new TimeSpan (currentSpan.Ticks * 2);
+				if (currentSpan.TotalDays > 50 * 365)
+					currentSpan = TimeSpan.FromDays (50 * 365);
+			} else {
+				testCount *= 2;
+				if (testCount > 100000)
+					testCount = 100000;
+			}
+			Fill (test);
+		}
+		
+		public void GoNext ()
+		{
+			if (showLastTest)
+				return;
+
+			if (timeScale) {
+				lastDateValue += (EndX - StartX) / 3;
+				UnitTestResult lastResult = test.Results.GetLastResult (DateTime.Now);
+				if (lastResult != null && new DateTime ((long)lastDateValue) > lastResult.TestDate)
+					showLastTest = true;
+			} else {
+				lastTestNumber -= (EndX - StartX) / 3;
+				if (lastTestNumber < 0)
+					showLastTest = true;
+			}
+			Fill (test);
+		}
+		
+		public void GoPrevious ()
+		{
+			if (timeScale) {
+				lastDateValue -= (EndX - StartX) / 3;
+			} else {
+				lastTestNumber += (EndX - StartX) / 3;
+			}
+			showLastTest = false;
+			Fill (test);
+		}
+		
+		public void GoLast ()
+		{
+			showLastTest = true;
+			resetCursors = true;
+			Fill (test);
+		}
+		
+		public void Fill (UnitTest test)
+		{
+			serieFailed.Clear ();
+			serieSuccess.Clear ();
+			serieIgnored.Clear ();
+			serieTime.Clear ();
+			
+			this.test = test;
+			
+			if (showLastTest) {
+				if (timeScale)
+					lastDateValue = DateTime.Now.Ticks;
+				else
+					lastTestNumber = 0;
+			}
+			
+			UnitTestResult first = null;
+			UnitTestResult[] results;
+			UnitTestResult lastResult = test.Results.GetLastResult (DateTime.Now);
+			if (lastResult == null)
+				return;
+			
+			if (timeScale) {
+				DateTime startDate;
+				if (showLastTest) {
+					startDate = lastResult.TestDate - currentSpan;
+					StartX = startDate.Ticks;
+					EndX = lastResult.TestDate.Ticks;
+					first = test.Results.GetLastResult (startDate);
+					results = test.Results.GetResults (startDate, lastResult.TestDate);
+				} else {
+					DateTime endDate = new DateTime ((long)lastDateValue);
+					startDate = endDate - currentSpan;
+					StartX = (double) startDate.Ticks;
+					EndX = (double) endDate.Ticks;
+					first = test.Results.GetLastResult (startDate);
+					results = test.Results.GetResults (startDate, lastResult.TestDate);
+				}
+				if (singleDayResult) {
+					first = test.Results.GetPreviousResult (new DateTime (startDate.Year, startDate.Month, startDate.Day));
+					ArrayList list = new ArrayList ();
+					if (first != null)
+						list.Add (first);
+					for (int n=0; n<results.Length - 1; n++) {
+						DateTime d1 = results [n].TestDate;
+						DateTime d2 = results [n + 1].TestDate;
+						if (d1.Day != d2.Day || d1.Month != d2.Month || d1.Year != d2.Year)
+							list.Add (results[n]);
+					}
+					list.Add (results [results.Length - 1]);
+					results = (UnitTestResult[]) list.ToArray (typeof(UnitTestResult));
+				}
+				
+				if (resetCursors) {
+					SelectionEnd.Value = EndX;
+					if (results.Length > 1)
+						SelectionStart.Value = results [results.Length - 2].TestDate.Ticks;
+					else
+						SelectionStart.Value = EndX;
+					resetCursors = false;
+				}
+			} else {
+				if (singleDayResult) {
+					ArrayList list = new ArrayList ();
+					list.Add (lastResult);
+					while (list.Count < testCount + (int)lastTestNumber + 1) {
+						UnitTestResult res = test.Results.GetPreviousResult (lastResult.TestDate);
+						if (res == null) break;
+						if (res.TestDate.Day != lastResult.TestDate.Day || res.TestDate.Month != lastResult.TestDate.Month || res.TestDate.Year != lastResult.TestDate.Year)
+							list.Add (res);
+						lastResult = res;
+					}
+					results = (UnitTestResult[]) list.ToArray (typeof(UnitTestResult));
+					Array.Reverse (results);
+				} else {
+					results = test.Results.GetResultsToDate (DateTime.Now, testCount + (int)lastTestNumber + 1);
+				}
+				EndX = lastTestNumber + testCount;
+				StartX = lastTestNumber;
+				
+				if (resetCursors) {
+					SelectionStart.Value = StartX;
+					SelectionEnd.Value = StartX + 1;
+					resetCursors = false;
+				}
+			}
+			
+			
+			currentResults = results;
+			if (testRunAxis != null)
+				testRunAxis.CurrentResults = currentResults;
+			
+			if (Type == TestChartType.Results) {
+				if (first != null) {
+					double x = timeScale ? first.TestDate.Ticks : results.Length;
+					serieFailed.AddData (x, first.TotalFailures);
+					serieSuccess.AddData (x, first.TotalSuccess);
+					serieIgnored.AddData (x, first.TotalIgnored);
+				}
+				
+				for (int n=0; n < results.Length; n++) {
+					UnitTestResult res = results [n];
+					double x = timeScale ? res.TestDate.Ticks : results.Length - n - 1;
+					serieFailed.AddData (x, res.TotalFailures);
+					serieSuccess.AddData (x, res.TotalSuccess);
+					serieIgnored.AddData (x, res.TotalIgnored);
+				}
+			} else {
+				if (first != null) {
+					double x = timeScale ? first.TestDate.Ticks : results.Length;
+					serieTime.AddData (x, first.Time.TotalMilliseconds);
+				}
+				for (int n=0; n < results.Length; n++) {
+					UnitTestResult res = results [n];
+					double x = timeScale ? res.TestDate.Ticks : results.Length - n - 1;
+					serieTime.AddData (x, results [n].Time.TotalMilliseconds);
+				}
+			}
+		}
+	}
+}

Added: trunk/MonoDevelop/Extras/NUnit/Gui/TestNodeBuilder.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Gui/TestNodeBuilder.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Gui/TestNodeBuilder.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,183 @@
+//
+// TestNodeBuilder.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+
+using MonoDevelop.Internal.Project;
+using MonoDevelop.Services;
+using MonoDevelop.Gui.Pads;
+using MonoDevelop.Commands;
+
+namespace MonoDevelop.NUnit
+{
+	public class TestNodeBuilder: TypeNodeBuilder
+	{
+		EventHandler testChanged;
+		EventHandler testStatusChanged;
+		
+		public TestNodeBuilder ()
+		{
+			testChanged = (EventHandler) Runtime.DispatchService.GuiDispatch (new EventHandler (OnTestChanged));
+			testStatusChanged = (EventHandler) Runtime.DispatchService.GuiDispatch (new EventHandler (OnTestStatusChanged));
+		}
+		
+		public override Type CommandHandlerType {
+			get { return typeof(TestNodeCommandHandler); }
+		}
+		
+		public override string ContextMenuAddinPath {
+			get { return "/SharpDevelop/Views/TestPad/ContextMenu"; }
+		}
+			
+		public override Type NodeDataType {
+			get { return typeof(UnitTest); }
+		}
+		
+		public override string GetNodeName (ITreeNavigator thisNode, object dataObject)
+		{
+			return ((UnitTest)dataObject).Name;
+		}
+		
+/*		public override void GetNodeAttributes (ITreeNavigator parentNode, object dataObject, ref NodeAttributes attributes)
+		{
+			attributes |= NodeAttributes.UseMarkup;
+		}
+*/
+		public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, ref string label, ref Gdk.Pixbuf icon, ref Gdk.Pixbuf closedIcon)
+		{
+			UnitTest test = dataObject as UnitTest;
+			
+			if (test.Status == TestStatus.Running) {
+				icon = CircleImage.Running;
+				label = test.Title;
+				return;
+			} else if (test.Status == TestStatus.Loading) {
+				icon = CircleImage.Loading;
+				label = test.Title + " (Loading)";
+				return;
+			} else if (test.Status == TestStatus.LoadError) {
+				icon = CircleImage.Failure;
+				label = test.Title + " (Load failed)";
+				return;
+			} else {
+				label = test.Title;
+
+				UnitTestResult res = test.GetLastResult ();
+				if (res == null)
+					icon = CircleImage.None;
+				else if (res.IsFailure && res.IsSuccess)
+					icon = CircleImage.SuccessAndFailure;
+				else if (res.IsFailure)
+					icon = CircleImage.Failure;
+				else if (res.IsSuccess) {
+					icon = CircleImage.Success;
+					if (treeBuilder.Options ["ShowTestTime"]) {
+						label += " (" + (res.Time.TotalMilliseconds) + " ms)";
+					}
+				}
+				else if (res.IsIgnored)
+					icon = CircleImage.NotRun;
+				else
+					icon = CircleImage.None;
+				
+				if (res != null && treeBuilder.Options ["ShowTestCounters"] && (test is UnitTestGroup)) {
+					label += string.Format (" ({0} success, {1} failed, {2} ignored)", res.TotalSuccess, res.TotalFailures, res.TotalIgnored);
+				}
+			}
+		}
+
+		public override void BuildChildNodes (ITreeBuilder builder, object dataObject)
+		{
+			UnitTestGroup test = dataObject as UnitTestGroup;
+			if (test == null)
+				return;
+				
+			foreach (UnitTest t in test.Tests)
+				builder.AddChild (t);
+		}
+
+		public override bool HasChildNodes (ITreeBuilder builder, object dataObject)
+		{
+			UnitTestGroup test = dataObject as UnitTestGroup;
+			return test != null && test.Tests.Count > 0;
+		}
+		
+		public override void OnNodeAdded (object dataObject)
+		{
+			UnitTest test = (UnitTest) dataObject;
+			test.TestChanged += testChanged;
+			test.TestStatusChanged += testStatusChanged;
+		}
+		
+		public override void OnNodeRemoved (object dataObject)
+		{
+			UnitTest test = (UnitTest) dataObject;
+			test.TestChanged -= testChanged;
+			test.TestStatusChanged -= testStatusChanged;
+		}
+		
+		public void OnTestChanged (object sender, EventArgs args)
+		{
+			ITreeBuilder tb = Context.GetTreeBuilder (sender);
+			if (tb != null) tb.UpdateAll ();
+		}
+		
+		public void OnTestStatusChanged (object sender, EventArgs args)
+		{
+			ITreeBuilder tb = Context.GetTreeBuilder (sender);
+			if (tb != null) tb.Update ();
+		}
+	}
+	
+	class TestNodeCommandHandler: NodeCommandHandler
+	{
+		[CommandHandler (TestCommands.ShowTestCode)]
+		protected void OnShowTest ()
+		{
+			UnitTest test = CurrentNode.DataItem as UnitTest;
+			SourceCodeLocation loc = test.SourceCodeLocation;
+			if (loc != null)
+				Runtime.FileService.OpenFile (loc.FileName, loc.Line, loc.Column, true);
+		}
+		
+		[CommandUpdateHandler (TestCommands.ShowTestCode)]
+		protected void OnUpdateRunTest (CommandInfo info)
+		{
+			UnitTest test = CurrentNode.DataItem as UnitTest;
+			info.Enabled = test.SourceCodeLocation != null;
+		}
+		
+		[CommandHandler (ProjectCommands.Options)]
+		protected void OnShowOptions ()
+		{
+			UnitTest test = CurrentNode.DataItem as UnitTest;
+			NUnitService.ShowOptionsDialog (test);
+		}
+	}
+}

Modified: trunk/MonoDevelop/Extras/NUnit/Gui/TestPad.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Gui/TestPad.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Gui/TestPad.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -1,268 +1,783 @@
+//
+// TestPad.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
 using System;
 using System.Collections;
-using System.Reflection;
+using System.Threading;
 using Gtk;
+using Gdk;
 
 using MonoDevelop.Core.Services;
 using MonoDevelop.Services;
 using MonoDevelop.Gui;
+using MonoDevelop.Gui.Pads;
 using MonoDevelop.Commands;
 
-using NUnit.Framework;
-using NUnit.Core;
-
 namespace MonoDevelop.NUnit
 {
-	public class TestPad : AbstractPadContent
+	public class TestPad: TreeViewPad
 	{
-		string assemblyName;
-		ScrolledWindow sw;
-		TreeView view;
-		TreeStore store;
-		Hashtable iters;
-		TestSuite rootTestSuite;
+		NUnitService testService = (NUnitService) ServiceManager.GetService (typeof(NUnitService));
+		
+		IAsyncOperation runningTestOperation;
+		VPaned paned;
+		TreeView detailsTree;
+		ListStore detailsStore;
+		HeaderLabel detailLabel;
+		TestChart chart;
+		UnitTest detailsTest;
+		DateTime detailsDate;
+		DateTime detailsReferenceDate;
+		ButtonNotebook infoBook;
+		TextView outputView;
+		TextView resultView;
+		TreeView regressionTree;
+		ListStore regressionStore;
+		TreeView failedTree;
+		ListStore failedStore;
+		
+		int TestSummaryPage;
+		int TestRegressionsPage;
+		int TestFailuresPage;
+		int TestResultPage;
+		int TestOutputPage;
+		
+		EventHandler testChangedHandler;
+		VBox detailsPad;
+		
+		ArrayList testNavigationHistory = new ArrayList ();
+		
+		public override void Initialize (string label, string icon, NodeBuilder[] builders, TreePadOption[] options)
+		{
+			base.Initialize (label, icon, builders, options);
+			
+			testChangedHandler = (EventHandler) Runtime.DispatchService.GuiDispatch (new EventHandler (OnDetailsTestChanged));
+			testService.TestSuiteChanged += (EventHandler) Runtime.DispatchService.GuiDispatch (new EventHandler (OnTestSuiteChanged));
+			
+			paned = new VPaned ();
+			paned.Pack1 (base.Control, true, true);
+			
+			detailsPad = new VBox ();
+			
+			EventBox eb = new EventBox ();
+			HBox header = new HBox ();
+			eb.Add (header);
 
-		IStatusBarService statusBarService;
-		NUnitService nunitService;
-
-		int currentTest, totalTests, finishedTests;
-		int ignoredTests, errorTests;
-
-		event FixtureAddedEventHandler FixtureAddedEvent;
-
-		public TestPad () : base ("NUnit")
-		{
+			detailLabel = new HeaderLabel ();
+			detailLabel.Padding = 6;
+			
+			Button hb = new Button (new Gtk.Image ("gtk-close", IconSize.SmallToolbar));
+			hb.Relief = ReliefStyle.None;
+			hb.Clicked += new EventHandler (OnCloseDetails);
+			header.PackEnd (hb, false, false, 0);
+			
+			hb = new Button (new Gtk.Image ("gtk-go-back", IconSize.SmallToolbar));
+			hb.Relief = ReliefStyle.None;
+			hb.Clicked += new EventHandler (OnGoBackTest);
+			header.PackEnd (hb, false, false, 0);
+			
+			header.Add (detailLabel);
+			Gdk.Color hcol = eb.Style.Background (StateType.Normal);
+			hcol.Red = (ushort) (((double)hcol.Red) * 0.9);
+			hcol.Green = (ushort) (((double)hcol.Green) * 0.9);
+			hcol.Blue = (ushort) (((double)hcol.Blue) * 0.9);
+		//	eb.ModifyBg (StateType.Normal, hcol);
+			
+			detailsPad.PackStart (eb, false, false, 0);
+			
+			VPaned panedDetails = new VPaned ();
+			panedDetails.BorderWidth = 3;
+			VBox boxPaned1 = new VBox ();
+			
+			chart = new TestChart ();
+			chart.ButtonReleaseEvent += new Gtk.ButtonReleaseEventHandler (OnChartPopupMenu);
+			chart.SelectionChanged += new EventHandler (OnChartDateChanged);
+			chart.HeightRequest = 50;
+			
+			Toolbar toolbar = new Toolbar ();
+			toolbar.IconSize = IconSize.SmallToolbar;
+			toolbar.ToolbarStyle = ToolbarStyle.Icons;
+			toolbar.ShowArrow = false;
+			ToolButton but = new ToolButton ("gtk-zoom-in");
+			but.Clicked += new EventHandler (OnZoomIn);
+			toolbar.Insert (but, -1);
+			
+			but = new ToolButton ("gtk-zoom-out");
+			but.Clicked += new EventHandler (OnZoomOut);
+			toolbar.Insert (but, -1);
+			
+			but = new ToolButton ("gtk-go-back");
+			but.Clicked += new EventHandler (OnChartBack);
+			toolbar.Insert (but, -1);
+			
+			but = new ToolButton ("gtk-go-forward");
+			but.Clicked += new EventHandler (OnChartForward);
+			toolbar.Insert (but, -1);
+			
+			but = new ToolButton ("gtk-goto-last");
+			but.Clicked += new EventHandler (OnChartLast);
+			toolbar.Insert (but, -1);
+			
+			boxPaned1.PackStart (toolbar, false, false, 0);
+			boxPaned1.PackStart (chart, true, true, 0);
+			
+			panedDetails.Pack1 (boxPaned1, false, false);
+			
+			// Detailed test information --------
+			
+			infoBook = new ButtonNotebook ();
+			infoBook.PageLoadRequired += new EventHandler (OnPageLoadRequired);
+			
+			// Info - Group summary
+			
+			Frame tf = new Frame ();
+			ScrolledWindow sw = new ScrolledWindow ();
+			detailsTree = new TreeView ();
+			
+			detailsTree.HeadersVisible = true;
+			detailsTree.RulesHint = true;
+			detailsStore = new ListStore (typeof(object), typeof(string), typeof (string), typeof (string), typeof (string));
+			
+			CellRendererText trtest = new CellRendererText ();
+			CellRendererText tr;
+			
+			TreeViewColumn col3 = new TreeViewColumn ();
+			col3.Expand = false;
+//			col3.Alignment = 0.5f;
+			col3.Widget = new Gtk.Image (CircleImage.Success);
+			col3.Widget.Show ();
+			tr = new CellRendererText ();
+//			tr.Xalign = 0.5f;
+			col3.PackStart (tr, false);
+			col3.AddAttribute (tr, "markup", 2);
+			detailsTree.AppendColumn (col3);
+			
+			TreeViewColumn col4 = new TreeViewColumn ();
+			col4.Expand = false;
+//			col4.Alignment = 0.5f;
+			col4.Widget = new Gtk.Image (CircleImage.Failure);
+			col4.Widget.Show ();
+			tr = new CellRendererText ();
+//			tr.Xalign = 0.5f;
+			col4.PackStart (tr, false);
+			col4.AddAttribute (tr, "markup", 3);
+			detailsTree.AppendColumn (col4);
+			
+			TreeViewColumn col5 = new TreeViewColumn ();
+			col5.Expand = false;
+//			col5.Alignment = 0.5f;
+			col5.Widget = new Gtk.Image (CircleImage.NotRun);
+			col5.Widget.Show ();
+			tr = new CellRendererText ();
+//			tr.Xalign = 0.5f;
+			col5.PackStart (tr, false);
+			col5.AddAttribute (tr, "markup", 4);
+			detailsTree.AppendColumn (col5);
+			
+			TreeViewColumn col1 = new TreeViewColumn ();
+//			col1.Resizable = true;
+//			col1.Expand = true;
+			col1.Title = "Test";
+			col1.PackStart (trtest, true);
+			col1.AddAttribute (trtest, "markup", 1);
+			detailsTree.AppendColumn (col1);
+			
+			detailsTree.Model = detailsStore;
+			
+			sw.Add (detailsTree);
+			tf.Add (sw);
+			tf.ShowAll ();
+			
+			TestSummaryPage = infoBook.AddPage ("Summary", tf);
+			
+			// Info - Regressions list
+			
+			tf = new Frame ();
 			sw = new ScrolledWindow ();
-			CreateView ();
-			sw.Add (view);
-			sw.ShowAll ();
-
-			// color, name
-			store = new TreeStore (typeof (Gdk.Pixbuf), typeof (string));
-
-			// services
-			statusBarService = ServiceManager.GetService (typeof (IStatusBarService)) as IStatusBarService;
-			nunitService = ServiceManager.GetService (typeof (NUnitService)) as NUnitService;
-
-			// register events
-			this.FixtureAddedEvent += OnFixtureAdded;
-			nunitService.AssemblyLoaded += OnAssemblyLoaded;
-			nunitService.TestStartedEvent += OnTestStarted;
-			nunitService.TestFinishedEvent += OnTestFinished;
+			tf.Add (sw);
+			regressionTree = new TreeView ();
+			regressionTree.HeadersVisible = false;
+			regressionTree.RulesHint = true;
+			regressionStore = new ListStore (typeof(object), typeof(string), typeof (Pixbuf));
+			
+			CellRendererText trtest2 = new CellRendererText ();
+			CellRendererPixbuf pr = new CellRendererPixbuf ();
+			
+			TreeViewColumn col = new TreeViewColumn ();
+			col.PackStart (pr, false);
+			col.AddAttribute (pr, "pixbuf", 2);
+			col.PackStart (trtest2, false);
+			col.AddAttribute (trtest2, "markup", 1);
+			regressionTree.AppendColumn (col);
+			regressionTree.Model = regressionStore;
+			sw.Add (regressionTree);
+			tf.ShowAll ();
+			
+			TestRegressionsPage = infoBook.AddPage ("Regressions", tf);
+			
+			// Info - Failed tests list
+			
+			tf = new Frame ();
+			sw = new ScrolledWindow ();
+			tf.Add (sw);
+			failedTree = new TreeView ();
+			failedTree.HeadersVisible = false;
+			failedTree.RulesHint = true;
+			failedStore = new ListStore (typeof(object), typeof(string), typeof (Pixbuf));
+			
+			trtest2 = new CellRendererText ();
+			pr = new CellRendererPixbuf ();
+			
+			col = new TreeViewColumn ();
+			col.PackStart (pr, false);
+			col.AddAttribute (pr, "pixbuf", 2);
+			col.PackStart (trtest2, false);
+			col.AddAttribute (trtest2, "markup", 1);
+			failedTree.AppendColumn (col);
+			failedTree.Model = failedStore;
+			sw.Add (failedTree);
+			tf.ShowAll ();
+			
+			TestFailuresPage = infoBook.AddPage ("Failed tests", tf);
+			
+			// Info - results
+			
+			tf = new Frame ();
+			sw = new ScrolledWindow ();
+			tf.Add (sw);
+			resultView = new TextView ();
+			resultView.Editable = false;
+			sw.Add (resultView);
+			tf.ShowAll ();
+			TestResultPage = infoBook.AddPage ("Result", tf);
+			
+			// Info - Output
+			
+			tf = new Frame ();
+			sw = new ScrolledWindow ();
+			tf.Add (sw);
+			outputView = new TextView ();
+			outputView.Editable = false;
+			sw.Add (outputView);
+			tf.ShowAll ();
+			TestOutputPage = infoBook.AddPage ("Output", tf);
+			
+			panedDetails.Pack2 (infoBook, true, true);
+			detailsPad.PackStart (panedDetails, true, true, 0);
+			paned.Pack2 (detailsPad, true, true);
+			
+			paned.ShowAll ();
+			
+			infoBook.HidePage (TestResultPage);
+			infoBook.HidePage (TestOutputPage);
+			infoBook.HidePage (TestSummaryPage);
+			infoBook.HidePage (TestRegressionsPage);
+			infoBook.HidePage (TestFailuresPage);
+			
+			detailsPad.Sensitive = false;
+			detailsPad.Hide ();
+			
+			detailsTree.RowActivated += new Gtk.RowActivatedHandler (OnTestActivated);
+			regressionTree.RowActivated += new Gtk.RowActivatedHandler (OnRegressionTestActivated);
+			failedTree.RowActivated += new Gtk.RowActivatedHandler (OnFailedTestActivated);
 		}
-
-		public override Widget Control {
-			get { return sw; }
+		
+		void OnTestSuiteChanged (object sender, EventArgs e)
+		{
+			if (testService.RootTest != null)
+				LoadTree (testService.RootTest);
+			else {
+				Clear ();
+				ClearDetails ();
+			}
 		}
-
-		void CreateView ()
+		
+		public void SelectTest (UnitTest t)
 		{
-			view = new TreeView ();
-			view.HeadersVisible = false;
+			ITreeNavigator node = FindTestNode (t);
+			node.ExpandToNode ();
+			node.Selected = true;
+		}
+		
+		ITreeNavigator FindTestNode (UnitTest t)
+		{
+			ITreeNavigator nav = GetNodeAtObject (t);
+			if (nav != null)
+				return nav;
+			if (t.Parent == null)
+				return null;
+				
+			nav = FindTestNode (t.Parent);
+			
+			if (nav == null)
+				return null;
+				
+			nav.MoveToFirstChild ();	// Make sure the children are created
+			return GetNodeAtObject (t);
+		}
+		
+		public override Gtk.Widget Control {
+			get {
+				return paned;
+			}
+		}
+		
+		[CommandHandler (TestCommands.RunTest)]
+		protected void OnRunTest ()
+		{
+			RunSelectedTest ();
+		}
+		
+		[CommandUpdateHandler (TestCommands.RunTest)]
+		protected void OnUpdateRunTest (CommandInfo info)
+		{
+			info.Enabled = runningTestOperation == null;
+		}
+		
+		public override void ActivateCurrentItem ()
+		{
+			RunSelectedTest ();
+		}
+		
+		void RunSelectedTest ()
+		{
+			ITreeNavigator nav = GetSelectedNode ();
+			UnitTest test = (UnitTest) nav.DataItem;
+			
+			runningTestOperation = testService.RunTest (test);
+			runningTestOperation.Completed += (OperationHandler) Runtime.DispatchService.GuiDispatch (new OperationHandler (TestSessionCompleted));
+		}
+		
+		void TestSessionCompleted (IAsyncOperation op)
+		{
+			if (op.Success)
+				RefreshDetails ();
+			runningTestOperation = null;
+		}
+		
+		protected override void OnSelectionChanged (object sender, EventArgs args)
+		{
+			base.OnSelectionChanged (sender, args);
+			ITreeNavigator nav = GetSelectedNode ();
+			UnitTest test = (UnitTest) nav.DataItem;
+			FillDetails (test, false);
+		}
+		
+		void OnGoBackTest (object sender, EventArgs args)
+		{
+			int c = testNavigationHistory.Count;
+			if (c > 1) {
+				UnitTest t = (UnitTest) testNavigationHistory [c - 2];
+				testNavigationHistory.RemoveAt (c - 1);
+				testNavigationHistory.RemoveAt (c - 2);
+				FillDetails (t, true);
+			}
+		}
+		
+		void OnCloseDetails (object sender, EventArgs args)
+		{
+			detailsPad.Hide ();
+		}
+		
+		[CommandHandler (TestCommands.ShowTestDetails)]
+		protected void OnShowDetails ()
+		{
+			if (!detailsPad.Visible) {
+				detailsPad.Show ();
+				
+				ITreeNavigator nav = GetSelectedNode ();
+				if (nav != null) {
+					UnitTest test = (UnitTest) nav.DataItem;
+					FillDetails (test, false);
+				} else
+					ClearDetails ();
+			}
+		}
+		
+		void ClearDetails ()
+		{
+			chart.Clear ();
+			detailLabel.Markup = "";
+			detailsStore.Clear ();
+			if (detailsTest != null)
+				detailsTest.TestChanged -= testChangedHandler;
+			detailsTest = null;
+			detailsDate = DateTime.MinValue;
+			detailsReferenceDate = DateTime.MinValue;
+			detailsPad.Sensitive = false;
+		}
+		
+		void RefreshDetails ()
+		{
+			if (detailsTest != null)
+				FillDetails (detailsTest, false);
+		}
+		
+		void FillDetails (UnitTest test, bool selectInTree)
+		{
+			if (!detailsPad.Visible)
+				return;
 
-			CellRendererPixbuf pr = new CellRendererPixbuf ();
-			CellRendererText tr = new CellRendererText ();
-			TreeViewColumn column = new TreeViewColumn ();
-			column.PackStart (pr, false);
-			column.AddAttribute (pr, "pixbuf", 0);
-			column.PackStart (tr, false);
-			column.AddAttribute (tr, "text", 1);
-			view.AppendColumn (column);
-
-			view.RowActivated += OnRowActivated;
-			view.PopupMenu += OnPopup;
-			view.ButtonReleaseEvent += OnButtonReleased;
+			detailsPad.Sensitive = true;
+			
+			if (detailsTest != null)
+				detailsTest.TestChanged -= testChangedHandler;
+			
+			if (detailsTest != test) {
+				detailsTest = test;
+				if (selectInTree)
+					SelectTest (test);
+				testNavigationHistory.Add (test);
+				if (testNavigationHistory.Count > 50)
+					testNavigationHistory.RemoveAt (0);
+			}
+			detailsTest.TestChanged += testChangedHandler;
+			
+			if (test is UnitTestGroup) {
+				infoBook.HidePage (TestResultPage);
+				infoBook.HidePage (TestOutputPage);
+				infoBook.ShowPage (TestSummaryPage);
+				infoBook.ShowPage (TestRegressionsPage);
+				infoBook.ShowPage (TestFailuresPage);
+			} else {
+				infoBook.HidePage (TestSummaryPage);
+				infoBook.HidePage (TestRegressionsPage);
+				infoBook.HidePage (TestFailuresPage);
+				infoBook.ShowPage (TestResultPage);
+				infoBook.ShowPage (TestOutputPage);
+			}
+			detailLabel.Markup = "<b>" + test.Name + "</b>";
+			detailsDate = DateTime.MinValue;
+			detailsReferenceDate = DateTime.MinValue;
+			chart.Fill (test);
+			infoBook.Reset ();
 		}
-
-		TreeIter AddFixture (TreeIter parent, string fullname)
+		
+		void FillTestInformation ()
 		{
-			string [] parts = fullname.Split ('.');
-			string index = String.Empty;
+			if (!detailsPad.Visible)
+				return;
 
-			foreach (string s in parts)
-			{
-				if (index.Length == 0)
-					index = s;
-				else
-					index += String.Format (".{0}", s);
-
-				if (iters.ContainsKey (index)) {
-					parent = (TreeIter) iters [index];
-					continue;
+			if (detailsTest is UnitTestGroup) {
+				UnitTestGroup group = detailsTest as UnitTestGroup;
+				if (infoBook.Page == TestSummaryPage) {
+					detailsStore.Clear ();
+					foreach (UnitTest t in group.Tests) {
+						UnitTestResult res = t.Results.GetLastResult (chart.CurrentDate);
+						if (res != null)
+							detailsStore.AppendValues (t, t.Name, res.TotalSuccess.ToString(), res.TotalFailures.ToString(), res.TotalIgnored.ToString());
+						else
+							detailsStore.AppendValues (t, t.Name, "", "", "");
+					}
 				}
-
-				parent = store.AppendValues (parent, CircleImage.None, s);
-				iters[index] = parent;
+				else if (infoBook.Page == TestRegressionsPage) {
+					regressionStore.Clear ();
+					UnitTestCollection regs = detailsTest.GetRegressions (chart.ReferenceDate, chart.CurrentDate);
+					if (regs.Count > 0) {
+						foreach (UnitTest t in regs)
+							regressionStore.AppendValues (t, t.Name, CircleImage.Failure);
+					} else
+						regressionStore.AppendValues (null, "No regressions found.");
+				}
+				else if (infoBook.Page == TestFailuresPage) {
+					failedStore.Clear ();
+					UnitTestCollection regs = group.GetFailedTests (chart.CurrentDate);
+					if (regs.Count > 0) {
+						foreach (UnitTest t in regs)
+							failedStore.AppendValues (t, t.Name, CircleImage.Failure);
+					} else
+						failedStore.AppendValues (null, "No failed tests found.");
+				}
+			} else {
+				UnitTestResult res = detailsTest.Results.GetLastResult (chart.CurrentDate);
+				if (infoBook.Page == TestOutputPage) {
+					outputView.Buffer.Clear ();
+					if (res != null)
+						outputView.Buffer.InsertAtCursor (res.ConsoleOutput);
+				} else if (infoBook.Page == TestResultPage) {
+					resultView.Buffer.Clear ();
+					if (res != null) {
+						string msg = res.Message + "\n\n" + res.StackTrace;
+						resultView.Buffer.InsertAtCursor (msg);
+					}
+				}
 			}
-
-			return parent;
 		}
-
-		void AddTestSuite (TreeIter parent, TestSuite suite)
+		
+		void OnDetailsTestChanged (object sender, EventArgs e)
 		{
-			TreeIter next;
-			foreach (Test t in suite.Tests)
-			{
-				next = AddFixture (parent, t.FullName);
-				while (GLib.MainContext.Iteration ());
-
-				if (t.IsSuite)
-					AddTestSuite (next, (TestSuite) t);
-				else if (FixtureAddedEvent != null)
-					FixtureAddedEvent (this, new FixtureAddedEventArgs (++currentTest, totalTests));
+			RefreshDetails ();
+		}
+		
+		void OnChartDateChanged (object sender, EventArgs e)
+		{
+			if (detailsTest != null && (detailsDate != chart.CurrentDate || detailsReferenceDate != chart.ReferenceDate)) {
+				detailsDate = chart.CurrentDate;
+				detailsReferenceDate = chart.ReferenceDate;
+				FillTestInformation ();
 			}
 		}
-
-		void OnAssemblyLoaded (object sender, EventArgs a)
+		
+		void OnPageLoadRequired (object o, EventArgs args)
 		{
-			GLib.Idle.Add (new GLib.IdleHandler (Populate));
+			if (detailsTest != null)
+				FillTestInformation ();
 		}
-
-		void OnButtonReleased (object sender, ButtonReleaseEventArgs a)
+		
+		protected virtual void OnTestActivated (object sender, Gtk.RowActivatedArgs args)
 		{
-			if (a.Event.Button == 3)
-				ShowPopup ();
+			TreeIter it;
+			detailsStore.GetIter (out it, args.Path);
+			UnitTest t = (UnitTest) detailsStore.GetValue (it, 0);
+			if (t != null)
+				FillDetails (t, true);
 		}
-
-		void OnFinishedLoad (object sender, EventArgs a)
+		
+		protected virtual void OnRegressionTestActivated (object sender, Gtk.RowActivatedArgs args)
 		{
-			string msg = String.Format (GettextCatalog.GetString ("{0} tests loaded."), totalTests);
-			statusBarService.SetProgressFraction (0.0);
-			statusBarService.SetMessage (msg);
-			// FIXME: set run menu items sensitive
-			TreeIter first;
-			if (store.GetIterFirst (out first))
-				view.Selection.SelectIter (first);
+			TreeIter it;
+			regressionStore.GetIter (out it, args.Path);
+			UnitTest t = (UnitTest) regressionStore.GetValue (it, 0);
+			if (t != null)
+				FillDetails (t, true);
 		}
-
-		void OnFinishedRunning (object sender, EventArgs a)
+		
+		protected virtual void OnFailedTestActivated (object sender, Gtk.RowActivatedArgs args)
 		{
-			statusBarService.SetMessage ("");
+			TreeIter it;
+			failedStore.GetIter (out it, args.Path);
+			UnitTest t = (UnitTest) failedStore.GetValue (it, 0);
+			if (t != null)
+				FillDetails (t, true);
 		}
-
-		void OnFixtureAdded (object sender, FixtureAddedEventArgs a)
+		
+		void OnZoomIn (object sender, EventArgs a)
 		{
-			string msg = String.Format (GettextCatalog.GetString ("Loading test {0} of {1}"), a.Current, a.Total);
-			statusBarService.SetProgressFraction (a.Current / a.Total);
-			statusBarService.SetMessage (msg);
+			if (detailsTest != null)
+				chart.ZoomIn ();
 		}
-
-		void OnLoadActivated (object sender, EventArgs a)
+		
+		void OnZoomOut (object sender, EventArgs a)
 		{
-			new NUnitLoadAssembly ().Run ();
+			if (detailsTest != null)
+				chart.ZoomOut ();
 		}
-
-		void OnPopup (object sender, EventArgs a)
+		
+		void OnChartBack (object sender, EventArgs a)
 		{
-			ShowPopup ();
+			if (detailsTest != null)
+				chart.GoPrevious ();
 		}
-
-		void ShowPopup ()
+		
+		void OnChartForward (object sender, EventArgs a)
 		{
-			Menu menu = new Menu ();
-
-			MenuItem load = new MenuItem ("Load test assembly");
-			load.Activated += OnLoadActivated;
-			menu.Append (load);
-
-			MenuItem run = new MenuItem ("Run tests");
-			run.Activated += OnRunActivated;
-			menu.Append (run);
-
-			//MenuItem save = new MenuItem ("Save Results");
-
-			menu.ShowAll ();
-			menu.Popup ();
+			if (detailsTest != null)
+				chart.GoNext ();
 		}
-
-		void OnRowActivated (object sender, RowActivatedArgs a)
+		
+		void OnChartLast (object sender, EventArgs a)
 		{
-			if (!nunitService.Running) {
-				finishedTests = 0;
-				RunTestAtPath (a.Path);
+			if (detailsTest != null)
+				chart.GoLast ();
+		}
+		
+		void OnChartPopupMenu (object o, Gtk.ButtonReleaseEventArgs args)
+		{
+			if (args.Event.Button == 3) {
+				CommandEntrySet eset = Runtime.Gui.CommandService.CreateCommandEntrySet ("/SharpDevelop/Views/TestChart/ContextMenu");
+				Gtk.Menu menu = Runtime.Gui.CommandService.CreateMenu (eset);
+				Runtime.Gui.Menus.ShowContextMenu (menu);
 			}
 		}
-
-		void OnRunActivated (object sender, EventArgs a)
+		
+		[CommandHandler (TestChartCommands.ShowResults)]
+		protected void OnShowResults ()
 		{
-			new NUnitRunTests ().Run ();
+			chart.Type = TestChartType.Results;
 		}
-
-		void OnTestFinished (object sender, TestEventArgs a)
+		
+		[CommandUpdateHandler (TestChartCommands.ShowResults)]
+		protected void OnUpdateShowResults (CommandInfo info)
 		{
-			SetIconFromResult (a.Result);
-			statusBarService.SetMessage ("");
+			info.Checked = chart.Type == TestChartType.Results;
 		}
-	
-		void OnTestStarted (object sender, TestEventArgs a)
+		
+		[CommandHandler (TestChartCommands.ShowTime)]
+		protected void OnShowTime ()
 		{
-			statusBarService.SetProgressFraction (++finishedTests / totalTests);
-			statusBarService.SetMessage ("Test: " + a.Test.FullName);
+			chart.Type = TestChartType.Time;
 		}
+		
+		[CommandUpdateHandler (TestChartCommands.ShowTime)]
+		protected void OnUpdateShowTime (CommandInfo info)
+		{
+			info.Checked = chart.Type == TestChartType.Time;
+		}
+		
+		[CommandHandler (TestChartCommands.UseTimeScale)]
+		protected void OnUseTimeScale ()
+		{
+			chart.UseTimeScale = !chart.UseTimeScale;
+		}
+		
+		[CommandUpdateHandler (TestChartCommands.UseTimeScale)]
+		protected void OnUpdateUseTimeScale (CommandInfo info)
+		{
+			info.Checked = chart.UseTimeScale;
+		}
+		
+		[CommandHandler (TestChartCommands.SingleDayResult)]
+		protected void OnSingleDayResult ()
+		{
+			chart.SingleDayResult = !chart.SingleDayResult;
+		}
+		
+		[CommandUpdateHandler (TestChartCommands.SingleDayResult)]
+		protected void OnUpdateSingleDayResult (CommandInfo info)
+		{
+			info.Checked = chart.SingleDayResult;
+		}
+		
+		[CommandHandler (TestChartCommands.ShowSuccessfulTests)]
+		protected void OnShowSuccessfulTests ()
+		{
+			chart.ShowSuccessfulTests = !chart.ShowSuccessfulTests;
+		}
+		
+		[CommandUpdateHandler (TestChartCommands.ShowSuccessfulTests)]
+		protected void OnUpdateShowSuccessfulTests (CommandInfo info)
+		{
+			info.Enabled = chart.Type == TestChartType.Results;
+			info.Checked = chart.ShowSuccessfulTests;
+		}
+		
+		[CommandHandler (TestChartCommands.ShowFailedTests)]
+		protected void OnShowFailedTests ()
+		{
+			chart.ShowFailedTests = !chart.ShowFailedTests;
+		}
+		
+		[CommandUpdateHandler (TestChartCommands.ShowFailedTests)]
+		protected void OnUpdateShowFailedTests (CommandInfo info)
+		{
+			info.Enabled = chart.Type == TestChartType.Results;
+			info.Checked = chart.ShowFailedTests;
+		}
+		
+		[CommandHandler (TestChartCommands.ShowIgnoredTests)]
+		protected void OnShowIgnoredTests ()
+		{
+			chart.ShowIgnoredTests = !chart.ShowIgnoredTests;
+		}
+		
+		[CommandUpdateHandler (TestChartCommands.ShowIgnoredTests)]
+		protected void OnUpdateShowIgnoredTests (CommandInfo info)
+		{
+			info.Enabled = chart.Type == TestChartType.Results;
+			info.Checked = chart.ShowIgnoredTests;
+		}
+	}
 	
-		bool Populate ()
+	class ButtonNotebook: Notebook
+	{
+		ArrayList loadedPages = new ArrayList ();
+		
+		public void Reset ()
 		{
-			Assembly test = nunitService.TestAssembly;
-			store.Clear ();
-			assemblyName = test.FullName;
-			TreeIter root = store.AppendValues (CircleImage.None, assemblyName);
-			iters = new Hashtable ();
-			iters[assemblyName] = root;
-
-			rootTestSuite = nunitService.GetTestSuite (assemblyName);
-
-			currentTest = 0;
-			totalTests = rootTestSuite.CountTestCases ();
-			AddTestSuite (root, rootTestSuite);
-			OnFinishedLoad (null, null);
-
-			view.Model = store;
-			return false;
+			loadedPages.Clear ();
+			OnPageLoadRequired ();
 		}
-
-		void RunTestAtPath (TreePath path)
+		
+		public int AddPage (string text, Widget widget)
 		{
-			Test test = GetTestFromPath (path.ToString (), null);
-			totalTests = test.CountTestCases ();
-			nunitService.RunTest (test);
+			return AppendPage (widget, new Label (text));
 		}
-
-		Test GetTestFromPath (string path, Test t)
+		
+		public void ShowPage (int n)
 		{
-            string [] parts = path.Split (':');
-            if (t == null) {
-                if (parts.Length > 1)
-                    return GetTestFromPath (String.Join (":", parts, 1, parts.Length - 1), rootTestSuite);
-
-                return rootTestSuite;
-            }
-
-            Test ret;
-            if (parts.Length == 1) {
-                ret = (Test) t.Tests [Int32.Parse (path)];
-                return ret;
-            }
-
-            ret = (Test) t.Tests [Int32.Parse (parts [0])];
-			return GetTestFromPath (String.Join (":", parts, 1, parts.Length - 1), ret);
+			GetNthPage (n).Show ();
 		}
-
-		void SetIconFromResult (TestResult result)
+		
+		public void HidePage (int n)
 		{
-			string fullname = result.Test.FullName;
-
-			if (iters.ContainsKey (fullname)) {
-				TreeIter iter = (TreeIter) iters [fullname];
-				if (!result.Executed)
-					store.SetValue (iter, 0, CircleImage.NotRun);
-				else if (result.IsFailure)
-					store.SetValue (iter, 0, CircleImage.Failure);
-				else if (result.IsSuccess)
-					store.SetValue (iter, 0, CircleImage.Success);
-				else
-					store.SetValue (iter, 0, CircleImage.None);
+			GetNthPage (n).Hide ();
+		}
+		
+		protected override void OnSwitchPage (NotebookPage page, uint n)
+		{
+			base.OnSwitchPage (page, n);
+			if (!loadedPages.Contains (Page))
+				OnPageLoadRequired ();
+		}
+		
+		void OnPageLoadRequired ()
+		{
+			loadedPages.Add (Page);
+			if (PageLoadRequired != null)
+				PageLoadRequired (this, EventArgs.Empty);
+		}
+		
+		public EventHandler PageLoadRequired;
+	}
+	
+	class HeaderLabel: Widget
+	{
+		string text;
+		Pango.Layout layout;
+		int padding;
+		
+		public HeaderLabel ()
+		{
+			WidgetFlags |= WidgetFlags.NoWindow;
+			layout = new Pango.Layout (this.PangoContext);
+		}
+		
+		public string Markup {
+			get { return text; }
+			set {
+				text = value;
+				layout.SetMarkup (text);
+				QueueDraw ();
 			}
 		}
+		
+		public int Padding {
+			get { return padding; }
+			set { padding = value; }
+		}
+		
+		protected override bool OnExposeEvent (Gdk.EventExpose args)
+		{
+			Gdk.GC gc = new Gdk.GC (GdkWindow);
+			gc.ClipRectangle = Allocation;
+			GdkWindow.DrawLayout (gc, padding, padding, layout);
+			return true;
+		}
 	}
 }
 

Added: trunk/MonoDevelop/Extras/NUnit/Gui/TestResultsPad.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Gui/TestResultsPad.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Gui/TestResultsPad.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,536 @@
+//
+// TestResultsPad.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+using System.Threading;
+using Gtk;
+using Gdk;
+
+using MonoDevelop.Core.Services;
+using MonoDevelop.Services;
+using MonoDevelop.Gui;
+using MonoDevelop.Gui.Pads;
+using MonoDevelop.Commands;
+
+namespace MonoDevelop.NUnit
+{
+	public class TestResultsPad: GuiSyncObject, IPadContent, ITestProgressMonitor
+	{
+		NUnitService testService = (NUnitService) ServiceManager.GetService (typeof(NUnitService));
+		
+		string title;
+		VBox panel;
+		HPaned book;
+		Gtk.Tooltips tips = new Gtk.Tooltips ();
+		
+		Label infoFailed = new Label ("<b>Failed</b>: 0");
+		Label infoIgnored = new Label ("<b>Ignored</b>: 0");
+		Label infoCurrent = new Label ();
+		HBox labels;
+		
+		Label resultLabel = new Label ();
+		
+		ProgressBar progressBar = new ProgressBar ();
+		TreeView failuresTreeView;
+		TreeStore failuresStore;
+		TextView outputView;
+		Widget outputViewScrolled;
+		VSeparator infoSep;
+		ToolButton buttonStop;
+		
+		ToggleToolButton buttonSuccess;
+		ToggleToolButton buttonFailures;
+		ToggleToolButton buttonIgnored;
+		ToggleToolButton buttonOutput;
+		
+		bool running;
+		int testsToRun;
+		int testsRun;
+		int testsFailed;
+		int testsIgnored;
+		
+		UnitTest rootTest;
+		string configuration;
+		ArrayList results = new ArrayList ();
+		
+		Exception error;
+		string errorMessage;
+		
+		bool cancel;
+		
+		public class ResultRecord {
+			public UnitTest Test;
+			public UnitTestResult Result;
+		}
+		
+		public TestResultsPad ()
+		{
+			title = "Test results";
+			
+			testService.TestSuiteChanged += new EventHandler (OnTestSuiteChanged);
+			
+			panel = new VBox ();
+			
+			Toolbar toolbar = new Toolbar ();
+			toolbar.IconSize = IconSize.SmallToolbar;
+			panel.PackStart (toolbar, false, false, 0);
+			
+			buttonSuccess = new ToggleToolButton ();
+			buttonSuccess.Label = "Successful Tests";
+			buttonSuccess.Active = false;
+			buttonSuccess.IconWidget = new Gtk.Image (CircleImage.Success);
+			buttonSuccess.IsImportant = true;
+			buttonSuccess.Toggled += new EventHandler (OnShowSuccessfulToggled);
+			buttonSuccess.SetTooltip (tips, "Show Successful Tests", "Show Successful Tests");
+			toolbar.Insert (buttonSuccess, -1);
+			
+			buttonFailures = new ToggleToolButton ();
+			buttonFailures.Label = "Failed Tests";
+			buttonFailures.Active = true;
+			buttonFailures.IconWidget = new Gtk.Image (CircleImage.Failure);
+			buttonFailures.IsImportant = true;
+			buttonFailures.Toggled += new EventHandler (OnShowFailuresToggled);
+			buttonFailures.SetTooltip (tips, "Show Failed Tests", "Show Failed Tests");
+			toolbar.Insert (buttonFailures, -1);
+			
+			buttonIgnored = new ToggleToolButton ();
+			buttonIgnored.Label = "Ignored Tests";
+			buttonIgnored.Active = true;
+			buttonIgnored.IconWidget = new Gtk.Image (CircleImage.NotRun);
+			buttonIgnored.Toggled += new EventHandler (OnShowIgnoredToggled);
+			buttonIgnored.IsImportant = true;
+			buttonIgnored.SetTooltip (tips, "Show Ignored Tests", "Show Ignored Tests");
+			toolbar.Insert (buttonIgnored, -1);
+			
+			buttonOutput = new ToggleToolButton ();
+			buttonOutput.Label = "Output";
+			buttonOutput.Active = false;
+			buttonOutput.IconWidget = Runtime.Gui.Resources.GetImage (MonoDevelop.Gui.Stock.OutputIcon, IconSize.SmallToolbar);
+			buttonOutput.Toggled += new EventHandler (OnShowOutputToggled);
+			buttonOutput.IsImportant = true;
+			buttonOutput.SetTooltip (tips, "Show Output", "Show Output");
+			toolbar.Insert (buttonOutput, -1);
+			
+			toolbar.Insert (new SeparatorToolItem (), -1);
+			
+			buttonStop = new ToolButton ("gtk-stop");
+			toolbar.Insert (buttonStop, -1);
+			
+			toolbar.ToolbarStyle = ToolbarStyle.BothHoriz;
+			toolbar.ShowArrow = false;
+			
+			// Results notebook
+			
+			book = new HPaned ();
+			panel.PackStart (book, true, true, 0);
+			
+			// Failures tree
+			failuresTreeView = new TreeView ();
+			failuresTreeView.HeadersVisible = false;
+			failuresStore = new TreeStore (typeof(Pixbuf), typeof (string), typeof(object));
+			CellRendererPixbuf pr = new CellRendererPixbuf ();
+			CellRendererText tr = new CellRendererText ();
+			TreeViewColumn col = new TreeViewColumn ();
+			col.PackStart (pr, false);
+			col.AddAttribute (pr, "pixbuf", 0);
+			col.PackStart (tr, false);
+			col.AddAttribute (tr, "markup", 1);
+			failuresTreeView.AppendColumn (col);
+			failuresTreeView.Model = failuresStore;
+		
+			Gtk.ScrolledWindow sw = new Gtk.ScrolledWindow ();
+			sw.Add(failuresTreeView);
+			Frame frame = new Frame ();
+			frame.Add (sw);
+			book.Pack1 (frame, true, true);
+			
+			outputView = new TextView();
+			outputView.Editable = false;
+			sw = new Gtk.ScrolledWindow ();
+			sw.Add(outputView);
+			frame = new Frame ();
+			frame.Add (sw);
+			book.Pack2 (frame, true, true);
+			outputViewScrolled = frame;
+			
+			// Run panel
+			
+			HBox runPanel = new HBox ();
+			runPanel.BorderWidth = 5;
+			
+			infoSep = new VSeparator ();
+			
+			resultLabel.UseMarkup = true;
+			runPanel.PackStart (resultLabel, false, false, 0);
+			runPanel.PackStart (progressBar, false, false, 0);
+			runPanel.PackStart (infoCurrent, true, true, 10);	
+			
+			labels = new HBox (false, 10);
+			
+			infoFailed.UseMarkup = true;
+			infoIgnored.UseMarkup = true;
+			
+			labels.PackStart (infoFailed, true, false, 0);
+			labels.PackStart (infoIgnored, true, false, 0);
+			
+			runPanel.PackEnd (labels, false, false, 0);
+			runPanel.PackEnd (infoSep, false, false, 10);
+			
+			panel.PackStart (runPanel, false, false, 0);
+			progressBar.HeightRequest = infoFailed.SizeRequest().Height;
+			
+			buttonStop.Clicked += new EventHandler (OnStopClicked);
+			failuresTreeView.ButtonReleaseEvent += new Gtk.ButtonReleaseEventHandler (OnPopupMenu);
+			
+			Control.ShowAll ();
+			
+			outputViewScrolled.Hide ();
+		}
+		
+		public void Dispose ()
+		{
+		}
+		
+		public void OnTestSuiteChanged (object sender, EventArgs e)
+		{
+			results.Clear ();
+			failuresStore.Clear ();
+			outputView.Buffer.Clear ();
+			progressBar.Fraction = 0;
+			progressBar.Text = "";
+			testsRun = 0;
+			testsFailed = 0;
+			testsIgnored = 0;
+			UpdateCounters ();
+		}
+		
+		public string Id {
+			get { return "MonoDevelop.NUnit.TestResultsPad"; }
+		}
+		
+		public string DefaultPlacement {
+			get { return "Bottom"; }
+		}
+		
+		public string Title {
+			get {
+				if (running) 
+					return "<span foreground=\"blue\">" + title + "</span>";
+				else
+					return title;
+			}
+		}
+		
+		public string Icon {
+			get { return "md-combine-icon"; }
+		}
+		
+		public Gtk.Widget Control {
+			get {
+				return panel;
+			}
+		}
+		
+		public void RedrawContent ()
+		{
+		}
+		
+		public event EventHandler TitleChanged;
+		public event EventHandler IconChanged;
+		
+		void UpdateCounters ()
+		{
+			infoFailed.Markup = "<b>Failed</b>: " + testsFailed;
+			infoIgnored.Markup = "<b>Ignored</b>: " + testsIgnored;
+		}
+		
+		public void InitializeTestRun (UnitTest test)
+		{
+			rootTest = test;
+			results.Clear ();
+			testsToRun = test.CountTestCases ();
+			progressBar.Fraction = 0;
+			progressBar.Text = "";
+			progressBar.Text = "0 / " + testsToRun;
+
+			testsRun = 0;
+			testsFailed = 0;
+			testsIgnored = 0;
+			UpdateCounters ();
+			
+			infoSep.Show ();
+			infoCurrent.Show ();
+			progressBar.Show ();
+			resultLabel.Hide ();
+			labels.Show ();
+			buttonStop.Sensitive = true;
+			
+			failuresStore.Clear ();
+			outputView.Buffer.Clear ();
+			cancel = false;
+			running = true;
+			
+			configuration = rootTest.ActiveConfiguration;
+			
+			AddStartMessage ();
+			OnTitleChanged ();
+		}
+		
+		public void AddStartMessage ()
+		{
+			Gdk.Pixbuf infoIcon = failuresTreeView.RenderIcon (Gtk.Stock.DialogInfo, Gtk.IconSize.SmallToolbar, "");
+			string msg = string.Format (GettextCatalog.GetString ("Running tests for <b>{0}</b> configuration <b>{1}</b>"), rootTest.Name, configuration);
+			failuresStore.AppendValues (infoIcon, msg, rootTest);
+		}
+
+		public void ReportRuntimeError (string message, Exception exception)
+		{
+			error = exception;
+			errorMessage = message;
+			AddErrorMessage ();
+		}
+		
+		public void AddErrorMessage ()
+		{
+			string msg = "Internal error";
+			if (errorMessage != null)
+				msg += ": " + errorMessage;
+
+			Gdk.Pixbuf stock = failuresTreeView.RenderIcon (Gtk.Stock.DialogError, Gtk.IconSize.SmallToolbar, "");
+			TreeIter testRow = failuresStore.AppendValues (stock, msg, null);
+			failuresStore.AppendValues (testRow, null, Escape (error.GetType().Name + ": " + error.Message), null);
+			TreeIter row = failuresStore.AppendValues (testRow, null, "Stack Trace", null);
+			failuresStore.AppendValues (row, null, Escape (error.StackTrace), null);
+		}
+		
+		public void FinishTestRun ()
+		{
+			infoCurrent.Text = "";
+			progressBar.Fraction = 1;
+			progressBar.Text = "";
+			
+			infoSep.Hide ();
+			infoCurrent.Hide ();
+			progressBar.Hide ();
+			resultLabel.Show ();
+			labels.Hide ();
+			buttonStop.Sensitive = false;
+			
+			resultLabel.Markup = "<b>Tests</b>: " + testsRun + "  <b>Failed</b>: " + testsFailed + "  <b>Ignored</b>: " + testsIgnored;
+			
+			running = false;
+			OnTitleChanged ();
+		}
+		
+		void OnStopClicked (object sender, EventArgs args)
+		{
+			Cancel ();
+			failuresStore.AppendValues (CircleImage.Failure, "Test execution cancelled.", null);
+		}
+		
+		void OnPopupMenu (object o, Gtk.ButtonReleaseEventArgs args)
+		{
+			if (args.Event.Button == 3) {
+				CommandEntrySet eset = Runtime.Gui.CommandService.CreateCommandEntrySet ("/SharpDevelop/Views/TestResultsPad/ContextMenu");
+				Gtk.Menu menu = Runtime.Gui.CommandService.CreateMenu (eset);
+				Runtime.Gui.Menus.ShowContextMenu (menu);
+			}
+		}
+		
+		[CommandHandler (TestCommands.SelectTestInTree)]
+		protected void OnSelectTestInTree ()
+		{
+			TestPad pad = (TestPad) Runtime.Gui.Workbench.GetPad (typeof (TestPad));
+			pad.SelectTest (GetSelectedTest ());
+		}
+		
+		[CommandUpdateHandler (TestCommands.SelectTestInTree)]
+		protected void OnUpdateSelectTestInTree (CommandInfo info)
+		{
+			UnitTest test = GetSelectedTest ();
+			info.Enabled = test != null;
+		}
+		
+		[CommandHandler (TestCommands.ShowTestCode)]
+		protected void OnShowTest ()
+		{
+			UnitTest test = GetSelectedTest ();
+			if (test == null)
+				return;
+			SourceCodeLocation loc = test.SourceCodeLocation;
+			if (loc != null)
+				Runtime.FileService.OpenFile (loc.FileName, loc.Line, loc.Column, true);
+		}
+		
+		[CommandUpdateHandler (TestCommands.ShowTestCode)]
+		protected void OnUpdateRunTest (CommandInfo info)
+		{
+			UnitTest test = GetSelectedTest ();
+			info.Enabled = test != null && test.SourceCodeLocation != null;
+		}
+		
+		UnitTest GetSelectedTest ()
+		{
+			Gtk.TreeModel foo;
+			Gtk.TreeIter iter;
+			if (!failuresTreeView.Selection.GetSelected (out foo, out iter))
+				return null;
+				
+			UnitTest t = (UnitTest) failuresStore.GetValue (iter, 2);
+			return t;
+		}
+		
+		void OnShowSuccessfulToggled (object sender, EventArgs args)
+		{
+			RefreshList ();
+		}
+
+		void OnShowFailuresToggled (object sender, EventArgs args)
+		{
+			RefreshList ();
+		}
+		
+		void OnShowIgnoredToggled (object sender, EventArgs args)
+		{
+			RefreshList ();
+		}
+		
+		void OnShowOutputToggled (object sender, EventArgs args)
+		{
+			outputViewScrolled.Visible = buttonOutput.Active;
+		}
+		
+		void RefreshList ()
+		{
+			failuresStore.Clear ();
+			AddStartMessage ();
+				
+			foreach (ResultRecord res in results) {
+				ShowTestResult (res.Test, res.Result);
+			}
+			
+			if (error != null)
+				AddErrorMessage ();
+		}
+		
+		void ShowTestResult (UnitTest test, UnitTestResult result)
+		{
+			if (result.IsSuccess) {
+				if (!buttonSuccess.Active)
+					return;
+				TreeIter testRow = failuresStore.AppendValues (CircleImage.Success, Escape (test.FullName), test);
+				failuresTreeView.ScrollToCell (failuresStore.GetPath (testRow), null, false, 0, 0);
+			}
+			if (result.IsFailure) {
+				if (!buttonFailures.Active)
+					return;
+				TreeIter testRow = failuresStore.AppendValues (CircleImage.Failure, Escape (test.FullName), test);
+				bool hasMessage = result.Message != null && result.Message.Length > 0;
+				if (hasMessage)
+					failuresStore.AppendValues (testRow, null, Escape (result.Message), test);
+				if (result.StackTrace != null && result.StackTrace.Length > 0) {
+					TreeIter row = testRow;
+					if (hasMessage)
+						row = failuresStore.AppendValues (testRow, null, "Stack Trace", test);
+					failuresStore.AppendValues (row, null, Escape (result.StackTrace), test);
+				}
+				failuresTreeView.ScrollToCell (failuresStore.GetPath (testRow), null, false, 0, 0);
+			}
+			if (result.IsIgnored) {
+				if (!buttonIgnored.Active)
+					return;
+				TreeIter testRow = failuresStore.AppendValues (CircleImage.NotRun, Escape (test.FullName), test);
+				failuresStore.AppendValues (testRow, null, Escape (result.Message), test);
+				failuresTreeView.ScrollToCell (failuresStore.GetPath (testRow), null, false, 0, 0);
+			}
+			if (result.ConsoleOutput != null)
+				outputView.Buffer.InsertAtCursor (result.ConsoleOutput);
+			if (result.ConsoleError != null)
+				outputView.Buffer.InsertAtCursor (result.ConsoleError);
+			outputView.ScrollMarkOnscreen (outputView.Buffer.InsertMark);
+		}
+		
+		string Escape (string s)
+		{
+			return s.Replace ("<","&lt;").Replace (">","&gt;");
+		}
+		
+		void ITestProgressMonitor.EndTest (UnitTest test, UnitTestResult result)
+		{
+			if (test is UnitTestGroup)
+				return;
+			
+			testsRun++;
+			ResultRecord rec = new ResultRecord ();
+			rec.Test = test;
+			rec.Result = result;
+			
+			if (result.IsFailure) {
+				testsFailed++;
+			}
+			if (result.IsIgnored) {
+				testsIgnored++;
+			}
+			results.Add (rec);
+			
+			ShowTestResult (test, result);
+			
+			UpdateCounters ();
+			progressBar.Fraction = ((double)testsRun / (double)testsToRun);
+			progressBar.Text = testsRun + " / " + testsToRun;
+		}
+		
+		void ITestProgressMonitor.BeginTest (UnitTest test)
+		{
+			infoCurrent.Text = "Running " + test.FullName;
+			infoCurrent.Xalign = 0;
+		}
+		
+		public void Cancel ()
+		{
+			cancel = true;
+			if (CancelRequested != null)
+				CancelRequested ();
+		}
+		
+		bool ITestProgressMonitor.IsCancelRequested {
+			get { return cancel; }
+		}
+		
+		public event TestHandler CancelRequested;
+
+		protected virtual void OnTitleChanged ()
+		{
+			if (TitleChanged != null) {
+				TitleChanged(this, null);
+			}
+		}
+	}
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Gui/UnitTestOptionsDialog.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Gui/UnitTestOptionsDialog.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Gui/UnitTestOptionsDialog.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,88 @@
+//
+// UnitTestOptionsDialog.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+using System;
+using System.Collections;
+
+using MonoDevelop.Core.AddIns;
+using MonoDevelop.Core.Services;
+using MonoDevelop.Core.Properties;
+using MonoDevelop.Core.AddIns.Codons;
+using MonoDevelop.Services;
+using MonoDevelop.Gui.Dialogs;
+
+namespace MonoDevelop.NUnit {
+
+	public class UnitTestOptionsDialog : TreeViewOptions
+	{
+		UnitTest test;
+		
+		IAddInTreeNode configurationNode;
+	
+		public UnitTestOptionsDialog (UnitTest test) : base(null, null)
+		{
+			IAddInTreeNode node = AddInTreeSingleton.AddInTree.GetTreeNode("/SharpDevelop/Workbench/UnitTestOptions/GeneralOptions");
+			configurationNode = AddInTreeSingleton.AddInTree.GetTreeNode("/SharpDevelop/Workbench/UnitTestOptions/ConfigurationProperties");
+				
+			this.test = test;
+			this.Title = GettextCatalog.GetString ("Unit Test Options");
+			
+			properties = new DefaultProperties();
+			properties.SetProperty ("UnitTest", test);
+			AddNodes (properties, Gtk.TreeIter.Zero, node.BuildChildItems (this));			
+			SelectFirstNode ();	
+		}
+		
+		void FillConfigurations (Gtk.TreeIter configIter)
+		{
+			foreach (string name in test.GetConfigurations ()) {
+				DefaultProperties configNodeProperties = new DefaultProperties();
+				configNodeProperties.SetProperty("UnitTest", test);
+				configNodeProperties.SetProperty("Config", name);
+				
+				ArrayList list = configurationNode.BuildChildItems (this);
+				if (list.Count > 1) {
+					Gtk.TreeIter newNode = AddPath (name, configIter);
+					AddNodes (configNodeProperties, newNode, list);
+				} else {
+					AddNode (name, configNodeProperties, configIter, (IDialogPanelDescriptor) list [0]);
+				}
+			}
+		}
+		
+		protected override void AddChildNodes (object customizer, Gtk.TreeIter iter, IDialogPanelDescriptor descriptor)
+		{
+			if (descriptor.ID != "Configurations") {
+				base.AddChildNodes (customizer, iter, descriptor);
+			} else {
+				FillConfigurations (iter);
+			}
+		}
+	}
+}

Modified: trunk/MonoDevelop/Extras/NUnit/Makefile
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Makefile	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Makefile	2005-07-13 16:55:25 UTC (rev 2641)
@@ -1,37 +1,522 @@
+# Makefile.in generated by automake 1.9.3 from Makefile.am.
+# Extras/NUnit/Makefile.  Generated from Makefile.in by configure.
 
-CSC = mcs
-ASSEMBLY_NAME = MonoDevelop.NUnit.dll
-ASSEMBLY = ../../build/AddIns/NUnit/$(ASSEMBLY_NAME)
-ADDIN = MonoDevelopNUnit.addin.xml
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004  Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
 
-DLLS = -r:nunit.framework.dll \
-	-r:nunit.core.dll \
-	-r:../../build/bin/MonoDevelop.Core.dll \
-	-r:../../build/bin/MonoDevelop.Base.dll \
-	-r:../../build/bin/MonoDevelop.Gui.Widgets.dll \
-	-pkg:gtk-sharp-2.0
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
 
+
+
+srcdir = .
+top_srcdir = ../..
+
+pkgdatadir = $(datadir)/monodevelop
+pkglibdir = $(libdir)/monodevelop
+pkgincludedir = $(includedir)/monodevelop
+top_builddir = ../..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = /usr/bin/install -c
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+	$(top_srcdir)/Makefile.include ChangeLog TODO
+subdir = Extras/NUnit
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_CLEAN_FILES =
+SOURCES =
+DIST_SOURCES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(assemblydir)" \
+	"$(DESTDIR)$(templatedir)"
+assemblyDATA_INSTALL = $(INSTALL_DATA)
+templateDATA_INSTALL = $(INSTALL_DATA)
+DATA = $(assembly_DATA) $(template_DATA)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = ${SHELL} /home/lluis/work/MonoDevelopSvn/missing --run aclocal-1.9
+AMDEP_FALSE = #
+AMDEP_TRUE = 
+AMTAR = ${SHELL} /home/lluis/work/MonoDevelopSvn/missing --run tar
+ASSEMBLY_VERSION = 0.7.0.0
+AUTOCONF = ${SHELL} /home/lluis/work/MonoDevelopSvn/missing --run autoconf
+AUTOHEADER = ${SHELL} /home/lluis/work/MonoDevelopSvn/missing --run autoheader
+AUTOMAKE = ${SHELL} /home/lluis/work/MonoDevelopSvn/missing --run automake-1.9
+AWK = gawk
+BOOC = /home/lluis/install/bin/booc
+BOO_CFLAGS =  
+BOO_LIBS = -r:/home/lluis/install/lib/mono/boo/Boo.Lang.dll -r:/home/lluis/install/lib/mono/boo/Boo.Lang.Useful.dll -r:/home/lluis/install/lib/mono/boo/Boo.Lang.CodeDom.dll -r:/home/lluis/install/lib/mono/boo/Boo.Lang.Compiler.dll -r:/home/lluis/install/lib/mono/boo/Boo.Lang.Parser.dll -r:/home/lluis/install/lib/mono/boo/Boo.Lang.Interpreter.dll  
+CATALOGS =  da.gmo de.gmo es.gmo fr.gmo ja_JP.gmo pt_BR.gmo tr.gmo pl.gmo
+CATOBJEXT = .gmo
+CC = gcc
+CCDEPMODE = depmode=gcc3
+CFLAGS = -g -O2
+CPP = gcc -E
+CPPFLAGS = 
+CSC = /home/lluis/install/bin/mcs
+CSC_FLAGS = -langversion:ISO-1
+CYGPATH_W = echo
+DATADIRNAME = share
+DEFS = -DPACKAGE_NAME=\"monodevelop\" -DPACKAGE_TARNAME=\"monodevelop\" -DPACKAGE_VERSION=\"0.7\" -DPACKAGE_STRING=\"monodevelop\ 0.7\" -DPACKAGE_BUGREPORT=\"monodevelop-list at lists.ximian.com\" -DPACKAGE=\"monodevelop\" -DVERSION=\"0.7\" -DGETTEXT_PACKAGE=\"monodevelop\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_LOCALE_H=1 -DHAVE_LC_MESSAGES=1 -DHAVE_BIND_TEXTDOMAIN_CODESET=1 -DHAVE_GETTEXT=1 -DHAVE_DCGETTEXT=1 -DENABLE_NLS=1 
+DEPDIR = .deps
+ECHO_C = 
+ECHO_N = -n
+ECHO_T = 
+EGREP = grep -E
+ENABLE_BOO_FALSE = #
+ENABLE_BOO_TRUE = 
+ENABLE_DEBUGGER_FALSE = 
+ENABLE_DEBUGGER_TRUE = #
+ENABLE_JAVA_FALSE = #
+ENABLE_JAVA_TRUE = 
+ENABLE_MONOEXTENSIONS_FALSE = #
+ENABLE_MONOEXTENSIONS_TRUE = 
+ENABLE_MONOQUERY_SQLITE_FALSE = #
+ENABLE_MONOQUERY_SQLITE_TRUE = 
+ENABLE_NUNIT_FALSE = #
+ENABLE_NUNIT_TRUE = 
+ENABLE_UPDATE_DESKTOPDB_FALSE = #
+ENABLE_UPDATE_DESKTOPDB_TRUE = 
+ENABLE_UPDATE_MIMEDB_FALSE = #
+ENABLE_UPDATE_MIMEDB_TRUE = 
+EXEEXT = 
+GCONF_SHARP_CFLAGS = -I:/home/lluis/install/share/gapi-2.0/gnome-api.xml -I:/home/lluis/install/share/gapi-2.0/pango-api.xml -I:/home/lluis/install/share/gapi-2.0/atk-api.xml -I:/home/lluis/install/share/gapi-2.0/gdk-api.xml -I:/home/lluis/install/share/gapi-2.0/gtk-api.xml -I:/home/lluis/install/share/gapi-2.0/art-api.xml  
+GCONF_SHARP_LIBS = -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/gconf-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/gconf-sharp-peditors.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/gnome-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/glib-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/pango-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/atk-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/gdk-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/gtk-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/art-sharp.dll  
+GECKO_SHARP_CFLAGS = -I:/home/lluis/install/share/gapi-2.0/pango-api.xml -I:/home/lluis/install/share/gapi-2.0/atk-api.xml -I:/home/lluis/install/share/gapi-2.0/gdk-api.xml -I:/home/lluis/install/share/gapi-2.0/gtk-api.xml  
+GECKO_SHARP_LIBS = -r:/home/lluis/install/lib/mono/gecko-sharp-2.0/gecko-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/glib-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/pango-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/atk-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/gdk-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/gtk-sharp.dll  
+GETTEXT_PACKAGE = monodevelop
+GLADE_SHARP_CFLAGS = -I:/home/lluis/install/share/gapi-2.0/glade-api.xml -I:/home/lluis/install/share/gapi-2.0/pango-api.xml -I:/home/lluis/install/share/gapi-2.0/atk-api.xml -I:/home/lluis/install/share/gapi-2.0/gdk-api.xml -I:/home/lluis/install/share/gapi-2.0/gtk-api.xml  
+GLADE_SHARP_LIBS = -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/glade-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/glib-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/pango-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/atk-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/gdk-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/gtk-sharp.dll  
+GMOFILES =  da.gmo de.gmo es.gmo fr.gmo ja_JP.gmo pt_BR.gmo tr.gmo pl.gmo
+GMSGFMT = /usr/bin/msgfmt
+GNOME_SHARP_CFLAGS = -I:/home/lluis/install/share/gapi-2.0/gnome-api.xml -I:/home/lluis/install/share/gapi-2.0/pango-api.xml -I:/home/lluis/install/share/gapi-2.0/atk-api.xml -I:/home/lluis/install/share/gapi-2.0/gdk-api.xml -I:/home/lluis/install/share/gapi-2.0/gtk-api.xml -I:/home/lluis/install/share/gapi-2.0/art-api.xml  
+GNOME_SHARP_LIBS = -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/gnome-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/glib-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/pango-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/atk-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/gdk-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/gtk-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/art-sharp.dll  
+GNOME_VFS_SHARP_CFLAGS = -I:/home/lluis/install/share/gapi-2.0/gnome-vfs-api.xml  
+GNOME_VFS_SHARP_LIBS = -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/gnome-vfs-sharp.dll  
+GTKHTML_SHARP_CFLAGS = -I:/home/lluis/install/share/gapi-2.0/gtkhtml-api.xml -I:/home/lluis/install/share/gapi-2.0/gnome-api.xml -I:/home/lluis/install/share/gapi-2.0/pango-api.xml -I:/home/lluis/install/share/gapi-2.0/atk-api.xml -I:/home/lluis/install/share/gapi-2.0/gdk-api.xml -I:/home/lluis/install/share/gapi-2.0/gtk-api.xml -I:/home/lluis/install/share/gapi-2.0/art-api.xml  
+GTKHTML_SHARP_LIBS = -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/gtkhtml-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/gnome-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/glib-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/pango-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/atk-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/gdk-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/gtk-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/art-sharp.dll  
+GTKSOURCEVIEW_SHARP_CFLAGS = -I:/home/lluis/install/share/gapi-2.0/gnome-api.xml -I:/home/lluis/install/share/gapi-2.0/pango-api.xml -I:/home/lluis/install/share/gapi-2.0/atk-api.xml -I:/home/lluis/install/share/gapi-2.0/gdk-api.xml -I:/home/lluis/install/share/gapi-2.0/gtk-api.xml -I:/home/lluis/install/share/gapi-2.0/art-api.xml  
+GTKSOURCEVIEW_SHARP_LIBS = -r:/home/lluis/install/lib/mono/gtksourceview-sharp-2.0/gtksourceview-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/gnome-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/glib-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/pango-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/atk-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/gdk-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/gtk-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/art-sharp.dll  
+GTK_SHARP_CFLAGS = -I:/home/lluis/install/share/gapi-2.0/pango-api.xml -I:/home/lluis/install/share/gapi-2.0/atk-api.xml -I:/home/lluis/install/share/gapi-2.0/gdk-api.xml -I:/home/lluis/install/share/gapi-2.0/gtk-api.xml  
+GTK_SHARP_LIBS = -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/glib-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/pango-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/atk-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/gdk-sharp.dll -r:/home/lluis/install/lib/mono/gtk-sharp-2.0/gtk-sharp.dll  
+IKVM_CFLAGS =  
+IKVM_LIBS = -r:/home/lluis/work/ikvm/bin/IKVM.GNU.Classpath.dll -r:/home/lluis/work/ikvm/bin/IKVM.Runtime.dll -r:/home/lluis/work/ikvm/bin/ikvm-native.dll -r:/home/lluis/work/ikvm/bin/IKVM.AWT.WinForms.dll  
+INSTALL_DATA = ${INSTALL} -m 644
+INSTALL_PROGRAM = ${INSTALL}
+INSTALL_SCRIPT = ${INSTALL}
+INSTALL_STRIP_PROGRAM = ${SHELL} $(install_sh) -c -s
+INSTOBJEXT = .mo
+INTLLIBS = 
+INTLTOOL_CAVES_RULE = %.caves:     %.caves.in     $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+INTLTOOL_DESKTOP_RULE = %.desktop:   %.desktop.in   $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+INTLTOOL_DIRECTORY_RULE = %.directory: %.directory.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+INTLTOOL_EXTRACT = $(top_builddir)/intltool-extract
+INTLTOOL_KBD_RULE = %.kbd:       %.kbd.in       $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -x -u -m -c $(top_builddir)/po/.intltool-merge-cache
+INTLTOOL_KEYS_RULE = %.keys:      %.keys.in      $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -k -u -c $(top_builddir)/po/.intltool-merge-cache
+INTLTOOL_MERGE = $(top_builddir)/intltool-merge
+INTLTOOL_OAF_RULE = %.oaf:       %.oaf.in       $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -o -p
+INTLTOOL_PERL = /usr/bin/perl
+INTLTOOL_PONG_RULE = %.pong:      %.pong.in      $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -x -u -c $(top_builddir)/po/.intltool-merge-cache
+INTLTOOL_PROP_RULE = %.prop:      %.prop.in      $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+INTLTOOL_SCHEMAS_RULE = %.schemas:   %.schemas.in   $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -s -u -c $(top_builddir)/po/.intltool-merge-cache
+INTLTOOL_SERVER_RULE = %.server:    %.server.in    $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -o -u -c $(top_builddir)/po/.intltool-merge-cache
+INTLTOOL_SHEET_RULE = %.sheet:     %.sheet.in     $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -x -u -c $(top_builddir)/po/.intltool-merge-cache
+INTLTOOL_SOUNDLIST_RULE = %.soundlist: %.soundlist.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+INTLTOOL_THEME_RULE = %.theme:     %.theme.in     $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+INTLTOOL_UI_RULE = %.ui:        %.ui.in        $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -x -u -c $(top_builddir)/po/.intltool-merge-cache
+INTLTOOL_UPDATE = $(top_builddir)/intltool-update
+INTLTOOL_XAM_RULE = %.xam:       %.xml.in       $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -x -u -c $(top_builddir)/po/.intltool-merge-cache
+INTLTOOL_XML_RULE = %.xml:       %.xml.in       $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -x -u -c $(top_builddir)/po/.intltool-merge-cache
+LDFLAGS = 
+LD_LIBRARY_PATH = /opt/mozilla/lib:/home/lluis/install/lib:/usr/local/lib
+LIBOBJS = 
+LIBS = 
+LIB_PREFIX = .so
+LIB_SUFFIX = 
+LTLIBOBJS = 
+MAINT = 
+MAINTAINER_MODE_FALSE = #
+MAINTAINER_MODE_TRUE = 
+MAKEINFO = ${SHELL} /home/lluis/work/MonoDevelopSvn/missing --run makeinfo
+MCS = /home/lluis/install/bin/mcs
+MD_ADDIN_DIR = $(prefix)/lib/monodevelop/AddIns
+MD_ASSEMBLY_DIR = $(prefix)/lib/monodevelop/bin
+MD_DIR = $(prefix)/lib/monodevelop
+MINT = 
+MONO = /home/lluis/install/bin/mono
+MONODOC_CFLAGS =  
+MONODOC_LIBS = /r:/home/lluis/install/lib/mono/gtk-sharp/monodoc.dll  
+MONOQUERY_DEPENDENCIES_SQLITE_CFLAGS = -D_REENTRANT -pthread -I/home/lluis/install/include -I/opt/gnome/include/glib-2.0 -I/opt/gnome/lib/glib-2.0/include  
+MONOQUERY_DEPENDENCIES_SQLITE_LIBS = -Wl,--export-dynamic -pthread -L/home/lluis/install/lib -L/opt/gnome/lib -lmono -lpthread -lm -lgmodule-2.0 -ldl -lgthread-2.0 -lglib-2.0  
+MONO_DEBUGGER_CFLAGS = 
+MONO_DEBUGGER_LIBS = 
+MONO_NUNIT_CFLAGS =  
+MONO_NUNIT_LIBS = -r:/home/lluis/install/lib/mono/1.0/nunit.core.dll -r:/home/lluis/install/lib/mono/1.0/nunit.framework.dll -r:/home/lluis/install/lib/mono/1.0/nunit.util.dll  
+MOZILLA_HOME = 
+MSGFMT = /usr/bin/msgfmt
+OBJEXT = o
+PACKAGE = monodevelop
+PACKAGE_BUGREPORT = monodevelop-list at lists.ximian.com
+PACKAGE_NAME = monodevelop
+PACKAGE_STRING = monodevelop 0.7
+PACKAGE_TARNAME = monodevelop
+PACKAGE_VERSION = 0.7
+PATH = /usr/lib/java/j2re1.4.2/bin:/home/lluis/install/lib:/home/lluis/install/bin:/usr/lib/java/j2re1.4.2/bin:/home/lluis/install/lib:/home/lluis/install/bin:/home/lluis/bin:/usr/local/bin:/usr/bin:/usr/X11R6/bin:/bin:/usr/games:/opt/gnome/bin:/opt/kde3/bin:/usr/lib/java/bin:/home/lluis/bin:/usr/local/bin:/home/lluis/bin:/usr/local/bin
+PATH_SEPARATOR = :
+PKG_CONFIG = /usr/bin/pkg-config
+POFILES =  da.po de.po es.po fr.po ja_JP.po pt_BR.po tr.po pl.po
+POSUB = po
+PO_IN_DATADIR_FALSE = 
+PO_IN_DATADIR_TRUE = 
+RUNTIME = /home/lluis/install/bin/mono
+SET_MAKE = 
+SHELL = /bin/sh
+SQLITE_XML = <MonoQueryConnection id="Sqlite" schema="MonoQuery.Connection.SqliteConnectionWrapper" node="MonoQuery.Gui.TreeView.MonoQueryNodeConnection" description="SQLite Database Server" showUnsuported="False"/>
+STRIP = 
+UNMANAGED_DEPENDENCIES_MINT_CFLAGS = -D_REENTRANT -pthread -I/home/lluis/install/include -I/opt/gnome/include/glib-2.0 -I/opt/gnome/lib/glib-2.0/include  
+UNMANAGED_DEPENDENCIES_MINT_LIBS = @ICU_LIBS@ -Wl,--export-dynamic -pthread -L/home/lluis/install/lib -L/opt/gnome/lib -lmint -lpthread -lm -lgmodule-2.0 -ldl -lgthread-2.0 -lglib-2.0  
+UNMANAGED_DEPENDENCIES_MONO_CFLAGS = -D_REENTRANT -pthread -I/home/lluis/install/include -I/opt/gnome/include/glib-2.0 -I/opt/gnome/lib/glib-2.0/include  
+UNMANAGED_DEPENDENCIES_MONO_LIBS = -Wl,--export-dynamic -pthread -L/home/lluis/install/lib -L/opt/gnome/lib -lmono -lpthread -lm -lgmodule-2.0 -ldl -lgthread-2.0 -lglib-2.0  
+UPDATE_DESKTOP_DB = no
+UPDATE_MIME_DB = /usr/bin/update-mime-database
+USE_NLS = yes
+VERSION = 0.7
+XGETTEXT = /usr/bin/xgettext
+ac_ct_CC = gcc
+ac_ct_STRIP = 
+am__fastdepCC_FALSE = #
+am__fastdepCC_TRUE = 
+am__include = include
+am__leading_dot = .
+am__quote = 
+am__tar = find "$$tardir" -print | cpio -o -H ustar -L
+am__untar = cpio -i -H ustar -d
+bindir = ${exec_prefix}/bin
+build_alias = 
+datadir = ${prefix}/share
+exec_prefix = ${prefix}
+gtksharp_prefix = /home/lluis/install
+host_alias = 
+includedir = ${prefix}/include
+infodir = ${prefix}/info
+install_sh = /home/lluis/work/MonoDevelopSvn/install-sh
+libdir = ${exec_prefix}/lib
+libexecdir = ${exec_prefix}/libexec
+localstatedir = ${prefix}/var
+mandir = ${prefix}/man
+mkdir_p = mkdir -p --
+oldincludedir = /usr/include
+prefix = /home/lluis/install
+program_transform_name = s,x,x,
+sbindir = ${exec_prefix}/sbin
+sharedstatedir = ${prefix}/com
+sysconfdir = ${prefix}/etc
+target_alias = 
+ADDIN_BUILD = $(top_builddir)/build/AddIns/NUnit
+ASSEMBLY = $(ADDIN_BUILD)/MonoDevelop.NUnit.dll
+DLLS = /r:nunit.framework.dll \
+	/r:nunit.core.dll \
+	/r:$(top_builddir)/build/bin/MonoDevelop.Core.dll \
+	/r:$(top_builddir)/build/bin/MonoDevelop.Base.dll \
+	/r:$(top_builddir)/build/bin/MonoDevelop.Gui.Widgets.dll \
+	$(GTK_SHARP_LIBS) \
+	$(GLADE_SHARP_LIBS)
+
 FILES = \
 AssemblyInfo.cs \
-FixtureAddedEventHandler.cs \
-FixtureLoadedErrorEventHandler.cs \
 Commands/NUnitCommands.cs \
 Gui/CircleImage.cs \
 Gui/TestPad.cs \
-Gui/ResultsView.cs \
-Services/NUnitService.cs
+Gui/TestResultsPad.cs \
+Gui/TestNodeBuilder.cs \
+Gui/TestChart.cs \
+Gui/NUnitOptionsPanel.cs \
+Gui/UnitTestOptionsDialog.cs \
+Gui/NUnitAssemblyGroupNodeBuilder.cs \
+Gui/NUnitAssemblyGroupConfigurationNodeBuilder.cs \
+Gui/TestAssemblyNodeBuilder.cs \
+Project/NUnitAssemblyGroupFileFormat.cs \
+Project/NUnitAssemblyGroupProject.cs \
+Project/TestAssembly.cs \
+Project/TestAssemblyCollection.cs \
+Services/CombineTestGroup.cs \
+Services/ITestProvider.cs \
+Services/NUnitService.cs \
+Services/SystemTestProvider.cs \
+Services/ITestProgressMonitor.cs \
+Services/TestContext.cs \
+Services/UnitTestCollection.cs \
+Services/UnitTest.cs \
+Services/UnitTestGroup.cs \
+Services/UnitTestResult.cs \
+Services/UnitTestStatus.cs \
+Services/ExternalTestRunner.cs \
+Services/NUnitAssemblyTestSuite.cs \
+Services/NUnitProjectTestSuite.cs \
+Services/NUnitTestCase.cs \
+Services/NUnitTestSuite.cs \
+Services/GeneralTestOptions.cs \
+Services/NUnitOptions.cs \
+Services/IResultsStore.cs \
+Services/UnitTestResultsStore.cs \
+Services/XmlResultsStore.cs
 
-all: $(ASSEMBLY)
+TEMPLATES = \
+templates/NUnitAssemblyGroup.xpt.xml
 
-../../build/AddIns/NUnit/$(ADDIN): $(ADDIN)
-	mkdir -p ../../build/AddIns/NUnit
-	cp $(ADDIN) ../../build/AddIns/NUnit/$(ADDIN)
+RES = -resource:$(srcdir)/Gui/NUnit.Running.png \
+	-resource:$(srcdir)/Gui/NUnit.Failed.png \
+	-resource:$(srcdir)/Gui/NUnit.SuccessAndFailed.png \
+	-resource:$(srcdir)/Gui/NUnit.None.png \
+	-resource:$(srcdir)/Gui/NUnit.NotRun.png \
+	-resource:$(srcdir)/Gui/NUnit.Loading.png \
+	-resource:$(srcdir)/Gui/NUnit.Success.png \
+	-resource:$(srcdir)/nunit.glade
 
-$(ASSEMBLY): $(FILES) ../../build/AddIns/NUnit/$(ADDIN)
-	mkdir -p ../../build/AddIns/NUnit
-	$(CSC) $(DLLS) $(FILES) -out:$@ -target:library
+ADDIN = MonoDevelopNUnit.addin.xml
+TEMPLATES_DIR = $(ADDIN_BUILD)/templates
+build_TEMPLATES = $(addprefix $(TEMPLATES_DIR)/, $(notdir $(TEMPLATES)))
+src_TEMPLATES = $(addprefix $(srcdir)/, $(TEMPLATES))
+assemblydir = $(MD_ADDIN_DIR)/NUnit
+assembly_DATA = $(ASSEMBLY) $(ADDIN)
+templatedir = $(assemblydir)/templates
+template_DATA = $(TEMPLATES)
+CLEANFILES = $(ASSEMBLY) $(ASSEMBLY).mdb
+EXTRA_DIST = $(FILES) nunit.glade $(ADDIN) $(TEMPLATES)
+build_sources = $(addprefix $(srcdir)/, $(FILES)) $(GENERATED_FILES)
+all: all-am
 
-clean:
-	rm -f $(ASSEMBLY) $(ASSEMBLY).mdb
-	rm -f ../../build/AddIns/NUnit/$(ADDIN)
+.SUFFIXES:
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am $(top_srcdir)/Makefile.include $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+		&& exit 0; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu  Extras/NUnit/Makefile'; \
+	cd $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu  Extras/NUnit/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
 
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure:  $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+uninstall-info-am:
+install-assemblyDATA: $(assembly_DATA)
+	@$(NORMAL_INSTALL)
+	test -z "$(assemblydir)" || $(mkdir_p) "$(DESTDIR)$(assemblydir)"
+	@list='$(assembly_DATA)'; for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  f=$(am__strip_dir) \
+	  echo " $(assemblyDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(assemblydir)/$$f'"; \
+	  $(assemblyDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(assemblydir)/$$f"; \
+	done
+
+uninstall-assemblyDATA:
+	@$(NORMAL_UNINSTALL)
+	@list='$(assembly_DATA)'; for p in $$list; do \
+	  f=$(am__strip_dir) \
+	  echo " rm -f '$(DESTDIR)$(assemblydir)/$$f'"; \
+	  rm -f "$(DESTDIR)$(assemblydir)/$$f"; \
+	done
+install-templateDATA: $(template_DATA)
+	@$(NORMAL_INSTALL)
+	test -z "$(templatedir)" || $(mkdir_p) "$(DESTDIR)$(templatedir)"
+	@list='$(template_DATA)'; for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  f=$(am__strip_dir) \
+	  echo " $(templateDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(templatedir)/$$f'"; \
+	  $(templateDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(templatedir)/$$f"; \
+	done
+
+uninstall-templateDATA:
+	@$(NORMAL_UNINSTALL)
+	@list='$(template_DATA)'; for p in $$list; do \
+	  f=$(am__strip_dir) \
+	  echo " rm -f '$(DESTDIR)$(templatedir)/$$f'"; \
+	  rm -f "$(DESTDIR)$(templatedir)/$$f"; \
+	done
+tags: TAGS
+TAGS:
+
+ctags: CTAGS
+CTAGS:
+
+
+distdir: $(DISTFILES)
+	$(mkdir_p) $(distdir)/../.. $(distdir)/Commands $(distdir)/Gui $(distdir)/Project $(distdir)/Services $(distdir)/templates
+	@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+	list='$(DISTFILES)'; for file in $$list; do \
+	  case $$file in \
+	    $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+	    $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+	  esac; \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+	  if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+	    dir="/$$dir"; \
+	    $(mkdir_p) "$(distdir)$$dir"; \
+	  else \
+	    dir=''; \
+	  fi; \
+	  if test -d $$d/$$file; then \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+	    fi; \
+	    cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+	  else \
+	    test -f $(distdir)/$$file \
+	    || cp -p $$d/$$file $(distdir)/$$file \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(DATA)
+installdirs:
+	for dir in "$(DESTDIR)$(assemblydir)" "$(DESTDIR)$(templatedir)"; do \
+	  test -z "$$dir" || $(mkdir_p) "$$dir"; \
+	done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic mostlyclean-am
+
+distclean: distclean-am
+	-rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am: install-assemblyDATA install-templateDATA
+
+install-exec-am:
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-assemblyDATA uninstall-info-am \
+	uninstall-templateDATA
+
+.PHONY: all all-am check check-am clean clean-generic distclean \
+	distclean-generic distdir dvi dvi-am html html-am info info-am \
+	install install-am install-assemblyDATA install-data \
+	install-data-am install-exec install-exec-am install-info \
+	install-info-am install-man install-strip install-templateDATA \
+	installcheck installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-generic pdf \
+	pdf-am ps ps-am uninstall uninstall-am uninstall-assemblyDATA \
+	uninstall-info-am uninstall-templateDATA
+
+
+all: $(ASSEMBLY) $(ADDIN_BUILD)/$(ADDIN) $(build_TEMPLATES)
+#all:
+
+$(filter %.xft.xml, $(build_TEMPLATES)): $(TEMPLATES_DIR)/%.xft.xml: $(srcdir)/templates/%.xft.xml
+	mkdir -p $(TEMPLATES_DIR)
+	cp $(srcdir)/templates/$(notdir $@) $@
+
+$(filter %.xpt.xml, $(build_TEMPLATES)): $(TEMPLATES_DIR)/%.xpt.xml: $(srcdir)/templates/%.xpt.xml
+	mkdir -p $(TEMPLATES_DIR)
+	cp $(srcdir)/templates/$(notdir $@) $@
+
+$(ADDIN_BUILD)/$(ADDIN): $(srcdir)/$(ADDIN)
+	mkdir -p $(ADDIN_BUILD)
+	cp $(srcdir)/$(ADDIN) $(ADDIN_BUILD)/.
+
+$(ASSEMBLY): $(FILES) $(srcdir)/nunit.glade
+	mkdir -p $(ADDIN_BUILD)
+	$(CSC) $(CSC_FLAGS) $(DLLS) $(RES) $(build_sources) -out:$@ -target:library
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:

Modified: trunk/MonoDevelop/Extras/NUnit/MonoDevelopNUnit.addin.xml
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/MonoDevelopNUnit.addin.xml	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/MonoDevelopNUnit.addin.xml	2005-07-13 16:55:25 UTC (rev 2641)
@@ -10,29 +10,102 @@
 	</Runtime>
 
 	<Extension path="/Workspace/Services">
-		<Class id = "NUnitService"
-		    class = "MonoDevelop.Services.NUnitService" />
+		<Class id = "NUnitService" class = "MonoDevelop.NUnit.NUnitService" insertafter = "CommandService"/>
 	</Extension>
 
 	<Extension path = "/SharpDevelop/Workbench/Pads">
-		<Pad id = "MonoDevelop.NUnit.TestPad" class = "MonoDevelop.NUnit.TestPad" />
+		<SolutionPad id = "MonoDevelop.NUnit.TestPad" defaultPlacement = "Left" _label = "Unit Tests" icon = "md-combine-icon" class = "MonoDevelop.NUnit.TestPad">
+			<PadOption id = "ShowTestTime" _label = "Show Test Time" defaultValue = "False" />
+			<PadOption id = "ShowTestCounters" _label = "Show Test Counters" defaultValue = "False" />
+			<NodeBuilder id = "TestNode" class = "MonoDevelop.NUnit.TestNodeBuilder"/>
+		</SolutionPad>
 	</Extension>
 
+	<Extension path = "/SharpDevelop/Workbench/Pads/MonoDevelop.Gui.Pads.ProjectPad">
+		<NodeBuilder id = "NUnitAssemblyGroupNodeBuilder" class = "MonoDevelop.NUnit.NUnitAssemblyGroupNodeBuilder"/>
+		<NodeBuilder id = "NUnitAssemblyGroupConfigurationNodeBuilder" class = "MonoDevelop.NUnit.NUnitAssemblyGroupConfigurationNodeBuilder"/>
+		<NodeBuilder id = "TestAssemblyNodeBuilder" class = "MonoDevelop.NUnit.TestAssemblyNodeBuilder"/>
+	</Extension>
+	
 	<Extension path = "/SharpDevelop/Workbench/Contexts/Edit">
 		<ContextPad id = "MonoDevelop.NUnit.TestPad" />
 	</Extension>
+	
+	<Extension path = "/SharpDevelop/Commands">
+		<Command id = "MonoDevelop.Commands.TestCommands.RunTest" _label = "Run Test" />
+		<Command id = "MonoDevelop.Commands.TestCommands.ShowTestCode" _label = "Show test source code" />
+		<Command id = "MonoDevelop.Commands.TestCommands.SelectTestInTree" _label = "Select test in tree" />
+		<Command id = "MonoDevelop.Commands.TestCommands.ShowTestDetails" _label = "Show results pad" />
+		<Command id = "MonoDevelop.Commands.TestChartCommands.UseTimeScale" _label = "Proportional time scale" type="check"/>
+		<Command id = "MonoDevelop.Commands.TestChartCommands.SingleDayResult" _label = "Show one result per day" type="check"/>
+		<Command id = "MonoDevelop.Commands.TestChartCommands.ShowResults" _label = "Results chart" type="radio"/>
+		<Command id = "MonoDevelop.Commands.TestChartCommands.ShowTime" _label = "Time chart" type="radio"/>
+		<Command id = "MonoDevelop.Commands.TestChartCommands.ShowSuccessfulTests" _label = "Show successful tests" type="check"/>
+		<Command id = "MonoDevelop.Commands.TestChartCommands.ShowFailedTests" _label = "Show failed tests" type="check"/>
+		<Command id = "MonoDevelop.Commands.TestChartCommands.ShowIgnoredTests" _label = "Show ignored tests" type="check"/>
+		<Command id = "MonoDevelop.Commands.NUnitProjectCommands.AddAssembly" _label = "Add assembly ..."/>
+	</Extension>
 
-	<Extension path = "/SharpDevelop/Workbench/MainMenu/Tools">
-		<MenuItem id = "NUnitMenu" _label = "NUnit">
-			<MenuItem id = "LoadTestAssembly"
-	                  _label = "Load test assembly"
-			          shortcut = ""
-		              class = "MonoDevelop.Commands.NUnitLoadAssembly" />
-			<MenuItem id = "NUnitRunTests"
-	                  _label = "Run tests"
-			          shortcut = ""
-		              class = "MonoDevelop.Commands.NUnitRunTests" />
-		</MenuItem>
+	<Extension path = "/SharpDevelop/Views/ProjectBrowser/ContextMenu/NUnitAssemblyGroup">
 	</Extension>
 
+	<Extension path = "/SharpDevelop/Views/ProjectBrowser/ContextMenu/NUnitAssemblyGroupConfiguration">
+		<CommandItem id = "MonoDevelop.Commands.NUnitProjectCommands.AddAssembly" />
+	</Extension>
+
+	<Extension path = "/SharpDevelop/Views/ProjectBrowser/ContextMenu/TestAssembly">
+		<CommandItem id = "MonoDevelop.Commands.EditCommands.Delete" />
+	</Extension>
+
+	<Extension path = "/SharpDevelop/Views/TestPad/ContextMenu">
+		<CommandItem id = "MonoDevelop.Commands.TestCommands.RunTest" />
+		<CommandItem id = "MonoDevelop.Commands.TestCommands.ShowTestCode" />
+		<CommandItem id = "MonoDevelop.Commands.TestCommands.ShowTestDetails" />
+		<SeparatorItem id = "s1" />
+		<CommandItem id = "MonoDevelop.Commands.ProjectCommands.Options" />
+	</Extension>
+
+	<Extension path = "/SharpDevelop/Views/TestResultsPad/ContextMenu">
+		<CommandItem id = "MonoDevelop.Commands.TestCommands.ShowTestCode" />
+		<CommandItem id = "MonoDevelop.Commands.TestCommands.SelectTestInTree" />
+	</Extension>
+
+	<Extension path = "/SharpDevelop/Views/TestChart/ContextMenu">
+		<CommandItem id = "MonoDevelop.Commands.TestChartCommands.ShowResults" />
+		<CommandItem id = "MonoDevelop.Commands.TestChartCommands.ShowTime" />
+		<SeparatorItem id = "s1" />
+		<CommandItem id = "MonoDevelop.Commands.TestChartCommands.ShowSuccessfulTests" />
+		<CommandItem id = "MonoDevelop.Commands.TestChartCommands.ShowFailedTests" />
+		<CommandItem id = "MonoDevelop.Commands.TestChartCommands.ShowIgnoredTests" />
+		<SeparatorItem id = "s2" />
+		<CommandItem id = "MonoDevelop.Commands.TestChartCommands.UseTimeScale" />
+		<CommandItem id = "MonoDevelop.Commands.TestChartCommands.SingleDayResult" />
+	</Extension>
+
+	<Extension path = "/SharpDevelop/Workbench/UnitTestOptions">
+		<DialogPanel id = "NUnitOptionsPanel"
+					 _label = "NUnit options"
+					 class = "MonoDevelop.NUnit.NUnitOptionsDialog"/>
+	</Extension>
+
+	<Extension path = "/SharpDevelop/Workbench/UnitTestOptions/GeneralOptions">
+		<DialogPanel id = "Configurations"
+					 _label = "Configurations" />
+	</Extension>
+	
+	<Extension path = "/SharpDevelop/Workbench/UnitTestOptions/ConfigurationProperties">
+		<DialogPanel id = "NUnitOptionsPanel"
+					 _label = "NUnit Categories"
+					 class = "MonoDevelop.NUnit.NUnitOptionsPanel"/>
+	</Extension>
+
+	<Extension path = "/MonoDevelop/ProjectTemplates">
+		<ProjectTemplate id = "NUnitAssemblyGroup" location = "templates/NUnitAssemblyGroup.xpt.xml"/>
+	</Extension>
+  
+	<Extension path = "/SharpDevelop/Workbench/ProjectFileFormats">
+		<FileFormat id="NUnitAssemblyGroupFileFormat"
+		            class="MonoDevelop.NUnit.NUnitAssemblyGroupFileFormat, MonoDevelop.NUnit"/>
+	</Extension>
+
 </AddIn>

Added: trunk/MonoDevelop/Extras/NUnit/Project/NUnitAssemblyGroupFileFormat.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Project/NUnitAssemblyGroupFileFormat.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Project/NUnitAssemblyGroupFileFormat.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,106 @@
+//
+// NUnitAssemblyGroupFileFormat.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+using System;
+using System.IO;
+using System.Xml;
+using MonoDevelop.Internal.Project;
+using MonoDevelop.Internal.Serialization;
+using MonoDevelop.Services;
+
+namespace MonoDevelop.NUnit
+{
+	public class NUnitAssemblyGroupFileFormat: IFileFormat
+	{
+		public string Name {
+			get { return "NUnit assembly group"; }
+		}
+		
+		public string GetValidFormatName (string fileName)
+		{
+			return Path.ChangeExtension (fileName, ".md-nunit");
+		}
+		
+		public bool CanReadFile (string file)
+		{
+			return Path.GetExtension (file) == ".md-nunit";
+		}
+		
+		public bool CanWriteFile (object obj)
+		{
+			return obj is NUnitAssemblyGroupProject;
+		}
+		
+		public void WriteFile (string file, object obj, IProgressMonitor monitor)
+		{
+			NUnitAssemblyGroupProject project = obj as NUnitAssemblyGroupProject;
+			if (project == null)
+				throw new InvalidOperationException ("The provided object is not a valid Project");
+
+			StreamWriter sw = new StreamWriter (file);
+			try {
+				monitor.BeginTask (string.Format (GettextCatalog.GetString("Saving project: {0}"), file), 1);
+				XmlDataSerializer ser = new XmlDataSerializer (Runtime.ProjectService.DataContext);
+				ser.SerializationContext.BaseFile = file;
+				ser.Serialize (sw, project, typeof(NUnitAssemblyGroupProject));
+			} catch (Exception ex) {
+				monitor.ReportError (string.Format (GettextCatalog.GetString ("Could not save project: {0}"), file), ex);
+			} finally {
+				monitor.EndTask ();
+				sw.Close ();
+			}
+		}
+		
+		public object ReadFile (string file, IProgressMonitor monitor)
+		{
+			XmlTextReader reader = new XmlTextReader (new StreamReader (file));
+			try {
+				monitor.BeginTask (string.Format (GettextCatalog.GetString ("Loading project: {0}"), file), 1);
+				
+				reader.MoveToContent ();
+				
+				XmlDataSerializer ser = new XmlDataSerializer (Runtime.ProjectService.DataContext);
+				ser.SerializationContext.BaseFile = file;
+				
+				CombineEntry entry = (CombineEntry) ser.Deserialize (reader, typeof(NUnitAssemblyGroupProject));
+				entry.FileName = file;
+				return entry;
+			}
+			catch (Exception ex) {
+				monitor.ReportError (string.Format (GettextCatalog.GetString ("Could not load project: {0}"), file), ex);
+				throw;
+			}
+			finally {
+				monitor.EndTask ();
+				reader.Close ();
+			}
+		}
+	}
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Project/NUnitAssemblyGroupProject.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Project/NUnitAssemblyGroupProject.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Project/NUnitAssemblyGroupProject.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,185 @@
+//
+// NUnitAssemblyGroupProject.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+using System;
+using System.IO;
+using System.Xml;
+using MonoDevelop.Internal.Project;
+using MonoDevelop.Services;
+using MonoDevelop.Internal.Serialization;
+
+namespace MonoDevelop.NUnit
+{
+	[DataInclude (typeof(NUnitAssemblyGroupProjectConfiguration))]
+	public class NUnitAssemblyGroupProject: CombineEntry
+	{
+		RootTest rootTest;
+		
+		public NUnitAssemblyGroupProject ()
+		{
+		}
+		
+		public override void InitializeFromTemplate (XmlElement element)
+		{
+			Configurations.Add (CreateConfiguration ("Default"));
+		}
+
+		public UnitTest RootTest {
+			get {
+				if (rootTest == null) {
+					rootTest = new RootTest (this);
+				}
+				return rootTest;
+			}
+		}
+		
+		public override IConfiguration CreateConfiguration (string name)
+		{
+			NUnitAssemblyGroupProjectConfiguration conf = new NUnitAssemblyGroupProjectConfiguration ();
+			conf.Name = name;
+			return conf;
+		}
+		
+		public override void Clean ()
+		{
+		}
+		
+		public override ICompilerResult Build (IProgressMonitor monitor)
+		{
+			return null;
+		}
+		
+		public override void Execute (IProgressMonitor monitor)
+		{
+		}
+		
+		public override void Debug (IProgressMonitor monitor)
+		{
+		}
+		
+		public override bool NeedsBuilding {
+			get { return false; }
+			set {}
+		}
+	}
+	
+	public class NUnitAssemblyGroupProjectConfiguration: AbstractConfiguration
+	{
+		TestAssemblyCollection assemblies;
+		
+		public NUnitAssemblyGroupProjectConfiguration ()
+		{
+			assemblies = new TestAssemblyCollection (this);
+		}
+		
+		public override void CopyFrom (IConfiguration other)
+		{
+			base.CopyFrom (other);
+			
+			NUnitAssemblyGroupProjectConfiguration conf = other as NUnitAssemblyGroupProjectConfiguration;
+			if (conf != null) {
+				assemblies.Clear ();
+				foreach (TestAssembly ta in conf.Assemblies) {
+					TestAssembly copy = new TestAssembly (ta.Path);
+					assemblies.Add (copy);
+				}
+			}
+		}
+		
+		[ItemProperty ("Assemblies")]
+		[ItemProperty ("Assembly", ValueType=typeof(TestAssembly), Scope=1)]
+		public TestAssemblyCollection Assemblies {
+			get { return assemblies; }
+		}
+		
+		internal void OnAssembliesChanged ()
+		{
+			if (AssembliesChanged != null)
+				AssembliesChanged (this, EventArgs.Empty);
+		}
+		
+		public event EventHandler AssembliesChanged;
+	}
+	
+	class RootTest: UnitTestGroup
+	{
+		NUnitAssemblyGroupProject project;
+		string resultsPath;
+		NUnitAssemblyGroupProjectConfiguration lastConfig;
+		
+		public RootTest (NUnitAssemblyGroupProject project): base (project.Name, project)
+		{
+			this.project = project;
+			resultsPath = Path.Combine (project.BaseDirectory, "test-results");
+			ResultsStore = new XmlResultsStore (resultsPath, Path.GetFileName (project.FileName));
+			
+			lastConfig = (NUnitAssemblyGroupProjectConfiguration) project.ActiveConfiguration;
+			if (lastConfig != null)
+				lastConfig.AssembliesChanged += new EventHandler (OnAssembliesChanged);
+		}
+
+		public override void Dispose ()
+		{
+			if (lastConfig != null)
+				lastConfig.AssembliesChanged -= new EventHandler (OnAssembliesChanged);
+		}
+		
+		internal string ResultsPath {
+			get { return resultsPath; }
+		}
+		
+		void OnAssembliesChanged (object sender, EventArgs args)
+		{
+			UpdateTests ();
+		}
+		
+		protected override void OnActiveConfigurationChanged ()
+		{
+			if (lastConfig != null)
+				lastConfig.AssembliesChanged -= new EventHandler (OnAssembliesChanged);
+
+			lastConfig = (NUnitAssemblyGroupProjectConfiguration) project.ActiveConfiguration;
+			if (lastConfig != null)
+				lastConfig.AssembliesChanged += new EventHandler (OnAssembliesChanged);
+
+			UpdateTests ();
+			base.OnActiveConfigurationChanged ();
+		}
+		
+		protected override void OnCreateTests ()
+		{
+			NUnitAssemblyGroupProjectConfiguration conf = (NUnitAssemblyGroupProjectConfiguration) project.GetConfiguration (ActiveConfiguration);
+			if (conf != null) {
+				foreach (TestAssembly t in conf.Assemblies)
+					Tests.Add (t);
+			}
+		}
+	}
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Project/TestAssembly.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Project/TestAssembly.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Project/TestAssembly.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,74 @@
+//
+// TestAssembly.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+using System;
+using System.IO;
+using MonoDevelop.Internal.Project;
+using MonoDevelop.Internal.Serialization;
+
+namespace MonoDevelop.NUnit
+{
+	public class TestAssembly: NUnitAssemblyTestSuite
+	{
+		[ItemProperty ("Path")]
+		string path;
+		
+		public TestAssembly (): base (null)
+		{
+		}
+		
+		public TestAssembly (string path): base (null)
+		{
+			this.path = path;
+		}
+		
+		public override string Name {
+			get { return System.IO.Path.GetFileNameWithoutExtension (path); }
+		}
+		
+		public string Path {
+			get { return path; }
+			set { path = value; }
+		}
+		
+		protected override string AssemblyPath {
+			get { return path; }
+		}
+		
+		protected override string TestInfoCachePath {
+			get {
+				if (Parent != null)
+					return System.IO.Path.Combine (((RootTest)Parent).ResultsPath, System.IO.Path.GetFileName (path) + ".test-cache");
+				else
+					return null;
+			}
+		}
+	}
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Project/TestAssemblyCollection.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Project/TestAssemblyCollection.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Project/TestAssemblyCollection.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,79 @@
+//
+// TestAssemblyCollection.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+using System;
+using System.Collections;
+
+namespace MonoDevelop.NUnit
+{
+	public class TestAssemblyCollection: CollectionBase
+	{
+		NUnitAssemblyGroupProjectConfiguration owner;
+		
+		internal TestAssemblyCollection (NUnitAssemblyGroupProjectConfiguration owner)
+		{
+			this.owner = owner;
+		}
+		
+		public new TestAssembly this [int n] {
+			get { return (TestAssembly) List [n]; }
+		}
+		
+		public void Add (TestAssembly asm)
+		{
+			List.Add (asm);
+		}
+		
+		public void Remove (TestAssembly asm)
+		{
+			List.Remove (asm);
+		}
+		
+		protected override void OnInsertComplete (int index, object value)
+		{
+			owner.OnAssembliesChanged ();
+		}
+		
+		protected override void OnRemoveComplete (int index, object value)
+		{
+			owner.OnAssembliesChanged ();
+		}
+		
+		protected override void OnSetComplete (int index, object oldValue, object newValue)
+		{
+			owner.OnAssembliesChanged ();
+		}
+		
+		protected override void OnClearComplete ()
+		{
+			owner.OnAssembliesChanged ();
+		}
+	}
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Services/CombineTestGroup.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Services/CombineTestGroup.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Services/CombineTestGroup.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,86 @@
+//
+// CombineTestGroup.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.IO;
+using System.Collections;
+using MonoDevelop.Core.Services;
+using MonoDevelop.Internal.Project;
+
+using NUnit.Core;
+
+namespace MonoDevelop.NUnit
+{
+	public class CombineTestGroup: UnitTestGroup
+	{
+		Combine combine;
+		
+		public CombineTestGroup (Combine c): base (c.Name, c)
+		{
+			string storeId = Path.GetFileName (c.FileName);
+			string resultsPath = Path.Combine (c.BaseDirectory, "test-results");
+			ResultsStore = new XmlResultsStore (resultsPath, storeId);
+			
+			combine = c;
+			combine.EntryAdded += new CombineEntryEventHandler (OnEntryChanged);
+			combine.EntryRemoved += new CombineEntryEventHandler (OnEntryChanged);
+			combine.NameChanged += new CombineEntryRenamedEventHandler (OnCombineRenamed);
+		}
+		
+		public override void Dispose ()
+		{
+			combine.EntryAdded -= new CombineEntryEventHandler (OnEntryChanged);
+			combine.EntryRemoved -= new CombineEntryEventHandler (OnEntryChanged);
+			combine.NameChanged -= new CombineEntryRenamedEventHandler (OnCombineRenamed);
+			base.Dispose ();
+		}
+		
+		void OnEntryChanged (object sender, CombineEntryEventArgs e)
+		{
+			UpdateTests ();
+		}
+		
+		void OnCombineRenamed (object sender, CombineEntryRenamedEventArgs e)
+		{
+			UnitTestGroup parent = Parent as UnitTestGroup;
+			if (parent != null)
+				parent.UpdateTests ();
+		}
+		
+		protected override void OnCreateTests ()
+		{
+			NUnitService testService = (NUnitService) ServiceManager.GetService (typeof(NUnitService));
+			foreach (CombineEntry e in combine.Entries) {
+				UnitTest t = testService.BuildTest (e);
+				if (t != null)
+					Tests.Add (t);
+			}
+		}
+	}
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Services/ExternalTestRunner.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Services/ExternalTestRunner.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Services/ExternalTestRunner.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,316 @@
+//
+// ExternalTestRunner.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+using System;
+using System.Reflection;
+using System.IO;
+using System.Collections;
+using System.Threading;
+
+using MonoDevelop.Services;
+using NUnit.Core;
+
+namespace MonoDevelop.NUnit
+{
+	class ExternalTestRunner: RemoteProcessObject
+	{
+		string assemblyName;
+		StringWriter stdout = new StringWriter ();
+		StringWriter stderr = new StringWriter ();
+		
+		public TestResult Run (EventListener listener, IFilter filter, string path, string suiteName, string testName)
+		{
+			TestSuite rootTS = LoadTestSuite (path, suiteName);
+
+			TextWriter origStdout = Console.Out;
+			TextWriter origStderr = Console.Error;
+			Console.SetOut (stdout);
+			Console.SetError (stderr);
+			
+			string cdir = Environment.CurrentDirectory;
+			Environment.CurrentDirectory = Path.GetDirectoryName (path);
+		
+			try {
+				Test nt = null;
+				if (testName != null) {
+					foreach (Test t in rootTS.Tests)
+						if (t.Name == testName) {
+							nt = t;
+							break;
+						}
+				} else
+					nt = rootTS;
+					
+				if (nt == null)
+					throw new Exception ("Test " + suiteName + "." + testName + " not found.");
+					
+				return nt.Run (listener, filter);
+			} finally {
+				Environment.CurrentDirectory = cdir;
+				Console.SetOut (origStdout);
+				Console.SetError (origStderr);
+			}
+		}
+		
+		public string ResetTestConsoleOutput ()
+		{
+			string s = stdout.ToString ();
+			stdout = new StringWriter ();
+			Console.SetOut (stdout);
+			return s;
+		}
+			
+		public string ResetTestConsoleError ()
+		{
+			string s = stderr.ToString ();
+			stderr = new StringWriter ();
+			Console.SetError (stderr);
+			return s;
+		}
+			
+		TestSuite LoadTestSuite (string path, string fullName)
+		{
+			ResolveEventHandler reh = new ResolveEventHandler (TryLoad);
+			AppDomain.CurrentDomain.AssemblyResolve += reh;
+			assemblyName = path;
+
+			try {
+				if (fullName != "")
+					return new TestSuiteBuilder ().Build (path, fullName);
+				else
+					return new TestSuiteBuilder ().Build (path);
+			} finally {
+				AppDomain.CurrentDomain.AssemblyResolve -= reh;
+			}
+		}
+
+		Assembly TryLoad (object sender, ResolveEventArgs args)
+		{
+			try {
+				// NUnit2 uses Assembly.Load on the filename without extension.
+				// This is done just to allow loading from a full path name.
+				return Assembly.LoadFrom (assemblyName);
+			} catch { }
+
+			return null;
+		}
+		
+		public TestInfo GetTestInfo (string path)
+		{
+			TestSuite rootTS = LoadTestSuite (path, "");
+			return BuildTestInfo (rootTS);
+		}
+		
+		TestInfo BuildTestInfo (Test test)
+		{
+			TestInfo ti = new TestInfo ();
+			ti.Name = test.Name;
+			int i = test.FullName.LastIndexOf ('.');
+			if (i != -1)
+				ti.PathName = test.FullName.Substring (0,i);
+			else
+				ti.PathName = null;
+				
+			if (test.Tests != null && test.Tests.Count > 0) {
+				ti.Tests = new TestInfo [test.Tests.Count];
+				for (int n=0; n<test.Tests.Count; n++)
+					ti.Tests [n] = BuildTestInfo ((Test)test.Tests [n]);
+			}
+			return ti;
+		}
+	}
+	
+	[Serializable]
+	class TestInfo
+	{
+		public string Name;
+		public string PathName;
+		public TestInfo[] Tests;
+	}
+	
+	class LocalTestMonitor: MarshalByRefObject, EventListener
+	{
+		TestContext context;
+		UnitTest rootTest;
+		string rootFullName;
+		ExternalTestRunner runner;
+		UnitTest runningTest;
+		
+		public LocalTestMonitor (TestContext context, ExternalTestRunner runner, UnitTest rootTest, string rootFullName)
+		{
+			this.runner = runner;
+			this.rootFullName = rootFullName;
+			this.rootTest = rootTest;
+			this.context = context;
+		}
+		
+		public UnitTest RunningTest {
+			get { return runningTest; }
+		}
+		
+		void EventListener.RunStarted (Test [] tests)
+		{
+		}
+
+		void EventListener.RunFinished (TestResult [] results)
+		{
+		}
+
+		void EventListener.UnhandledException (Exception exception)
+		{
+		}
+
+		void EventListener.RunFinished (Exception exc)
+		{
+		}
+
+		void EventListener.TestStarted (TestCase testCase)
+		{
+			UnitTest t = GetLocalTest (testCase);
+			runningTest = t;
+			context.Monitor.BeginTest (t);
+			t.Status = TestStatus.Running;
+		}
+			
+		void EventListener.TestFinished (TestCaseResult result)
+		{
+			UnitTest t = GetLocalTest ((Test) result.Test);
+			UnitTestResult res = GetLocalTestResult (result);
+			res.ConsoleOutput = runner.ResetTestConsoleOutput ();
+			res.ConsoleError = runner.ResetTestConsoleError ();
+			t.RegisterResult (context, res);
+			context.Monitor.EndTest (t, res);
+			t.Status = TestStatus.Ready;
+			runningTest = null;
+		}
+
+		void EventListener.SuiteStarted (TestSuite suite)
+		{
+			UnitTest t = GetLocalTest (suite);
+			t.Status = TestStatus.Running;
+			context.Monitor.BeginTest (t);
+		}
+
+		void EventListener.SuiteFinished (TestSuiteResult result)
+		{
+			UnitTest t = GetLocalTest ((Test) result.Test);
+			UnitTestResult res = GetLocalTestResult (result);
+			t.RegisterResult (context, res);
+			t.Status = TestStatus.Ready;
+			context.Monitor.EndTest (t, res);
+		}
+		
+		UnitTest GetLocalTest (Test t)
+		{
+			if (t == null) return null;
+			if (t.Parent == null) return rootTest;
+			
+			string fn = t.FullName;
+			string sname = fn.Substring (rootFullName.Length);
+			if (sname.StartsWith (".")) sname = sname.Substring (1);
+			UnitTest tt = FindTest (rootTest, sname);
+			return tt;
+		}
+		
+		UnitTest FindTest (UnitTest t, string testPath)
+		{
+			if (testPath == "")
+				return t;
+
+			string[] path = testPath.Split ('.');
+			foreach (string part in path) {
+				UnitTestGroup group = t as UnitTestGroup;
+				if (group == null)
+					return null;
+				
+				t = group.Tests [part];
+				if (t == null)
+					return null;
+			}
+			return t;
+		}
+		
+		public UnitTestResult GetLocalTestResult (TestResult t)
+		{
+			UnitTestResult res = new UnitTestResult ();
+			
+			if (t is TestSuiteResult) {
+				int s=0, f=0, i=0;
+				CountResults ((TestSuiteResult)t, ref s, ref f, ref i);
+				res.TotalFailures = f;
+				res.TotalSuccess = s;
+				res.TotalIgnored = i;
+				if (f > 0)
+					res.Status |= ResultStatus.Failure;
+				if (s > 0)
+					res.Status |= ResultStatus.Success;
+				if (i > 0)
+					res.Status |= ResultStatus.Ignored;
+			} else {
+				if (t.IsFailure) {
+					res.Status = ResultStatus.Failure;
+					res.TotalFailures = 1;
+				}
+				else if (!t.Executed) {
+					res.Status = ResultStatus.Ignored;
+					res.TotalIgnored = 1;
+				}
+				else {
+					res.Status = ResultStatus.Success;
+					res.TotalSuccess = 1;
+				}
+			}
+			
+			res.Message = t.Message;
+			res.StackTrace = t.StackTrace;
+			res.Time = TimeSpan.FromSeconds (t.Time);
+			return res;
+		}
+		
+		void CountResults (TestSuiteResult ts, ref int s, ref int f, ref int i)
+		{
+			if (ts.Results == null)
+				return;
+
+			foreach (TestResult t in ts.Results) {
+				if (t is TestCaseResult) {
+					if (t.IsFailure)
+						f++;
+					else if (!t.Executed)
+						i++;
+					else
+						s++;
+				} else if (t is TestSuiteResult) {
+					CountResults ((TestSuiteResult) t, ref s, ref f, ref i);
+				}
+			}
+		}
+	}	
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Services/GeneralTestOptions.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Services/GeneralTestOptions.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Services/GeneralTestOptions.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,43 @@
+//
+// GeneralTestOptions.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+using System;
+using System.Collections;
+
+namespace MonoDevelop.NUnit
+{
+	public class GeneralTestOptions: ICloneable
+	{
+		public object Clone ()
+		{
+			return new GeneralTestOptions ();
+		}
+	}
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Services/IResultsStore.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Services/IResultsStore.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Services/IResultsStore.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,46 @@
+//
+// IResultsStore.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace MonoDevelop.NUnit
+{
+	public interface IResultsStore
+	{
+		void RegisterResult (string configuration, UnitTest test, UnitTestResult result);
+		
+		UnitTestResult GetLastResult (string configuration, UnitTest test, DateTime date);
+		UnitTestResult GetNextResult (string configuration, UnitTest test, DateTime date);
+		UnitTestResult GetPreviousResult (string configuration, UnitTest test, DateTime date);
+		UnitTestResult[] GetResults (string configuration, UnitTest test, DateTime startDate, DateTime endDate);
+		UnitTestResult[] GetResultsToDate (string configuration, UnitTest test, DateTime endDate, int count);
+		
+		void Save ();
+	}
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Services/ITestProgressMonitor.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Services/ITestProgressMonitor.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Services/ITestProgressMonitor.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,45 @@
+//
+// ITestProgressMonitor.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace MonoDevelop.NUnit
+{
+	public interface ITestProgressMonitor
+	{
+		void BeginTest (UnitTest test);
+		void EndTest (UnitTest test, UnitTestResult result);
+		void ReportRuntimeError (string message, Exception exception);
+		
+		bool IsCancelRequested { get; }
+		event TestHandler CancelRequested;
+	}
+	
+	public delegate void TestHandler ();
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Services/ITestProvider.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Services/ITestProvider.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Services/ITestProvider.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,42 @@
+//
+// ITestProvider.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+using System;
+using MonoDevelop.Internal.Project;
+using NUnit.Core;
+
+namespace MonoDevelop.NUnit
+{
+	public interface ITestProvider
+	{
+		UnitTest CreateUnitTest (CombineEntry entry);
+		Type[] GetOptionTypes ();
+	}
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Services/NUnitAssemblyTestSuite.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Services/NUnitAssemblyTestSuite.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Services/NUnitAssemblyTestSuite.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,409 @@
+//
+// NUnitAssemblyTestSuite.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+using System;
+using System.Reflection;
+using System.IO;
+using System.Collections;
+using System.Threading;
+using System.Runtime.Serialization.Formatters.Binary;
+
+using MonoDevelop.Internal.Project;
+using MonoDevelop.Services;
+using NUnit.Core;
+
+namespace MonoDevelop.NUnit
+{
+	public abstract class NUnitAssemblyTestSuite: UnitTestGroup
+	{
+		object locker = new object ();
+		UnitTest[] oldList;
+		TestInfoCache testInfoCache = new TestInfoCache ();
+		bool cacheLoaded;
+		
+		static Queue loadQueue = new Queue ();
+		static bool loaderRunning;
+		
+		public NUnitAssemblyTestSuite (string name): base (name)
+		{
+		}
+		
+		public NUnitAssemblyTestSuite (string name, CombineEntry ownerCombineEntry): base (name, ownerCombineEntry)
+		{
+		}
+		
+		public override void Dispose ()
+		{
+			try {
+				if (TestInfoCachePath != null) {
+					Console.WriteLine ("saving TestInfoCachePath = " + TestInfoCachePath);
+					testInfoCache.Write (TestInfoCachePath);
+				}
+			} catch {
+			}
+		}
+		
+		protected override void OnActiveConfigurationChanged ()
+		{
+			UpdateTests ();
+			base.OnActiveConfigurationChanged ();
+		}
+		
+		internal SourceCodeLocation GetSourceCodeLocation (UnitTest test)
+		{
+			if (test is NUnitTestCase) {
+				NUnitTestCase t = (NUnitTestCase) test;
+				return GetSourceCodeLocation (t.ClassName, t.Name);
+			} else if (test is NUnitTestSuite) {
+				NUnitTestSuite t = (NUnitTestSuite) test;
+				return GetSourceCodeLocation (t.ClassName, null);
+			} else
+				return null;
+		}
+		
+		protected virtual SourceCodeLocation GetSourceCodeLocation (string fullClassName, string methodName)
+		{
+			return null;
+		}
+		
+		protected override void OnCreateTests ()
+		{
+			lock (locker) {
+				if (Status == TestStatus.Loading)
+					return;
+					
+				TestInfo ti = testInfoCache.GetInfo (AssemblyPath);
+				if (ti != null) {
+					FillTests (ti);
+					return;
+				}
+				
+				Status = TestStatus.Loading;
+			}
+			
+			if (oldList != null) {
+				foreach (UnitTest t in oldList)
+					Tests.Add (t);
+			}
+
+			OnTestStatusChanged ();
+			
+			LoadData ld = new LoadData ();
+			ld.Path = AssemblyPath;
+			ld.TestInfoCachePath = cacheLoaded ? null : TestInfoCachePath;
+			ld.Callback = new WaitCallback (AsyncCreateTests);
+			
+			AsyncLoadTest (ld);
+
+			// Read the cache from disk only once
+			cacheLoaded = true;
+		}
+		
+		void AsyncCreateTests (object ob)
+		{
+			TestStatus newStatus = TestStatus.Ready;
+			
+			try {
+				LoadData loadData = (LoadData) ob;
+				
+				if (loadData.Error != null) {
+					newStatus = TestStatus.LoadError;
+					return;
+				}
+				
+				Tests.Clear ();
+
+				if (loadData.Info == null) {
+					oldList = new UnitTest [0];
+					return;
+				}
+
+				FillTests (loadData.Info);
+				
+				// If the async loader has loaded a cache, reuse it.
+				if (loadData.InfoCache != null)
+					testInfoCache = loadData.InfoCache;
+				
+				testInfoCache.SetInfo (AssemblyPath, loadData.Info);
+			}
+			catch (Exception ex) {
+				Console.WriteLine (ex);
+				newStatus = TestStatus.LoadError;
+			}
+			finally {
+				lock (locker) {
+					Status = newStatus;
+				}
+				OnTestChanged ();
+			}
+		}
+		
+		void FillTests (TestInfo ti)
+		{
+			if (ti.Tests == null) return;
+			foreach (TestInfo test in ti.Tests) {
+				if (test.Tests != null)
+					Tests.Add (new NUnitTestSuite (this, test));
+				else
+					Tests.Add (new NUnitTestCase (this, test));
+			}
+			oldList = new UnitTest [Tests.Count];
+			Tests.CopyTo (oldList, 0);
+		}
+		
+		static void AsyncLoadTest (LoadData ld)
+		{
+			lock (loadQueue) {
+				if (!loaderRunning) {
+					Thread t = new Thread (new ThreadStart (RunAsyncLoadTest));
+					t.IsBackground = true;
+					t.Start ();
+					loaderRunning = true;
+				}
+				loadQueue.Enqueue (ld);
+				Monitor.Pulse (loadQueue);
+			}
+		}
+		
+		static void RunAsyncLoadTest ()
+		{
+			while (true)
+			{
+				LoadData ld;
+				lock (loadQueue) {
+					if (loadQueue.Count == 0) {
+						if (!Monitor.Wait (loadQueue, 5000, true)) {
+							loaderRunning = false;
+							return;
+						}
+					}
+					ld = (LoadData)loadQueue.Dequeue ();
+				}
+				
+				try {
+					// If the information is cached in a file and it is up to date information,
+					// there is no need to parse again the assembly.
+
+					if (ld.TestInfoCachePath != null && File.Exists (ld.TestInfoCachePath)) {
+						ld.InfoCache = TestInfoCache.Read (ld.TestInfoCachePath);
+						TestInfo info = ld.InfoCache.GetInfo (ld.Path);
+						if (info != null) {
+							ld.Info = info;
+							ld.Callback (ld);
+							continue;
+						}
+					}
+				} catch (Exception ex) {
+					Console.WriteLine (ex);	// Remove this
+				}
+				
+				ExternalTestRunner runner = null;
+				
+				try {
+					if (File.Exists (ld.Path)) {
+						runner = (ExternalTestRunner) Runtime.ProcessService.CreateExternalProcessObject (typeof(ExternalTestRunner), false);
+						ld.Info = runner.GetTestInfo (ld.Path);
+					}
+				} catch (Exception ex) {
+					ld.Error = ex;
+				}
+				finally {
+					try {
+						if (runner != null)
+							runner.Dispose ();
+					} catch {}
+				}
+				
+				try {
+					ld.Callback (ld);
+				} catch {
+				}
+			}
+		}
+		
+		protected override UnitTestResult OnRun (TestContext testContext)
+		{
+			return RunUnitTest (this, "", testContext);
+		}
+		
+		internal UnitTestResult RunUnitTest (UnitTest test, string suiteName, TestContext testContext)
+		{
+			ExternalTestRunner runner = (ExternalTestRunner) Runtime.ProcessService.CreateExternalProcessObject (typeof(ExternalTestRunner), false);
+			LocalTestMonitor localMonitor = new LocalTestMonitor (testContext, runner, test, suiteName);
+			
+			IFilter filter = null;
+			
+			NUnitCategoryOptions categoryOptions = (NUnitCategoryOptions) test.GetOptions (typeof(NUnitCategoryOptions));
+			if (categoryOptions.EnableFilter && categoryOptions.Categories.Count > 0) {
+				string[] cats = new string [categoryOptions.Categories.Count];
+				categoryOptions.Categories.CopyTo (cats, 0);
+				filter = new CategoryFilter (cats, categoryOptions.Exclude);
+			}
+			
+			RunData rd = new RunData ();
+			rd.Runner = runner;
+			rd.Test = this;
+			testContext.Monitor.CancelRequested += new TestHandler (rd.Cancel);
+			
+			UnitTestResult result;
+			
+			try {
+				TestResult res = runner.Run (localMonitor, filter, AssemblyPath, suiteName, null);
+				result = localMonitor.GetLocalTestResult (res);
+			} catch (Exception ex) {
+				Console.WriteLine (ex);
+				if (localMonitor.RunningTest != null) {
+					RuntimeErrorCleanup (testContext, localMonitor.RunningTest, ex);
+				} else {
+					testContext.Monitor.ReportRuntimeError (null, ex);
+					throw ex;
+				}
+				result = UnitTestResult.CreateFailure (ex);
+			} finally {
+				testContext.Monitor.CancelRequested -= new TestHandler (rd.Cancel);
+				runner.Dispose ();
+			}
+			
+			return result;
+		}
+		
+		void RuntimeErrorCleanup (TestContext testContext, UnitTest t, Exception ex)
+		{
+			UnitTestResult result = UnitTestResult.CreateFailure (ex);
+			t.RegisterResult (testContext, result);
+			while (t != null && t != this) {
+				testContext.Monitor.EndTest (t, result);
+				t.Status = TestStatus.Ready;
+				t = t.Parent;
+			}
+		}
+		
+		protected abstract string AssemblyPath {
+			get;
+		}
+		
+		// File where cached test info for this test suite will be saved
+		// Returns null by default which means that test info will not be saved.
+		protected virtual string TestInfoCachePath {
+			get { return null; }
+		}
+		
+		class LoadData
+		{
+			public string Path;
+			public string TestInfoCachePath;
+			public Exception Error;
+			public TestInfo Info;
+			public TestInfoCache InfoCache;
+			public WaitCallback Callback;
+		}
+		
+		class RunData
+		{
+			public ExternalTestRunner Runner;
+			public UnitTest Test;
+			
+			public void Cancel ()
+			{
+				Runner.Dispose ();
+				ClearRunningStatus (Test);
+			}
+			
+			void ClearRunningStatus (UnitTest t)
+			{
+				t.Status = TestStatus.Ready;
+				UnitTestGroup group = t as UnitTestGroup;
+				if (group == null) return;
+				foreach (UnitTest ct in group.Tests)
+					ClearRunningStatus (ct);
+			}
+		}
+		
+		[Serializable]
+		class TestInfoCache
+		{
+			Hashtable table = new Hashtable ();
+			
+			[NonSerialized]
+			bool modified;
+			
+			public void SetInfo (string path, TestInfo info)
+			{
+				if (File.Exists (path)) {
+					CachedTestInfo cti = new CachedTestInfo ();
+					cti.LastWriteTime = File.GetLastWriteTime (path);
+					cti.Info = info;
+					table [path] = cti;
+					modified = true;
+				}
+			}
+			
+			public TestInfo GetInfo (string path)
+			{
+				CachedTestInfo cti = (CachedTestInfo) table [path];
+				if (cti != null && File.Exists (path) && File.GetLastWriteTime (path) == cti.LastWriteTime)
+					return cti.Info;
+				else
+					return null;
+			}
+			
+			public static TestInfoCache Read (string file)
+			{
+				BinaryFormatter bf = new BinaryFormatter ();
+				Stream s = new FileStream (file, FileMode.Open, FileAccess.Read);
+				try {
+					return (TestInfoCache) bf.Deserialize (s);
+				} finally {
+					s.Close ();
+				}
+			}
+			
+			public void Write (string file)
+			{
+				if (modified) {
+					BinaryFormatter bf = new BinaryFormatter ();
+					Stream s = new FileStream (file, FileMode.Create, FileAccess.Write);
+					try {
+						bf.Serialize (s, this);
+					} finally {
+						s.Close ();
+					}
+				}
+			}
+		}
+		
+		[Serializable]
+		class CachedTestInfo
+		{
+			public DateTime LastWriteTime;
+			public TestInfo Info;
+		}
+	}
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Services/NUnitOptions.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Services/NUnitOptions.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Services/NUnitOptions.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,93 @@
+//
+// NUnitCategoryOptions.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+using System;
+using System.Text;
+using System.Collections;
+using System.Collections.Specialized;
+using MonoDevelop.Internal.Serialization;
+using MonoDevelop.Services;
+
+namespace MonoDevelop.NUnit
+{
+	public class NUnitCategoryOptions: ICloneable
+	{
+		[ItemProperty ("Categories")]
+		[ItemProperty ("Category", ValueType=typeof(string), Scope=1)]
+		StringCollection categories = new StringCollection ();
+		
+		bool enableFilter;
+		bool exclude;
+		
+		public StringCollection Categories {
+			get { return categories; }
+		}
+		
+		[ItemProperty]
+		public bool EnableFilter {
+			get { return enableFilter; }
+			set { enableFilter = value; }
+		}
+		
+		[ItemProperty]
+		public bool Exclude {
+			get { return exclude; }
+			set { exclude = value; }
+		}
+		
+		public object Clone ()
+		{
+			NUnitCategoryOptions op = new NUnitCategoryOptions ();
+			op.enableFilter = enableFilter;
+			op.exclude = exclude;
+			op.categories = new StringCollection ();
+			foreach (string s in categories)
+				op.categories.Add (s);
+			return op;
+		}
+		
+		public override string ToString ()
+		{
+			if (EnableFilter && Categories.Count > 0) {
+				StringBuilder s = new StringBuilder ();
+				if (Exclude)
+					s.Append (GettextCatalog.GetString ("Exclude the following categories: "));
+				else
+					s.Append (GettextCatalog.GetString ("Include the following categories: "));
+				for (int n=0; n<Categories.Count; n++) {
+					if (n > 0) s.Append (", ");
+					s.Append (Categories [n]);
+				}
+				return s.ToString ();
+			} else
+				return "";
+		}
+	}
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Services/NUnitProjectTestSuite.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Services/NUnitProjectTestSuite.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Services/NUnitProjectTestSuite.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,114 @@
+//
+// NUnitProjectTestSuite.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+using System;
+using System.Reflection;
+using System.IO;
+using System.Collections;
+
+using MonoDevelop.Services;
+using MonoDevelop.Internal.Project;
+using MonoDevelop.Internal.Parser;
+
+using NUnit.Core;
+
+namespace MonoDevelop.NUnit
+{
+	public class NUnitProjectTestSuite: NUnitAssemblyTestSuite
+	{
+		Project project;
+		DateTime lastAssemblyTime;
+		string resultsPath;
+		string storeId;
+		
+		public NUnitProjectTestSuite (Project project): base (project.Name, project)
+		{
+			storeId = Path.GetFileName (project.FileName);
+			resultsPath = Path.Combine (project.BaseDirectory, "test-results");
+			ResultsStore = new XmlResultsStore (resultsPath, storeId);
+			this.project = project;
+			lastAssemblyTime = GetAssemblyTime ();
+			project.NameChanged += new CombineEntryRenamedEventHandler (OnProjectRenamed);
+			Runtime.ProjectService.EndBuild += new ProjectCompileEventHandler (OnProjectBuilt);
+		}
+
+		protected override SourceCodeLocation GetSourceCodeLocation (string fullClassName, string methodName)
+		{
+			IClass cls = Runtime.ParserService.GetClass (project, fullClassName);
+			if (cls == null)
+				return null;
+			
+			foreach (IMethod met in cls.Methods) {
+				if (met.Name == methodName)
+					return new SourceCodeLocation (cls.Region.FileName, met.Region.BeginLine, met.Region.BeginColumn);
+			}
+			return new SourceCodeLocation (cls.Region.FileName, cls.Region.BeginLine, cls.Region.BeginColumn);
+		}
+		
+		public override void Dispose ()
+		{
+			project.NameChanged -= new CombineEntryRenamedEventHandler (OnProjectRenamed);
+			Runtime.ProjectService.EndBuild -= new ProjectCompileEventHandler (OnProjectBuilt);
+			base.Dispose ();
+		}
+		
+		void OnProjectRenamed (object sender, CombineEntryRenamedEventArgs e)
+		{
+			UnitTestGroup parent = Parent as UnitTestGroup;
+			if (parent != null)
+				parent.UpdateTests ();
+		}
+		
+		void OnProjectBuilt (bool success)
+		{
+			if (lastAssemblyTime != GetAssemblyTime ()) {
+				lastAssemblyTime = GetAssemblyTime ();
+				UpdateTests ();
+			}
+		}
+		
+		DateTime GetAssemblyTime ()
+		{
+			string path = AssemblyPath;
+			if (File.Exists (path))
+				return File.GetLastWriteTime (path);
+			else
+				return DateTime.MinValue;
+		}
+	
+		protected override string AssemblyPath {
+			get { return project.GetOutputFileName (); }
+		}
+		
+		protected override string TestInfoCachePath {
+			get { return Path.Combine (resultsPath, storeId + ".test-cache"); }
+		}
+	}
+}
+

Modified: trunk/MonoDevelop/Extras/NUnit/Services/NUnitService.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Services/NUnitService.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Services/NUnitService.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -1,136 +1,245 @@
+//
+// NUnitService.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
 using System;
-using System.IO;
-using System.Reflection;
+using System.Collections;
+using System.Threading;
 
+using MonoDevelop.Core.Services;
+using MonoDevelop.Services;
+using MonoDevelop.Gui.Dialogs;
+using MonoDevelop.Core.AddIns;
+using MonoDevelop.Internal.Project;
 using NUnit.Core;
-using MonoDevelop.Core.Services;
-using MonoDevelop.NUnit;
 
-namespace MonoDevelop.Services
+namespace MonoDevelop.NUnit
 {
-	public class NUnitService : AbstractService, EventListener
+	public class NUnitService : AbstractService
 	{
-		Assembly asm;
-		bool running = false;
-		TestSuite rootTestSuite;
-
-		public event EventHandler AssemblyLoaded;
-		public event FixtureLoadedErrorEventHandler FixtureLoadError;
-		public event TestEventHandler SuiteFinishedEvent;
-		public event TestEventHandler SuiteStartedEvent;
-		public event TestEventHandler TestFinishedEvent;
-		public event TestEventHandler TestStartedEvent;
-
+		ArrayList providers = new ArrayList ();
+		UnitTest rootTest;
+		TestResultsPad resultsPad;
+		
 		public NUnitService ()
 		{
 		}
-
-		public bool Running {
-			get { return running; }
+		
+		public override void InitializeService ()
+		{
+			RegisterTestProvider (new SystemTestProvider ());
+			Runtime.ProjectService.CombineOpened += new CombineEventHandler (OnOpenCombine);
+			Runtime.ProjectService.CombineClosed += new CombineEventHandler (OnCloseCombine);
+			
+			Runtime.ProjectService.DataContext.IncludeType (typeof(UnitTestOptionsSet));
+			Runtime.ProjectService.DataContext.RegisterProperty (typeof(AbstractConfiguration), "UnitTestInformation", typeof(UnitTestOptionsSet));
 		}
-
-		public Assembly TestAssembly {
-			get { return asm; }
+		
+		public IAsyncOperation RunTest (UnitTest test)
+		{
+			if (resultsPad == null) {
+				resultsPad = new TestResultsPad ();
+				Runtime.Gui.Workbench.ShowPad (resultsPad);
+			}
+			
+			Runtime.Gui.Workbench.BringToFront (resultsPad);
+			TestSession session = new TestSession (test, resultsPad);
+			session.Start ();
+			return session;
 		}
+		
+		
+		protected virtual void OnOpenCombine (object sender, CombineEventArgs e)
+		{
+			rootTest = BuildTest (e.Combine);
+			
+			if (TestSuiteChanged != null)
+				TestSuiteChanged (this, EventArgs.Empty);
+		}
 
-		public TestSuite GetTestSuite (string assemblyName)
+		protected virtual void OnCloseCombine (object sender, CombineEventArgs e)
 		{
-			ResolveEventHandler reh = new ResolveEventHandler (TryLoad);
-			AppDomain.CurrentDomain.AssemblyResolve += reh;
-
-			TestSuite suite = null;
-			try {
-				suite = new TestSuiteBuilder ().Build (assemblyName);
+			if (rootTest != null) {
+				((IDisposable)rootTest).Dispose ();
+				rootTest = null;
 			}
-			catch (Exception e) {
-				if (FixtureLoadError != null)
-					FixtureLoadError (this, new FixtureLoadedErrorEventArgs (assemblyName, e));
+			if (TestSuiteChanged != null)
+				TestSuiteChanged (this, EventArgs.Empty);
+		}
+		
+		public UnitTest BuildTest (CombineEntry entry)
+		{
+			foreach (ITestProvider p in providers) {
+				UnitTest t = p.CreateUnitTest (entry);
+				if (t != null) return t;
 			}
-			finally {
-				AppDomain.CurrentDomain.AssemblyResolve -= reh;
-			}
-			rootTestSuite = suite;
-			return suite;
+			return null;
 		}
-
-		public void LoadAssembly (string path)
+		
+		public UnitTest RootTest {
+			get { return rootTest; }
+		}
+		
+		public void RegisterTestProvider (ITestProvider provider)
 		{
-			if (path == null)
-				throw new ArgumentNullException ("path");
-			if (!File.Exists (path))
-				throw new Exception ("assembly could not be found: " + path);
-
-			asm = Assembly.LoadFrom (path);
-			if (AssemblyLoaded != null)
-				AssemblyLoaded (this, EventArgs.Empty);
+			providers.Add (provider);
+			Type[] types = provider.GetOptionTypes ();
+			if (types != null) {
+				foreach (Type t in types) {
+					if (!typeof(ICloneable).IsAssignableFrom (t))
+						throw new InvalidOperationException ("Option types must implement ICloneable: " + t);
+					Runtime.ProjectService.DataContext.IncludeType (t);
+				}
+			}
 		}
-
-		public void RunFinished (Exception exception)
+		
+		public static void ShowOptionsDialog (UnitTest test)
 		{
+			UnitTestOptionsDialog optionsDialog = new UnitTestOptionsDialog (test);
+			optionsDialog.Run ();
 		}
-
-		public void RunFinished (TestResult[] results)
+		
+		public event EventHandler TestSuiteChanged;
+	}
+	
+	
+	class TestSession: IAsyncOperation, ITestProgressMonitor
+	{
+		UnitTest test;
+		ITestProgressMonitor monitor;
+		TestResultsPad resultsPad;
+		Thread runThread;
+		bool success;
+		ManualResetEvent waitEvent;
+		
+		public TestSession (UnitTest test, TestResultsPad resultsPad)
 		{
+			this.test = test;
+			this.monitor = resultsPad;
+			this.resultsPad = resultsPad;
 		}
-
-		public void RunStarted (Test[] tests)
+		
+		public void Start ()
 		{
+			runThread = new Thread (new ThreadStart (RunTests));
+			runThread.IsBackground = true;
+			runThread.Start ();
 		}
-
-		public void RunTest (Test test)
+		
+		void RunTests ()
 		{
-			if (running) {
-				Console.WriteLine ("already running a test");
-				return;
+			try {
+				ResetResult (test);
+				resultsPad.InitializeTestRun (test);
+				TestContext ctx = new TestContext (monitor, DateTime.Now);
+				test.Run (ctx);
+				test.SaveResults ();
+				success = true;
+			} catch (Exception ex) {
+				Console.WriteLine (ex);
+				resultsPad.ReportRuntimeError (null, ex);
+				success = false;
+			} finally {
+				resultsPad.FinishTestRun ();
+				runThread = null;
 			}
-			running = true;
-			test.Run (this);
-			running = false;
+			lock (this) {
+				if (waitEvent != null)
+					waitEvent.Set ();
+			}
+			if (Completed != null)
+				Completed (this);
 		}
-
-		public void RunTests ()
+		
+		void ResetResult (UnitTest test)
 		{
-			if (rootTestSuite != null)
-				RunTest (rootTestSuite);
+			test.ResetLastResult ();
+			UnitTestGroup group = test as UnitTestGroup;
+			if (group == null) return;
+			foreach (UnitTest t in group.Tests)
+				ResetResult (t);
 		}
-
-		public void SuiteFinished (TestSuiteResult result)
+		
+		void ITestProgressMonitor.BeginTest (UnitTest test)
 		{
-			if (SuiteFinishedEvent != null)
-				SuiteFinishedEvent (this, new TestEventArgs (TestAction.SuiteFinished, result));
+			monitor.BeginTest (test);
 		}
-
-		public void SuiteStarted (TestSuite suite)
+		
+		void ITestProgressMonitor.EndTest (UnitTest test, UnitTestResult result)
 		{
-			if (SuiteStartedEvent != null)
-				SuiteStartedEvent (this, new TestEventArgs (TestAction.SuiteStarting, suite));
+			monitor.EndTest (test, result);
 		}
-
-		public void TestFinished (TestCaseResult result)
+		
+		void ITestProgressMonitor.ReportRuntimeError (string message, Exception exception)
 		{
-			if (TestFinishedEvent != null)
-				TestFinishedEvent (this, new TestEventArgs (TestAction.TestFinished, result));
+			monitor.ReportRuntimeError (message, exception);
 		}
-
-		public void TestStarted (TestCase test)
+		
+		bool ITestProgressMonitor.IsCancelRequested {
+			get { return monitor.IsCancelRequested; }
+		}
+		
+		void IAsyncOperation.Cancel ()
 		{
-			if (TestStartedEvent != null)
-				TestStartedEvent (this, new TestEventArgs (TestAction.TestStarting, test));
+			resultsPad.Cancel ();
 		}
-
-		Assembly TryLoad (object sender, ResolveEventArgs a)
+		
+		public void WaitForCompleted ()
 		{
-			try {
-				// NUnit2 uses Assembly.Load on the filename without extension.
-				// This is done just to allow loading from a full path name.
-				return Assembly.LoadFrom (asm.FullName);
+			if (IsCompleted) return;
+			
+			if (Runtime.DispatchService.IsGuiThread) {
+				while (!IsCompleted) {
+					while (Gtk.Application.EventsPending ())
+						Gtk.Application.RunIteration ();
+					Thread.Sleep (100);
+				}
+			} else {
+				lock (this) {
+					if (waitEvent == null)
+						waitEvent = new ManualResetEvent (false);
+				}
+				waitEvent.WaitOne ();
 			}
-			catch { }
-			return null;
 		}
+		
+		public bool IsCompleted {
+			get { return runThread == null; }
+		}
+		
+		public bool Success {
+			get { return success; }
+		}
 
-		public void UnhandledException (Exception exception)
-		{
+		public event OperationHandler Completed;
+		
+		public event TestHandler CancelRequested {
+			add { monitor.CancelRequested += value; }
+			remove { monitor.CancelRequested -= value; }
 		}
 	}
 }

Added: trunk/MonoDevelop/Extras/NUnit/Services/NUnitTestCase.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Services/NUnitTestCase.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Services/NUnitTestCase.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,74 @@
+//
+// NUnitTestCase.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+using System;
+using System.Collections;
+
+using MonoDevelop.Services;
+using NUnit.Core;
+
+namespace MonoDevelop.NUnit
+{
+	class NUnitTestCase: UnitTest
+	{
+		NUnitAssemblyTestSuite rootSuite;
+		string fullName;
+		string className;
+		
+		public NUnitTestCase (NUnitAssemblyTestSuite rootSuite, TestInfo tinfo): base (tinfo.Name)
+		{
+			className = tinfo.PathName;
+			fullName = tinfo.PathName + "." + tinfo.Name;
+			this.rootSuite = rootSuite;
+		}
+		
+		public string ClassName {
+			get { return className; }
+		}
+		
+		protected override UnitTestResult OnRun (TestContext testContext)
+		{
+			return rootSuite.RunUnitTest (this, fullName, testContext);
+		}
+		
+		public override SourceCodeLocation SourceCodeLocation {
+			get {
+				UnitTest p = Parent;
+				while (p != null) {
+					NUnitAssemblyTestSuite root = p as NUnitAssemblyTestSuite;
+					if (root != null)
+						return root.GetSourceCodeLocation (this);
+					p = p.Parent;
+				}
+				return null; 
+			}
+		}
+	}
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Services/NUnitTestSuite.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Services/NUnitTestSuite.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Services/NUnitTestSuite.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,87 @@
+//
+// NUnitTestSuite.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+using System;
+using System.IO;
+using System.Collections;
+using MonoDevelop.Services;
+using NUnit.Core;
+
+namespace MonoDevelop.NUnit
+{
+	class NUnitTestSuite: UnitTestGroup
+	{
+		TestInfo testInfo;
+		NUnitAssemblyTestSuite rootSuite;
+		string fullName;
+		
+		public NUnitTestSuite (NUnitAssemblyTestSuite rootSuite, TestInfo tinfo): base (tinfo.Name)
+		{
+			fullName = tinfo.PathName != null && tinfo.PathName.Length > 0 ? tinfo.PathName + "." + tinfo.Name : tinfo.Name;
+			this.testInfo = tinfo;
+			this.rootSuite = rootSuite;
+		}
+		
+		public string ClassName {
+			get { return fullName; }
+		}
+		
+		protected override UnitTestResult OnRun (TestContext testContext)
+		{
+			return rootSuite.RunUnitTest (this, fullName, testContext);
+		}
+		
+		protected override void OnCreateTests ()
+		{
+			if (testInfo.Tests == null)
+				return;
+			
+			foreach (TestInfo test in testInfo.Tests) {
+				if (test.Tests != null)
+					Tests.Add (new NUnitTestSuite (rootSuite, test));
+				else
+					Tests.Add (new NUnitTestCase (rootSuite, test));
+			}
+		}
+		
+		public override SourceCodeLocation SourceCodeLocation {
+			get {
+				UnitTest p = Parent;
+				while (p != null) {
+					NUnitAssemblyTestSuite root = p as NUnitAssemblyTestSuite;
+					if (root != null)
+						return root.GetSourceCodeLocation (this);
+					p = p.Parent;
+				}
+				return null; 
+			}
+		}
+	}
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Services/SystemTestProvider.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Services/SystemTestProvider.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Services/SystemTestProvider.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,57 @@
+//
+// SystemTestProvider.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using MonoDevelop.Internal.Project;
+using NUnit.Core;
+
+namespace MonoDevelop.NUnit
+{
+	public class SystemTestProvider: ITestProvider
+	{
+		public UnitTest CreateUnitTest (CombineEntry entry)
+		{
+			if (entry is Combine)
+				return new CombineTestGroup ((Combine)entry);
+			if (entry is DotNetProject)
+				return new NUnitProjectTestSuite ((Project)entry);
+			if (entry is NUnitAssemblyGroupProject)
+				return ((NUnitAssemblyGroupProject)entry).RootTest;
+			return null;
+		}
+		
+		public Type[] GetOptionTypes ()
+		{
+			return new Type[] {
+				typeof(GeneralTestOptions),
+				typeof(NUnitCategoryOptions)
+			};
+		}
+	}
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Services/TestContext.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Services/TestContext.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Services/TestContext.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,61 @@
+//
+// TestContext.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using MonoDevelop.Core.Services;
+
+namespace MonoDevelop.NUnit
+{
+	public class TestContext
+	{
+		ITestProgressMonitor monitor;
+		DateTime testDate;
+		object contextData;
+		
+		public TestContext (ITestProgressMonitor monitor, DateTime testDate)
+		{
+			this.monitor = monitor;
+			// Round to seconds
+			this.testDate = new DateTime ((testDate.Ticks / TimeSpan.TicksPerSecond) * TimeSpan.TicksPerSecond);
+		}
+		
+		public ITestProgressMonitor Monitor {
+			get { return monitor; }
+		}
+		
+		public DateTime TestDate {
+			get { return testDate; }
+		}
+		
+		public object ContextData {
+			get { return contextData; }
+			set { contextData = value; }
+		}
+	}
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Services/UnitTest.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Services/UnitTest.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Services/UnitTest.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,550 @@
+//
+// UnitTest.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+using System;
+using System.Collections;
+using MonoDevelop.Core.Services;
+using MonoDevelop.Internal.Project;
+using MonoDevelop.Internal.Serialization;
+using MonoDevelop.Services;
+
+namespace MonoDevelop.NUnit
+{
+	public abstract class UnitTest: IDisposable
+	{
+		string name;
+		IResultsStore resultsStore;
+		UnitTestResult lastResult;
+		UnitTest parent;
+		TestStatus status;
+		Hashtable options;
+		CombineEntry ownerCombineEntry;
+		UnitTestResultsStore results;
+		
+		protected UnitTest (string name)
+		{
+			this.name = name;
+		}
+		
+		protected UnitTest (string name, CombineEntry ownerCombineEntry)
+		{
+			this.name = name;
+			this.ownerCombineEntry = ownerCombineEntry;
+			ownerCombineEntry.ActiveConfigurationChanged += new ConfigurationEventHandler (OnConfugurationChanged);
+		}
+		
+		public virtual void Dispose ()
+		{
+			if (ownerCombineEntry != null)
+				ownerCombineEntry.ActiveConfigurationChanged -= new ConfigurationEventHandler (OnConfugurationChanged);
+		}
+		
+		internal void SetParent (UnitTest t)
+		{
+			parent = t;
+		}
+		
+		public virtual string ActiveConfiguration {
+			get {
+				if (ownerCombineEntry != null) {
+					if (ownerCombineEntry.ActiveConfiguration == null)
+						return "";
+					return ownerCombineEntry.ActiveConfiguration.Name;
+				} else if (Parent != null) {
+					return Parent.ActiveConfiguration;
+				} else {
+					return "default";
+				}
+			}
+		}
+		
+		public virtual string[] GetConfigurations ()
+		{
+			if (ownerCombineEntry != null) {
+				string[] res = new string [ownerCombineEntry.Configurations.Count];
+				for (int n=0; n<ownerCombineEntry.Configurations.Count; n++)
+					res [n] = ownerCombineEntry.Configurations [n].Name;
+				return res;
+			} else if (Parent != null) {
+				return Parent.GetConfigurations ();
+			} else {
+				return new string [] { "default" };
+			}
+		}
+		
+		public ICloneable GetOptions (Type optionsType)
+		{
+			return GetOptions (optionsType, ActiveConfiguration);
+		}
+		
+		public bool HasOptions (Type optionsType, string configuration)
+		{
+			return GetOptions (optionsType, configuration, false) != null;
+		}
+		
+		public void ResetOptions (Type optionsType, string configuration)
+		{
+			if (GetOptions (optionsType, configuration, false) == null)
+				return;
+				
+			if (options == null || !options.ContainsKey (configuration))
+				return;
+
+			Hashtable configOptions = (Hashtable) options [configuration];
+			if (configOptions != null)
+				configOptions.Remove (optionsType);
+			SaveOptions ();
+		}
+		
+		public ICloneable GetOptions (Type optionsType, string configuration)
+		{
+			return GetOptions (optionsType, configuration, true);
+		}
+		
+		public ICollection GetAllOptions (string configuration)
+		{
+			Hashtable localOptions = GetOptionsTable (configuration);
+			if (localOptions == null || localOptions.Count == 0) {
+				if (Parent != null)
+					return Parent.GetAllOptions (configuration);
+				else
+					return new object[0];
+			}
+			if (Parent == null)
+				return localOptions.Values;
+
+			ICollection parentOptions = Parent.GetAllOptions (configuration);
+			if (parentOptions.Count == 0)
+				return localOptions.Values;
+
+			Hashtable t = new Hashtable ();
+			foreach (object ob in parentOptions)
+				t [ob.GetType()] = ob;
+
+			foreach (ICloneable ob in localOptions.Values)
+				t [ob.GetType()] = ob.Clone ();
+
+			return t.Values;
+		}
+
+		ICloneable GetOptions (Type optionsType, string configuration, bool createDefault)
+		{
+			Hashtable configOptions = GetOptionsTable (configuration);
+			
+			if (configOptions != null) {
+				ICloneable ob = (ICloneable) configOptions [optionsType];
+				if (ob != null)
+					return (ICloneable) ob.Clone ();
+			}
+			if (!createDefault)
+				return null;
+			if (parent != null)
+				return parent.GetOptions (optionsType, configuration);
+			else
+				return (ICloneable) Activator.CreateInstance (optionsType);
+		}
+		
+		Hashtable GetOptionsTable (string configuration)
+		{
+			Hashtable configOptions = null;
+			
+			if (options == null || !options.ContainsKey (configuration)) {
+				ICollection col = OnLoadOptions (configuration);
+				if (col != null && col.Count > 0) {
+					if (options == null)
+						options = new Hashtable ();
+					configOptions = (Hashtable) options [configuration];
+					if (configOptions == null) {
+						configOptions = new Hashtable ();
+						options [configuration] = configOptions;
+					}
+					foreach (object op in col)
+						configOptions [op.GetType ()] = op;
+				}
+			} else
+				configOptions = (Hashtable) options [configuration];
+			return configOptions;
+		}
+		
+		public virtual void SetOptions (ICloneable ops, string configuration)
+		{
+			if (options == null)
+				options = new Hashtable ();
+				
+			Hashtable configOptions = (Hashtable) options [configuration];
+			if (configOptions == null) {
+				configOptions = new Hashtable ();
+				options [configuration] = configOptions;
+			}
+			
+			configOptions [ops.GetType ()] = ops.Clone ();
+			SaveOptions ();
+		}
+		
+		void SaveOptions ()
+		{
+			if (options == null) {
+				OnSaveOptions (null);
+				return;
+			}
+			
+			ArrayList list = new ArrayList ();
+			foreach (DictionaryEntry e in options) {
+				OptionsData d = new OptionsData ((string) e.Key, ((Hashtable) e.Value).Values);
+				list.Add (d);
+			}
+			
+			OnSaveOptions ((OptionsData[]) list.ToArray (typeof(OptionsData)));
+		}
+		
+		public UnitTestResultsStore Results {
+			get {
+				if (results == null) {
+					results = new UnitTestResultsStore (this, GetResultsStore ());
+				}
+				return results;
+			}
+		}
+		
+		public UnitTestResult GetLastResult ()
+		{
+			return lastResult;
+		}
+		
+		public void ResetLastResult ()
+		{
+			lastResult = null;
+			OnTestStatusChanged ();
+		}
+		
+		public UnitTestCollection GetRegressions (DateTime fromDate, DateTime toDate)
+		{
+			UnitTestCollection list = new UnitTestCollection ();
+			FindRegressions (list, fromDate, toDate);
+			return list;
+		}
+		
+		public virtual int CountTestCases ()
+		{
+			return 1;
+		}
+		
+		public virtual SourceCodeLocation SourceCodeLocation {
+			get { return null; }
+		}
+		
+		public UnitTest Parent {
+			get { return parent; }
+		}
+		
+		public virtual string Name {
+			get { return name; }
+		}
+		
+		public virtual string Title {
+			get { return Name; }
+		}
+		
+		public TestStatus Status {
+			get { return status; }
+			set { status = value; OnTestStatusChanged (); }
+		}
+		
+		public string FullName {
+			get {
+				if (parent != null)
+					return parent.FullName + "." + Name;
+				else
+					return Name;
+			}
+		}
+		
+		protected CombineEntry OwnerCombineEntry {
+			get { return ownerCombineEntry; }
+		}
+		
+		internal string StoreRelativeName {
+			get {
+				if (resultsStore != null || Parent == null)
+					return "";
+				else if (Parent.resultsStore != null)
+					return Name;
+				else
+					return Parent.StoreRelativeName + "." + Name;
+			}
+		}
+		
+		public UnitTestResult Run (TestContext testContext)
+		{
+			testContext.Monitor.BeginTest (this);
+			UnitTestResult res = null;
+			object ctx = testContext.ContextData;
+			
+			try {
+				Status = TestStatus.Running;
+				res = OnRun (testContext);
+			} catch (Exception ex) {
+				res = UnitTestResult.CreateFailure (ex);
+			} finally {
+				Status = TestStatus.Ready;
+				testContext.Monitor.EndTest (this, res);
+			}
+			RegisterResult (testContext, res);
+			testContext.ContextData = ctx;
+			return res;
+		}
+		
+		protected abstract UnitTestResult OnRun (TestContext testContext);
+		
+		public void RegisterResult (TestContext context, UnitTestResult result)
+		{
+			// Avoid registering results twice
+			if (lastResult != null && lastResult.TestDate == context.TestDate)
+				return;
+
+			result.TestDate = context.TestDate;
+			if ((int)result.Status == 0)
+				result.Status = ResultStatus.Ignored;
+			lastResult = result;
+			IResultsStore store = GetResultsStore ();
+			if (store != null)
+				store.RegisterResult (ActiveConfiguration, this, result);
+			OnTestStatusChanged ();
+		}
+		
+		IResultsStore GetResultsStore ()
+		{
+			if (resultsStore != null)
+				return resultsStore;
+			if (Parent != null)
+				return Parent.GetResultsStore ();
+			else
+				return null;
+		}
+		
+		protected IResultsStore ResultsStore {
+			get { return resultsStore; }
+			set { resultsStore = value; }
+		}
+		
+		public virtual void SaveResults ()
+		{
+			if (resultsStore != null)
+				resultsStore.Save ();
+		}
+		
+		internal virtual void FindRegressions (UnitTestCollection list, DateTime fromDate, DateTime toDate)
+		{
+			UnitTestResult res1 = Results.GetLastResult (fromDate);
+			UnitTestResult res2 = Results.GetLastResult (toDate);
+			if ((res1 == null || res1.IsSuccess) && (res2 != null && !res2.IsSuccess))
+				list.Add (this);
+		}
+		
+		protected virtual void OnSaveOptions (OptionsData[] data)
+		{
+			CombineEntry ce;
+			string path;
+			
+			GetOwnerCombineEntry (this, out ce, out path);
+			
+			if (ce == null)
+				throw new InvalidOperationException ("Options can't be saved.");
+			
+			foreach (OptionsData d in data) {
+				IExtendedDataItem edi = (IExtendedDataItem) ce.GetConfiguration (d.Configuration);
+				if (edi == null)
+					continue;
+				UnitTestOptionsSet oset = (UnitTestOptionsSet) edi.ExtendedProperties ["UnitTestInformation"];
+				if (oset == null) {
+					oset = new UnitTestOptionsSet ();
+					edi.ExtendedProperties ["UnitTestInformation"] = oset;
+				}
+				
+				UnitTestOptionsEntry te = oset.FindEntry (path);
+
+				if (d.Options.Count > 0) {
+					if (te == null) {
+						te = new UnitTestOptionsEntry ();
+						te.Path = path;
+						oset.Tests.Add (te);
+					}
+					te.Options.Clear ();
+					te.Options.AddRange (d.Options);
+				} else if (te != null) {
+					oset.Tests.Remove (te);
+				}
+			}
+			
+			ce.Save (new NullProgressMonitor ());
+		}
+		
+		protected virtual ICollection OnLoadOptions (string configuration)
+		{
+			CombineEntry ce;
+			string path;
+			
+			GetOwnerCombineEntry (this, out ce, out path);
+			
+			if (ce == null)
+				return null;
+			
+			IExtendedDataItem edi = (IExtendedDataItem) ce.GetConfiguration (configuration);
+			if (edi == null)
+				return null;
+
+			UnitTestOptionsSet oset = (UnitTestOptionsSet) edi.ExtendedProperties ["UnitTestInformation"];
+			if (oset == null)
+				return null;
+			
+			UnitTestOptionsEntry te = oset.FindEntry (path);
+			if (te != null)
+				return te.Options;
+			else
+				return null;
+		}
+		
+		void GetOwnerCombineEntry (UnitTest t, out CombineEntry c, out string path)
+		{
+			if (OwnerCombineEntry != null) {
+				c = OwnerCombineEntry;
+				path = "";
+			} else if (parent != null) {
+				parent.GetOwnerCombineEntry (t, out c, out path);
+				if (c == null) return;
+				if (path.Length > 0)
+					path += "/" + t.Name;
+				else
+					path = t.Name;
+			} else {
+				c = null;
+				path = null;
+			}
+		}
+		
+		void OnConfugurationChanged (object ob, ConfigurationEventArgs args)
+		{
+			OnActiveConfigurationChanged ();
+		}
+		
+		protected virtual void OnActiveConfigurationChanged ()
+		{
+			OnTestChanged ();
+		}
+		
+		protected virtual void OnTestChanged ()
+		{
+			if (TestChanged != null)
+				TestChanged (this, EventArgs.Empty);
+		}
+		
+		protected virtual void OnTestStatusChanged ()
+		{
+			if (TestStatusChanged != null)
+				TestStatusChanged (this, EventArgs.Empty);
+		}
+		
+		public event EventHandler TestChanged;
+		public event EventHandler TestStatusChanged;
+	}
+	
+	public class SourceCodeLocation
+	{
+		string fileName;
+		int line;
+		int column;
+		
+		public SourceCodeLocation (string fileName, int line, int column)
+		{
+			this.fileName = fileName;
+			this.line = line;
+			this.column = column;
+		}
+		
+		public string FileName {
+			get { return fileName; }
+		}
+		
+		public int Line {
+			get { return line; }
+		}
+		
+		public int Column {
+			get { return column; }
+		}
+	}
+
+	public class OptionsData
+	{
+		string configuration;
+		ICollection options;
+		
+		public OptionsData (string configuration, ICollection options)
+		{
+			this.configuration = configuration;
+			this.options = options;
+		}
+		
+		public string Configuration {
+			get { return configuration; }
+		}
+		
+		public ICollection Options {
+			get { return options; }
+		}
+	}
+	
+	
+	[DataItem ("UnitTestOptions")]
+	class UnitTestOptionsSet
+	{
+		[ExpandedCollection]
+		[ItemProperty ("Test", ValueType = typeof(UnitTestOptionsEntry))]
+		public ArrayList Tests = new ArrayList ();
+		
+		public UnitTestOptionsEntry FindEntry (string testPath)
+		{
+			foreach (UnitTestOptionsEntry t in Tests)
+				if (t.Path == testPath) return t;
+			return null;
+		}
+	}
+	
+	[DataItem ("Test")]
+	class UnitTestOptionsEntry
+	{
+		[ItemProperty ("Path")]
+		public string Path;
+
+		[ItemProperty ("Options")]
+		[ExpandedCollection]
+		public ArrayList Options = new ArrayList ();
+	}
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Services/UnitTestCollection.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Services/UnitTestCollection.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Services/UnitTestCollection.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,86 @@
+//
+// UnitTestCollection.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+using System;
+using System.Collections;
+
+namespace MonoDevelop.NUnit
+{
+	public class UnitTestCollection: CollectionBase
+	{
+		UnitTest owner;
+		
+		internal UnitTestCollection (UnitTest owner)
+		{
+			this.owner = owner;
+		}
+		
+		public UnitTestCollection ()
+		{
+		}
+		
+		public new UnitTest this [int n] {
+			get { return (UnitTest) List [n]; }
+		}
+		
+		public new UnitTest this [string name] {
+			get {
+				for (int n=0; n<List.Count; n++)
+					if (((UnitTest)List [n]).Name == name)
+						return (UnitTest) List [n];
+				return null;
+			}
+		}
+		
+		public void Add (UnitTest test)
+		{
+			((IList)this).Add (test);
+		}
+		
+		public void CopyTo (UnitTest[] array, int index)
+		{
+			List.CopyTo (array, index);
+		}
+		
+		protected override void OnInsert (int index, object value)
+		{
+			if (owner != null)
+				((UnitTest)value).SetParent (owner);
+		}
+		
+		protected override void OnSet (int index, object oldValue, object newValue)
+		{
+			if (owner != null) {
+				((UnitTest)oldValue).SetParent (null);
+				((UnitTest)newValue).SetParent (owner);
+			}
+		}
+	}
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Services/UnitTestGroup.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Services/UnitTestGroup.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Services/UnitTestGroup.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,168 @@
+//
+// UnitTestGroup.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using MonoDevelop.Core.Services;
+using System.Collections;
+using MonoDevelop.Internal.Project;
+
+namespace MonoDevelop.NUnit
+{
+	public class UnitTestGroup: UnitTest
+	{
+		UnitTestCollection tests;
+		
+		public UnitTestGroup (string name): base (name)
+		{
+		}
+		
+		protected UnitTestGroup (string name, CombineEntry ownerCombineEntry): base (name, ownerCombineEntry)
+		{
+		}
+		
+		public UnitTestCollection Tests {
+			get {
+				if (tests == null) {
+					tests = new UnitTestCollection (this);
+					OnCreateTests ();
+				}
+				return tests;
+			}
+		}
+		
+		public UnitTestCollection GetFailedTests (DateTime date)
+		{
+			UnitTestCollection col = new UnitTestCollection ();
+			CollectFailedTests (col, date);
+			return col;
+		}
+		
+		void CollectFailedTests (UnitTestCollection col, DateTime date)
+		{
+			foreach (UnitTest t in Tests) {
+				if (t is UnitTestGroup)
+					((UnitTestGroup)t).CollectFailedTests (col, date);
+				else {
+					UnitTestResult res = t.Results.GetLastResult (date);
+					if (res != null && res.IsFailure)
+						col.Add (t);
+				}
+			}
+		}
+		
+		public void UpdateTests ()
+		{
+			if (tests != null) {
+				foreach (UnitTest t in tests)
+					t.Dispose ();
+				tests = null;
+				OnTestChanged ();
+			}
+		}
+		
+		public override void SaveResults ()
+		{
+			base.SaveResults ();
+			if (tests != null) {
+				foreach (UnitTest t in tests)
+					t.SaveResults ();
+			}
+		}
+		
+		
+		public override int CountTestCases ()
+		{
+			int total = 0;
+			foreach (UnitTest t in Tests)
+				total += t.CountTestCases ();
+			return total;
+		}
+		
+		protected virtual void OnCreateTests ()
+		{
+		}
+		
+		protected override UnitTestResult OnRun (TestContext testContext)
+		{
+			UnitTestResult tres = new UnitTestResult ();
+			OnBeginTest (testContext);
+			
+			try {
+				foreach (UnitTest t in Tests) {
+					UnitTestResult res;
+					try {
+						res = OnRunChildTest (t, testContext);
+						if (testContext.Monitor.IsCancelRequested)
+							break;
+					} catch (Exception ex) {
+						res = UnitTestResult.CreateFailure (ex);
+					}
+					tres.Time += res.Time;
+					tres.Status |= res.Status;
+					tres.TotalFailures += res.TotalFailures;
+					tres.TotalSuccess += res.TotalSuccess;
+					tres.TotalIgnored += res.TotalIgnored;
+				}
+			} finally {
+				OnEndTest (testContext);
+			}
+			return tres;
+		}
+		
+		protected virtual void OnBeginTest (TestContext testContext)
+		{
+		}
+		
+		protected virtual UnitTestResult OnRunChildTest (UnitTest test, TestContext testContext)
+		{
+			return test.Run (testContext);
+		}
+		
+		protected virtual void OnEndTest (TestContext testContext)
+		{
+		}
+		
+		internal override void FindRegressions (UnitTestCollection list, DateTime fromDate, DateTime toDate)
+		{
+			foreach (UnitTest test in Tests)
+				test.FindRegressions (list, fromDate, toDate);
+		}
+		
+		public override void Dispose ()
+		{
+			base.Dispose ();
+
+			if (tests != null) {
+				foreach (UnitTest t in tests)
+					t.Dispose ();
+			}
+		}
+		
+	}
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Services/UnitTestResult.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Services/UnitTestResult.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Services/UnitTestResult.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,150 @@
+//
+// UnitTestResult.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.IO;
+using System.Collections;
+using System.Xml.Serialization;
+using System.Globalization;
+
+namespace MonoDevelop.NUnit
+{
+	public class UnitTestResult
+	{
+		DateTime testDate;
+		ResultStatus status;
+		TimeSpan time;
+		string message;
+		string output;
+		string stackTrace;
+		int totalFailures;
+		int totalSuccess;
+		int totalIgnored;
+		string cerror;
+		
+		public UnitTestResult ()
+		{
+		}
+		
+		public static UnitTestResult CreateFailure (Exception ex)
+		{
+			UnitTestResult res = new UnitTestResult ();
+			res.status = ResultStatus.Failure;
+			res.Message = ex.Message;
+			res.stackTrace = ex.StackTrace;
+			return res;
+		}
+		
+		public static UnitTestResult CreateFailure (string message, Exception ex)
+		{
+			UnitTestResult res = new UnitTestResult ();
+			res.status = ResultStatus.Failure;
+			res.Message = message;
+			res.stackTrace = ex.Message + "\n" + ex.StackTrace;
+			return res;
+		}
+		
+		public static UnitTestResult CreateIgnored (string message)
+		{
+			UnitTestResult res = new UnitTestResult ();
+			res.status = ResultStatus.Ignored;
+			res.Message = message;
+			return res;
+		}
+		
+		public static UnitTestResult CreateSuccess ()
+		{
+			UnitTestResult res = new UnitTestResult ();
+			res.status = ResultStatus.Success;
+			return res;
+		}
+		
+		public DateTime TestDate {
+			get { return testDate; }
+			set { testDate = value; }
+		}
+		
+		public ResultStatus Status {
+			get { return status; }
+			set { status = value; }
+		}
+		
+		public bool IsFailure {
+			get { return (status & ResultStatus.Failure) != 0; }
+		}
+		
+		public bool IsIgnored {
+			get { return (status & ResultStatus.Ignored) != 0; }
+		}
+		
+		public bool IsSuccess {
+			get { return (status & ResultStatus.Success) != 0; }
+		}
+		
+		public int TotalFailures {
+			get { return totalFailures; }
+			set { totalFailures = value; }
+		}
+		
+		public int TotalSuccess {
+			get { return totalSuccess; }
+			set { totalSuccess = value; }
+		}
+		
+		public int TotalIgnored {
+			get { return totalIgnored; }
+			set { totalIgnored = value; }
+		}
+		
+		public TimeSpan Time {
+			get { return time; }
+			set { time = value; }
+		}
+		
+		public string Message {
+			get { return message; }
+			set { message = value; }
+		}
+		
+		public string StackTrace {
+			get { return stackTrace; }
+			set { stackTrace = value; }
+		}
+		
+		public string ConsoleOutput {
+			get { return output; }
+			set { output = value; }
+		}
+		
+		public string ConsoleError {
+			get { return cerror; }
+			set { cerror = value; }
+		}
+	}
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Services/UnitTestResultsStore.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Services/UnitTestResultsStore.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Services/UnitTestResultsStore.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,76 @@
+//
+// TestNodeBuilder.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+using System;
+
+namespace MonoDevelop.NUnit
+{
+	public class UnitTestResultsStore
+	{
+		UnitTest test;
+		IResultsStore store;
+		
+		internal UnitTestResultsStore (UnitTest test, IResultsStore store)
+		{
+			this.test = test;
+			this.store = store;
+		}
+		
+		public UnitTestResult GetLastResult (DateTime date)
+		{
+			if (store == null) return null;
+			return store.GetLastResult (test.ActiveConfiguration, test, date);
+		}
+		
+		public UnitTestResult GetNextResult (DateTime date)
+		{
+			if (store == null) return null;
+			return store.GetNextResult (test.ActiveConfiguration, test, date);
+		}
+		
+		public UnitTestResult GetPreviousResult (DateTime date)
+		{
+			if (store == null) return null;
+			return store.GetPreviousResult (test.ActiveConfiguration, test, date);
+		}
+		
+		public UnitTestResult[] GetResults (DateTime startDate, DateTime endDate)
+		{
+			if (store == null) return new UnitTestResult [0];
+			return store.GetResults (test.ActiveConfiguration, test, startDate, endDate);
+		}
+		
+		public UnitTestResult[] GetResultsToDate (DateTime endDate, int count)
+		{
+			if (store == null) return new UnitTestResult [0];
+			return store.GetResultsToDate (test.ActiveConfiguration, test, endDate, count);
+		}
+	}
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Services/UnitTestStatus.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Services/UnitTestStatus.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Services/UnitTestStatus.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,49 @@
+//
+// TestStatus.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace MonoDevelop.NUnit
+{
+	public enum TestStatus
+	{
+		Ready,
+		Loading,
+		LoadError,
+		Running
+	}
+	
+	[Flags]
+	public enum ResultStatus
+	{
+		Success = 1,
+		Failure = 2,
+		Ignored = 4
+	}
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/Services/XmlResultsStore.cs
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/Services/XmlResultsStore.cs	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/Services/XmlResultsStore.cs	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,364 @@
+//
+// XmlResultsStore.cs
+//
+// Author:
+//   Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.IO;
+using System.Collections;
+using System.Xml.Serialization;
+using System.Globalization;
+
+namespace MonoDevelop.NUnit
+{
+	public class XmlResultsStore: IResultsStore
+	{
+		Hashtable fileCache = new Hashtable ();
+		string basePath;
+		string storeId;
+		Hashtable cachedRootList = new Hashtable ();
+		
+		static XmlSerializer serializer = new XmlSerializer (typeof(TestRecord));
+		
+		public XmlResultsStore (string directory, string storeId)
+		{
+			basePath = directory;
+			this.storeId = storeId;
+		}
+		
+		public void RegisterResult (string configuration, UnitTest test, UnitTestResult result)
+		{
+			string aname = test.StoreRelativeName;
+			
+			TestRecord root = GetRootRecord (configuration, result.TestDate);
+			if (root == null) {
+				root = new TestRecord ();
+				fileCache [GetRootFileName (configuration, result.TestDate)] = root;
+			}
+			root.Modified = true;
+			TestRecord record = root;
+			
+			if (aname.Length > 0) {
+				string[] path = test.StoreRelativeName.Split ('.');
+				foreach (string p in path) {
+					TestRecord ctr = record.Tests != null ? record.Tests [p] : null;
+					if (ctr == null) {
+						ctr = new TestRecord ();
+						ctr.Name = p;
+						if (record.Tests == null)
+							record.Tests = new TestRecordCollection ();
+						record.Tests.Add (ctr);
+					}
+					record = ctr;
+				}
+			}
+			
+			if (record.Results == null)
+				record.Results = new UnitTestResultCollection ();
+			record.Results.Add (result);
+		}
+		
+		public UnitTestResult GetNextResult (string configuration, UnitTest test, DateTime date)
+		{
+			DateTime currentDate = date;
+			TestRecord root = GetRootRecord (configuration, currentDate);
+			if (root == null)
+				root = GetNextRootRecord (configuration, ref currentDate);
+			
+			while (root != null) {
+				TestRecord tr = FindRecord (root, test.StoreRelativeName);
+				if (tr != null && tr.Results != null) {
+					foreach (UnitTestResult res in tr.Results) {
+						if (res.TestDate > date)
+							return res;
+					}
+				}
+				root = GetNextRootRecord (configuration, ref currentDate);
+			}
+			return null;
+		}
+		
+		public UnitTestResult GetPreviousResult (string configuration, UnitTest test, DateTime date)
+		{
+			DateTime currentDate = date;
+			TestRecord root = GetRootRecord (configuration, currentDate);
+			if (root == null)
+				root = GetPreviousRootRecord (configuration, ref currentDate);
+			
+			while (root != null) {
+				TestRecord tr = FindRecord (root, test.StoreRelativeName);
+				if (tr != null && tr.Results != null) {
+					for (int n = tr.Results.Count - 1; n >= 0; n--) {
+						UnitTestResult res = (UnitTestResult) tr.Results [n];
+						if (res.TestDate < date)
+							return res;
+					}
+				}
+				root = GetPreviousRootRecord (configuration, ref currentDate);
+			}
+			return null;
+		}
+		
+		public UnitTestResult GetLastResult (string configuration, UnitTest test, DateTime date)
+		{
+			return GetPreviousResult (configuration, test, date.AddTicks (1));
+		}
+		
+		public UnitTestResult[] GetResults (string configuration, UnitTest test, DateTime startDate, DateTime endDate)
+		{
+			ArrayList list = new ArrayList ();
+			DateTime firstDay = new DateTime (startDate.Year, startDate.Month, startDate.Day);
+			
+			DateTime[] dates = GetStoreDates (configuration);
+			
+			foreach (DateTime date in dates) {
+				if (date < firstDay)
+					continue;
+				if (date > endDate)
+					break;
+				
+				TestRecord root = GetRootRecord (configuration, date);
+				if (root == null) continue;
+
+				TestRecord tr = FindRecord (root, test.StoreRelativeName);
+				if (tr != null && tr.Results != null) {
+					foreach (UnitTestResult res in tr.Results) {
+						if (res.TestDate >= startDate && res.TestDate <= endDate)
+							list.Add (res);
+					}
+				}
+			}
+			
+			return (UnitTestResult[]) list.ToArray (typeof(UnitTestResult));
+		}
+		
+		public UnitTestResult[] GetResultsToDate (string configuration, UnitTest test, DateTime endDate, int count)
+		{
+			ArrayList list = new ArrayList ();
+			DateTime[] dates = GetStoreDates (configuration);
+			
+			for (int n = dates.Length - 1; n >= 0 && list.Count < count; n--) {
+				if (dates [n] > endDate)
+					continue;
+					
+				TestRecord root = GetRootRecord (configuration, dates [n]);
+				if (root == null) continue;
+
+				TestRecord tr = FindRecord (root, test.StoreRelativeName);
+				if (tr != null && tr.Results != null) {
+					for (int m = tr.Results.Count - 1; m >= 0 && list.Count < count; m--) {
+						UnitTestResult res = (UnitTestResult) tr.Results [m];
+						if (res.TestDate <= endDate)
+							list.Add (res);
+					}
+				}
+			}
+			
+			UnitTestResult[] array = (UnitTestResult[]) list.ToArray (typeof(UnitTestResult));
+			Array.Reverse (array);
+			return array;
+		}
+		
+		public void Save ()
+		{
+			if (!Directory.Exists (basePath))
+				Directory.CreateDirectory (basePath);
+
+			foreach (DictionaryEntry entry in fileCache) {
+				TestRecord record = (TestRecord) entry.Value;
+				if (!record.Modified)
+					continue;
+
+				string file = Path.Combine (basePath, (string)entry.Key);
+				StreamWriter writer = new StreamWriter (file);
+				try {
+					serializer.Serialize (writer, record);
+				} finally {
+					writer.Close ();
+				}
+			}
+			cachedRootList.Clear ();
+		}
+		
+		TestRecord FindRecord (TestRecord root, string aname)
+		{
+			if (aname.Length == 0)
+				return root;
+			else {
+				string[] path = aname.Split ('.');
+				TestRecord tr = root;
+				foreach (string p in path) {
+					if (tr.Tests == null)
+						return null;
+					tr = tr.Tests [p];
+					if (tr == null)
+						return null;
+				}
+				return tr;
+			}
+		}
+		
+		TestRecord GetRootRecord (string configuration, DateTime date)
+		{
+			string file = GetRootFileName (configuration, date);
+			TestRecord res = (TestRecord) fileCache [file];
+			if (res != null)
+				return res;
+			
+			string filePath = Path.Combine (basePath, file);
+			if (!File.Exists (filePath))
+				return null;
+
+			StreamReader s = new StreamReader (filePath);
+			try {
+				res = (TestRecord) serializer.Deserialize (s);
+			} catch (Exception ex) {
+				Console.WriteLine (ex);
+				return null;
+			} finally {
+				s.Close ();
+			}
+			fileCache [file] = res;
+			return res;
+		}
+		
+		TestRecord GetNextRootRecord (string configuration, ref DateTime date)
+		{
+			DateTime[] dates = GetStoreDates (configuration);
+			foreach (DateTime d in dates) {
+				if (d > date) {
+					date = d;
+					return GetRootRecord (configuration, d);
+				}
+			}
+			return null;
+		}
+		
+		TestRecord GetPreviousRootRecord (string configuration, ref DateTime date)
+		{
+			date = new DateTime (date.Year, date.Month, date.Day);
+			DateTime[] dates = GetStoreDates (configuration);
+			for (int n = dates.Length - 1; n >= 0; n--) {
+				if (dates [n] < date) {
+					date = dates [n];
+					return GetRootRecord (configuration, dates [n]);
+				}
+			}
+			return null;
+		}
+		
+		string GetRootFileName (string configuration, DateTime date)
+		{
+			return storeId + "-" + configuration + "-" + date.ToString ("yyyy-MM-dd", CultureInfo.InvariantCulture) + ".xml";
+		}
+		
+		DateTime ParseFileNameDate (string configuration, string fileName)
+		{
+			fileName = Path.GetFileNameWithoutExtension (fileName);
+			fileName = fileName.Substring (storeId.Length + configuration.Length + 2);
+			return DateTime.ParseExact (fileName, "yyyy-MM-dd", CultureInfo.InvariantCulture);
+		}
+		
+		DateTime[] GetStoreDates (string configuration)
+		{
+			if (!Directory.Exists (basePath))
+				return new DateTime [0];
+			
+			DateTime[] res = (DateTime[]) cachedRootList [configuration];
+			if (res != null)
+				return res;
+
+			ArrayList dates = new ArrayList ();
+			foreach (string file in Directory.GetFiles (basePath, storeId + "-" + configuration + "-*")) {
+				try {
+					DateTime t = ParseFileNameDate (configuration, Path.GetFileName (file));
+					dates.Add (t);
+				} catch { }
+			}
+			res = (DateTime[]) dates.ToArray (typeof(DateTime));
+			cachedRootList [configuration] = res;
+			return res;
+		}
+	}
+	
+	public class TestRecord
+	{
+		string name;
+		UnitTestResultCollection results;
+		TestRecordCollection tests;
+		internal bool Modified;
+		
+		[XmlAttribute]
+		public string Name {
+			get { return name; }
+			set { name = value; }
+		}
+		
+		public UnitTestResultCollection Results {
+			get { return results; }
+			set { results = value; }
+		}
+		
+		public TestRecordCollection Tests {
+			get { return tests; }
+			set { tests = value; }
+		}
+	}
+	
+	public class TestRecordCollection: CollectionBase
+	{
+		public new TestRecord this [int n] {
+			get { return (TestRecord) ((IList)this) [n]; }
+		}
+		
+		public new TestRecord this [string name] {
+			get {
+				for (int n=0; n<List.Count; n++)
+					if (((TestRecord)List [n]).Name == name)
+						return (TestRecord) List [n];
+				return null;
+			}
+		}
+		
+		public void Add (TestRecord test)
+		{
+			((IList)this).Add (test);
+		}
+	}
+	
+	public class UnitTestResultCollection: CollectionBase
+	{
+		public new UnitTestResult this [int n] {
+			get { return (UnitTestResult) ((IList)this) [n]; }
+		}
+		
+		public void Add (UnitTestResult test)
+		{
+			((IList)this).Add (test);
+		}
+	}	
+}
+

Added: trunk/MonoDevelop/Extras/NUnit/nunit.glade
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/nunit.glade	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/nunit.glade	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,328 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="NUnitOptions">
+  <property name="visible">True</property>
+  <property name="title" translatable="yes">NUnit Options</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">False</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+
+  <child>
+    <widget class="GtkVBox" id="vbox1">
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">6</property>
+
+      <child>
+	<widget class="GtkCheckButton" id="useParentCheck">
+	  <property name="visible">True</property>
+	  <property name="can_focus">True</property>
+	  <property name="label" translatable="yes">Use parent test settings</property>
+	  <property name="use_underline">True</property>
+	  <property name="relief">GTK_RELIEF_NORMAL</property>
+	  <property name="focus_on_click">True</property>
+	  <property name="active">False</property>
+	  <property name="inconsistent">False</property>
+	  <property name="draw_indicator">True</property>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">False</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkHSeparator" id="hseparator1">
+	  <property name="visible">True</property>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">False</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkVBox" id="vbox3">
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">6</property>
+
+	  <child>
+	    <widget class="GtkLabel" id="label1">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="yes">The following filter will be applied when running the tests:</property>
+	      <property name="use_underline">False</property>
+	      <property name="use_markup">False</property>
+	      <property name="justify">GTK_JUSTIFY_LEFT</property>
+	      <property name="wrap">False</property>
+	      <property name="selectable">False</property>
+	      <property name="xalign">0</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xpad">0</property>
+	      <property name="ypad">0</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkHBox" id="hbox2">
+	      <property name="visible">True</property>
+	      <property name="homogeneous">False</property>
+	      <property name="spacing">0</property>
+
+	      <child>
+		<widget class="GtkLabel" id="label2">
+		  <property name="width_request">18</property>
+		  <property name="visible">True</property>
+		  <property name="label" translatable="yes"></property>
+		  <property name="use_underline">False</property>
+		  <property name="use_markup">False</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">False</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0.5</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">0</property>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">False</property>
+		  <property name="fill">False</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkVBox" id="vbox4">
+		  <property name="visible">True</property>
+		  <property name="homogeneous">False</property>
+		  <property name="spacing">6</property>
+
+		  <child>
+		    <widget class="GtkRadioButton" id="noFilterRadio">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="label" translatable="yes">Don't apply any filter</property>
+		      <property name="use_underline">True</property>
+		      <property name="relief">GTK_RELIEF_NORMAL</property>
+		      <property name="focus_on_click">True</property>
+		      <property name="active">False</property>
+		      <property name="inconsistent">False</property>
+		      <property name="draw_indicator">True</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkRadioButton" id="includeRadio">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="label" translatable="yes">Include the following categories</property>
+		      <property name="use_underline">True</property>
+		      <property name="relief">GTK_RELIEF_NORMAL</property>
+		      <property name="focus_on_click">True</property>
+		      <property name="active">False</property>
+		      <property name="inconsistent">False</property>
+		      <property name="draw_indicator">True</property>
+		      <property name="group">noFilterRadio</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkRadioButton" id="excludeRadio">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="label" translatable="yes">Exclude the following categories</property>
+		      <property name="use_underline">True</property>
+		      <property name="relief">GTK_RELIEF_NORMAL</property>
+		      <property name="focus_on_click">True</property>
+		      <property name="active">False</property>
+		      <property name="inconsistent">False</property>
+		      <property name="draw_indicator">True</property>
+		      <property name="group">noFilterRadio</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">True</property>
+		  <property name="fill">True</property>
+		</packing>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">False</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkLabel" id="label3">
+	  <property name="visible">True</property>
+	  <property name="label" translatable="yes">Categories:</property>
+	  <property name="use_underline">False</property>
+	  <property name="use_markup">False</property>
+	  <property name="justify">GTK_JUSTIFY_LEFT</property>
+	  <property name="wrap">False</property>
+	  <property name="selectable">False</property>
+	  <property name="xalign">0</property>
+	  <property name="yalign">0.5</property>
+	  <property name="xpad">0</property>
+	  <property name="ypad">0</property>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">False</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkHBox" id="hbox1">
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">6</property>
+
+	  <child>
+	    <widget class="GtkLabel" id="label4">
+	      <property name="width_request">18</property>
+	      <property name="visible">True</property>
+	      <property name="label" translatable="yes"></property>
+	      <property name="use_underline">False</property>
+	      <property name="use_markup">False</property>
+	      <property name="justify">GTK_JUSTIFY_LEFT</property>
+	      <property name="wrap">False</property>
+	      <property name="selectable">False</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xpad">0</property>
+	      <property name="ypad">0</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkScrolledWindow" id="scrolledwindow1">
+	      <property name="visible">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
+	      <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
+	      <property name="shadow_type">GTK_SHADOW_IN</property>
+	      <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+	      <child>
+		<widget class="GtkTreeView" id="categoryTree">
+		  <property name="visible">True</property>
+		  <property name="can_focus">True</property>
+		  <property name="headers_visible">True</property>
+		  <property name="rules_hint">False</property>
+		  <property name="reorderable">False</property>
+		  <property name="enable_search">True</property>
+		</widget>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkVBox" id="vbox2">
+	      <property name="visible">True</property>
+	      <property name="homogeneous">False</property>
+	      <property name="spacing">6</property>
+
+	      <child>
+		<widget class="GtkButton" id="addButton">
+		  <property name="visible">True</property>
+		  <property name="can_focus">True</property>
+		  <property name="label">gtk-add</property>
+		  <property name="use_stock">True</property>
+		  <property name="relief">GTK_RELIEF_NORMAL</property>
+		  <property name="focus_on_click">True</property>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">False</property>
+		  <property name="fill">False</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkButton" id="removeButton">
+		  <property name="visible">True</property>
+		  <property name="can_focus">True</property>
+		  <property name="label">gtk-remove</property>
+		  <property name="use_stock">True</property>
+		  <property name="relief">GTK_RELIEF_NORMAL</property>
+		  <property name="focus_on_click">True</property>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">False</property>
+		  <property name="fill">False</property>
+		</packing>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+</glade-interface>

Added: trunk/MonoDevelop/Extras/NUnit/templates/NUnitAssemblyGroup.xpt.xml
===================================================================
--- trunk/MonoDevelop/Extras/NUnit/templates/NUnitAssemblyGroup.xpt.xml	2005-07-13 16:52:57 UTC (rev 2640)
+++ trunk/MonoDevelop/Extras/NUnit/templates/NUnitAssemblyGroup.xpt.xml	2005-07-13 16:55:25 UTC (rev 2641)
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<Template originator   = "Lluis Sanchez" 
+          created      = "12/07/2005"
+          lastModified = "12/07/2005">
+	
+	<!-- Template Header -->
+	<TemplateConfiguration>
+		<_Name>NUnit assembly test collection</_Name>
+		<Category>NUnit</Category>
+<!--		<Icon>Java.Project.DOSProject</Icon> -->
+		<_Description>Create an NUnit assembly test collection</_Description>
+	</TemplateConfiguration>
+	
+	<!-- Template Content -->
+	<Combine name = "${ProjectName}" directory = ".">
+		<Options>
+			<StartupProject>${ProjectName}</StartupProject>
+		</Options>
+		
+		<CombineEntry name = "${ProjectName}" directory = "." type = "MonoDevelop.NUnit.NUnitAssemblyGroupProject, MonoDevelop.NUnit">
+		</CombineEntry>
+	</Combine>
+</Template>




More information about the Monodevelop-patches-list mailing list