~halega/+junk/sharpdevelop

« back to all changes in this revision

Viewing changes to src/AddIns/Analysis/UnitTesting/Src/RunTestCommands.cs

  • Committer: sk
  • Date: 2011-09-10 05:17:57 UTC
  • Revision ID: halega@halega.com-20110910051757-qfouz1llya9m6boy
4.1.0.7915 Release Candidate 1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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)
 
3
 
 
4
using System;
 
5
using System.Collections.Generic;
 
6
using System.Diagnostics;
 
7
using System.IO;
 
8
 
 
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;
 
18
 
 
19
namespace ICSharpCode.UnitTesting
 
20
{
 
21
        public abstract class AbstractRunTestCommand : AbstractMenuCommand
 
22
        {
 
23
                static MessageViewCategory testRunnerCategory;
 
24
                static AbstractRunTestCommand runningTestCommand;
 
25
                List<IProject> projects;
 
26
                IProject currentProject;
 
27
                TestResultsMonitor testResultsMonitor;
 
28
                
 
29
                public AbstractRunTestCommand()
 
30
                {
 
31
                        testResultsMonitor = new TestResultsMonitor();
 
32
                        testResultsMonitor.TestFinished += TestFinished;
 
33
                }
 
34
                
 
35
                /// <summary>
 
36
                /// Gets the running test command.
 
37
                /// </summary>
 
38
                public static AbstractRunTestCommand RunningTestCommand {
 
39
                        get {
 
40
                                return runningTestCommand;
 
41
                        }
 
42
                }
 
43
                
 
44
                /// <summary>
 
45
                /// Gets whether a test is currently running.
 
46
                /// </summary>
 
47
                public static bool IsRunningTest {
 
48
                        get {
 
49
                                return runningTestCommand != null;
 
50
                        }
 
51
                }
 
52
                
 
53
                public override void Run()
 
54
                {
 
55
                        projects = new List<IProject>();
 
56
                        
 
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);
 
61
                        
 
62
                        if (project != null) {
 
63
                                projects.Add(project);
 
64
                        } else if (UnitTestsPad.Instance != null) {
 
65
                                projects.AddRange(UnitTestsPad.Instance.TestTreeView.GetProjects());
 
66
                        }
 
67
                        
 
68
                        if (projects.Count > 0) {
 
69
                                runningTestCommand = this;
 
70
                                try {
 
71
                                        BeforeRun();
 
72
                                        if (IsRunningTest) {
 
73
                                                currentProject = projects[0];
 
74
                                                Run(currentProject, namespaceFilter, c, m);
 
75
                                        }
 
76
                                } catch {
 
77
                                        runningTestCommand = null;
 
78
                                        throw;
 
79
                                }
 
80
                        }
 
81
                }
 
82
                
 
83
                public static MessageViewCategory TestRunnerCategory {
 
84
                        get {
 
85
                                if (testRunnerCategory == null) {
 
86
                                        MessageViewCategory.Create(ref testRunnerCategory, "UnitTesting", "${res:ICSharpCode.NUnitPad.NUnitPadContent.PadName}");
 
87
                                }
 
88
                                return testRunnerCategory;
 
89
                        }
 
90
                }
 
91
                
 
92
                /// <summary>
 
93
                /// Stops running the tests.
 
94
                /// </summary>
 
95
                public void Stop()
 
96
                {
 
97
                        runningTestCommand = null;
 
98
                        UpdateUnitTestsPadToolbar();
 
99
                        
 
100
                        projects.Clear();
 
101
                        
 
102
                        testResultsMonitor.Stop();
 
103
                        StopMonitoring();
 
104
 
 
105
                        OnStop();
 
106
                }
 
107
                
 
108
                /// <summary>
 
109
                /// Called before all tests are run. If multiple projects are
 
110
                /// to be tested this is called only once.
 
111
                /// </summary>
 
112
                protected virtual void OnBeforeRunTests()
 
113
                {
 
114
                }
 
115
                
 
116
                /// <summary>
 
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.
 
119
                /// </summary>
 
120
                protected virtual void OnAfterRunTests()
 
121
                {
 
122
                }
 
123
 
 
124
                protected abstract void RunTests(UnitTestApplicationStartHelper helper);
 
125
                
 
126
                /// <summary>
 
127
                /// Called by derived classes when a single test run
 
128
                /// is finished.
 
129
                /// </summary>
 
130
                protected void TestsFinished()
 
131
                {
 
132
                        WorkbenchSingleton.AssertMainThread();
 
133
                        
 
134
                        // Read the rest of the file just in case.
 
135
                        testResultsMonitor.Stop();
 
136
                        testResultsMonitor.Read();
 
137
                        StopMonitoring();
 
138
                        
 
139
                        projects.Remove(currentProject);
 
140
                        if (projects.Count > 0) {
 
141
                                currentProject = projects[0];
 
142
                                Run(currentProject, null, null, null);
 
143
                        } else {
 
144
                                runningTestCommand = null;
 
145
                                UpdateUnitTestsPadToolbar();
 
146
                                if (TaskService.SomethingWentWrong && ErrorListPad.ShowAfterBuild) {
 
147
                                        ShowErrorList();
 
148
                                }
 
149
                                OnAfterRunTests();
 
150
                        }
 
151
                }
 
152
                
 
153
                /// <summary>
 
154
                /// Called by derived classes to show a single test result.
 
155
                /// </summary>
 
156
                protected void ShowResult(TestResult result)
 
157
                {
 
158
                        if (result.IsFailure || result.IsIgnored) {
 
159
                                TaskService.Add(CreateTask(result));
 
160
                        }
 
161
                        UpdateTestResult(result);
 
162
                }
 
163
                
 
164
                /// <summary>
 
165
                /// Called when the test run should be stopped.
 
166
                /// </summary>
 
167
                protected virtual void OnStop()
 
168
                {
 
169
                }
 
170
                
 
171
                /// <summary>
 
172
                /// Brings the specified pad to the front.
 
173
                /// </summary>
 
174
                protected void ShowPad(PadDescriptor padDescriptor)
 
175
                {
 
176
                        if (padDescriptor != null) {
 
177
                                WorkbenchSingleton.SafeThreadAsyncCall(padDescriptor.BringPadToFront);
 
178
                        }
 
179
                }
 
180
                
 
181
                /// <summary>
 
182
                /// Runs the tests after building the project under test.
 
183
                /// </summary>
 
184
                void Run(IProject project, string namespaceFilter, IClass fixture, IMember test)
 
185
                {
 
186
                        BuildProjectBeforeTestRun build = new BuildProjectBeforeTestRun(project);
 
187
                        build.BuildComplete += delegate {
 
188
                                OnBuildComplete(build.LastBuildResults, project, namespaceFilter, fixture, test);
 
189
                        };
 
190
                        build.Run();
 
191
                }
 
192
                
 
193
                void ShowUnitTestsPad()
 
194
                {
 
195
                        ShowPad(WorkbenchSingleton.Workbench.GetPad(typeof(UnitTestsPad)));
 
196
                }
 
197
                
 
198
                void UpdateUnitTestsPadToolbar()
 
199
                {
 
200
                        if (UnitTestsPad.Instance != null) {
 
201
                                UnitTestsPad.Instance.UpdateToolbar();
 
202
                        }
 
203
                }
 
204
                
 
205
                /// <summary>
 
206
                /// Sets the initial workbench state before starting
 
207
                /// a test run.
 
208
                /// </summary>
 
209
                void BeforeRun()
 
210
                {
 
211
                        TaskService.BuildMessageViewCategory.ClearText();
 
212
                        TaskService.InUpdate = true;
 
213
                        TaskService.ClearExceptCommentTasks();
 
214
                        TaskService.InUpdate = false;
 
215
                        
 
216
                        TestRunnerCategory.ClearText();
 
217
                        
 
218
                        ShowUnitTestsPad();
 
219
                        ShowOutputPad();
 
220
                        
 
221
                        UpdateUnitTestsPadToolbar();
 
222
                        ResetAllTestResults();
 
223
                        
 
224
                        OnBeforeRunTests();
 
225
                }
 
226
                
 
227
                /// <summary>
 
228
                /// Brings output pad to the front.
 
229
                /// </summary>
 
230
                void ShowOutputPad()
 
231
                {
 
232
                        ShowPad(WorkbenchSingleton.Workbench.GetPad(typeof(CompilerMessageView)));
 
233
                }
 
234
                
 
235
                Task CreateTask(TestResult result)
 
236
                {
 
237
                        TaskType taskType = TaskType.Warning;
 
238
                        FileLineReference lineRef = null;
 
239
                        string message = String.Empty;
 
240
                        
 
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}");
 
247
                        }
 
