diff --git a/src/AddIns/Misc/MbUnitPad/Project/MbUnitPad.addin b/src/AddIns/Misc/MbUnitPad/Project/MbUnitPad.addin index a93d0d1fbd..3efe5ef0ce 100644 --- a/src/AddIns/Misc/MbUnitPad/Project/MbUnitPad.addin +++ b/src/AddIns/Misc/MbUnitPad/Project/MbUnitPad.addin @@ -14,7 +14,7 @@ @@ -26,6 +26,20 @@ icon = "Icons.16x16.BrowserRefresh" tooltip = "${res:NUnitPad.NUnitPadContent.RefreshItem}" class = "MbUnitPad.ReloadCommand"/> + + + + + Debug AnyCPU {B1CE28A0-04E8-490D-8256-E0C4D52C93C8} + 8.0.50215 + 2.0 ..\..\..\..\..\AddIns\AddIns\Misc\MbUnitPad\ DEBUG;TRACE - bin\Release\ + ..\..\..\..\..\AddIns\AddIns\Misc\MbUnitPad\ true TRACE @@ -40,7 +42,9 @@ - + + UserControl + diff --git a/src/AddIns/Misc/MbUnitPad/Project/MbUnitPad.csproj.user b/src/AddIns/Misc/MbUnitPad/Project/MbUnitPad.csproj.user index c439b83dc4..7b8f1b2e86 100644 --- a/src/AddIns/Misc/MbUnitPad/Project/MbUnitPad.csproj.user +++ b/src/AddIns/Misc/MbUnitPad/Project/MbUnitPad.csproj.user @@ -1,3 +1,6 @@  + + 8.0.50215 + \ No newline at end of file diff --git a/src/AddIns/Misc/MbUnitPad/Project/Src/MbUnitCommands.cs b/src/AddIns/Misc/MbUnitPad/Project/Src/MbUnitCommands.cs index a2cfb58a12..1e3b9c95b4 100644 --- a/src/AddIns/Misc/MbUnitPad/Project/Src/MbUnitCommands.cs +++ b/src/AddIns/Misc/MbUnitPad/Project/Src/MbUnitCommands.cs @@ -8,6 +8,7 @@ using System; using ICSharpCode.Core; using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Project; using MbUnit.Forms; namespace MbUnitPad @@ -20,11 +21,41 @@ namespace MbUnitPad } } + public class UnloadCommand : AbstractMenuCommand + { + public override void Run() + { + MbUnitPadContent.Instance.TreeView.RemoveAssemblies(); + } + } + public class RunTestsCommand : AbstractMenuCommand { public override void Run() { - MbUnitPadContent.Instance.TreeView.ThreadedRunTests(); + MbUnitPadContent.Instance.RunTests(); + } + } + + public class AddNUnitReferenceCommand : AbstractMenuCommand + { + public override void Run() + { + if (ProjectService.CurrentProject != null) { + ProjectService.AddProjectItem(ProjectService.CurrentProject, new ReferenceProjectItem(ProjectService.CurrentProject, "nunit.framework")); + ProjectService.CurrentProject.Save(); + } + } + } + + public class AddMbUnitReferenceCommand : AbstractMenuCommand + { + public override void Run() + { + if (ProjectService.CurrentProject != null) { + ProjectService.AddProjectItem(ProjectService.CurrentProject, new ReferenceProjectItem(ProjectService.CurrentProject, "MbUnit.Framework")); + ProjectService.CurrentProject.Save(); + } } } } diff --git a/src/AddIns/Misc/MbUnitPad/Project/Src/MbUnitPad.cs b/src/AddIns/Misc/MbUnitPad/Project/Src/MbUnitPad.cs index 7c2bc660b8..265903cd75 100644 --- a/src/AddIns/Misc/MbUnitPad/Project/Src/MbUnitPad.cs +++ b/src/AddIns/Misc/MbUnitPad/Project/Src/MbUnitPad.cs @@ -67,13 +67,29 @@ namespace MbUnitPad void OnSolutionClosed(object sender, EventArgs e) { - LoggingService.Info("Solution closed"); - treeView.RemoveAssemblies(); + treeView.NewConfig(); + } + + public void RunTests() + { + if (treeView.TypeTree.Nodes.Count == 0) { + treeView.TreePopulated += StartTestsAfterTreePopulation; + ReloadAssemblyList(); + } else { + treeView.ThreadedRunTests(); + } + } + + void StartTestsAfterTreePopulation(object sender, EventArgs e) + { + treeView.TreePopulated -= StartTestsAfterTreePopulation; + // we cannot run the tests on this thread because we have to wait for the worker thread to exit + WorkbenchSingleton.SafeThreadAsyncCall(treeView, "ThreadedRunTests"); } public void ReloadAssemblyList() { - treeView.RemoveAssemblies(); + treeView.TestDomains.Clear(); foreach (IProject project in ProjectService.OpenSolution.Projects) { bool referenceFound = false; foreach (ProjectItem item in project.Items) { @@ -105,6 +121,7 @@ namespace MbUnitPad } } } + treeView.ThreadedPopulateTree(true); } /// diff --git a/src/AddIns/Misc/MbUnitPad/Project/Src/TestTreeView.cs b/src/AddIns/Misc/MbUnitPad/Project/Src/TestTreeView.cs index 05abd42924..a624488631 100644 --- a/src/AddIns/Misc/MbUnitPad/Project/Src/TestTreeView.cs +++ b/src/AddIns/Misc/MbUnitPad/Project/Src/TestTreeView.cs @@ -6,21 +6,99 @@ // using System; +using System.Collections; +using System.IO; +using System.Reflection; using System.Windows.Forms; using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Gui; using MbUnit.Forms; +using MbUnit.Core; using MbUnit.Core.Remoting; +using MbUnit.Core.Reports.Serialization; namespace MbUnitPad { public class TestTreeView : ReflectorTreeView { + Hashtable pipeNodes; + public TestTreeView() { TypeTree.ContextMenu = null; TypeTree.ContextMenuStrip = MenuService.CreateContextMenu(this, "/MbUnitPad/ContextMenu"); + this.StartTests += OnTestsStarted; + this.FinishTests += delegate { BeginInvoke(new MethodInvoker(ExpandAllFailures)); }; + this.Facade.Updated += OnFacadeUpdated; + // I don't see another good way to get a pipe node by GUID but stealing the facade's hashtable + pipeNodes = (Hashtable)typeof(TestTreeNodeFacade).InvokeMember("pipeNodes", BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic, null, Facade, null); + } + + static MessageViewCategory testRunnerCategory; + + void OnTestsStarted(object sender, EventArgs e) + { + BeginInvoke(new EventHandler(OnTestsStartedInvoked)); + } + + void OnTestsStartedInvoked(object sender, EventArgs e) + { + if (testRunnerCategory == null) { + testRunnerCategory = new MessageViewCategory("MbUnit"); + CompilerMessageView cmv = (CompilerMessageView)WorkbenchSingleton.Workbench.GetPad(typeof(CompilerMessageView)).PadContent; + cmv.AddCategory(testRunnerCategory); + } else { + testRunnerCategory.ClearText(); + } } + void OnFacadeUpdated(ResultEventArgs e) + { + try { + if (e.State == TestState.Failure) { + IList list = (IList)pipeNodes[e.PipeIdentifier]; + for (int i = 0; i < list.Count; i++) { + UnitTreeNode node = list[i] as UnitTreeNode; + if (node != null) { + AppendResult(this.TestDomains.GetResult(node)); + break; + } + } + } + } catch (Exception ex) { + MessageService.ShowError(ex); + } + } + + void AppendResult(ReportRun result) + { + ReportException ex = result.Exception; + string message = (ex != null) ? ex.Message : "-"; + string outputMessage = StringParser.Parse("${res:NUnitPad.NUnitPadContent.TestTreeView.TestFailedMessage}", new string[,] { + {"TestCase", result.Name}, + {"Message", message} + }); + + testRunnerCategory.AppendText(outputMessage + Environment.NewLine); + testRunnerCategory.AppendText(result.Description + Environment.NewLine); + if (ex != null) { + testRunnerCategory.AppendText(ex.StackTrace + Environment.NewLine); + + FileLineReference LineRef = OutputTextLineParser.GetNUnitOutputFileLineReference(ex.StackTrace, true); + if (LineRef != null) { + Task task = new Task(Path.GetFullPath(LineRef.FileName), + outputMessage, + LineRef.Column, + LineRef.Line, + TaskType.Error); + + BeginInvoke(new AddTaskInvoker(TaskService.Add), new object[] { task }); + } + } + } + + delegate void AddTaskInvoker(Task task); + /// /// Default MbUnit-GUI doesn't use shadow copy, we have to override that behaviour. /// diff --git a/src/Main/StartUp/Project/Dialogs/ExceptionBox.cs b/src/Main/StartUp/Project/Dialogs/ExceptionBox.cs index 57bb20eac1..450cd7e771 100644 --- a/src/Main/StartUp/Project/Dialogs/ExceptionBox.cs +++ b/src/Main/StartUp/Project/Dialogs/ExceptionBox.cs @@ -30,17 +30,24 @@ namespace ICSharpCode.SharpDevelop Exception exceptionThrown; string message; - public ExceptionBox(Exception e, string message) + public ExceptionBox(Exception e, string message, bool mustTerminate) { this.exceptionThrown = e; this.message = message; InitializeComponent(); - RightToLeftConverter.Convert(this); + if (mustTerminate) { + closeButton.Visible = false; + continueButton.Text = closeButton.Text; + continueButton.Left -= closeButton.Width - continueButton.Width; + continueButton.Width = closeButton.Width; + } exceptionTextBox.Text = getClipboardString(); - ResourceManager resources = new ResourceManager("Resources.BitmapResources", Assembly.GetEntryAssembly()); - this.pictureBox.Image = (Bitmap)resources.GetObject("ErrorReport"); + try { + ResourceManager resources = new ResourceManager("Resources.BitmapResources", Assembly.GetEntryAssembly()); + this.pictureBox.Image = (Bitmap)resources.GetObject("ErrorReport"); + } catch {} } string getClipboardString() @@ -157,8 +164,8 @@ namespace ICSharpCode.SharpDevelop label.Size = new System.Drawing.Size(448, 48); label.TabIndex = 6; label.Text = "An unhandled exception has occurred in SharpDevelop. This is unexpected and we\'d " + - "ask you to help us improve SharpDevelop by reporting this error to the SharpDeve" + - "lop team."; + "ask you to help us improve SharpDevelop by reporting this error to the SharpDeve" + + "lop team."; continueButton = new System.Windows.Forms.Button(); // // continueButton diff --git a/src/Main/StartUp/Project/SharpDevelopMain.cs b/src/Main/StartUp/Project/SharpDevelopMain.cs index 5ef8c92407..58856a1f97 100644 --- a/src/Main/StartUp/Project/SharpDevelopMain.cs +++ b/src/Main/StartUp/Project/SharpDevelopMain.cs @@ -39,20 +39,39 @@ namespace ICSharpCode.SharpDevelop } } - static void ShowErrorBox(object sender, ThreadExceptionEventArgs eargs) + static void ShowErrorBox(object sender, ThreadExceptionEventArgs e) { - LoggingService.Error("ThreadException caught", eargs.Exception); - ShowErrorBox(eargs.Exception, null); + LoggingService.Error("ThreadException caught", e.Exception); + ShowErrorBox(e.Exception, null); + } + + static void ShowErrorBox(object sender, UnhandledExceptionEventArgs e) + { + Exception ex = e.ExceptionObject as Exception; + LoggingService.Fatal("UnhandledException caught", ex); + if (e.IsTerminating) + LoggingService.Fatal("Runtime is terminating because of unhandled exception."); + ShowErrorBox(ex, "Unhandled exception", e.IsTerminating); } static void ShowErrorBox(Exception exception, string message) { - using (ExceptionBox box = new ExceptionBox(exception, message)) { - DialogResult result = box.ShowDialog(ICSharpCode.SharpDevelop.Gui.WorkbenchSingleton.MainForm); - - if (result == DialogResult.Abort) { - Application.Exit(); + ShowErrorBox(exception, message, false); + } + + static void ShowErrorBox(Exception exception, string message, bool mustTerminate) + { + try { + using (ExceptionBox box = new ExceptionBox(exception, message, mustTerminate)) { + try { + box.ShowDialog(ICSharpCode.SharpDevelop.Gui.WorkbenchSingleton.MainForm); + } catch (InvalidOperationException) { + box.ShowDialog(); + } } + } catch (Exception ex) { + LoggingService.Warn("Error showing ExceptionBox", ex); + MessageBox.Show(exception.ToString()); } } @@ -80,7 +99,7 @@ namespace ICSharpCode.SharpDevelop { LoggingService.Fatal(ex); try { - Application.Run(new ExceptionBox(ex, "Unhandled exception terminated SharpDevelop")); + Application.Run(new ExceptionBox(ex, "Unhandled exception terminated SharpDevelop", true)); } catch { MessageBox.Show(ex.ToString(), "Critical error (cannot use ExceptionBox)"); } @@ -127,10 +146,12 @@ namespace ICSharpCode.SharpDevelop { LoggingService.Info("Starting SharpDevelop..."); try { + #if DEBUG if (!Debugger.IsAttached) { Application.ThreadException += ShowErrorBox; + AppDomain.CurrentDomain.UnhandledException += ShowErrorBox; } - #if !DEBUG + #else MessageService.CustomErrorReporter = ShowErrorBox; #endif