1
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
2
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
5
using System.Collections.Generic;
6
using System.Diagnostics;
9
using ICSharpCode.Core;
10
using ICSharpCode.SharpDevelop;
11
using ICSharpCode.SharpDevelop.Commands;
12
using ICSharpCode.SharpDevelop.Debugging;
13
using ICSharpCode.SharpDevelop.Dom;
14
using ICSharpCode.SharpDevelop.Gui;
15
using ICSharpCode.SharpDevelop.Project;
16
using ICSharpCode.SharpDevelop.Project.Commands;
17
using ICSharpCode.SharpDevelop.Util;
19
namespace ICSharpCode.UnitTesting
21
public abstract class AbstractRunTestCommand : AbstractMenuCommand
23
static MessageViewCategory testRunnerCategory;
24
static AbstractRunTestCommand runningTestCommand;
25
List<IProject> projects;
26
IProject currentProject;
27
TestResultsMonitor testResultsMonitor;
29
public AbstractRunTestCommand()
31
testResultsMonitor = new TestResultsMonitor();
32
testResultsMonitor.TestFinished += TestFinished;
36
/// Gets the running test command.
38
public static AbstractRunTestCommand RunningTestCommand {
40
return runningTestCommand;
45
/// Gets whether a test is currently running.
47
public static bool IsRunningTest {
49
return runningTestCommand != null;
53
public override void Run()
55
projects = new List<IProject>();
57
IMember m = TestableCondition.GetMember(Owner);
58
IClass c = (m != null) ? m.DeclaringType : TestableCondition.GetClass(Owner);
59
IProject project = TestableCondition.GetProject(Owner);
60
string namespaceFilter = TestableCondition.GetNamespace(Owner);
62
if (project != null) {
63
projects.Add(project);
64
} else if (UnitTestsPad.Instance != null) {
65
projects.AddRange(UnitTestsPad.Instance.TestTreeView.GetProjects());
68
if (projects.Count > 0) {
69
runningTestCommand = this;
73
currentProject = projects[0];
74
Run(currentProject, namespaceFilter, c, m);
77
runningTestCommand = null;
83
public static MessageViewCategory TestRunnerCategory {
85
if (testRunnerCategory == null) {
86
MessageViewCategory.Create(ref testRunnerCategory, "UnitTesting", "${res:ICSharpCode.NUnitPad.NUnitPadContent.PadName}");
88
return testRunnerCategory;
93
/// Stops running the tests.
97
runningTestCommand = null;
98
UpdateUnitTestsPadToolbar();
102
testResultsMonitor.Stop();
109
/// Called before all tests are run. If multiple projects are
110
/// to be tested this is called only once.
112
protected virtual void OnBeforeRunTests()
117
/// Called after all tests have been run even if there have
118
/// been errors. If multiple projects are to be tested this is called only once.
120
protected virtual void OnAfterRunTests()
124
protected abstract void RunTests(UnitTestApplicationStartHelper helper);
127
/// Called by derived classes when a single test run
130
protected void TestsFinished()
132
WorkbenchSingleton.AssertMainThread();
134
// Read the rest of the file just in case.
135
testResultsMonitor.Stop();
136
testResultsMonitor.Read();
139
projects.Remove(currentProject);
140
if (projects.Count > 0) {
141
currentProject = projects[0];
142
Run(currentProject, null, null, null);
144
runningTestCommand = null;
145
UpdateUnitTestsPadToolbar();
146
if (TaskService.SomethingWentWrong && ErrorListPad.ShowAfterBuild) {
154
/// Called by derived classes to show a single test result.
156
protected void ShowResult(TestResult result)
158
if (result.IsFailure || result.IsIgnored) {
159
TaskService.Add(CreateTask(result));
161
UpdateTestResult(result);
165
/// Called when the test run should be stopped.
167
protected virtual void OnStop()
172
/// Brings the specified pad to the front.
174
protected void ShowPad(PadDescriptor padDescriptor)
176
if (padDescriptor != null) {
177
WorkbenchSingleton.SafeThreadAsyncCall(padDescriptor.BringPadToFront);
182
/// Runs the tests after building the project under test.
184
void Run(IProject project, string namespaceFilter, IClass fixture, IMember test)
186
BuildProjectBeforeTestRun build = new BuildProjectBeforeTestRun(project);
187
build.BuildComplete += delegate {
188
OnBuildComplete(build.LastBuildResults, project, namespaceFilter, fixture, test);
193
void ShowUnitTestsPad()
195
ShowPad(WorkbenchSingleton.Workbench.GetPad(typeof(UnitTestsPad)));
198
void UpdateUnitTestsPadToolbar()
200
if (UnitTestsPad.Instance != null) {
201
UnitTestsPad.Instance.UpdateToolbar();
206
/// Sets the initial workbench state before starting
211
TaskService.BuildMessageViewCategory.ClearText();
212
TaskService.InUpdate = true;
213
TaskService.ClearExceptCommentTasks();
214
TaskService.InUpdate = false;
216
TestRunnerCategory.ClearText();
221
UpdateUnitTestsPadToolbar();
222
ResetAllTestResults();
228
/// Brings output pad to the front.
232
ShowPad(WorkbenchSingleton.Workbench.GetPad(typeof(CompilerMessageView)));
235
Task CreateTask(TestResult result)
237
TaskType taskType = TaskType.Warning;
238
FileLineReference lineRef = null;
239
string message = String.Empty;
241
if (result.IsFailure) {
242
taskType = TaskType.Error;
243
lineRef = OutputTextLineParser.GetNUnitOutputFileLineReference(result.StackTrace, true);
244
message = GetTestResultMessage(result, "${res:NUnitPad.NUnitPadContent.TestTreeView.TestFailedMessage}");
245
} else if (result.IsIgnored) {
246
message = GetTestResultMessage(result, "${res:NUnitPad.NUnitPadContent.TestTreeView.TestNotExecutedMessage}");
248
if (lineRef == null) {
249
lineRef = FindTest(result.Name);
251
if (lineRef != null) {
252
return new Task(FileName.Create(lineRef.FileName),
253
message, lineRef.Column, lineRef.Line, taskType);
255
return new Task(null, message, 0, 0, taskType);
259
/// Returns the test result message if there is on otherwise
260
/// uses the string resource to create a message.
262
string GetTestResultMessage(TestResult result, string stringResource)
264
if (result.Message.Length > 0) {
265
return result.Message;
267
return StringParser.Parse(stringResource, new string[,] {{"TestCase", result.Name}});
271
/// Returns the location of the specified test method in the
272
/// project being tested.
274
FileLineReference FindTest(string methodName)
276
TestProject testProject = GetTestProject(currentProject);
277
if (testProject != null) {
278
TestMethod method = testProject.TestClasses.GetTestMethod(methodName);
279
if (method != null) {
280
MemberResolveResult resolveResult = new MemberResolveResult(null, null, method.Method);
281
FilePosition filePos = resolveResult.GetDefinitionPosition();
282
return new FileLineReference(filePos.FileName, filePos.Line, filePos.Column);
290
ShowPad(WorkbenchSingleton.Workbench.GetPad(typeof(ErrorListPad)));
294
/// Runs the test for the project after a successful build.
296
void OnBuildComplete(BuildResults results, IProject project, string namespaceFilter, IClass fixture, IMember test)
298
if (results.ErrorCount == 0 && IsRunningTest) {
299
UnitTestApplicationStartHelper helper = new UnitTestApplicationStartHelper();
301
UnitTestingOptions options = new UnitTestingOptions();
302
helper.NoThread = options.NoThread;
303
helper.NoLogo = options.NoLogo;
304
helper.NoDots = options.NoDots;
305
helper.Labels = options.Labels;
306
helper.ShadowCopy = !options.NoShadow;
308
if (options.CreateXmlOutputFile) {
309
helper.XmlOutputFile = Path.Combine(Path.GetDirectoryName(project.OutputAssemblyFullPath), project.AssemblyName + "-TestResult.xml");
312
helper.Initialize(project, namespaceFilter, fixture, test);
313
helper.Results = Path.GetTempFileName();
315
ResetTestResults(project);
317
testResultsMonitor.FileName = helper.Results;
318
testResultsMonitor.Start();
330
if (TaskService.SomethingWentWrong && ErrorListPad.ShowAfterBuild) {
337
/// Clears the test results in the test tree view for the
338
/// project currently being tested.
340
void ResetTestResults(IProject project)
342
TestProject testProject = GetTestProject(project);
343
if (testProject != null) {
344
testProject.ResetTestResults();
349
/// Clears the test results in the test tree view for all the
350
/// displayed projects.
352
void ResetAllTestResults()
354
if (UnitTestsPad.Instance != null) {
355
UnitTestsPad.Instance.TestTreeView.ResetTestResults();
360
/// Gets the TestProject associated with the specified project
361
/// from the test tree view.
363
TestProject GetTestProject(IProject project)
365
if (UnitTestsPad.Instance != null) {
366
return UnitTestsPad.Instance.TestTreeView.GetTestProject(project);
372
/// Updates the test result in the test tree view.
374
void UpdateTestResult(TestResult result)
376
TestProject testProject = GetTestProject(currentProject);
377
if (testProject != null) {
378
testProject.UpdateTestResult(result);
382
void StopMonitoring()
385
File.Delete(testResultsMonitor.FileName);
388
testResultsMonitor.Dispose();
391
void TestFinished(object source, TestFinishedEventArgs e)
393
WorkbenchSingleton.SafeThreadAsyncCall(ShowResult, e.Result);
398
/// Custom build command that makes sure errors and warnings
399
/// are not cleared from the Errors list before every build since
400
/// we may be running multiple tests after each other.
402
public class BuildProjectBeforeTestRun : BuildProjectBeforeExecute
404
public BuildProjectBeforeTestRun(IProject targetProject)
405
: base(targetProject)
410
/// Before a build do not clear the tasks, just save any
413
public override void BeforeBuild()
415
SaveAllFiles.SaveAll();
419
public class RunTestInPadCommand : AbstractRunTestCommand
421
ProcessRunner runner;
423
public RunTestInPadCommand()
425
runner = new ProcessRunner();
426
runner.LogStandardOutputAndError = false;
427
runner.OutputLineReceived += OutputLineReceived;
428
runner.ProcessExited += ProcessExited;
431
protected override void RunTests(UnitTestApplicationStartHelper helper)
433
TestRunnerCategory.AppendLine(helper.GetCommandLine());
434
runner.Start(helper.UnitTestApplication, helper.GetArguments());
437
protected override void OnStop()
442
protected ProcessRunner GetProcessRunner()
447
void OutputLineReceived(object source, LineReceivedEventArgs e)
449
TestRunnerCategory.AppendLine(e.Line);
452
void ProcessExited(object source, EventArgs e)
454
WorkbenchSingleton.SafeThreadAsyncCall(TestsFinished);
457
void TestFinished(object source, TestFinishedEventArgs e)
459
WorkbenchSingleton.SafeThreadAsyncCall(ShowResult, e.Result);
463
public class RunTestWithDebuggerCommand : AbstractRunTestCommand
465
public override void Run()
467
if (DebuggerService.IsDebuggerLoaded && DebuggerService.CurrentDebugger.IsDebugging) {
468
if (MessageService.AskQuestion("${res:XML.MainMenu.RunMenu.Compile.StopDebuggingQuestion}",
469
"${res:XML.MainMenu.RunMenu.Compile.StopDebuggingTitle}"))
471
DebuggerService.CurrentDebugger.Stop();
479
protected override void RunTests(UnitTestApplicationStartHelper helper)
481
bool running = false;
484
TestRunnerCategory.AppendLine(helper.GetCommandLine());
485
ProcessStartInfo startInfo = new ProcessStartInfo(helper.UnitTestApplication);
486
startInfo.Arguments = helper.GetArguments();
487
startInfo.WorkingDirectory = UnitTestApplicationStartHelper.UnitTestApplicationDirectory;
488
DebuggerService.DebugStopped += DebuggerFinished;
489
DebuggerService.CurrentDebugger.Start(startInfo);
493
DebuggerService.DebugStopped -= DebuggerFinished;
498
protected override void OnStop()
500
if (DebuggerService.CurrentDebugger.IsDebugging) {
501
DebuggerService.CurrentDebugger.Stop();
505
void DebuggerFinished(object sender, EventArgs e)
507
DebuggerService.DebugStopped -= DebuggerFinished;
508
WorkbenchSingleton.SafeThreadAsyncCall(TestsFinished);
512
public class RunAllTestsInPadCommand : RunTestInPadCommand
514
public override void Run()
516
// To make sure all tests are run we set the Owner to null.
522
public class RunProjectTestsInPadCommand : RunTestInPadCommand, ITestTreeView
524
public override void Run()
530
public IMember SelectedMethod {
534
public IClass SelectedClass {
538
public IProject SelectedProject {
539
get { return ProjectService.CurrentProject; }
542
public string SelectedNamespace {