248
                        if (lineRef == null) {
 
249
                                lineRef = FindTest(result.Name);
 
250
                        }
 
251
                        if (lineRef != null) {
 
252
                                return new Task(FileName.Create(lineRef.FileName),
 
253
                                                message, lineRef.Column, lineRef.Line, taskType);
 
254
                        }
 
255
                        return new Task(null, message, 0, 0, taskType);
 
256
                }
 
257
                
 
258
                /// <summary>
 
259
                /// Returns the test result message if there is on otherwise
 
260
                /// uses the string resource to create a message.
 
261
                /// </summary>
 
262
                string GetTestResultMessage(TestResult result, string stringResource)
 
263
                {
 
264
                        if (result.Message.Length > 0) {
 
265
                                return result.Message;
 
266
                        }
 
267
                        return StringParser.Parse(stringResource, new string[,] {{"TestCase", result.Name}});
 
268
                }
 
269
                
 
270
                /// <summary>
 
271
                /// Returns the location of the specified test method in the
 
272
                /// project being tested.
 
273
                /// </summary>
 
274
                FileLineReference FindTest(string methodName)
 
275
                {
 
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);
 
283
                                }
 
284
                        }
 
285
                        return null;
 
286
                }
 
287
                
 
288
                void ShowErrorList()
 
289
                {
 
290
                        ShowPad(WorkbenchSingleton.Workbench.GetPad(typeof(ErrorListPad)));
 
291
                }
 
292
                
 
293
                /// <summary>
 
294
                /// Runs the test for the project after a successful build.
 
295
                /// </summary>
 
296
                void OnBuildComplete(BuildResults results, IProject project, string namespaceFilter, IClass fixture, IMember test)
 
297
                {
 
298
                        if (results.ErrorCount == 0 && IsRunningTest) {
 
299
                                UnitTestApplicationStartHelper helper = new UnitTestApplicationStartHelper();
 
300
                                
 
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;
 
307
                                
 
308
                                if (options.CreateXmlOutputFile) {
 
309
                                        helper.XmlOutputFile = Path.Combine(Path.GetDirectoryName(project.OutputAssemblyFullPath), project.AssemblyName + "-TestResult.xml");
 
310
                                }
 
311
                                
 
312
                                helper.Initialize(project, namespaceFilter, fixture, test);
 
313
                                helper.Results = Path.GetTempFileName();
 
314
                                
 
315
                                ResetTestResults(project);
 
316
 
 
317
                                testResultsMonitor.FileName = helper.Results;
 
318
                                testResultsMonitor.Start();
 
319
                                
 
320
                                try {
 
321
                                        RunTests(helper);
 
322
                                } catch {
 
323
                                        StopMonitoring();
 
324
                                        throw;
 
325
                                }
 
326
                        } else {
 
327
                                if (IsRunningTest) {
 
328
                                        Stop();
 
329
                                }
 
330
                                if (TaskService.SomethingWentWrong && ErrorListPad.ShowAfterBuild) {
 
331
                                        ShowErrorList();
 
332
                                }
 
333
                        }
 
334
                }
 
335
                
 
336
                /// <summary>
 
337
                /// Clears the test results in the test tree view for the
 
338
                /// project currently being tested.
 
339
                /// </summary>
 
340
                void ResetTestResults(IProject project)
 
341
                {
 
342
                        TestProject testProject = GetTestProject(project);
 
343
                        if (testProject != null) {
 
344
                                testProject.ResetTestResults();
 
345
                        }
 
346
                }
 
347
                
 
348
                /// <summary>
 
349
                /// Clears the test results in the test tree view for all the
 
350
                /// displayed projects.
 
351
                /// </summary>
 
352
                void ResetAllTestResults()
 
353
                {
 
354
                        if (UnitTestsPad.Instance != null) {
 
355
                                UnitTestsPad.Instance.TestTreeView.ResetTestResults();
 
356
                        }
 
357
                }
 
358
                
 
359
                /// <summary>
 
360
                /// Gets the TestProject associated with the specified project
 
361
                /// from the test tree view.
 
362
                /// </summary>
 
363
                TestProject GetTestProject(IProject project)
 
364
                {
 
365
                        if (UnitTestsPad.Instance != null) {
 
366
                                return UnitTestsPad.Instance.TestTreeView.GetTestProject(project);
 
367
                        }
 
368
                        return null;
 
369
                }
 
370
                
 
371
                /// <summary>
 
372
                /// Updates the test result in the test tree view.
 
373
                /// </summary>
 
374
                void UpdateTestResult(TestResult result)
 
375
                {
 
376
                        TestProject testProject = GetTestProject(currentProject);
 
377
                        if (testProject != null) {
 
378
                                testProject.UpdateTestResult(result);
 
379
                        }
 
380
                }
 
381
                
 
382
                void StopMonitoring()
 
383
                {
 
384
                        try {
 
385
                                File.Delete(testResultsMonitor.FileName);
 
386
                        } catch { }
 
387
                        
 
388
                        testResultsMonitor.Dispose();
 
389
                }
 
390
                
 
391
                void TestFinished(object source, TestFinishedEventArgs e)
 
392
                {
 
393
                        WorkbenchSingleton.SafeThreadAsyncCall(ShowResult, e.Result);
 
394
                }
 
395
        }
 
396
        
 
397
        /// <summary>
 
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.
 
401
        /// </summary>
 
402
        public class BuildProjectBeforeTestRun : BuildProjectBeforeExecute
 
403
        {
 
404
                public BuildProjectBeforeTestRun(IProject targetProject)
 
405
                        : base(targetProject)
 
406
                {
 
407
                }
 
408
                
 
409
                /// <summary>
 
410
                /// Before a build do not clear the tasks, just save any
 
411
                /// dirty files.
 
412
                /// </summary>
 
413
                public override void BeforeBuild()
 
414
                {
 
415
                        SaveAllFiles.SaveAll();
 
416
                }
 
417
        }
 
418
        
 
419
        public class RunTestInPadCommand : AbstractRunTestCommand
 
420
        {
 
421
                ProcessRunner runner;
 
422
                
 
423
                public RunTestInPadCommand()
 
424
                {
 
425
                        runner = new ProcessRunner();
 
426
                        runner.LogStandardOutputAndError = false;
 
427
                        runner.OutputLineReceived += OutputLineReceived;
 
428
                        runner.ProcessExited += ProcessExited;
 
429
                }
 
430
                
 
431
                protected override void RunTests(UnitTestApplicationStartHelper helper)
 
432
                {
 
433
                        TestRunnerCategory.AppendLine(helper.GetCommandLine());
 
434
                        runner.Start(helper.UnitTestApplication, helper.GetArguments());
 
435
                }
 
436
                
 
437
                protected override void OnStop()
 
438
                {
 
439
                        runner.Kill();
 
440
                }
 
441
                
 
442
                protected ProcessRunner GetProcessRunner()
 
443
                {
 
444
                        return runner;
 
445
                }
 
446
                
 
447
                void OutputLineReceived(object source, LineReceivedEventArgs e)
 
448
                {
 
449
                        TestRunnerCategory.AppendLine(e.Line);
 
450
                }
 
451
                
 
452
                void ProcessExited(object source, EventArgs e)
 
453
                {
 
454
                        WorkbenchSingleton.SafeThreadAsyncCall(TestsFinished);
 
455
                }
 
456
                
 
457
                void TestFinished(object source, TestFinishedEventArgs e)
 
458
                {
 
459
                        WorkbenchSingleton.SafeThreadAsyncCall(ShowResult, e.Result);
 
460
                }
 
461
        }
 
462
        
 
463
        public class RunTestWithDebuggerCommand : AbstractRunTestCommand
 
464
        {
 
465
                public override void Run()
 
466
                {
 
467
                        if (DebuggerService.IsDebuggerLoaded && DebuggerService.CurrentDebugger.IsDebugging) {
 
468
                                if (MessageService.AskQuestion("${res:XML.MainMenu.RunMenu.Compile.StopDebuggingQuestion}",
 
469
                                                               "${res:XML.MainMenu.RunMenu.Compile.StopDebuggingTitle}"))
 
470
                                {
 
471
                                        DebuggerService.CurrentDebugger.Stop();
 
472
                                        base.Run();
 
473
                                }
 
474
                        } else {
 
475
                                base.Run();
 
476
                        }
 
477
                }
 
478
                
 
479
                protected override void RunTests(UnitTestApplicationStartHelper helper)
 
480
                {
 
481
                        bool running = false;
 
482
                        
 
483
                        try {
 
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);
 
490
                                running = true;
 
491
                        } finally {
 
492
                                if (!running) {
 
493
                                        DebuggerService.DebugStopped -= DebuggerFinished;
 
494
                                }
 
495
                        }
 
496
                }
 
497
                
 
498
                protected override void OnStop()
 
499
                {
 
500
                        if (DebuggerService.CurrentDebugger.IsDebugging) {
 
501
                                DebuggerService.CurrentDebugger.Stop();
 
502
                        }
 
503
                }
 
504
                
 
505
                void DebuggerFinished(object sender, EventArgs e)
 
506
                {
 
507
                        DebuggerService.DebugStopped -= DebuggerFinished;
 
508
                        WorkbenchSingleton.SafeThreadAsyncCall(TestsFinished);
 
509
                }
 
510
        }
 
511
                
 
512
        public class RunAllTestsInPadCommand : RunTestInPadCommand
 
513
        {
 
514
                public override void Run()
 
515
                {
 
516
                        // To make sure all tests are run we set the Owner to null.
 
517
                        Owner = null;
 
518
                        base.Run();
 
519
                }
 
520
        }
 
521
        
 
522
        public class RunProjectTestsInPadCommand : RunTestInPadCommand, ITestTreeView
 
523
        {
 
524
                public override void Run()
 
525
                {
 
526
                        Owner = this;
 
527
                        base.Run();
 
528
                }
 
529
                
 
530
                public IMember SelectedMethod {
 
531
                        get { return null; }
 
532
                }
 
533
                
 
534
                public IClass SelectedClass {
 
535
                        get { return null; }
 
536
                }
 
537
                
 
538
                public IProject SelectedProject {
 
539
                        get { return ProjectService.CurrentProject; }
 
540
                }
 
541
                
 
542
                public string SelectedNamespace {
 
543
                        get { return null; }
 
544
                }
 
545
        }
 
546
}