Browse Source

fix build

newNRvisualizers
Siegfried Pammer 13 years ago
parent
commit
b8b10ca157
  1. 398
      src/AddIns/Analysis/UnitTesting/Commands/AbstractRunTestCommand.cs
  2. 26
      src/AddIns/Analysis/UnitTesting/Commands/RunAllTestsInPadCommand.cs
  3. 546
      src/AddIns/Analysis/UnitTesting/Commands/RunTestCommands.cs
  4. 26
      src/AddIns/Analysis/UnitTesting/Commands/RunTestInPadCommand.cs
  5. 26
      src/AddIns/Analysis/UnitTesting/Commands/RunTestWithDebuggerCommand.cs
  6. 53
      src/AddIns/Analysis/UnitTesting/Gui/EmptyUnitTestsPad.cs
  7. 33
      src/AddIns/Analysis/UnitTesting/Gui/UnitTestingOptionsPanel.xaml
  8. 15
      src/AddIns/Analysis/UnitTesting/Gui/UnitTestingOptionsPanel.xaml.cs
  9. 348
      src/AddIns/Analysis/UnitTesting/Gui/UnitTestsPad.cs
  10. 17
      src/AddIns/Analysis/UnitTesting/Implementation/UnitTestAddInTree.cs
  11. 15
      src/AddIns/Analysis/UnitTesting/Implementation/UnitTestBuildOptions.cs
  12. 18
      src/AddIns/Analysis/UnitTesting/Implementation/UnitTestBuildProjectFactory.cs
  13. 19
      src/AddIns/Analysis/UnitTesting/Implementation/UnitTestDebuggerService.cs
  14. 27
      src/AddIns/Analysis/UnitTesting/Implementation/UnitTestFileService.cs
  15. 15
      src/AddIns/Analysis/UnitTesting/Implementation/UnitTestSaveAllFilesCommand.cs
  16. 40
      src/AddIns/Analysis/UnitTesting/Implementation/UnitTestTaskService.cs
  17. 27
      src/AddIns/Analysis/UnitTesting/Implementation/UnitTestWorkbench.cs
  18. 13
      src/AddIns/Analysis/UnitTesting/Interfaces/IAddInTree.cs
  19. 12
      src/AddIns/Analysis/UnitTesting/Interfaces/IBuildOptions.cs
  20. 15
      src/AddIns/Analysis/UnitTesting/Interfaces/IBuildProjectFactory.cs
  21. 12
      src/AddIns/Analysis/UnitTesting/Interfaces/IFileSystem.cs
  22. 25
      src/AddIns/Analysis/UnitTesting/Interfaces/IRegisteredTestFrameworks.cs
  23. 25
      src/AddIns/Analysis/UnitTesting/Interfaces/IRunTestCommandContext.cs
  24. 24
      src/AddIns/Analysis/UnitTesting/Interfaces/ITestFramework.cs
  25. 12
      src/AddIns/Analysis/UnitTesting/Interfaces/ITestFrameworkFactory.cs
  26. 20
      src/AddIns/Analysis/UnitTesting/Interfaces/ITestResultsMonitor.cs
  27. 17
      src/AddIns/Analysis/UnitTesting/Interfaces/ITestRunner.cs
  28. 34
      src/AddIns/Analysis/UnitTesting/Interfaces/ITestTreeView.cs
  29. 14
      src/AddIns/Analysis/UnitTesting/Interfaces/IUnitTestDebuggerService.cs
  30. 13
      src/AddIns/Analysis/UnitTesting/Interfaces/IUnitTestFileService.cs
  31. 24
      src/AddIns/Analysis/UnitTesting/Interfaces/IUnitTestProcessRunner.cs
  32. 12
      src/AddIns/Analysis/UnitTesting/Interfaces/IUnitTestSaveAllFilesCommand.cs
  33. 20
      src/AddIns/Analysis/UnitTesting/Interfaces/IUnitTestTaskService.cs
  34. 15
      src/AddIns/Analysis/UnitTesting/Interfaces/IUnitTestWorkbench.cs
  35. 18
      src/AddIns/Analysis/UnitTesting/Interfaces/IUnitTestsPad.cs
  36. 47
      src/AddIns/Analysis/UnitTesting/MultipleProjectBuildable.cs
  37. 249
      src/AddIns/Analysis/UnitTesting/NUnit/NUnitConsoleCommandLine.cs
  38. 44
      src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestDebugger.cs
  39. 131
      src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestFramework.cs
  40. 37
      src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestResult.cs
  41. 38
      src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestRunner.cs
  42. 102
      src/AddIns/Analysis/UnitTesting/Nodes/AllTestsTreeNode.cs
  43. 39
      src/AddIns/Analysis/UnitTesting/Nodes/ProjectUnitTestNode.cs
  44. 70
      src/AddIns/Analysis/UnitTesting/Nodes/RootUnitTestNode.cs
  45. 98
      src/AddIns/Analysis/UnitTesting/Nodes/TestClassTreeNode.cs
  46. 53
      src/AddIns/Analysis/UnitTesting/Nodes/TestMemberTreeNode.cs
  47. 55
      src/AddIns/Analysis/UnitTesting/Nodes/TestMethodTreeNode.cs
  48. 336
      src/AddIns/Analysis/UnitTesting/Nodes/TestNamespaceTreeNode.cs
  49. 113
      src/AddIns/Analysis/UnitTesting/Nodes/TestProjectTreeNode.cs
  50. 103
      src/AddIns/Analysis/UnitTesting/Nodes/TestTreeNode.cs
  51. 44
      src/AddIns/Analysis/UnitTesting/Nodes/UnitTestBaseNode.cs
  52. 51
      src/AddIns/Analysis/UnitTesting/RemovedClasses.cs
  53. 23
      src/AddIns/Analysis/UnitTesting/Resources/Images.cs
  54. 42
      src/AddIns/Analysis/UnitTesting/RunProjectTestsInPadCommand.cs
  55. 68
      src/AddIns/Analysis/UnitTesting/RunTestCommandContext.cs
  56. 47
      src/AddIns/Analysis/UnitTesting/Service/InnerClassEnumerator.cs
  57. 23
      src/AddIns/Analysis/UnitTesting/Service/MessageReceivedEventArgs.cs
  58. 116
      src/AddIns/Analysis/UnitTesting/Service/RegisteredTestFrameworks.cs
  59. 19
      src/AddIns/Analysis/UnitTesting/Service/RunningTestsCondition.cs
  60. 96
      src/AddIns/Analysis/UnitTesting/Service/SelectedTests.cs
  61. 383
      src/AddIns/Analysis/UnitTesting/Service/TestClass.cs
  62. 214
      src/AddIns/Analysis/UnitTesting/Service/TestClassCollection.cs
  63. 30
      src/AddIns/Analysis/UnitTesting/Service/TestClassEventArgs.cs
  64. 108
      src/AddIns/Analysis/UnitTesting/Service/TestDebuggerBase.cs
  65. 23
      src/AddIns/Analysis/UnitTesting/Service/TestFinishedEventArgs.cs
  66. 90
      src/AddIns/Analysis/UnitTesting/Service/TestFrameworkDescriptor.cs
  67. 30
      src/AddIns/Analysis/UnitTesting/Service/TestFrameworkDoozer.cs
  68. 23
      src/AddIns/Analysis/UnitTesting/Service/TestFrameworkFactory.cs
  69. 155
      src/AddIns/Analysis/UnitTesting/Service/TestMember.cs
  70. 153
      src/AddIns/Analysis/UnitTesting/Service/TestMemberCollection.cs
  71. 30
      src/AddIns/Analysis/UnitTesting/Service/TestMemberEventArgs.cs
  72. 298
      src/AddIns/Analysis/UnitTesting/Service/TestProject.cs
  73. 92
      src/AddIns/Analysis/UnitTesting/Service/TestResult.cs
  74. 68
      src/AddIns/Analysis/UnitTesting/Service/TestRunnerBase.cs
  75. 47
      src/AddIns/Analysis/UnitTesting/Service/TestService.cs
  76. 121
      src/AddIns/Analysis/UnitTesting/Service/TestableCondition.cs
  77. 50
      src/AddIns/Analysis/UnitTesting/TestAttributeName.cs
  78. 109
      src/AddIns/Analysis/UnitTesting/TestProcessRunnerBase.cs
  79. 52
      src/AddIns/Analysis/UnitTesting/TestProcessRunnerBaseContext.cs
  80. 97
      src/AddIns/Analysis/UnitTesting/TestResultTask.cs
  81. 178
      src/AddIns/Analysis/UnitTesting/TestResultsMonitor.cs
  82. 155
      src/AddIns/Analysis/UnitTesting/TestResultsReader.cs
  83. 434
      src/AddIns/Analysis/UnitTesting/TestTreeView.cs
  84. 221
      src/AddIns/Analysis/UnitTesting/UnitTestApplicationStartHelper.cs
  85. 118
      src/AddIns/Analysis/UnitTesting/UnitTestCommands.cs
  86. 58
      src/AddIns/Analysis/UnitTesting/UnitTestProcessRunner.cs
  87. 118
      src/AddIns/Analysis/UnitTesting/UnitTestingOptions.cs
  88. 4
      src/AddIns/Analysis/UnitTesting/license.txt

398
src/AddIns/Analysis/UnitTesting/Commands/AbstractRunTestCommand.cs

@ -0,0 +1,398 @@ @@ -0,0 +1,398 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.SharpDevelop.Project.Commands;
namespace ICSharpCode.UnitTesting
{
public abstract class AbstractRunTestCommand : AbstractMenuCommand
{
static AbstractRunTestCommand runningTestCommand;
IUnitTestsPad unitTestsPad;
SelectedTests selectedTests;
IRunTestCommandContext context;
ITestRunner testRunner;
IProgressMonitor testProgressMonitor;
int totalProjectCount;
public AbstractRunTestCommand()
: this(new RunTestCommandContext())
{
}
public AbstractRunTestCommand(IRunTestCommandContext context)
{
this.context = context;
}
protected IRunTestCommandContext Context {
get { return context; }
}
/// <summary>
/// Gets the running test command.
/// </summary>
public static AbstractRunTestCommand RunningTestCommand {
get { return runningTestCommand; }
set { runningTestCommand = value; }
}
/// <summary>
/// Gets whether a test is currently running.
/// </summary>
public static bool IsRunningTest {
get { return runningTestCommand != null; }
}
public override void Run()
{
GetUnitTestsPad();
selectedTests = GetSelectedTests();
if (selectedTests.HasProjects) {
runningTestCommand = this;
BeforeRun();
BuildAndRunTests();
}
}
SelectedTests GetSelectedTests()
{
return new SelectedTests(Owner, unitTestsPad.GetProjects());
}
void GetUnitTestsPad()
{
unitTestsPad = context.OpenUnitTestsPad;
if (unitTestsPad == null) {
unitTestsPad = CreateEmptyUnitTestsPad();
}
}
EmptyUnitTestsPad CreateEmptyUnitTestsPad()
{
return new EmptyUnitTestsPad(context.OpenSolution);
}
/// <summary>
/// Sets the initial workbench state before starting
/// a test run.
/// </summary>
void BeforeRun()
{
ClearTasks();
ClearUnitTestCategoryText();
ShowUnitTestsPad();
ShowOutputPad();
UpdateUnitTestsPadToolbar();
ResetAllTestResultsInUnitTestsPad();
OnBeforeBuild();
}
void BuildAndRunTests()
{
if (IsBuildNeededBeforeTestRun()) {
BuildProjectBeforeRunningTests(selectedTests);
} else {
context.SaveAllFilesCommand.SaveAllFiles();
RunTests(selectedTests);
}
}
bool IsBuildNeededBeforeTestRun()
{
return selectedTests.Projects.Any(p => context.RegisteredTestFrameworks.IsBuildNeededBeforeTestRunForProject(p));
}
void ClearTasks()
{
context.TaskService.BuildMessageViewCategory.ClearText();
context.TaskService.InUpdate = true;
context.TaskService.ClearExceptCommentTasks();
context.TaskService.InUpdate = false;
}
void ClearUnitTestCategoryText()
{
context.UnitTestCategory.ClearText();
}
void ShowUnitTestsPad()
{
unitTestsPad.BringToFront();
}
void StopProgressMonitor()
{
if (testProgressMonitor != null) {
testProgressMonitor.Dispose();
testProgressMonitor = null;
}
}
void UpdateUnitTestsPadToolbar()
{
unitTestsPad.UpdateToolbar();
}
/// <summary>
/// Called before the build is started (even if no build needs to be performed).
/// If multiple projects are to be tested this is called only once.
/// </summary>
protected virtual void OnBeforeBuild()
{
}
/// <summary>
/// Called before all tests are run (after the build has finished successfully).
/// If multiple projects are to be tested this is called only once.
/// </summary>
protected virtual void OnBeforeRunTests()
{
}
/// <summary>
/// Runs the tests after building the project(s) under test.
/// </summary>
void BuildProjectBeforeRunningTests(SelectedTests selectedTests)
{
BuildProject build = CreateBuildProjectBeforeTestRun(selectedTests);
build.BuildComplete += delegate {
OnBuildComplete(build.LastBuildResults, selectedTests);
};
build.Run();
}
BuildProject CreateBuildProjectBeforeTestRun(SelectedTests selectedTests)
{
IEnumerable<IProject> projects = GetProjectsRequiringBuildBeforeTestRun(selectedTests);
return context.BuildProjectFactory.CreateBuildProjectBeforeTestRun(projects);
}
IEnumerable<IProject> GetProjectsRequiringBuildBeforeTestRun(SelectedTests selectedTests)
{
return selectedTests
.Projects
.Where(p => context.RegisteredTestFrameworks.IsBuildNeededBeforeTestRunForProject(p));
}
/// <summary>
/// Stops running the tests.
/// </summary>
public void Stop()
{
StopActiveTestRunner();
runningTestCommand = null;
UpdateUnitTestsPadToolbar();
OnStop();
}
void StopActiveTestRunner()
{
if (testRunner != null) {
testRunner.Stop();
testRunner.Dispose();
testRunner = null;
}
}
/// <summary>
/// Called after all tests have been run even if there have
/// been errors. If multiple projects are to be tested this is called only once.
/// </summary>
protected virtual void OnAfterRunTests()
{
}
/// <summary>
/// Called by derived classes when a single test run
/// is finished.
/// </summary>
protected void TestRunCompleted()
{
StopActiveTestRunner();
selectedTests.RemoveFirstProject();
if (selectedTests.HasProjects) {
RunTests(selectedTests);
} else {
StopProgressMonitor();
runningTestCommand = null;
UpdateUnitTestsPadToolbar();
ShowErrorList();
OnAfterRunTests();
}
}
void TestFinished(object source, TestFinishedEventArgs e)
{
context.Workbench.SafeThreadAsyncCall(ShowResult, e.Result);
}
protected void ShowResult(TestResult result)
{
if (IsTestResultFailureOrIsIgnored(result)) {
AddTaskForTestResult(result);
UpdateProgressMonitorStatus(result);
}
UpdateTestResult(result);
}
bool IsTestResultFailureOrIsIgnored(TestResult result)
{
return result.IsFailure || result.IsIgnored;
}
void AddTaskForTestResult(TestResult testResult)
{
TestProject project = GetTestProjectForProject(selectedTests.Project);
SDTask task = TestResultTask.Create(testResult, project);
context.TaskService.Add(task);
}
/// <summary>
/// Called when the test run should be stopped.
/// </summary>
protected virtual void OnStop()
{
}
void ShowOutputPad()
{
ShowPad(context.Workbench.GetPad(typeof(CompilerMessageView)));
}
protected void ShowPad(PadDescriptor padDescriptor)
{
context.Workbench.SafeThreadAsyncCall(padDescriptor.BringPadToFront);
}
void ShowErrorList()
{
if (HasErrorsThatShouldBeDisplayed()) {
ShowPad(context.Workbench.GetPad(typeof(ErrorListPad)));
}
}
bool HasErrorsThatShouldBeDisplayed()
{
return context.TaskService.SomethingWentWrong &&
context.BuildOptions.ShowErrorListAfterBuild;
}
/// <summary>
/// Runs the test for the project after a successful build.
/// </summary>
void OnBuildComplete(BuildResults results, SelectedTests selectedTests)
{
if (BuildHasNoErrorsAndStillRunningTests(results)) {
RunTests(selectedTests);
} else {
if (IsRunningTest) {
Stop();
}
ShowErrorList();
}
}
bool BuildHasNoErrorsAndStillRunningTests(BuildResults results)
{
return (results.ErrorCount == 0) && IsRunningTest;
}
void RunTests(SelectedTests selectedTests)
{
if (testProgressMonitor == null) {
OnBeforeRunTests();
testProgressMonitor = context.StatusBarService.CreateProgressMonitor();
totalProjectCount = selectedTests.ProjectsCount;
}
testProgressMonitor.TaskName = GetProgressMonitorLabel(selectedTests.Project);
testProgressMonitor.Progress = GetProgress(selectedTests.ProjectsCount);
testRunner = CreateTestRunner(selectedTests.Project);
if (testRunner != null) {
StartTestRunner();
}
}
void StartTestRunner()
{
testRunner.MessageReceived += TestRunnerMessageReceived;
testRunner.AllTestsFinished += AllTestsFinished;
testRunner.TestFinished += TestFinished;
testRunner.Start(selectedTests);
}
string GetProgressMonitorLabel(IProject project)
{
StringTagPair tagPair = new StringTagPair("Name", project.Name);
return StringParser.Parse("${res:ICSharpCode.UnitTesting.StatusBarProgressLabel}", tagPair);
}
double GetProgress(int projectsLeftToRunCount)
{
return (double)(totalProjectCount - projectsLeftToRunCount) / totalProjectCount;
}
protected virtual ITestRunner CreateTestRunner(IProject project)
{
return null;
}
protected virtual void TestRunnerMessageReceived(object source, MessageReceivedEventArgs e)
{
context.UnitTestCategory.AppendLine(e.Message);
}
void AllTestsFinished(object source, EventArgs e)
{
context.Workbench.SafeThreadAsyncCall(TestRunCompleted);
}
/// <summary>
/// Clears the test results in the test tree view for all the
/// displayed projects.
/// </summary>
void ResetAllTestResultsInUnitTestsPad()
{
unitTestsPad.ResetTestResults();
}
TestProject GetTestProjectForProject(IProject project)
{
return unitTestsPad.GetTestProject(project);
}
void UpdateTestResult(TestResult result)
{
TestProject testProject = GetTestProjectForProject(selectedTests.Project);
if (testProject != null) {
testProject.UpdateTestResult(result);
}
}
void UpdateProgressMonitorStatus(TestResult result)
{
if (testProgressMonitor != null) {
if (result.IsFailure) {
testProgressMonitor.Status = OperationStatus.Error;
} else if (result.IsIgnored && testProgressMonitor.Status == OperationStatus.Normal) {
testProgressMonitor.Status = OperationStatus.Warning;
}
}
}
}
}

26
src/AddIns/Analysis/UnitTesting/Commands/RunAllTestsInPadCommand.cs

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
namespace ICSharpCode.UnitTesting
{
public class RunAllTestsInPadCommand : RunTestInPadCommand
{
public RunAllTestsInPadCommand()
{
}
public RunAllTestsInPadCommand(IRunTestCommandContext context)
: base(context)
{
}
public override void Run()
{
// To make sure all tests are run we set the Owner to null.
Owner = null;
base.Run();
}
}
}

546
src/AddIns/Analysis/UnitTesting/Commands/RunTestCommands.cs

@ -0,0 +1,546 @@ @@ -0,0 +1,546 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Commands;
using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.SharpDevelop.Project.Commands;
using ICSharpCode.SharpDevelop.Util;
namespace ICSharpCode.UnitTesting
{
public abstract class AbstractRunTestCommand : AbstractMenuCommand
{
static MessageViewCategory testRunnerCategory;
static AbstractRunTestCommand runningTestCommand;
List<IProject> projects;
IProject currentProject;
TestResultsMonitor testResultsMonitor;
public AbstractRunTestCommand()
{
testResultsMonitor = new TestResultsMonitor();
testResultsMonitor.TestFinished += TestFinished;
}
/// <summary>
/// Gets the running test command.
/// </summary>
public static AbstractRunTestCommand RunningTestCommand {
get {
return runningTestCommand;
}
}
/// <summary>
/// Gets whether a test is currently running.
/// </summary>
public static bool IsRunningTest {
get {
return runningTestCommand != null;
}
}
public override void Run()
{
projects = new List<IProject>();
IMember m = TestableCondition.GetMember(Owner);
IClass c = (m != null) ? m.DeclaringType : TestableCondition.GetClass(Owner);
IProject project = TestableCondition.GetProject(Owner);
string namespaceFilter = TestableCondition.GetNamespace(Owner);
if (project != null) {
projects.Add(project);
} else if (UnitTestsPad.Instance != null) {
projects.AddRange(UnitTestsPad.Instance.TestTreeView.GetProjects());
}
if (projects.Count > 0) {
runningTestCommand = this;
try {
BeforeRun();
if (IsRunningTest) {
currentProject = projects[0];
Run(currentProject, namespaceFilter, c, m);
}
} catch {
runningTestCommand = null;
throw;
}
}
}
public static MessageViewCategory TestRunnerCategory {
get {
if (testRunnerCategory == null) {
MessageViewCategory.Create(ref testRunnerCategory, "UnitTesting", "${res:ICSharpCode.NUnitPad.NUnitPadContent.PadName}");
}
return testRunnerCategory;
}
}
/// <summary>
/// Stops running the tests.
/// </summary>
public void Stop()
{
runningTestCommand = null;
UpdateUnitTestsPadToolbar();
projects.Clear();
testResultsMonitor.Stop();
StopMonitoring();
OnStop();
}
/// <summary>
/// Called before all tests are run. If multiple projects are
/// to be tested this is called only once.
/// </summary>
protected virtual void OnBeforeRunTests()
{
}
/// <summary>
/// Called after all tests have been run even if there have
/// been errors. If multiple projects are to be tested this is called only once.
/// </summary>
protected virtual void OnAfterRunTests()
{
}
protected abstract void RunTests(UnitTestApplicationStartHelper helper);
/// <summary>
/// Called by derived classes when a single test run
/// is finished.
/// </summary>
protected void TestsFinished()
{
WorkbenchSingleton.AssertMainThread();
// Read the rest of the file just in case.
testResultsMonitor.Stop();
testResultsMonitor.Read();
StopMonitoring();
projects.Remove(currentProject);
if (projects.Count > 0) {
currentProject = projects[0];
Run(currentProject, null, null, null);
} else {
runningTestCommand = null;
UpdateUnitTestsPadToolbar();
if (TaskService.SomethingWentWrong && ErrorListPad.ShowAfterBuild) {
ShowErrorList();
}
OnAfterRunTests();
}
}
/// <summary>
/// Called by derived classes to show a single test result.
/// </summary>
protected void ShowResult(TestResult result)
{
if (result.IsFailure || result.IsIgnored) {
TaskService.Add(CreateTask(result));
}
UpdateTestResult(result);
}
/// <summary>
/// Called when the test run should be stopped.
/// </summary>
protected virtual void OnStop()
{
}
/// <summary>
/// Brings the specified pad to the front.
/// </summary>
protected void ShowPad(PadDescriptor padDescriptor)
{
if (padDescriptor != null) {
WorkbenchSingleton.SafeThreadAsyncCall(padDescriptor.BringPadToFront);
}
}
/// <summary>
/// Runs the tests after building the project under test.
/// </summary>
void Run(IProject project, string namespaceFilter, IClass fixture, IMember test)
{
BuildProjectBeforeTestRun build = new BuildProjectBeforeTestRun(project);
build.BuildComplete += delegate {
OnBuildComplete(build.LastBuildResults, project, namespaceFilter, fixture, test);
};
build.Run();
}
void ShowUnitTestsPad()
{
ShowPad(WorkbenchSingleton.Workbench.GetPad(typeof(UnitTestsPad)));
}
void UpdateUnitTestsPadToolbar()
{
if (UnitTestsPad.Instance != null) {
UnitTestsPad.Instance.UpdateToolbar();
}
}
/// <summary>
/// Sets the initial workbench state before starting
/// a test run.
/// </summary>
void BeforeRun()
{
TaskService.BuildMessageViewCategory.ClearText();
TaskService.InUpdate = true;
TaskService.ClearExceptCommentTasks();
TaskService.InUpdate = false;
TestRunnerCategory.ClearText();
ShowUnitTestsPad();
ShowOutputPad();
UpdateUnitTestsPadToolbar();
ResetAllTestResults();
OnBeforeRunTests();
}
/// <summary>
/// Brings output pad to the front.
/// </summary>
void ShowOutputPad()
{
ShowPad(WorkbenchSingleton.Workbench.GetPad(typeof(CompilerMessageView)));
}
Task CreateTask(TestResult result)
{
TaskType taskType = TaskType.Warning;
FileLineReference lineRef = null;
string message = String.Empty;
if (result.IsFailure) {
taskType = TaskType.Error;
lineRef = OutputTextLineParser.GetNUnitOutputFileLineReference(result.StackTrace, true);
message = GetTestResultMessage(result, "${res:NUnitPad.NUnitPadContent.TestTreeView.TestFailedMessage}");
} else if (result.IsIgnored) {
message = GetTestResultMessage(result, "${res:NUnitPad.NUnitPadContent.TestTreeView.TestNotExecutedMessage}");
}
if (lineRef == null) {
lineRef = FindTest(result.Name);
}
if (lineRef != null) {
return new Task(FileName.Create(lineRef.FileName),
message, lineRef.Column, lineRef.Line, taskType);
}
return new Task(null, message, 0, 0, taskType);
}
/// <summary>
/// Returns the test result message if there is on otherwise
/// uses the string resource to create a message.
/// </summary>
string GetTestResultMessage(TestResult result, string stringResource)
{
if (result.Message.Length > 0) {
return result.Message;
}
return StringParser.Parse(stringResource, new string[,] {{"TestCase", result.Name}});
}
/// <summary>
/// Returns the location of the specified test method in the
/// project being tested.
/// </summary>
FileLineReference FindTest(string methodName)
{
TestProject testProject = GetTestProject(currentProject);
if (testProject != null) {
TestMethod method = testProject.TestClasses.GetTestMethod(methodName);
if (method != null) {
MemberResolveResult resolveResult = new MemberResolveResult(null, null, method.Method);
FilePosition filePos = resolveResult.GetDefinitionPosition();
return new FileLineReference(filePos.FileName, filePos.Line, filePos.Column);
}
}
return null;
}
void ShowErrorList()
{
ShowPad(WorkbenchSingleton.Workbench.GetPad(typeof(ErrorListPad)));
}
/// <summary>
/// Runs the test for the project after a successful build.
/// </summary>
void OnBuildComplete(BuildResults results, IProject project, string namespaceFilter, IClass fixture, IMember test)
{
if (results.ErrorCount == 0 && IsRunningTest) {
UnitTestApplicationStartHelper helper = new UnitTestApplicationStartHelper();
UnitTestingOptions options = new UnitTestingOptions();
helper.NoThread = options.NoThread;
helper.NoLogo = options.NoLogo;
helper.NoDots = options.NoDots;
helper.Labels = options.Labels;
helper.ShadowCopy = !options.NoShadow;
if (options.CreateXmlOutputFile) {
helper.XmlOutputFile = Path.Combine(Path.GetDirectoryName(project.OutputAssemblyFullPath), project.AssemblyName + "-TestResult.xml");
}
helper.Initialize(project, namespaceFilter, fixture, test);
helper.Results = Path.GetTempFileName();
ResetTestResults(project);
testResultsMonitor.FileName = helper.Results;
testResultsMonitor.Start();
try {
RunTests(helper);
} catch {
StopMonitoring();
throw;
}
} else {
if (IsRunningTest) {
Stop();
}
if (TaskService.SomethingWentWrong && ErrorListPad.ShowAfterBuild) {
ShowErrorList();
}
}
}
/// <summary>
/// Clears the test results in the test tree view for the
/// project currently being tested.
/// </summary>
void ResetTestResults(IProject project)
{
TestProject testProject = GetTestProject(project);
if (testProject != null) {
testProject.ResetTestResults();
}
}
/// <summary>
/// Clears the test results in the test tree view for all the
/// displayed projects.
/// </summary>
void ResetAllTestResults()
{
if (UnitTestsPad.Instance != null) {
UnitTestsPad.Instance.TestTreeView.ResetTestResults();
}
}
/// <summary>
/// Gets the TestProject associated with the specified project
/// from the test tree view.
/// </summary>
TestProject GetTestProject(IProject project)
{
if (UnitTestsPad.Instance != null) {
return UnitTestsPad.Instance.TestTreeView.GetTestProject(project);
}
return null;
}
/// <summary>
/// Updates the test result in the test tree view.
/// </summary>
void UpdateTestResult(TestResult result)
{
TestProject testProject = GetTestProject(currentProject);
if (testProject != null) {
testProject.UpdateTestResult(result);
}
}
void StopMonitoring()
{
try {
File.Delete(testResultsMonitor.FileName);
} catch { }
testResultsMonitor.Dispose();
}
void TestFinished(object source, TestFinishedEventArgs e)
{
WorkbenchSingleton.SafeThreadAsyncCall(ShowResult, e.Result);
}
}
/// <summary>
/// Custom build command that makes sure errors and warnings
/// are not cleared from the Errors list before every build since
/// we may be running multiple tests after each other.
/// </summary>
public class BuildProjectBeforeTestRun : BuildProjectBeforeExecute
{
public BuildProjectBeforeTestRun(IProject targetProject)
: base(targetProject)
{
}
/// <summary>
/// Before a build do not clear the tasks, just save any
/// dirty files.
/// </summary>
public override void BeforeBuild()
{
SaveAllFiles.SaveAll();
}
}
public class RunTestInPadCommand : AbstractRunTestCommand
{
ProcessRunner runner;
public RunTestInPadCommand()
{
runner = new ProcessRunner();
runner.LogStandardOutputAndError = false;
runner.OutputLineReceived += OutputLineReceived;
runner.ProcessExited += ProcessExited;
}
protected override void RunTests(UnitTestApplicationStartHelper helper)
{
TestRunnerCategory.AppendLine(helper.GetCommandLine());
runner.Start(helper.UnitTestApplication, helper.GetArguments());
}
protected override void OnStop()
{
runner.Kill();
}
protected ProcessRunner GetProcessRunner()
{
return runner;
}
void OutputLineReceived(object source, LineReceivedEventArgs e)
{
TestRunnerCategory.AppendLine(e.Line);
}
void ProcessExited(object source, EventArgs e)
{
WorkbenchSingleton.SafeThreadAsyncCall(TestsFinished);
}
void TestFinished(object source, TestFinishedEventArgs e)
{
WorkbenchSingleton.SafeThreadAsyncCall(ShowResult, e.Result);
}
}
public class RunTestWithDebuggerCommand : AbstractRunTestCommand
{
public override void Run()
{
if (DebuggerService.IsDebuggerLoaded && DebuggerService.CurrentDebugger.IsDebugging) {
if (MessageService.AskQuestion("${res:XML.MainMenu.RunMenu.Compile.StopDebuggingQuestion}",
"${res:XML.MainMenu.RunMenu.Compile.StopDebuggingTitle}"))
{
DebuggerService.CurrentDebugger.Stop();
base.Run();
}
} else {
base.Run();
}
}
protected override void RunTests(UnitTestApplicationStartHelper helper)
{
bool running = false;
try {
TestRunnerCategory.AppendLine(helper.GetCommandLine());
ProcessStartInfo startInfo = new ProcessStartInfo(helper.UnitTestApplication);
startInfo.Arguments = helper.GetArguments();
startInfo.WorkingDirectory = UnitTestApplicationStartHelper.UnitTestApplicationDirectory;
DebuggerService.DebugStopped += DebuggerFinished;
DebuggerService.CurrentDebugger.Start(startInfo);
running = true;
} finally {
if (!running) {
DebuggerService.DebugStopped -= DebuggerFinished;
}
}
}
protected override void OnStop()
{
if (DebuggerService.CurrentDebugger.IsDebugging) {
DebuggerService.CurrentDebugger.Stop();
}
}
void DebuggerFinished(object sender, EventArgs e)
{
DebuggerService.DebugStopped -= DebuggerFinished;
WorkbenchSingleton.SafeThreadAsyncCall(TestsFinished);
}
}
public class RunAllTestsInPadCommand : RunTestInPadCommand
{
public override void Run()
{
// To make sure all tests are run we set the Owner to null.
Owner = null;
base.Run();
}
}
public class RunProjectTestsInPadCommand : RunTestInPadCommand, ITestTreeView
{
public override void Run()
{
Owner = this;
base.Run();
}
public IMember SelectedMethod {
get { return null; }
}
public IClass SelectedClass {
get { return null; }
}
public IProject SelectedProject {
get { return ProjectService.CurrentProject; }
}
public string SelectedNamespace {
get { return null; }
}
}
}

26
src/AddIns/Analysis/UnitTesting/Commands/RunTestInPadCommand.cs

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.SharpDevelop.Util;
namespace ICSharpCode.UnitTesting
{
public class RunTestInPadCommand : AbstractRunTestCommand
{
public RunTestInPadCommand()
{
}
public RunTestInPadCommand(IRunTestCommandContext context)
: base(context)
{
}
protected override ITestRunner CreateTestRunner(IProject project)
{
return Context.RegisteredTestFrameworks.CreateTestRunner(project);
}
}
}

26
src/AddIns/Analysis/UnitTesting/Commands/RunTestWithDebuggerCommand.cs

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Diagnostics;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
public class RunTestWithDebuggerCommand : AbstractRunTestCommand
{
public RunTestWithDebuggerCommand()
{
}
public RunTestWithDebuggerCommand(IRunTestCommandContext context)
: base(context)
{
}
protected override ITestRunner CreateTestRunner(IProject project)
{
return Context.RegisteredTestFrameworks.CreateTestDebugger(project);
}
}
}

53
src/AddIns/Analysis/UnitTesting/Gui/EmptyUnitTestsPad.cs

@ -0,0 +1,53 @@ @@ -0,0 +1,53 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Linq;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
public class EmptyUnitTestsPad : IUnitTestsPad
{
Solution solution;
public EmptyUnitTestsPad()
: this(null)
{
}
public EmptyUnitTestsPad(Solution solution)
{
this.solution = solution;
}
public void UpdateToolbar()
{
}
public void BringToFront()
{
}
public void ResetTestResults()
{
}
public IProject[] GetProjects()
{
if (solution != null) {
return solution.Projects.ToArray();
}
return new IProject[0];
}
public TestProject GetTestProject(IProject project)
{
return null;
}
public void CollapseAll()
{
}
}
}

33
src/AddIns/Analysis/UnitTesting/Gui/UnitTestingOptionsPanel.xaml

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
<gui:OptionPanel x:Class="ICSharpCode.UnitTesting.UnitTestingOptionsPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:ICSharpCode.UnitTesting"
xmlns:gui="clr-namespace:ICSharpCode.SharpDevelop.Gui;assembly=ICSharpCode.SharpDevelop"
xmlns:widgets="http://icsharpcode.net/sharpdevelop/widgets"
xmlns:sd="http://icsharpcode.net/sharpdevelop/core"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<gui:OptionPanel.Resources>
<sd:NotBoolConverter x:Key="negator" />
</gui:OptionPanel.Resources>
<GroupBox Header="{sd:Localize ICSharpCode.NUnitPad.NUnitPadContent.PadName}">
<widgets:StackPanelWithSpacing SpaceBetweenItems="5">
<CheckBox
IsChecked="{sd:OptionBinding local:UnitTestingOptions.Labels}"
Content="{sd:Localize ICSharpCode.UnitTesting.OptionsPanel.LabelEachTest}" />
<CheckBox
IsChecked="{sd:OptionBinding local:UnitTestingOptions.NoLogo, Converter={StaticResource negator}}"
Content="{sd:Localize ICSharpCode.UnitTesting.OptionsPanel.ShowLogo}" />
<CheckBox
IsChecked="{sd:OptionBinding local:UnitTestingOptions.NoDots, Converter={StaticResource negator}}"
Content="{sd:Localize ICSharpCode.UnitTesting.OptionsPanel.ShowProgress}" />
<CheckBox
IsChecked="{sd:OptionBinding local:UnitTestingOptions.NoShadow, Converter={StaticResource negator}}"
Content="{sd:Localize ICSharpCode.UnitTesting.OptionsPanel.ShadowCopy}" />
<CheckBox
IsChecked="{sd:OptionBinding local:UnitTestingOptions.NoThread, Converter={StaticResource negator}}"
Content="{sd:Localize ICSharpCode.UnitTesting.OptionsPanel.Thread}" />
<CheckBox
IsChecked="{sd:OptionBinding local:UnitTestingOptions.CreateXmlOutputFile}"
Content="{sd:Localize ICSharpCode.UnitTesting.OptionsPanel.CreateXmlOutputFile}" />
</widgets:StackPanelWithSpacing>
</GroupBox>
</gui:OptionPanel>

15
src/AddIns/Analysis/UnitTesting/Gui/UnitTestingOptionsPanel.xaml.cs

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.SharpDevelop.Gui;
namespace ICSharpCode.UnitTesting
{
public partial class UnitTestingOptionsPanel : OptionPanel
{
public UnitTestingOptionsPanel()
{
}
}
}

348
src/AddIns/Analysis/UnitTesting/Gui/UnitTestsPad.cs

@ -0,0 +1,348 @@ @@ -0,0 +1,348 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using ICSharpCode.Core;
using ICSharpCode.Core.Presentation;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Parser;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.TreeView;
namespace ICSharpCode.UnitTesting
{
public class UnitTestsPad : AbstractPadContent
{
SharpTreeView treeView;
bool disposed;
DockPanel panel;
ToolBar toolBar;
List<Tuple<IParsedFile, IParsedFile>> pending = new List<Tuple<IParsedFile, IParsedFile>>();
static UnitTestsPad instance;
public UnitTestsPad()
: this(TestService.RegisteredTestFrameworks)
{
}
public UnitTestsPad(IRegisteredTestFrameworks testFrameworks)
{
instance = this;
panel = new DockPanel();
toolBar = CreateToolBar("/SharpDevelop/Pads/UnitTestsPad/Toolbar");
panel.Children.Add(toolBar);
DockPanel.SetDock(toolBar, Dock.Top);
treeView = new SharpTreeView();
treeView.MouseDoubleClick += TestTreeViewDoubleClick;
// treeView.KeyDown += TestTreeViewKeyPress;
panel.Children.Add(treeView);
// Add the load solution projects thread ended handler before
// we try to display the open solution so the event does not
// get missed.
ParserService.LoadSolutionProjectsThreadEnded += LoadSolutionProjectsThreadEnded;
OnAddedLoadSolutionProjectsThreadEndedHandler();
// Display currently open solution.
if (!IsParserLoadingSolution) {
Solution openSolution = GetOpenSolution();
if (openSolution != null) {
SolutionLoaded(openSolution);
}
}
ParserService.ParseInformationUpdated += ParseInformationUpdated;
ProjectService.SolutionClosed += SolutionClosed;
ProjectService.SolutionFolderRemoved += SolutionFolderRemoved;
ProjectService.ProjectAdded += ProjectAdded;
ProjectService.ProjectItemAdded += ProjectItemAdded;
ProjectService.ProjectItemRemoved += ProjectItemRemoved;
treeView.ContextMenu = CreateContextMenu("/SharpDevelop/Pads/UnitTestsPad/ContextMenu");
}
public static UnitTestsPad Instance {
get { return instance; }
}
public override object Control {
get { return panel; }
}
public override void Dispose()
{
if (!disposed) {
disposed = true;
ProjectService.ProjectItemRemoved -= ProjectItemRemoved;
ProjectService.ProjectItemAdded -= ProjectItemAdded;
ProjectService.ProjectAdded -= ProjectAdded;
ProjectService.SolutionFolderRemoved -= SolutionFolderRemoved;
ProjectService.SolutionClosed -= SolutionClosed;
ParserService.ParseInformationUpdated -= ParseInformationUpdated;
ParserService.LoadSolutionProjectsThreadEnded -= LoadSolutionProjectsThreadEnded;
}
}
// public TestTreeView TestTreeView {
// get { return treeView; }
// }
// public void ResetTestResults()
// {
// treeView.ResetTestResults();
// }
//
// public IProject[] GetProjects()
// {
// return treeView.GetProjects();
// }
// public TestProject GetTestProject(IProject project)
// {
// return treeView.GetTestProject(project);
// }
/// <summary>
/// Updates the state of the buttons on the Unit Tests pad's
/// toolbar.
/// </summary>
public void UpdateToolbar()
{
// ToolbarService.UpdateToolbar(toolBar);
}
/// <summary>
/// Collapses all nodes.
/// </summary>
// public void CollapseAll()
// {
// if (treeView == null || treeView.Nodes == null || treeView.Nodes.Count == 0)
// return;
//
// treeView.CollapseAll();
// }
/// <summary>
/// Called when a solution has been loaded.
/// </summary>
protected void SolutionLoaded(Solution solution)
{
// SolutionLoaded will be invoked from another thread.
// The UnitTestsPad might be disposed by the time the event is processed by the main thread.
if (treeView != null) {
if (solution != null) {
treeView.Root = new RootUnitTestNode(solution);
} else {
treeView.Root = null;
}
}
}
/// <summary>
/// Called when a solution has been closed.
/// </summary>
// protected void SolutionClosed()
// {
// treeView.Clear();
// }
//
// protected void SolutionFolderRemoved(ISolutionFolder solutionFolder)
// {
// treeView.RemoveSolutionFolder(solutionFolder);
// }
//
/// <summary>
/// The project is added to the tree view only if it has a
/// reference to a unit testing framework.
/// </summary>
protected void ProjectAdded(IProject project)
{
SolutionLoaded(GetOpenSolution());
}
/// <summary>
/// If the project item removed is a reference to a unit
/// test framework then the project will be removed from the
/// test tree.
/// </summary>
// protected void ProjectItemRemoved(ProjectItem projectItem)
// {
// treeView.ProjectItemRemoved(projectItem);
// }
//
// protected void ProjectItemAdded(ProjectItem projectItem)
// {
// treeView.ProjectItemAdded(projectItem);
// }
/// <summary>
/// Protected method so we can test this method.
/// </summary>
protected void UpdateParseInfo(IParsedFile oldFile, IParsedFile newFile)
{
RootUnitTestNode root = (RootUnitTestNode)treeView.Root;
if (root == null) {
SolutionLoaded(GetOpenSolution());
root = (RootUnitTestNode)treeView.Root;
if (root == null) return;
}
var solution = GetOpenSolution();
if (solution == null)
return;
var project = solution.FindProjectContainingFile((oldFile ?? newFile).FileName);
if (project == null)
return;
var projectNode = root.Children.OfType<ProjectUnitTestNode>().FirstOrDefault(node => node.Project == project);
}
/// <summary>
/// Virtual method so we can override this method and return
/// a dummy ToolBar when testing.
/// </summary>
protected virtual ToolBar CreateToolBar(string name)
{
return ToolBarService.CreateToolBar(treeView, treeView, name);
}
/// <summary>
/// Virtual method so we can override this method and return
/// a dummy ContextMenu when testing.
/// </summary>
protected virtual ContextMenu CreateContextMenu(string name)
{
return MenuService.CreateContextMenu(treeView, name);
}
// /// <summary>
// /// Virtual method so we can override this method and return
// /// a dummy TestTreeView when testing.
// /// </summary>
// protected virtual SharpTreeView CreateTestTreeView(IRegisteredTestFrameworks testFrameworks)
// {
// return new SharpTreeView();
// }
/// <summary>
/// Gets the currently open solution.
/// </summary>
protected virtual Solution GetOpenSolution()
{
return ProjectService.OpenSolution;
}
/// <summary>
/// Determines whether the parser is currently still loading the
/// solution.
/// </summary>
protected virtual bool IsParserLoadingSolution {
get { return ParserService.LoadSolutionProjectsThreadRunning; }
}
/// <summary>
/// Indicates that an event handler for the ParserService's
/// LoadSolutionProjectsThreadEnded event has been added
/// </summary>
protected virtual void OnAddedLoadSolutionProjectsThreadEndedHandler()
{
}
void SolutionClosed(object source, EventArgs e)
{
// SolutionClosed();
UpdateToolbar();
}
void SolutionFolderRemoved(object source, SolutionFolderEventArgs e)
{
// SolutionFolderRemoved(e.SolutionFolder);
UpdateToolbar();
}
void ProjectAdded(object source, ProjectEventArgs e)
{
ProjectAdded(e.Project);
UpdateToolbar();
}
void LoadSolutionProjectsThreadEnded(object source, EventArgs e)
{
WorkbenchSingleton.SafeThreadAsyncCall(UpdateToolbar);
Solution solution = ProjectService.OpenSolution;
// WorkbenchSingleton.SafeThreadAsyncCall(SolutionLoaded, solution);
}
void ParseInformationUpdated(object source, ParseInformationEventArgs e)
{
lock (pending) {
var files = Tuple.Create(e.OldParsedFile, e.NewParsedFile);
pending.Add(files);
}
WorkbenchSingleton.SafeThreadAsyncCall(UpdateParseInfo);
}
void UpdateParseInfo()
{
lock (pending) {
foreach (var files in pending) {
UpdateParseInfo(files.Item1, files.Item2);
}
pending.Clear();
}
}
void TestTreeViewDoubleClick(object source, EventArgs e)
{
GotoDefinition();
}
// void TestTreeViewKeyPress(object source, KeyPressEventArgs e)
// {
// if (e.KeyChar == '\r') {
// e.Handled = true;
// GotoDefinition();
// } else if (e.KeyChar == ' ') {
// e.Handled = true;
// RunTests();
// }
// }
void GotoDefinition()
{
// RunCommand(new GotoDefinitionCommand());
}
void RunTests()
{
// RunCommand(new RunTestInPadCommand());
}
void RunCommand(ICommand command)
{
command.Owner = treeView;
command.Run();
}
void ProjectItemAdded(object source, ProjectItemEventArgs e)
{
// ProjectItemAdded(e.ProjectItem);
}
void ProjectItemRemoved(object source, ProjectItemEventArgs e)
{
// ProjectItemRemoved(e.ProjectItem);
}
}
}

17
src/AddIns/Analysis/UnitTesting/Implementation/UnitTestAddInTree.cs

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using ICSharpCode.Core;
namespace ICSharpCode.UnitTesting
{
public class UnitTestAddInTree : IAddInTree
{
public List<T> BuildItems<T>(string path, object caller)
{
return AddInTree.BuildItems<T>(path, caller);
}
}
}

15
src/AddIns/Analysis/UnitTesting/Implementation/UnitTestBuildOptions.cs

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
public class UnitTestBuildOptions : IBuildOptions
{
public bool ShowErrorListAfterBuild {
get { return BuildOptions.ShowErrorListAfterBuild; }
}
}
}

18
src/AddIns/Analysis/UnitTesting/Implementation/UnitTestBuildProjectFactory.cs

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.SharpDevelop.Project.Commands;
namespace ICSharpCode.UnitTesting
{
public class UnitTestBuildProjectFactory : IBuildProjectFactory
{
public BuildProject CreateBuildProjectBeforeTestRun(IEnumerable<IProject> projects)
{
return new BuildProjectBeforeExecute(new MultipleProjectBuildable(projects));
}
}
}

19
src/AddIns/Analysis/UnitTesting/Implementation/UnitTestDebuggerService.cs

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.SharpDevelop.Debugging;
namespace ICSharpCode.UnitTesting
{
public class UnitTestDebuggerService : IUnitTestDebuggerService
{
public bool IsDebuggerLoaded {
get { return DebuggerService.IsDebuggerLoaded; }
}
public IDebugger CurrentDebugger {
get { return DebuggerService.CurrentDebugger; }
}
}
}

27
src/AddIns/Analysis/UnitTesting/Implementation/UnitTestFileService.cs

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.IO;
using ICSharpCode.SharpDevelop;
namespace ICSharpCode.UnitTesting
{
public class UnitTestFileService : IUnitTestFileService
{
public void OpenFile(string fileName)
{
FileService.OpenFile(fileName);
}
public void JumpToFilePosition(string fileName, int line, int column)
{
FileService.JumpToFilePosition(fileName, line, column);
}
public bool FileExists(string fileName)
{
return File.Exists(fileName);
}
}
}

15
src/AddIns/Analysis/UnitTesting/Implementation/UnitTestSaveAllFilesCommand.cs

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
namespace ICSharpCode.UnitTesting
{
public class UnitTestSaveAllFilesCommand : IUnitTestSaveAllFilesCommand
{
public void SaveAllFiles()
{
ICSharpCode.SharpDevelop.Commands.SaveAllFiles.SaveAll();
}
}
}

40
src/AddIns/Analysis/UnitTesting/Implementation/UnitTestTaskService.cs

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Gui;
namespace ICSharpCode.UnitTesting
{
public class UnitTestTaskService : IUnitTestTaskService
{
public void ClearExceptCommentTasks()
{
TaskService.ClearExceptCommentTasks();
}
public bool InUpdate {
get { return TaskService.InUpdate; }
set { TaskService.InUpdate = value; }
}
public MessageViewCategory BuildMessageViewCategory {
get { return TaskService.BuildMessageViewCategory; }
}
public void Add(SDTask task)
{
TaskService.Add(task);
}
public bool SomethingWentWrong {
get { return TaskService.SomethingWentWrong; }
}
public bool HasCriticalErrors(bool treatWarningsAsErrors)
{
return TaskService.HasCriticalErrors(treatWarningsAsErrors);
}
}
}

27
src/AddIns/Analysis/UnitTesting/Implementation/UnitTestWorkbench.cs

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Gui;
namespace ICSharpCode.UnitTesting
{
public class UnitTestWorkbench : IUnitTestWorkbench
{
public PadDescriptor GetPad(Type type)
{
return WorkbenchSingleton.Workbench.GetPad(type);
}
public void SafeThreadAsyncCall(Action method)
{
WorkbenchSingleton.SafeThreadAsyncCall(method);
}
public void SafeThreadAsyncCall<A>(Action<A> method, A arg1)
{
WorkbenchSingleton.SafeThreadAsyncCall(method, arg1);
}
}
}

13
src/AddIns/Analysis/UnitTesting/Interfaces/IAddInTree.cs

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
namespace ICSharpCode.UnitTesting
{
public interface IAddInTree
{
List<T> BuildItems<T>(string path, object caller);
}
}

12
src/AddIns/Analysis/UnitTesting/Interfaces/IBuildOptions.cs

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
namespace ICSharpCode.UnitTesting
{
public interface IBuildOptions
{
bool ShowErrorListAfterBuild { get; }
}
}

15
src/AddIns/Analysis/UnitTesting/Interfaces/IBuildProjectFactory.cs

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.SharpDevelop.Project.Commands;
namespace ICSharpCode.UnitTesting
{
public interface IBuildProjectFactory
{
BuildProject CreateBuildProjectBeforeTestRun(IEnumerable<IProject> projects);
}
}

12
src/AddIns/Analysis/UnitTesting/Interfaces/IFileSystem.cs

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
namespace ICSharpCode.UnitTesting
{
public interface IFileSystem
{
bool FileExists(string fileName);
}
}

25
src/AddIns/Analysis/UnitTesting/Interfaces/IRegisteredTestFrameworks.cs

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
public interface IRegisteredTestFrameworks
{
ITestFramework GetTestFrameworkForProject(IProject project);
ITestRunner CreateTestRunner(IProject project);
ITestRunner CreateTestDebugger(IProject project);
bool IsTestMember(IUnresolvedMember member);
bool IsTestClass(IUnresolvedTypeDefinition typeDefinition);
bool IsTestProject(IProject project);
IEnumerable<IUnresolvedMember> GetTestMembersFor(IUnresolvedTypeDefinition typeDefinition);
bool IsBuildNeededBeforeTestRunForProject(IProject project);
}
}

25
src/AddIns/Analysis/UnitTesting/Interfaces/IRunTestCommandContext.cs

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
public interface IRunTestCommandContext
{
IRegisteredTestFrameworks RegisteredTestFrameworks { get; }
IUnitTestTaskService TaskService { get; }
IUnitTestWorkbench Workbench { get; }
IBuildProjectFactory BuildProjectFactory { get; }
IBuildOptions BuildOptions { get; }
MessageViewCategory UnitTestCategory { get; }
IUnitTestsPad OpenUnitTestsPad { get; }
IMessageService MessageService { get; }
IUnitTestSaveAllFilesCommand SaveAllFilesCommand { get; }
IStatusBarService StatusBarService { get; }
Solution OpenSolution { get; }
}
}

24
src/AddIns/Analysis/UnitTesting/Interfaces/ITestFramework.cs

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
public interface ITestFramework
{
bool IsTestMember(IUnresolvedMember member);
bool IsTestClass(IUnresolvedTypeDefinition c);
bool IsTestProject(IProject project);
IEnumerable<IUnresolvedMember> GetTestMembersFor(IUnresolvedTypeDefinition typeDefinition);
ITestRunner CreateTestRunner();
ITestRunner CreateTestDebugger();
bool IsBuildNeededBeforeTestRun { get; }
}
}

12
src/AddIns/Analysis/UnitTesting/Interfaces/ITestFrameworkFactory.cs

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
namespace ICSharpCode.UnitTesting
{
public interface ITestFrameworkFactory
{
ITestFramework Create(string className);
}
}

20
src/AddIns/Analysis/UnitTesting/Interfaces/ITestResultsMonitor.cs

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
namespace ICSharpCode.UnitTesting
{
public interface ITestResultsMonitor : IDisposable
{
event TestFinishedEventHandler TestFinished;
string FileName { get; set; }
void Stop();
void Start();
void Read();
long InitialFilePosition { get; set; }
}
}

17
src/AddIns/Analysis/UnitTesting/Interfaces/ITestRunner.cs

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.SharpDevelop.Util;
namespace ICSharpCode.UnitTesting
{
public interface ITestRunner : IDisposable
{
event TestFinishedEventHandler TestFinished;
event EventHandler AllTestsFinished;
event MessageReceivedEventHandler MessageReceived;
void Start(SelectedTests selectedTests);
void Stop();
}
}

34
src/AddIns/Analysis/UnitTesting/Interfaces/ITestTreeView.cs

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Project;
using System;
namespace ICSharpCode.UnitTesting
{
public interface ITestTreeView
{
/// <summary>
/// Gets the selected member in the test tree view.
/// </summary>
IUnresolvedMember SelectedMember {get;}
/// <summary>
/// Gets the selected class in the test tree view.
/// </summary>
IUnresolvedTypeDefinition SelectedClass {get;}
/// <summary>
/// Gets the selected project for the selected node
/// in the test tree view.
/// </summary>
IProject SelectedProject {get;}
/// <summary>
/// Gets the namespace for the selected namespace node.
/// </summary>
string SelectedNamespace {get;}
}
}

14
src/AddIns/Analysis/UnitTesting/Interfaces/IUnitTestDebuggerService.cs

@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.SharpDevelop.Debugging;
namespace ICSharpCode.UnitTesting
{
public interface IUnitTestDebuggerService
{
bool IsDebuggerLoaded { get; }
IDebugger CurrentDebugger { get; }
}
}

13
src/AddIns/Analysis/UnitTesting/Interfaces/IUnitTestFileService.cs

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
namespace ICSharpCode.UnitTesting
{
public interface IUnitTestFileService : IFileSystem
{
void OpenFile(string fileName);
void JumpToFilePosition(string fileName, int line, int column);
}
}

24
src/AddIns/Analysis/UnitTesting/Interfaces/IUnitTestProcessRunner.cs

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using ICSharpCode.SharpDevelop.Util;
namespace ICSharpCode.UnitTesting
{
public interface IUnitTestProcessRunner
{
bool LogStandardOutputAndError { get; set; }
string WorkingDirectory { get; set; }
Dictionary<string, string> EnvironmentVariables { get; }
void Start(string command, string arguments);
void Kill();
event LineReceivedEventHandler OutputLineReceived;
event LineReceivedEventHandler ErrorLineReceived;
event EventHandler ProcessExited;
}
}

12
src/AddIns/Analysis/UnitTesting/Interfaces/IUnitTestSaveAllFilesCommand.cs

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
namespace ICSharpCode.UnitTesting
{
public interface IUnitTestSaveAllFilesCommand
{
void SaveAllFiles();
}
}

20
src/AddIns/Analysis/UnitTesting/Interfaces/IUnitTestTaskService.cs

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Gui;
namespace ICSharpCode.UnitTesting
{
public interface IUnitTestTaskService
{
MessageViewCategory BuildMessageViewCategory { get; }
bool InUpdate { get; set; }
void ClearExceptCommentTasks();
void Add(SDTask task);
bool SomethingWentWrong { get; }
bool HasCriticalErrors(bool treatWarningsAsErrors);
}
}

15
src/AddIns/Analysis/UnitTesting/Interfaces/IUnitTestWorkbench.cs

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.SharpDevelop;
namespace ICSharpCode.UnitTesting
{
public interface IUnitTestWorkbench
{
PadDescriptor GetPad(Type type);
void SafeThreadAsyncCall(Action method);
void SafeThreadAsyncCall<A>(Action<A> method, A arg1);
}
}

18
src/AddIns/Analysis/UnitTesting/Interfaces/IUnitTestsPad.cs

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
public interface IUnitTestsPad
{
void UpdateToolbar();
void BringToFront();
void ResetTestResults();
IProject[] GetProjects();
TestProject GetTestProject(IProject project);
void CollapseAll();
}
}

47
src/AddIns/Analysis/UnitTesting/MultipleProjectBuildable.cs

@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// IBuildable implementation that builds several projects.
/// </summary>
public class MultipleProjectBuildable : IBuildable
{
readonly IBuildable[] projects;
public MultipleProjectBuildable(IEnumerable<IBuildable> projects)
{
this.projects = projects.ToArray();
}
public string Name {
get { return string.Empty; }
}
public Solution ParentSolution {
get { return projects.Length > 0 ? projects[0].ParentSolution : null; }
}
public ICollection<IBuildable> GetBuildDependencies(ProjectBuildOptions buildOptions)
{
return projects;
}
public void StartBuild(ProjectBuildOptions buildOptions, IBuildFeedbackSink feedbackSink)
{
// SharpDevelop already has built our dependencies, so we're done immediately.
feedbackSink.Done(true);
}
public ProjectBuildOptions CreateProjectBuildOptions(BuildOptions options, bool isRootBuildable)
{
return null;
}
}
}

249
src/AddIns/Analysis/UnitTesting/NUnit/NUnitConsoleCommandLine.cs

@ -0,0 +1,249 @@ @@ -0,0 +1,249 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
public class NUnitConsoleApplication
{
public NUnitConsoleApplication(SelectedTests selectedTests, UnitTestingOptions options)
{
Initialize(selectedTests);
InitializeOptions(options);
}
public NUnitConsoleApplication(SelectedTests selectedTests)
{
Initialize(selectedTests);
}
void Initialize(SelectedTests selectedTests)
{
this.selectedTests = selectedTests;
this.project = selectedTests.Project;
Assemblies.Add(project.OutputAssemblyFullPath);
if (selectedTests.NamespaceFilter != null) {
NamespaceFilter = selectedTests.NamespaceFilter;
}
if (selectedTests.Class != null) {
Fixture = selectedTests.Class.DotNetName;
if (selectedTests.Member != null) {
Test = selectedTests.Member.Name;
}
}
}
void InitializeOptions(UnitTestingOptions options)
{
NoThread = options.NoThread;
NoLogo = options.NoLogo;
NoDots = options.NoDots;
Labels = options.Labels;
ShadowCopy = !options.NoShadow;
if (options.CreateXmlOutputFile) {
GenerateXmlOutputFileName();
}
}
void GenerateXmlOutputFileName()
{
string directory = Path.GetDirectoryName(project.OutputAssemblyFullPath);
string fileName = project.AssemblyName + "-TestResult.xml";
XmlOutputFile = Path.Combine(directory, fileName);
}
/// <summary>
/// returns full/path/to/Tools/NUnit
/// </summary>
string WorkingDirectory {
get { return Path.Combine(FileUtility.ApplicationRootPath, @"bin\Tools\NUnit"); }
}
/// <summary>
/// returns full/path/to/Tools/NUnit/nunit-console.exe or nunit-console-x86.exe if the
/// project platform target is x86.
/// </summary>
public string FileName {
get {
string exe = "nunit-console";
if (ProjectUsesDotnet20Runtime(project)) {
exe += "-dotnet2";
}
if (IsPlatformTarget32Bit(project)) {
exe += "-x86";
}
exe += ".exe";
return Path.Combine(WorkingDirectory, exe);
}
}
public readonly List<string> Assemblies = new List<string>();
/// <summary>
/// Use shadow copy assemblies. Default = true.
/// </summary>
public bool ShadowCopy = true;
/// <summary>
/// Disables the use of a separate thread to run tests on separate thread. Default = false;
/// </summary>
public bool NoThread = false;
/// <summary>
/// Use /nologo directive.
/// </summary>
public bool NoLogo = false;
/// <summary>
/// Use /labels directive.
/// </summary>
public bool Labels = false;
/// <summary>
/// Use /nodots directive.
/// </summary>
public bool NoDots = false;
/// <summary>
/// File to write xml output to. Default = null.
/// </summary>
public string XmlOutputFile;
/// <summary>
/// Fixture to test. Null = test all fixtures.
/// </summary>
public string Fixture;
/// <summary>
/// Test to run. Null = run all tests. Only valid together with the Fixture property.
/// </summary>
public string Test;
/// <summary>
/// File to write test results to.
/// </summary>
public string Results;
/// <summary>
/// The namespace that tests need to be a part of if they are to
/// be run.
/// </summary>
public string NamespaceFilter;
IProject project;
SelectedTests selectedTests;
public SelectedTests SelectedTests {
get { return selectedTests; }
}
public IProject Project {
get { return project; }
}
public ProcessStartInfo GetProcessStartInfo()
{
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = FileName;
startInfo.Arguments = GetArguments();
startInfo.WorkingDirectory = WorkingDirectory;
return startInfo;
}
/// <summary>
/// Gets the full command line to run the unit test application.
/// This is the combination of the UnitTestApplication and
/// the command line arguments.
/// </summary>
public string GetCommandLine()
{
return String.Format("\"{0}\" {1}", FileName, GetArguments());
}
/// <summary>
/// Gets the arguments to use on the command line to run NUnit.
/// </summary>
public string GetArguments()
{
StringBuilder b = new StringBuilder();
foreach (string assembly in Assemblies) {
if (b.Length > 0)
b.Append(' ');
b.Append('"');
b.Append(assembly);
b.Append('"');
}
if (!ShadowCopy)
b.Append(" /noshadow");
if (NoThread)
b.Append(" /nothread");
if (NoLogo)
b.Append(" /nologo");
if (Labels)
b.Append(" /labels");
if (NoDots)
b.Append(" /nodots");
if (XmlOutputFile != null) {
b.Append(" /xml=\"");
b.Append(XmlOutputFile);
b.Append('"');
}
if (Results != null) {
b.Append(" /results=\"");
b.Append(Results);
b.Append('"');
}
string run = null;
if (NamespaceFilter != null) {
run = NamespaceFilter;
} else if (Fixture != null) {
if (Test != null) {
run = Fixture + "." + Test;
} else {
run = Fixture;
}
} else if (Test != null) {
run = Test;
}
if (run != null) {
b.Append(" /run=\"");
b.Append(run);
b.Append('"');
}
return b.ToString();
}
/// <summary>
/// Checks that the project's PlatformTarget is x86 for the active configuration.
/// </summary>
bool IsPlatformTarget32Bit(IProject project)
{
MSBuildBasedProject msbuildProject = project as MSBuildBasedProject;
if (msbuildProject != null) {
string platformTarget = msbuildProject.GetEvaluatedProperty("PlatformTarget");
return String.Compare(platformTarget, "x86", true) == 0;
}
return false;
}
bool ProjectUsesDotnet20Runtime(IProject project)
{
var p = project as ICSharpCode.SharpDevelop.Project.Converter.IUpgradableProject;
if (p != null && p.CurrentTargetFramework != null) {
return p.CurrentTargetFramework.SupportedRuntimeVersion == "v2.0.50727";
}
return false;
}
}
}

44
src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestDebugger.cs

@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Diagnostics;
using ICSharpCode.Core.Services;
using ICSharpCode.SharpDevelop.Debugging;
namespace ICSharpCode.UnitTesting
{
public class NUnitTestDebugger : TestDebuggerBase
{
UnitTestingOptions options;
public NUnitTestDebugger()
: this(new UnitTestDebuggerService(),
new UnitTestMessageService(),
new TestResultsMonitor(),
UnitTestingOptions.Instance.Clone())
{
}
public NUnitTestDebugger(IUnitTestDebuggerService debuggerService,
IUnitTestMessageService messageService,
ITestResultsMonitor testResultsMonitor,
UnitTestingOptions options)
: base(debuggerService, messageService, testResultsMonitor)
{
this.options = options;
}
protected override ProcessStartInfo GetProcessStartInfo(SelectedTests selectedTests)
{
NUnitConsoleApplication app = new NUnitConsoleApplication(selectedTests, options);
app.Results = base.TestResultsMonitor.FileName;
return app.GetProcessStartInfo();
}
protected override TestResult CreateTestResultForTestFramework(TestResult testResult)
{
return new NUnitTestResult(testResult);
}
}
}

131
src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestFramework.cs

@ -0,0 +1,131 @@ @@ -0,0 +1,131 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Linq;
using System.Collections.Generic;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
public class NUnitTestFramework : ITestFramework
{
/// <summary>
/// Determines whether the project is a test project. A project
/// is considered to be a test project if it contains a reference
/// to the NUnit.Framework assembly.
/// </summary>
public bool IsTestProject(IProject project)
{
if (project != null) {
foreach (ProjectItem projectItem in project.Items) {
var referenceProjectItem = projectItem as ReferenceProjectItem;
if (IsNUnitFrameworkAssemblyReference(referenceProjectItem)) {
return true;
}
}
}
return false;
}
bool IsNUnitFrameworkAssemblyReference(ReferenceProjectItem referenceProjectItem)
{
if (referenceProjectItem != null) {
string name = referenceProjectItem.ShortName;
return name.Equals("NUnit.Framework", StringComparison.OrdinalIgnoreCase);
}
return false;
}
/// <summary>
/// Determines whether the class is a test fixture. A class
/// is considered to be a test class if it contains a TestFixture attribute.
/// </summary>
public bool IsTestClass(IClass c)
{
if (c == null) return false;
if (c.IsAbstract) return false;
StringComparer nameComparer = GetNameComparer(c);
if (nameComparer != null) {
NUnitTestAttributeName testAttributeName = new NUnitTestAttributeName("TestFixture", nameComparer);
foreach (IAttribute attribute in c.Attributes) {
if (testAttributeName.IsEqual(attribute)) {
return true;
}
}
}
while (c != null) {
if (HasTestMethod(c)) return true;
c = c.BaseClass;
}
return false;
}
private bool HasTestMethod(IClass c) {
return GetTestMembersFor(c).Any();
}
static StringComparer GetNameComparer(IClass c)
{
if (c != null) {
IProjectContent projectContent = c.ProjectContent;
if (projectContent != null) {
LanguageProperties language = projectContent.Language;
if (language != null) {
return language.NameComparer;
}
}
}
return null;
}
/// <summary>
/// Determines whether the method is a test method. A method
/// is considered to be a test method if it contains the NUnit Test attribute.
/// If the method has parameters it cannot be a test method.
/// </summary>
public bool IsTestMember(IMember member)
{
var method = member as IMethod;
if (method != null) {
return IsTestMethod(method);
}
return false;
}
public IEnumerable<TestMember> GetTestMembersFor(IClass @class) {
return @class.Methods.Where(IsTestMethod).Select(member => new TestMember(member));
}
static bool IsTestMethod(IMethod method)
{
var nameComparer = GetNameComparer(method.DeclaringType);
if (nameComparer != null) {
var testAttribute = new NUnitTestAttributeName("Test", nameComparer);
foreach (IAttribute attribute in method.Attributes) {
if (testAttribute.IsEqual(attribute)) {
if (method.Parameters.Count == 0) {
return true;
}
}
}
}
return false;
}
public bool IsBuildNeededBeforeTestRun {
get { return true; }
}
public ITestRunner CreateTestRunner()
{
return new NUnitTestRunner();
}
public ITestRunner CreateTestDebugger()
{
return new NUnitTestDebugger();
}
}
}

37
src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestResult.cs

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.IO;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Gui;
namespace ICSharpCode.UnitTesting
{
public class NUnitTestResult : TestResult
{
public NUnitTestResult(TestResult testResult)
: base(testResult.Name)
{
Message = testResult.Message;
ResultType = testResult.ResultType;
StackTrace = testResult.StackTrace;
}
protected override void OnStackTraceChanged()
{
FileLineReference fileLineRef = OutputTextLineParser.GetNUnitOutputFileLineReference(StackTrace, true);
if (fileLineRef != null) {
StackTraceFilePosition = CreateFilePosition(fileLineRef);
} else {
StackTraceFilePosition = FilePosition.Empty;
}
}
DomRegion CreateFilePosition(FileLineReference fileLineRef)
{
string fileName = Path.GetFullPath(fileLineRef.FileName);
return new FilePosition(fileName, fileLineRef.Line, fileLineRef.Column + 1);
}
}
}

38
src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestRunner.cs

@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Diagnostics;
using ICSharpCode.SharpDevelop.Util;
namespace ICSharpCode.UnitTesting
{
public class NUnitTestRunner : TestProcessRunnerBase
{
UnitTestingOptions options;
public NUnitTestRunner()
: this(new TestProcessRunnerBaseContext(),
UnitTestingOptions.Instance.Clone())
{
}
public NUnitTestRunner(TestProcessRunnerBaseContext context, UnitTestingOptions options)
: base(context)
{
this.options = options;
}
protected override ProcessStartInfo GetProcessStartInfo(SelectedTests selectedTests)
{
NUnitConsoleApplication app = new NUnitConsoleApplication(selectedTests, options);
app.Results = base.TestResultsMonitor.FileName;
return app.GetProcessStartInfo();
}
protected override TestResult CreateTestResultForTestFramework(TestResult testResult)
{
return new NUnitTestResult(testResult);
}
}
}

102
src/AddIns/Analysis/UnitTesting/Nodes/AllTestsTreeNode.cs

@ -0,0 +1,102 @@ @@ -0,0 +1,102 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.Core;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// All Tests root tree node that is added to the test tree when the
/// solution has multiple test projects.
/// </summary>
public class AllTestsTreeNode : TestTreeNode
{
public AllTestsTreeNode()
: base(null, StringParser.Parse("${res:ICSharpCode.UnitTesting.AllTestsTreeNode.Text}"))
{
}
/// <summary>
/// Raised when the all tests tree node is disposed.
/// </summary>
public event EventHandler Disposed;
/// <summary>
/// Disposes this tree node.
/// </summary>
public override void Dispose()
{
base.Dispose();
if (Disposed != null) {
Disposed(this, new EventArgs());
}
}
/// <summary>
/// Adds a new project node as a child of the All Tests node.
/// </summary>
public void AddProjectNode(TestProjectTreeNode node)
{
node.AddTo(this);
node.ImageIndexChanged += TestProjectTreeNodeImageIndexChanged;
}
/// <summary>
/// Removes the project node.
/// </summary>
public void RemoveProjectNode(TestProjectTreeNode node)
{
if (Nodes.Contains(node)) {
node.ImageIndexChanged -= TestProjectTreeNodeImageIndexChanged;
node.Remove();
}
}
void TestProjectTreeNodeImageIndexChanged(object source, EventArgs e)
{
UpdateImageListIndex();
}
/// <summary>
/// Sets the All Tests image index based on the current image
/// indexes of the child project tree nodes.
/// </summary>
void UpdateImageListIndex()
{
int ignored = 0;
int failed = 0;
int passed = 0;
int notRun = 0;
int total = Nodes.Count;
foreach (TestProjectTreeNode projectNode in Nodes) {
switch (projectNode.ImageIndex) {
case (int)TestTreeViewImageListIndex.TestFailed:
failed++;
break;
case (int)TestTreeViewImageListIndex.TestPassed:
passed++;
break;
case (int)TestTreeViewImageListIndex.TestIgnored:
ignored++;
break;
default: // Not run.
notRun++;
break;
}
}
// Update the image index based on the test project results.
if (failed > 0) {
UpdateImageListIndex(TestResultType.Failure);
} else if (ignored > 0) {
UpdateImageListIndex(TestResultType.Ignored);
} else if (passed > 0 && notRun == 0) {
UpdateImageListIndex(TestResultType.Success);
} else {
UpdateImageListIndex(TestResultType.None);
}
}
}
}

39
src/AddIns/Analysis/UnitTesting/Nodes/ProjectUnitTestNode.cs

@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Linq;
using System.Reflection;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.TreeView;
namespace ICSharpCode.UnitTesting
{
public class ProjectUnitTestNode : UnitTestBaseNode
{
TestProject project;
public TestProject Project {
get { return project; }
}
public ProjectUnitTestNode(TestProject project)
{
this.project = project;
project.
}
protected override void LoadChildren()
{
base.LoadChildren();
}
public override object Text {
get { return project.Name; }
}
}
}

70
src/AddIns/Analysis/UnitTesting/Nodes/RootUnitTestNode.cs

@ -0,0 +1,70 @@ @@ -0,0 +1,70 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Linq;
using System.Reflection;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Parser;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.TreeView;
using NUnit.Framework;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Description of RootUnitTestNode.
/// </summary>
public class RootUnitTestNode : UnitTestBaseNode
{
Solution solution;
public RootUnitTestNode(Solution solution)
{
this.solution = solution;
ProjectService.ProjectAdded += OnProjectAdded;
ProjectService.ProjectRemoved += OnProjectRemoved;
ParserService.LoadSolutionProjectsThreadEnded += delegate { LoadChildren(); };
LazyLoading = true;
}
void OnProjectRemoved(object sender, ProjectEventArgs e)
{
LoadChildren();
}
void OnProjectAdded(object sender, ProjectEventArgs e)
{
LoadChildren();
}
protected override void LoadChildren()
{
this.Children.Clear();
if (!ParserService.LoadSolutionProjectsThreadRunning)
this.Children.AddRange(solution.Projects.Where(p => p.IsTestProject()).Select(p => new ProjectUnitTestNode(new TestProject(p))));
}
public override object Text {
get { return ResourceService.GetString("ICSharpCode.UnitTesting.AllTestsTreeNode.Text"); }
}
}
public static class Extensions
{
public static bool IsTestProject(this IProject project)
{
if (project == null)
throw new ArgumentNullException("project");
if (project.ProjectContent == null)
return false;
return ParserService.GetCompilation(project).FindType(typeof(TestAttribute)).Kind != TypeKind.Unknown;
}
}
}

98
src/AddIns/Analysis/UnitTesting/Nodes/TestClassTreeNode.cs

@ -0,0 +1,98 @@ @@ -0,0 +1,98 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Represents a class that has the [TestFixture] attribute
/// associated with it.
/// </summary>
public class TestClassTreeNode : TestTreeNode
{
TestClass testClass;
public TestClassTreeNode(TestProject project, TestClass testClass)
: base(project, testClass.Name)
{
this.testClass = testClass;
testClass.ResultChanged += TestClassResultChanged;
Nodes.Add(new ExtTreeNode());
UpdateImageListIndex(testClass.Result);
}
/// <summary>
/// Gets the underlying IClass for this test class.
/// </summary>
public itd Class {
get { return testClass.Class; }
}
public override void Dispose()
{
if (!IsDisposed) {
testClass.ResultChanged -= TestClassResultChanged;
testClass.TestMembers.TestMemberAdded -= TestMemberAdded;
testClass.TestMembers.TestMemberRemoved -= TestMemberRemoved;
}
base.Dispose();
}
protected override void Initialize()
{
Nodes.Clear();
foreach (TestMember member in testClass.TestMembers) {
AddTestMemberTreeNode(member);
}
testClass.TestMembers.TestMemberAdded += TestMemberAdded;
testClass.TestMembers.TestMemberRemoved += TestMemberRemoved;
}
/// <summary>
/// Adds a new TestMemberTreeNode to this node.
/// </summary>
void AddTestMemberTreeNode(TestMember member)
{
TestMemberTreeNode node = new TestMemberTreeNode(TestProject, member);
node.AddTo(this);
}
/// <summary>
/// Updates the node's icon based on the test class test result.
/// </summary>
void TestClassResultChanged(object source, EventArgs e)
{
UpdateImageListIndex(testClass.Result);
}
/// <summary>
/// Adds a new test member tree node to this class node after a new
/// TestMember has been added to the TestClass.
/// </summary>
void TestMemberAdded(object source, TestMemberEventArgs e)
{
AddTestMemberTreeNode(e.TestMember);
SortChildNodes();
}
/// <summary>
/// Removes the corresponding test member node after it has been
/// removed from the TestClass.
/// </summary>
void TestMemberRemoved(object source, TestMemberEventArgs e)
{
foreach (TestMemberTreeNode memberNode in Nodes) {
if (memberNode.Text == e.TestMember.Name) {
Nodes.Remove(memberNode);
memberNode.Dispose();
break;
}
}
}
}
}

53
src/AddIns/Analysis/UnitTesting/Nodes/TestMemberTreeNode.cs

@ -0,0 +1,53 @@ @@ -0,0 +1,53 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Represents a member that has the [Test] attribute associated with it.
/// </summary>
public class TestMemberTreeNode : TestTreeNode
{
TestMember testMember;
public TestMemberTreeNode(TestProject project, TestMember testMember)
: base(project, testMember.Name)
{
this.testMember = testMember;
testMember.ResultChanged += TestMemberResultChanged;
UpdateImageListIndex(testMember.Result);
}
/// <summary>
/// Gets the underlying IMember for this test member.
/// </summary>
public IMember Member {
get { return testMember.Member; }
}
/// <summary>
/// Removes the TestMember.ResultChanged event handler.
/// </summary>
public override void Dispose()
{
if (!IsDisposed) {
testMember.ResultChanged -= TestMemberResultChanged;
}
base.Dispose();
}
/// <summary>
/// Updates the node's icon after the test member result
/// has changed.
/// </summary>
void TestMemberResultChanged(object source, EventArgs e)
{
UpdateImageListIndex(testMember.Result);
}
}
}

55
src/AddIns/Analysis/UnitTesting/Nodes/TestMethodTreeNode.cs

@ -0,0 +1,55 @@ @@ -0,0 +1,55 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Represents a method that has the [Test] attribute associated with it.
/// </summary>
public class TestMethodTreeNode : TestTreeNode
{
TestMethod testMethod;
public TestMethodTreeNode(TestProject project, TestMethod testMethod)
: base(project, testMethod.Name)
{
this.testMethod = testMethod;
testMethod.ResultChanged += TestMethodResultChanged;
UpdateImageListIndex(testMethod.Result);
}
/// <summary>
/// Gets the underlying IMethod for this test method.
/// </summary>
public IMember Method {
get {
return testMethod.Method;
}
}
/// <summary>
/// Removes the TestMethod.ResultChanged event handler.
/// </summary>
public override void Dispose()
{
if (!IsDisposed) {
testMethod.ResultChanged -= TestMethodResultChanged;
}
base.Dispose();
}
/// <summary>
/// Updates the node's icon after the test method result
/// has changed.
/// </summary>
void TestMethodResultChanged(object source, EventArgs e)
{
UpdateImageListIndex(testMethod.Result);
}
}
}

336
src/AddIns/Analysis/UnitTesting/Nodes/TestNamespaceTreeNode.cs

@ -0,0 +1,336 @@ @@ -0,0 +1,336 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Represents a namespace in the TestTreeView.
/// </summary>
public class TestNamespaceTreeNode : TestTreeNode
{
string ns = String.Empty;
string namespacePrefix = String.Empty;
string fullNamespace;
TestClassCollection testClasses;
List<TestNamespaceTreeNode> namespaceChildNodes = new List<TestNamespaceTreeNode>();
ExtTreeNode dummyNode;
/// <summary>
/// Creates a new TestNamespaceTreeNode
/// </summary>
/// <remarks>
/// Note that the Namespace child nodes are added in
/// the constructor not whilst the node is expanding
/// via the Initialize method. This is so the icon for the
/// node can be updated even if the parent node is not
/// expanded. The alternative is to have each namespace node,
/// even if it does not have any class child nodes, to
/// store all the classes that are below it in the tree and
/// update the icon based on their results. The assumption
/// is that there are fewer namespace nodes than classes so
/// adding the namespace nodes here does not matter.
/// </remarks>
/// <param name="namespacePrefix">The first part of the
/// namespace (including any dot characters) before this
/// particular namespace.</param>
/// <param name="name">The name of the namespace without any
/// dot characters (e.g. the name at this particular
/// location in the tree).</param>
public TestNamespaceTreeNode(TestProject testProject, string namespacePrefix, string name)
: base(testProject, name)
{
ns = name;
this.namespacePrefix = namespacePrefix;
fullNamespace = GetFullNamespace(namespacePrefix, ns);
GetTestClasses();
testProject.TestClasses.TestClassAdded += TestClassAdded;
testProject.TestClasses.TestClassRemoved += TestClassRemoved;
// Add namespace nodes - do not add them on node expansion.
foreach (string namespaceName in TestProject.GetChildNamespaces(fullNamespace)) {
TestNamespaceTreeNode node = new TestNamespaceTreeNode(TestProject, fullNamespace, namespaceName);
node.AddTo(this);
namespaceChildNodes.Add(node);
node.ImageIndexChanged += TestNamespaceNodeImageIndexChanged;
}
// Add a dummy node if there are no namespaces since
// there might be class nodes which will be added
// lazily when the node is expanded.
if (namespaceChildNodes.Count == 0) {
dummyNode = new ExtTreeNode();
Nodes.Add(dummyNode);
}
UpdateImageListIndex();
}
/// <summary>
/// Creates a new TestNamespaceTreeNode with the specified
/// namespace name. This node will have no namespace prefix.
/// </summary>
public TestNamespaceTreeNode(TestProject testProject, string name)
: this(testProject, String.Empty, name)
{
}
/// <summary>
/// Frees any resources held by this class.
/// </summary>
public override void Dispose()
{
if (!IsDisposed) {
TestProject.TestClasses.TestClassAdded -= TestClassAdded;
TestProject.TestClasses.TestClassRemoved -= TestClassRemoved;
testClasses.ResultChanged -= TestClassesResultChanged;
}
base.Dispose();
}
/// <summary>
/// Gets whether this namespace node is considered empty. If
/// the node has been expanded then the node is empty if it
/// has no child nodes. If it has not been expanded then there
/// will be a dummy child node or a set of namespace
/// nodes but no test class nodes.
/// </summary>
public bool IsEmpty {
get {
if (isInitialized) {
return Nodes.Count == 0;
} else if (dummyNode != null) {
return testClasses.Count == 0;
} else {
return Nodes.Count == 0 && testClasses.Count == 0;
}
}
}
/// <summary>
/// Gets the full namespace of this tree node. This includes any
/// parent namespaces prefixed to the namespace associated
/// with this tree node.
/// </summary>
public string FullNamespace {
get {
return fullNamespace;
}
}
/// <summary>
/// Adds the test class nodes for this namespace when the
/// node is expanded.
/// </summary>
protected override void Initialize()
{
if (dummyNode != null) {
Nodes.Clear();
}
// Add class nodes for this namespace.
foreach (TestClass c in testClasses) {
TestClassTreeNode classNode = new TestClassTreeNode(TestProject, c);
classNode.AddTo(this);
}
// Sort the nodes.
SortChildNodes();
}
/// <summary>
/// Adds the child namespace to the namespace prefix.
/// </summary>
static string GetFullNamespace(string prefix, string name)
{
if (prefix.Length > 0) {
return String.Concat(prefix, ".", name);
}
return name;
}
/// <summary>
/// Updates this node's icon because one of its child namespace nodes
/// has changed.
/// </summary>
void TestNamespaceNodeImageIndexChanged(object source, EventArgs e)
{
UpdateImageListIndex();
}
/// <summary>
/// Gets the test classes for this namespace.
/// </summary>
void GetTestClasses()
{
testClasses = new TestClassCollection();
foreach (TestClass c in TestProject.GetTestClasses(fullNamespace)) {
testClasses.Add(c);
}
testClasses.ResultChanged += TestClassesResultChanged;
}
/// <summary>
/// The overall test result for the classes in this namespace
/// have changed so the node's icon is updated.
/// </summary>
void TestClassesResultChanged(object source, EventArgs e)
{
UpdateImageListIndex();
}
/// <summary>
/// Determines the image list index for this node based
/// on the current state of all the child nodes.
/// </summary>
/// <remarks>
/// Since the test classes overall test result is
/// available via the TestClassCollection we do not
/// need to sum all the individual test classes.
/// </remarks>
void UpdateImageListIndex()
{
int ignoredCount = 0;
int passedCount = 0;
int failedCount = 0;
// Count the passes, failures and ignores for the
// namespace child nodes.
foreach (ExtTreeNode node in namespaceChildNodes) {
switch (node.ImageIndex) {
case (int)TestTreeViewImageListIndex.TestFailed:
failedCount++;
break;
case (int)TestTreeViewImageListIndex.TestIgnored:
ignoredCount++;
break;
case (int)TestTreeViewImageListIndex.TestPassed:
passedCount++;
break;
}
}
// Check the passes, failures and ignores for the
// test classes that belong to this namespace.
switch (testClasses.Result) {
case TestResultType.Failure:
failedCount++;
break;
case TestResultType.Success:
passedCount++;
break;
case TestResultType.Ignored:
ignoredCount++;
break;
}
// Work out the total number of passes we are expecting
int total = namespaceChildNodes.Count;
if (testClasses.Count > 0) {
// Only add one for the testClasses since the
// overall pass or failure is indicated via
// the TestClassCollection so we do not need
// to add all the test classes.
total++;
}
// Determine the correct image list index for this node.
if (failedCount > 0) {
UpdateImageListIndex(TestResultType.Failure);
} else if (ignoredCount > 0) {
UpdateImageListIndex(TestResultType.Ignored);
} else if (passedCount == total) {
UpdateImageListIndex(TestResultType.Success);
} else {
UpdateImageListIndex(TestResultType.None);
}
}
/// <summary>
/// A new test class has been added to the project so a new
/// tree node is added if the class belongs to this namespace.
/// </summary>
void TestClassAdded(object source, TestClassEventArgs e)
{
if (e.TestClass.Namespace == fullNamespace) {
// Add test class to our monitored test classes.
testClasses.Add(e.TestClass);
// Add a new tree node.
TestClassTreeNode classNode = new TestClassTreeNode(TestProject, e.TestClass);
classNode.AddTo(this);
// Sort the nodes.
SortChildNodes();
} else if (isInitialized && NamespaceStartsWith(e.TestClass.Namespace)) {
// Check if there is a child namespace node for the class.
string childNamespace = TestClass.GetChildNamespace(e.TestClass.Namespace, fullNamespace);
if (!NamespaceNodeExists(childNamespace)) {
// Add a new namespace node.
TestNamespaceTreeNode node = new TestNamespaceTreeNode(TestProject, fullNamespace, childNamespace);
node.AddTo(this);
// Sort the nodes.
SortChildNodes();
}
}
}
/// <summary>
/// Determines whether the namespace for this node starts
/// with the namespace specified.
/// </summary>
bool NamespaceStartsWith(string ns)
{
return ns.StartsWith(String.Concat(fullNamespace, "."));
}
/// <summary>
/// A test class has been removed from the project so the
/// corresponding tree node is removed if it belongs to this
/// namespace.
/// </summary>
void TestClassRemoved(object source, TestClassEventArgs e)
{
if (e.TestClass.Namespace == fullNamespace) {
// Remove test class from our monitored test classes.
testClasses.Remove(e.TestClass);
// Remove the corresponding tree node.
foreach (ExtTreeNode node in Nodes) {
TestClassTreeNode classNode = node as TestClassTreeNode;
if (classNode != null && classNode.Text == e.TestClass.Name) {
classNode.Remove();
classNode.Dispose();
break;
}
}
// Remove this namespace node if there are no more child nodes.
RemoveIfEmpty();
}
}
/// <summary>
/// Removes this node if it has no child nodes. This
/// method also calls the same method on the parent
/// namespace node so it can check whether it should
/// remove itself.
/// </summary>
void RemoveIfEmpty()
{
if (IsEmpty) {
Remove();
Dispose();
TestNamespaceTreeNode parentNode = Parent as TestNamespaceTreeNode;
if (parentNode != null) {
parentNode.RemoveIfEmpty();
}
}
}
}
}

113
src/AddIns/Analysis/UnitTesting/Nodes/TestProjectTreeNode.cs

@ -0,0 +1,113 @@ @@ -0,0 +1,113 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// The root tree node for a project that has tests.
/// </summary>
public class TestProjectTreeNode : TestTreeNode
{
public TestProjectTreeNode(TestProject project)
: base(project, project.Name)
{
Nodes.Add(new ExtTreeNode());
TestProject.TestClasses.ResultChanged += TestClassesResultChanged;
TestProject.TestClasses.TestClassAdded += TestClassAdded;
TestProject.TestClasses.TestClassRemoved += TestClassRemoved;
}
public override void Dispose()
{
if (!IsDisposed) {
TestProject.TestClasses.ResultChanged -= TestClassesResultChanged;
TestProject.TestClasses.TestClassAdded -= TestClassAdded;
TestProject.TestClasses.TestClassRemoved -= TestClassRemoved;
}
base.Dispose();
}
/// <summary>
/// Adds the child nodes after this node has been expanded.
/// </summary>
protected override void Initialize()
{
Nodes.Clear();
// Add namespace nodes.
foreach (string rootNamespace in TestProject.RootNamespaces) {
TestNamespaceTreeNode node = new TestNamespaceTreeNode(TestProject, rootNamespace);
node.AddTo(this);
}
// Add class nodes.
foreach (TestClass c in TestProject.GetTestClasses(String.Empty)) {
AddClassNode(c);
}
// Sort the nodes.
SortChildNodes();
}
/// <summary>
/// Updates this node's icon based on the overall result of the
/// test classes.
/// </summary>
void TestClassesResultChanged(object source, EventArgs e)
{
UpdateImageListIndex(TestProject.TestClasses.Result);
}
/// <summary>
/// Adds a new class node to this project node if the
/// class added has no root namespace.
/// </summary>
void TestClassAdded(object source, TestClassEventArgs e)
{
if (e.TestClass.Namespace == String.Empty) {
AddClassNode(e.TestClass);
SortChildNodes();
} else if (isInitialized) {
// Check that we have a namespace node for this class.
if (!NamespaceNodeExists(e.TestClass.RootNamespace)) {
// Add a new namespace node.
TestNamespaceTreeNode node = new TestNamespaceTreeNode(TestProject, e.TestClass.RootNamespace);
node.AddTo(this);
SortChildNodes();
}
}
}
/// <summary>
/// Removes the corresponding tree node that is a child of
/// this project tree node if the class has no root namespace.
/// </summary>
void TestClassRemoved(object source, TestClassEventArgs e)
{
if (e.TestClass.Namespace == String.Empty) {
foreach (ExtTreeNode node in Nodes) {
TestClassTreeNode classNode = node as TestClassTreeNode;
if (classNode != null && classNode.Text == e.TestClass.Name) {
classNode.Remove();
classNode.Dispose();
break;
}
}
}
}
/// <summary>
/// Adds a new TestClassTreeNode to this node.
/// </summary>
void AddClassNode(TestClass c)
{
TestClassTreeNode node = new TestClassTreeNode(TestProject, c);
node.AddTo(this);
}
}
}

103
src/AddIns/Analysis/UnitTesting/Nodes/TestTreeNode.cs

@ -0,0 +1,103 @@ @@ -0,0 +1,103 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.TreeView;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Base class for all test tree nodes.
/// </summary>
public abstract class TestTreeNode : SharpTreeNode
{
TestProject testProject;
public event EventHandler ImageIndexChanged;
public TestTreeNode(TestProject testProject, string text)
{
this.testProject = testProject;
this.Text = text;
ImageIndex = (int)TestTreeViewImageListIndex.TestNotRun;
}
public IProject Project {
get {
return testProject.Project;
}
}
public TestProject TestProject {
get {
return testProject;
}
}
/// <summary>
/// Changes the node's icon based on the specified test result.
/// </summary>
protected void UpdateImageListIndex(TestResultType result)
{
int previousImageIndex = ImageIndex;
switch (result) {
case TestResultType.Failure:
ImageIndex = (int)TestTreeViewImageListIndex.TestFailed;
break;
case TestResultType.Success:
ImageIndex = (int)TestTreeViewImageListIndex.TestPassed;
break;
case TestResultType.Ignored:
ImageIndex = (int)TestTreeViewImageListIndex.TestIgnored;
break;
case TestResultType.None:
ImageIndex = (int)TestTreeViewImageListIndex.TestNotRun;
break;
}
SelectedImageIndex = ImageIndex;
if (previousImageIndex != ImageIndex) {
OnImageIndexChanged();
}
}
/// <summary>
/// Raises the ImageIndexChanged event.
/// </summary>
protected void OnImageIndexChanged()
{
if (ImageIndexChanged != null) {
ImageIndexChanged(this, new EventArgs());
}
}
/// <summary>
/// Checks all the child nodes of this node to see
/// if the root namespace (i.e. the first part of the
/// namespace) exists in the tree.
/// </summary>
/// <param name="rootNamespace">The first dotted part
/// of the namespace.</param>
protected bool NamespaceNodeExists(string rootNamespace)
{
foreach (ExtTreeNode node in Nodes) {
TestNamespaceTreeNode namespaceNode = node as TestNamespaceTreeNode;
if (namespaceNode != null && namespaceNode.Text == rootNamespace) {
return true;
}
}
return false;
}
/// <summary>
/// Sorts the immediate child nodes of this node. The sort
/// is not done recursively.
/// </summary>
protected void SortChildNodes()
{
ExtTreeView treeView = (ExtTreeView)TreeView;
treeView.SortNodes(Nodes, false);
}
}
}

44
src/AddIns/Analysis/UnitTesting/Nodes/UnitTestBaseNode.cs

@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Linq;
using ICSharpCode.TreeView;
namespace ICSharpCode.UnitTesting
{
public abstract class UnitTestBaseNode : SharpTreeNode
{
internal virtual TestResultType TestResultType {
get {
if (Children.Count == 0) return TestResultType.None;
if (Children.OfType<UnitTestBaseNode>().Any(node => node.TestResultType == TestResultType.Failure))
return TestResultType.Failure;
if (Children.OfType<UnitTestBaseNode>().Any(node => node.TestResultType == TestResultType.None))
return TestResultType.None;
if (Children.OfType<UnitTestBaseNode>().Any(node => node.TestResultType == TestResultType.Ignored))
return TestResultType.Ignored;
return TestResultType.Success;
}
}
public override object Icon {
get { return GetIcon(TestResultType); }
}
object GetIcon(TestResultType testResultType)
{
switch (testResultType) {
case TestResultType.None:
return Images.Grey;
case TestResultType.Success:
return Images.Green;
case TestResultType.Failure:
return Images.Red;
case TestResultType.Ignored:
return Images.Yellow;
default:
throw new Exception("Invalid value for TestResultType");
}
}
}
}

51
src/AddIns/Analysis/UnitTesting/RemovedClasses.cs

@ -0,0 +1,51 @@ @@ -0,0 +1,51 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Used to determine which classes have been removed by the user in the project.
/// </summary>
public class RemovedClasses
{
Dictionary<string, IClass> classDictionary = new Dictionary<string, IClass>();
public RemovedClasses()
{
}
public void Add(IList<IClass> classes)
{
foreach (IClass c in classes) {
classDictionary[c.DotNetName] = c;
foreach (IClass innerClass in c.InnerClasses) {
classDictionary[innerClass.DotNetName] = innerClass;
}
}
}
/// <summary>
///
/// </summary>
/// <param name="c"></param>
public void Remove(IClass c)
{
classDictionary.Remove(c.DotNetName);
foreach (IClass innerClass in c.InnerClasses) {
classDictionary.Remove(innerClass.DotNetName);
}
}
public IList<IClass> GetMissingClasses()
{
List<IClass> missingClasses = new List<IClass>();
foreach (IClass c in classDictionary.Values) {
missingClasses.Add(c);
}
return missingClasses;
}
}
}

23
src/AddIns/Analysis/UnitTesting/Resources/Images.cs

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Windows.Media.Imaging;
namespace ICSharpCode.UnitTesting
{
static class Images
{
static BitmapImage LoadBitmap(string name)
{
BitmapImage image = new BitmapImage(new Uri("pack://application:,,,/UnitTesting;component/Resources/" + name + ".png"));
image.Freeze();
return image;
}
public static readonly BitmapImage Grey = LoadBitmap("Grey");
public static readonly BitmapImage Green = LoadBitmap("Green");
public static readonly BitmapImage Red = LoadBitmap("Red");
public static readonly BitmapImage Yellow = LoadBitmap("Yellow");
}
}

42
src/AddIns/Analysis/UnitTesting/RunProjectTestsInPadCommand.cs

@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
public class RunProjectTestsInPadCommand : RunTestInPadCommand, ITestTreeView
{
public RunProjectTestsInPadCommand()
{
}
public RunProjectTestsInPadCommand(IRunTestCommandContext context)
: base(context)
{
}
public override void Run()
{
Owner = this;
base.Run();
}
public IMember SelectedMember {
get { return null; }
}
public IClass SelectedClass {
get { return null; }
}
public IProject SelectedProject {
get { return ProjectService.CurrentProject; }
}
public string SelectedNamespace {
get { return null; }
}
}
}

68
src/AddIns/Analysis/UnitTesting/RunTestCommandContext.cs

@ -0,0 +1,68 @@ @@ -0,0 +1,68 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
public class RunTestCommandContext : IRunTestCommandContext
{
UnitTestingOptions options = UnitTestingOptions.Instance.Clone();
IRegisteredTestFrameworks testFrameworks = TestService.RegisteredTestFrameworks;
UnitTestTaskService taskService = new UnitTestTaskService();
UnitTestWorkbench workbench = new UnitTestWorkbench();
UnitTestBuildProjectFactory buildProjectFactory = new UnitTestBuildProjectFactory();
UnitTestBuildOptions buildOptions = new UnitTestBuildOptions();
MessageViewCategory unitTestCategory = TestService.UnitTestMessageView;
UnitTestSaveAllFilesCommand saveAllFilesCommand = new UnitTestSaveAllFilesCommand();
IStatusBarService statusBarService = SD.StatusBar;
public IRegisteredTestFrameworks RegisteredTestFrameworks {
get { return testFrameworks; }
}
public IUnitTestTaskService TaskService {
get { return taskService; }
}
public IUnitTestWorkbench Workbench {
get { return workbench; }
}
public IBuildProjectFactory BuildProjectFactory {
get { return buildProjectFactory; }
}
public IBuildOptions BuildOptions {
get { return buildOptions; }
}
public MessageViewCategory UnitTestCategory {
get { return unitTestCategory; }
}
public IUnitTestsPad OpenUnitTestsPad {
get { return null; }
}
public IMessageService MessageService {
get { return SD.MessageService; }
}
public IUnitTestSaveAllFilesCommand SaveAllFilesCommand {
get { return saveAllFilesCommand; }
}
public IStatusBarService StatusBarService {
get { return statusBarService; }
}
public Solution OpenSolution {
get { return ProjectService.OpenSolution; }
}
}
}

47
src/AddIns/Analysis/UnitTesting/Service/InnerClassEnumerator.cs

@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ICSharpCode.UnitTesting
{
public class InnerClassEnumerator : IEnumerable<IClass>
{
readonly IClass _c;
public InnerClassEnumerator(IClass c)
{
_c = c;
}
#region IEnumerable<IClass> Members
public IEnumerator<IClass> GetEnumerator()
{
var result = new List<IClass>();
Visit(_c, result);
return result.GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
private void Visit(IClass c, List<IClass> resultList)
{
foreach (var innerClass in c.InnerClasses)
{
resultList.Add(innerClass);
Visit(innerClass, resultList);
}
}
}
}

23
src/AddIns/Analysis/UnitTesting/Service/MessageReceivedEventArgs.cs

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
namespace ICSharpCode.UnitTesting
{
public delegate void MessageReceivedEventHandler(object sender, MessageReceivedEventArgs e);
public class MessageReceivedEventArgs : EventArgs
{
string message;
public MessageReceivedEventArgs(string message)
{
this.message = message;
}
public string Message {
get { return message; }
}
}
}

116
src/AddIns/Analysis/UnitTesting/Service/RegisteredTestFrameworks.cs

@ -0,0 +1,116 @@ @@ -0,0 +1,116 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
public class RegisteredTestFrameworks : IRegisteredTestFrameworks
{
List<TestFrameworkDescriptor> testFrameworkDescriptors;
public const string AddInPath = "/SharpDevelop/UnitTesting/TestFrameworks";
public RegisteredTestFrameworks(IAddInTree addInTree)
{
testFrameworkDescriptors = addInTree.BuildItems<TestFrameworkDescriptor>(AddInPath, null);
}
public ITestFramework GetTestFrameworkForProject(IProject project)
{
if (project != null) {
foreach (TestFrameworkDescriptor descriptor in testFrameworkDescriptors) {
if (descriptor.IsSupportedProject(project) && descriptor.TestFramework.IsTestProject(project)) {
return descriptor.TestFramework;
}
}
}
return null;
}
public bool IsTestMember(IUnresolvedMember member)
{
ITestFramework testFramework = GetTestFramework(member);
if (testFramework != null) {
return testFramework.IsTestMember(member);
}
return false;
}
public IEnumerable<IUnresolvedMember> GetTestMembersFor(IUnresolvedTypeDefinition @class) {
ITestFramework testFramework = GetTestFramework(@class);
if (testFramework != null)
return testFramework.GetTestMembersFor(@class);
return new IUnresolvedMember[0];
}
ITestFramework GetTestFramework(IUnresolvedMember member)
{
if (member != null) {
return GetTestFramework(member.DeclaringTypeDefinition);
}
return null;
}
ITestFramework GetTestFramework(IUnresolvedTypeDefinition c)
{
IProject project = GetProject(c);
return GetTestFrameworkForProject(project);
}
IProject GetProject(IUnresolvedTypeDefinition c)
{
if (c != null && ProjectService.OpenSolution != null) {
return ProjectService.OpenSolution.FindProjectContainingFile(c.ParsedFile.FileName);
}
return null;
}
public bool IsTestClass(IUnresolvedTypeDefinition c)
{
ITestFramework testFramework = GetTestFramework(c);
if (testFramework != null) {
return testFramework.IsTestClass(c);
}
return false;
}
public bool IsTestProject(IProject project)
{
ITestFramework testFramework = GetTestFrameworkForProject(project);
if (testFramework != null) {
return testFramework.IsTestProject(project);
}
return false;
}
public ITestRunner CreateTestRunner(IProject project)
{
ITestFramework testFramework = GetTestFrameworkForProject(project);
if (testFramework != null) {
return testFramework.CreateTestRunner();
}
return null;
}
public ITestRunner CreateTestDebugger(IProject project)
{
ITestFramework testFramework = GetTestFrameworkForProject(project);
if (testFramework != null) {
return testFramework.CreateTestDebugger();
}
return null;
}
public bool IsBuildNeededBeforeTestRunForProject(IProject project)
{
ITestFramework testFramework = GetTestFrameworkForProject(project);
if (testFramework != null) {
return testFramework.IsBuildNeededBeforeTestRun;
}
return true;
}
}
}

19
src/AddIns/Analysis/UnitTesting/Service/RunningTestsCondition.cs

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.Core;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Determines whether #develop is currently running unit tests.
/// </summary>
public class RunningTestsCondition : IConditionEvaluator
{
public bool IsValid(object caller, Condition condition)
{
return AbstractRunTestCommand.IsRunningTest;
}
}
}

96
src/AddIns/Analysis/UnitTesting/Service/SelectedTests.cs

@ -0,0 +1,96 @@ @@ -0,0 +1,96 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
public class SelectedTests
{
string namespaceFilter;
TestClass c;
// TestMember member;
List<IProject> projects = new List<IProject>();
public SelectedTests(IProject project, string namespaceFilter, TestClass c)//, TestMember member)
{
this.namespaceFilter = namespaceFilter;
this.c = c;
// this.member = member;
if (project != null) {
projects.Add(project);
}
}
public SelectedTests(IProject project)
: this(project, null, null)//, null)
{
}
public SelectedTests(object owner, IProject[] selectedProjects)
{
// IProject project = TestableCondition.GetProject(owner);
// if (project != null) {
// projects.Add(project);
// } else {
// projects.AddRange(selectedProjects);
// }
//
// member = TestableCondition.GetMember(owner);
// c = GetClass(member, owner);
// namespaceFilter = TestableCondition.GetNamespace(owner);
}
// static TestClass GetClass(IMember member, Object owner)
// {
// if (member != null) {
// return member.DeclaringType;
// }
// return TestableCondition.GetClass(owner);
// }
public bool HasProjects {
get { return projects.Count > 0; }
}
public void RemoveFirstProject()
{
if (HasProjects) {
projects.RemoveAt(0);
}
}
public IEnumerable<IProject> Projects {
get { return projects; }
}
public int ProjectsCount {
get {return projects.Count;}
}
public IProject Project {
get {
if (projects.Count > 0) {
return projects[0];
}
return null;
}
}
public string NamespaceFilter {
get { return namespaceFilter; }
}
// public IUnresolvedTypeDefinition Class {
// get { return c; }
// }
//
// public IUnresolvedMember Member {
// get { return member; }
// }
}
}

383
src/AddIns/Analysis/UnitTesting/Service/TestClass.cs

@ -0,0 +1,383 @@ @@ -0,0 +1,383 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Linq;
using System.Collections.Generic;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Represents a class that can be tested.
/// </summary>
public class TestClass
{
List<IUnresolvedTypeDefinition> parts;
// TestMemberCollection testMembers;
TestResultType testResultType;
IRegisteredTestFrameworks testFrameworks;
/// <summary>
/// Raised when the test class result is changed.
/// </summary>
public event EventHandler ResultChanged;
public TestClass(IRegisteredTestFrameworks testFrameworks, params IUnresolvedTypeDefinition[] parts)
{
this.parts = new List<IUnresolvedTypeDefinition>(parts);
this.testFrameworks = testFrameworks;
}
/// <summary>
/// Gets the underlying IClass for this test class.
/// </summary>
public IEnumerable<IUnresolvedTypeDefinition> Parts {
get { return parts; }
}
/// <summary>
/// Gets the list of other (e.g. base types) classes where from which test members included in this test class come from.
/// </summary>
private readonly ICollection<string> baseClassesFQNames = new List<string>();
/// <summary>
/// Gets the test classes that exist in the specified namespace.
/// </summary>
public static TestClass[] GetTestClasses(ICollection<TestClass> classes, string ns)
{
List<TestClass> matchedClasses = new List<TestClass>();
foreach (TestClass c in classes) {
if (c.Namespace == ns) {
matchedClasses.Add(c);
}
}
return matchedClasses.ToArray();
}
/// <summary>
/// Gets the test classes that namespaces starts with the specified
/// string.
/// </summary>
public static TestClass[] GetAllTestClasses(ICollection<TestClass> classes, string namespaceStartsWith)
{
List<TestClass> matchedClasses = new List<TestClass>();
foreach (TestClass c in classes) {
if (c.Namespace.StartsWith(namespaceStartsWith)) {
matchedClasses.Add(c);
}
}
return matchedClasses.ToArray();
}
/// <summary>
/// Gets all child namespaces that starts with the specified string.
/// </summary>
/// <remarks>
/// If the starts with string is 'ICSharpCode' and there is a code coverage
/// method with a namespace of 'ICSharpCode.XmlEditor.Tests', then this
/// method will return 'XmlEditor' as one of its strings.
/// </remarks>
public static string[] GetChildNamespaces(ICollection<TestClass> classes, string parentNamespace) {
List<string> items = new List<string>();
foreach (TestClass c in classes) {
string ns = c.GetChildNamespace(parentNamespace);
if (ns.Length > 0) {
if (!items.Contains(ns)) {
items.Add(ns);
}
}
}
return items.ToArray();
}
/// <summary>
/// Gets the name of the class.
/// </summary>
public string Name {
get
{
// var currentClass = c;
// var name = c.Name;
// while(currentClass.DeclaringType != null)
// {
// name = String.Concat(currentClass.DeclaringType.Name, "+", name);
// currentClass = currentClass.DeclaringType;
// }
return "";//name;
}
}
/// <summary>
/// Gets the fully qualified name of the class.
/// </summary>
public string QualifiedName {
get { return ""; }// c.DotNetName; }
}
/// <summary>
/// Gets the namespace of this class.
/// </summary>
public string Namespace {
get {
// var currentClass = c;
// while (currentClass.DeclaringType != null)
// currentClass = currentClass.DeclaringType;
// return currentClass.Namespace;
throw new NotImplementedException();
}
}
/// <summary>
/// Gets the root namespace for this class.
/// </summary>
// public string RootNamespace {
// get { return GetRootNamespace(c.Namespace); }
// }
/// <summary>
/// Gets the test result for this class.
/// </summary>
public TestResultType Result {
get { return testResultType; }
set {
TestResultType previousTestResultType = testResultType;
testResultType = value;
if (previousTestResultType != testResultType) {
OnResultChanged();
}
}
}
/// <summary>
/// Gets the child namespace from the specified namespace
/// based on the parent namespace.
/// </summary>
/// <param name="parentNamespace">Can contain multiple namespaces
/// (e.g. ICSharpCode.XmlEditor).</param>
public static string GetChildNamespace(string ns, string parentNamespace)
{
if (parentNamespace.Length > 0) {
if (ns.StartsWith(String.Concat(parentNamespace, "."))) {
string end = ns.Substring(parentNamespace.Length + 1);
return GetRootNamespace(end);
}
return String.Empty;
}
return ns;
}
/// <summary>
/// Gets the child namespace based on the parent namespace
/// from this class.
/// </summary>
/// <param name="parentNamespace">Can contain multiple namespaces
/// (e.g. ICSharpCode.XmlEditor).</param>
public string GetChildNamespace(string parentNamespace)
{
return GetChildNamespace(Namespace, parentNamespace);
}
/// <summary>
/// Gets the test members in this class.
/// </summary>
// public TestMemberCollection TestMembers {
// get {
// if (testMembers == null) {
// GetTestMembers();
// }
// return testMembers;
// }
// }
/// <summary>
/// Gets the test member with the specified name.
/// </summary>
/// <returns>Null if the member cannot be found.</returns>
// public TestMember GetTestMember(string name)
// {
// if (TestMembers.Contains(name)) {
// return TestMembers[name];
// }
// return null;
// }
/// <summary>
/// Updates the test member with the specified test result.
/// </summary>
public void UpdateTestResult(TestResult testResult)
{
// TestMember member = null;
// string memberName = TestMember.GetMemberName(testResult.Name);
// if (memberName != null) {
// member = GetTestMember(memberName);
// if (member == null) {
// member = GetPrefixedTestMember(testResult.Name);
// }
// }
// if (member != null) {
// member.Result = testResult.ResultType;
// }
}
/// <summary>
/// Resets all the test results back to none.
/// </summary>
public void ResetTestResults()
{
Result = TestResultType.None;
// TestMembers.ResetTestResults();
}
/// <summary>
/// Updates the members and class based on the new class
/// information that has been parsed.
/// </summary>
public void UpdateClass(IUnresolvedTypeDefinition c)
{
#warning not implemented!
// this.c = c.GetCompoundClass();
//
// // Remove missing members.
// TestMemberCollection newTestMembers = GetTestMembers(this.c);
// TestMemberCollection existingTestMembers = TestMembers;
// for (int i = existingTestMembers.Count - 1; i >= 0; --i) {
// TestMember member = existingTestMembers[i];
// if (newTestMembers.Contains(member.Name)) {
// member.Update(newTestMembers[member.Name].Member);
// } else {
// existingTestMembers.RemoveAt(i);
// }
// }
//
// // Add new members.
// foreach (TestMember member in newTestMembers) {
// if (existingTestMembers.Contains(member.Name)) {
// } else {
// existingTestMembers.Add(member);
// }
// }
}
/// <summary>
/// Gets the first dotted part of the namespace.
/// </summary>
static string GetRootNamespace(string ns)
{
int index = ns.IndexOf('.');
if (index > 0) {
return ns.Substring(0, index);
}
return ns;
}
/// <summary>
/// Gets the test members for the class.
/// </summary>
void GetTestMembers()
{
// testMembers = GetTestMembers(c);
// testMembers.ResultChanged += TestMembersResultChanged;
}
/// <summary>
/// Gets the test members for the specified class.
/// </summary>
// TestMemberCollection GetTestMembers(IUnresolvedTypeDefinition c)
// {
// TestMemberCollection testMembers = new TestMemberCollection();
// foreach (var member in testFrameworks.GetTestMembersFor(c))
// if (!testMembers.Contains(member.Name)) {
// testMembers.Add(member);
// }
//
// // Add base class test members.
// IClass declaringType = c;
// while (c.BaseClass != null)
// {
// foreach (var testMember in testFrameworks.GetTestMembersFor(c.BaseClass)) {
// BaseTestMember baseTestMethod = new BaseTestMember(declaringType, testMember.Member);
// TestMember testMethod = new TestMember(c.BaseClass, baseTestMethod);
// if (testMember.Member.IsVirtual) {
// if (!testMembers.Contains(testMember.Name)) {
// testMembers.Add(testMethod);
// }
// } else {
// if (!testMembers.Contains(testMethod.Name)) {
// testMembers.Add(testMethod);
// }
// }
// }
// c = c.BaseClass;
// }
//
// baseClassesFQNames.Clear();
// foreach (var memberDeclaringClass in testMembers.Select(member => member.DeclaringType).Distinct())
// if (memberDeclaringClass.CompareTo(declaringType) != 0)
// baseClassesFQNames.Add(memberDeclaringClass.FullyQualifiedName);
// return testMembers;
// }
/// <summary>
/// Updates the test class's test result after the test member's
/// test result has changed.
/// </summary>
void TestMembersResultChanged(object source, EventArgs e)
{
// Result = testMembers.Result;
}
/// <summary>
/// Raises the ResultChanged event.
/// </summary>
void OnResultChanged()
{
if (ResultChanged != null) {
ResultChanged(this, new EventArgs());
}
}
/// <summary>
/// This function adds the base class as a prefix and tries to find
/// the corresponding test member.
///
/// Actual method name:
///
/// RootNamespace.TestFixture.TestFixtureBaseClass.TestMethod
/// </summary>
/// <remarks>
/// NUnit 2.4 uses the correct test method name when a test
/// class uses a base class with test methods. It does
/// not prefix the test method name with the base class name
/// in the test results returned from nunit-console. It still
/// displays the name in the NUnit GUI with the base class
/// name prefixed. Older versions of NUnit-console (2.2.9) returned
/// the test result with the test method name as follows:
///
/// RootNamespace.TestFixture.BaseTestFixture.TestMethod
///
/// The test method name would have the base class name prefixed
/// to it.
/// </remarks>
// TestMember GetPrefixedTestMember(string testResultName)
// {
// IClass baseClass = c.BaseClass;
// while (baseClass != null) {
// string memberName = TestMember.GetMemberName(testResultName);
// string actualMemberName = String.Concat(baseClass.Name, ".", memberName);
// TestMember member = GetTestMember(actualMemberName);
// if (member != null) {
// return member;
// }
// baseClass = baseClass.BaseClass;
// }
// return null;
// }
public bool IsDerivedFrom(IUnresolvedTypeDefinition c)
{
return baseClassesFQNames.Contains(c.FullName);
}
}
}

214
src/AddIns/Analysis/UnitTesting/Service/TestClassCollection.cs

@ -0,0 +1,214 @@ @@ -0,0 +1,214 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ICSharpCode.Core;
namespace ICSharpCode.UnitTesting
{
public class TestClassCollection : KeyedCollection<string, TestClass>
{
TestResultType testResult = TestResultType.None;
Dictionary<string, TestClass> passedTestClasses = new Dictionary<string, TestClass>();
Dictionary<string, TestClass> failedTestClasses = new Dictionary<string, TestClass>();
Dictionary<string, TestClass> ignoredTestClasses = new Dictionary<string, TestClass>();
/// <summary>
/// Raised when the test result for this collection of
/// classes has changed.
/// </summary>
public event EventHandler ResultChanged;
/// <summary>
/// Raised when a class is added to this collection.
/// </summary>
public event TestClassEventHandler TestClassAdded;
/// <summary>
/// Raised when a class is removed from this collection.
/// </summary>
public event TestClassEventHandler TestClassRemoved;
/// <summary>
/// Gets the overall test results for the collection of
/// test classes.
/// </summary>
public TestResultType Result {
get {
return testResult;
}
}
/// <summary>
/// Sets all the test class test results back to none.
/// </summary>
public void ResetTestResults()
{
passedTestClasses.Clear();
failedTestClasses.Clear();
ignoredTestClasses.Clear();
foreach (TestClass c in this) {
c.ResetTestResults();
}
SetTestResult(TestResultType.None);
}
/// <summary>
/// Updates the test method with the specified test result.
/// </summary>
public void UpdateTestResult(TestResult testResult)
{
TestClass testClass = GetTestClassFromTestMemberName(testResult.Name);
if (testClass != null) {
testClass.UpdateTestResult(testResult);
}
}
/// <summary>
/// Gets the matching test member from this set of classes.
/// </summary>
/// <param name="fullyQualifiedName">The fully qualified
/// method name (e.g. Namespace.ClassName.MethodName).</param>
/// <returns>Null if the method cannot be found.</returns>
public TestMember GetTestMember(string fullyQualifiedName)
{
string className = TestMember.GetQualifiedClassName(fullyQualifiedName);
if (className != null) {
if (Contains(className)) {
TestClass testClass = this[className];
string memberName = TestMember.GetMemberName(fullyQualifiedName);
if (memberName != null) {
return testClass.GetTestMember(memberName);
}
} else {
LoggingService.Debug("TestClass not found: " + className);
}
} else {
LoggingService.Debug("Invalid test member name: " + fullyQualifiedName);
}
return null;
}
protected override string GetKeyForItem(TestClass item)
{
return item.QualifiedName;
}
protected override void InsertItem(int index, TestClass item)
{
item.ResultChanged += TestClassResultChanged;
base.InsertItem(index, item);
TestClassResultChanged(item, new EventArgs());
OnTestClassAdded(item);
}
protected override void RemoveItem(int index)
{
TestClass c = this[index];
c.ResultChanged -= TestClassResultChanged;
base.RemoveItem(index);
OnTestResultNone(c.Name);
OnTestClassRemoved(c);
}
protected void OnTestClassAdded(TestClass testClass)
{
if (TestClassAdded != null) {
TestClassAdded(this, new TestClassEventArgs(testClass));
}
}
protected void OnTestClassRemoved(TestClass testClass)
{
if (TestClassRemoved != null) {
TestClassRemoved(this, new TestClassEventArgs(testClass));
}
}
void TestClassResultChanged(object source, EventArgs e)
{
TestClass c = (TestClass)source;
switch (c.Result) {
case TestResultType.None:
OnTestResultNone(c.QualifiedName);
break;
case TestResultType.Failure:
SetTestResult(TestResultType.Failure);
failedTestClasses.Add(c.QualifiedName, c);
break;
case TestResultType.Success:
passedTestClasses.Add(c.QualifiedName, c);
if (passedTestClasses.Count == Count) {
SetTestResult(TestResultType.Success);
} else if (passedTestClasses.Count + ignoredTestClasses.Count == Count) {
SetTestResult(TestResultType.Ignored);
}
break;
case TestResultType.Ignored:
ignoredTestClasses.Add(c.QualifiedName, c);
if (ignoredTestClasses.Count == Count ||
ignoredTestClasses.Count + passedTestClasses.Count == Count) {
SetTestResult(TestResultType.Ignored);
}
break;
}
}
void SetTestResult(TestResultType value)
{
TestResultType previousTestResult = testResult;
testResult = value;
if (testResult != previousTestResult) {
OnResultChanged();
}
}
void OnResultChanged()
{
if (ResultChanged != null) {
ResultChanged(this, new EventArgs());
}
}
/// <summary>
/// Removes the specified test class from the list of
/// failed, passed and ignored tests and updates the
/// test result state of the test class collection.
/// </summary>
void OnTestResultNone(string qualifiedName)
{
passedTestClasses.Remove(qualifiedName);
failedTestClasses.Remove(qualifiedName);
ignoredTestClasses.Remove(qualifiedName);
if (ignoredTestClasses.Count + failedTestClasses.Count == 0) {
SetTestResult(TestResultType.None);
}
}
/// <summary>
/// Gets the test class from the specified test result.
/// </summary>
TestClass GetTestClassFromTestMemberName(string memberName)
{
if (memberName != null) {
string className = TestMember.GetQualifiedClassName(memberName);
if (className != null) {
if (Contains(className)) {
return this[className];
} else {
LoggingService.Debug("TestClass not found: " + className);
return GetTestClassFromTestMemberName(className);
}
} else {
LoggingService.Debug("Invalid TestMember.Name: " + memberName);
}
}
return null;
}
}
}

30
src/AddIns/Analysis/UnitTesting/Service/TestClassEventArgs.cs

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Represents the class that will handle the TestCollection's
/// TestClassAdded or TestClassRemoved events.
/// </summary>
public delegate void TestClassEventHandler(object source, TestClassEventArgs e);
/// <summary>
/// Provides data for the TestCollection's TestClassAdded and TestClassRemoved events.
/// </summary>
public class TestClassEventArgs
{
TestClass testClass;
public TestClassEventArgs(TestClass testClass)
{
this.testClass = testClass;
}
public TestClass TestClass {
get { return testClass; }
}
}
}

108
src/AddIns/Analysis/UnitTesting/Service/TestDebuggerBase.cs

@ -0,0 +1,108 @@ @@ -0,0 +1,108 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Diagnostics;
using ICSharpCode.SharpDevelop.Debugging;
namespace ICSharpCode.UnitTesting
{
public abstract class TestDebuggerBase : TestRunnerBase
{
IUnitTestDebuggerService debuggerService;
IUnitTestMessageService messageService;
IDebugger debugger;
ITestResultsMonitor testResultsMonitor;
public TestDebuggerBase()
: this(new UnitTestDebuggerService(),
new UnitTestMessageService(),
new TestResultsMonitor())
{
}
public TestDebuggerBase(IUnitTestDebuggerService debuggerService,
IUnitTestMessageService messageService,
ITestResultsMonitor testResultsMonitor)
{
this.debuggerService = debuggerService;
this.messageService = messageService;
this.testResultsMonitor = testResultsMonitor;
this.debugger = debuggerService.CurrentDebugger;
testResultsMonitor.TestFinished += OnTestFinished;
}
protected ITestResultsMonitor TestResultsMonitor {
get { return testResultsMonitor; }
}
public override void Start(SelectedTests selectedTests)
{
ProcessStartInfo startInfo = GetProcessStartInfo(selectedTests);
if (IsDebuggerRunning) {
if (CanStopDebugging()) {
debugger.Stop();
Start(startInfo);
}
} else {
Start(startInfo);
}
}
public bool IsDebuggerRunning {
get { return debuggerService.IsDebuggerLoaded && debugger.IsDebugging; }
}
bool CanStopDebugging()
{
string question = "${res:XML.MainMenu.RunMenu.Compile.StopDebuggingQuestion}";
string caption = "${res:XML.MainMenu.RunMenu.Compile.StopDebuggingTitle}";
return messageService.AskQuestion(question, caption);
}
void Start(ProcessStartInfo startInfo)
{
testResultsMonitor.Start();
StartDebugger(startInfo);
}
void StartDebugger(ProcessStartInfo startInfo)
{
LogCommandLine(startInfo);
bool running = false;
debugger.DebugStopped += DebugStopped;
try {
debugger.Start(startInfo);
running = true;
} finally {
if (!running) {
debugger.DebugStopped -= DebugStopped;
}
}
}
void DebugStopped(object source, EventArgs e)
{
debugger.DebugStopped -= DebugStopped;
OnAllTestsFinished(source, e);
}
public override void Stop()
{
if (debugger.IsDebugging) {
debugger.Stop();
}
testResultsMonitor.Stop();
testResultsMonitor.Read();
}
public override void Dispose()
{
Stop();
testResultsMonitor.Dispose();
}
}
}

23
src/AddIns/Analysis/UnitTesting/Service/TestFinishedEventArgs.cs

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
namespace ICSharpCode.UnitTesting
{
public delegate void TestFinishedEventHandler(object source, TestFinishedEventArgs e);
public class TestFinishedEventArgs : EventArgs
{
TestResult result;
public TestFinishedEventArgs(TestResult result)
{
this.result = result;
}
public TestResult Result {
get { return result; }
}
}
}

90
src/AddIns/Analysis/UnitTesting/Service/TestFrameworkDescriptor.cs

@ -0,0 +1,90 @@ @@ -0,0 +1,90 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.IO;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
public class TestFrameworkDescriptor
{
Properties properties;
ITestFrameworkFactory factory;
ITestFramework testFramework;
List<string> supportedProjectFileExtensions = new List<string>();
public TestFrameworkDescriptor(Properties properties, ITestFrameworkFactory factory)
{
this.properties = properties;
this.factory = factory;
GetSupportedProjectFileExtensions();
}
void GetSupportedProjectFileExtensions()
{
string extensions = properties["supportedProjects"];
foreach (string extension in extensions.Split(';')) {
supportedProjectFileExtensions.Add(extension.ToLowerInvariant().Trim());
}
}
public string Id {
get { return properties["id"]; }
}
public ITestFramework TestFramework {
get {
CreateTestFrameworkIfNotCreated();
return testFramework;
}
}
void CreateTestFrameworkIfNotCreated()
{
if (testFramework == null) {
testFramework = factory.Create(ClassName);
}
}
string ClassName {
get { return properties["class"]; }
}
public bool IsSupportedProject(IProject project)
{
if (IsSupportedProjectFileExtension(project)) {
return IsSupportedByTestFramework(project);
}
return false;
}
bool IsSupportedProjectFileExtension(IProject project)
{
string extension = GetProjectFileExtension(project);
return IsSupportedProjectFileExtension(extension);
}
string GetProjectFileExtension(IProject project)
{
if (project != null) {
return Path.GetExtension(project.FileName).ToLowerInvariant();
}
return null;
}
bool IsSupportedProjectFileExtension(string extension)
{
return supportedProjectFileExtensions.Contains(extension);
}
bool IsSupportedByTestFramework(IProject project)
{
return TestFramework.IsTestProject(project);
}
}
}

30
src/AddIns/Analysis/UnitTesting/Service/TestFrameworkDoozer.cs

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections;
using ICSharpCode.Core;
namespace ICSharpCode.UnitTesting
{
public class TestFrameworkDoozer : IDoozer
{
public TestFrameworkDoozer()
{
}
public bool HandleConditions {
get { return false; }
}
public object BuildItem(BuildItemArgs args)
{
return BuildItem(args.Codon, new TestFrameworkFactory(args.AddIn));
}
public TestFrameworkDescriptor BuildItem(Codon codon, ITestFrameworkFactory factory)
{
return new TestFrameworkDescriptor(codon.Properties, factory);
}
}
}

23
src/AddIns/Analysis/UnitTesting/Service/TestFrameworkFactory.cs

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.Core;
namespace ICSharpCode.UnitTesting
{
public class TestFrameworkFactory : ITestFrameworkFactory
{
AddIn addin;
public TestFrameworkFactory(AddIn addin)
{
this.addin = addin;
}
public ITestFramework Create(string className)
{
return addin.CreateObject(className) as ITestFramework;
}
}
}

155
src/AddIns/Analysis/UnitTesting/Service/TestMember.cs

@ -0,0 +1,155 @@ @@ -0,0 +1,155 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Represents a member that can be tested.
/// </summary>
public class TestMember
{
#if unused
string prefix = String.Empty;
#endif
IUnresolvedMember member;
TestResultType testResultType = TestResultType.None;
/// <summary>
/// Raised when the test result has changed.
/// </summary>
public event EventHandler ResultChanged;
public TestMember(IUnresolvedMember member)
{
this.member = member;
this.DeclaringType = member.DeclaringTypeDefinition;
}
#if unused
/// <summary>
/// Creates a new TestMember instance.
/// </summary>
/// <param name="prefix">The prefix to be added to the
/// member name with a dot character. This is specified
/// when the test member refers to a base class method. NUnit
/// refers to base class test methods by prefixing the
/// class name to it:
///
/// [TestFixture]
/// public class DerivedClass : BaseClass
///
/// Test member name:
///
/// RootNamespace.DerivedClass.BaseClass.BaseClassMethod
/// </param>
public TestMember(string prefix, IUnresolvedMember member)
{
this.member = member;
this.prefix = prefix;
}
#endif
public TestMember(IUnresolvedTypeDefinition testClass, IUnresolvedMember member)
{
this.DeclaringType = testClass;
this.member = member;
}
/// <summary>
/// Gets the underlying IMember for this TestMember.
/// </summary>
public IUnresolvedMember Member {
get { return member; }
}
/// <summary>
/// Gets the class where the test member is actually declared.
/// In case of test member from base class it will be the base class not the
/// </summary>
public IUnresolvedTypeDefinition DeclaringType { get; private set; }
/// <summary>
/// Updates the test member based on new information
/// in the specified IMember.
/// </summary>
public void Update(IMember member)
{
this.member = member;
}
/// <summary>
/// Gets the test result for this member.
/// </summary>
public TestResultType Result {
get { return testResultType; }
set {
TestResultType previousTestResultType = testResultType;
testResultType = value;
if (previousTestResultType != testResultType) {
OnResultChanged();
}
}
}
/// <summary>
/// Gets the member's name.
/// </summary>
public string Name {
get {
#if unused
if (prefix.Length > 0) {
return String.Concat(prefix, ".", member.Name);
}
#endif
return member.Name;
}
}
/// <summary>
/// Returns the member name from a fully qualified name
/// of the form:
///
/// Namespace.Class.Member
/// </summary>
public static string GetMemberName(string qualifiedName)
{
if (qualifiedName != null) {
int index = qualifiedName.LastIndexOf('.');
if (index >= 0) {
return qualifiedName.Substring(index + 1);
}
}
return null;
}
/// <summary>
/// Returns the namespace and class name from a fully qualified
/// name of the form:
///
/// Namespace.Class.Member
/// </summary>
public static string GetQualifiedClassName(string qualifiedName)
{
if (qualifiedName != null) {
int memberIndex = qualifiedName.LastIndexOf('.');
if (memberIndex >= 0) {
return qualifiedName.Substring(0, memberIndex);
}
}
return null;
}
/// <summary>
/// Raises the ResultChanged event.
/// </summary>
void OnResultChanged()
{
if (ResultChanged != null) {
ResultChanged(this, new EventArgs());
}
}
}
}

153
src/AddIns/Analysis/UnitTesting/Service/TestMemberCollection.cs

@ -0,0 +1,153 @@ @@ -0,0 +1,153 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace ICSharpCode.UnitTesting
{
public class TestMemberCollection : KeyedCollection<string, TestMember>
{
TestResultType testResult = TestResultType.None;
Dictionary<string, TestMember> passedTestMembers = new Dictionary<string, TestMember>();
Dictionary<string, TestMember> failedTestMembers = new Dictionary<string, TestMember>();
Dictionary<string, TestMember> ignoredTestMembers = new Dictionary<string, TestMember>();
/// <summary>
/// Raised when the test result for this collection of
/// members has changed.
/// </summary>
public event EventHandler ResultChanged;
/// <summary>
/// Raised when a member is added to this collection.
/// </summary>
public event TestMemberEventHandler TestMemberAdded;
/// <summary>
/// Raised when a member is removed from this collection.
/// </summary>
public event TestMemberEventHandler TestMemberRemoved;
/// <summary>
/// Gets the overall test results for the collection of
/// test members.
/// </summary>
public TestResultType Result {
get { return testResult; }
}
/// <summary>
/// Sets all the test members test results back to none.
/// </summary>
public void ResetTestResults()
{
passedTestMembers.Clear();
failedTestMembers.Clear();
ignoredTestMembers.Clear();
foreach (TestMember member in this) {
member.Result = TestResultType.None;
}
SetTestResult(TestResultType.None);
}
protected override void InsertItem(int index, TestMember item)
{
item.ResultChanged += TestMemberResultChanged;
base.InsertItem(index, item);
TestMemberResultChanged(item, new EventArgs());
OnTestMemberAdded(item);
}
protected override string GetKeyForItem(TestMember item)
{
return item.Name;
}
protected override void RemoveItem(int index)
{
TestMember member = this[index];
member.ResultChanged -= TestMemberResultChanged;
base.RemoveItem(index);
OnTestResultNone(member.Name);
OnTestMemberRemoved(member);
}
protected void OnTestMemberAdded(TestMember testMember)
{
if (TestMemberAdded != null) {
TestMemberAdded(this, new TestMemberEventArgs(testMember));
}
}
protected void OnTestMemberRemoved(TestMember testMember)
{
if (TestMemberRemoved != null) {
TestMemberRemoved(this, new TestMemberEventArgs(testMember));
}
}
void TestMemberResultChanged(object source, EventArgs e)
{
TestMember member = (TestMember)source;
switch (member.Result) {
case TestResultType.None:
OnTestResultNone(member.Name);
break;
case TestResultType.Failure:
SetTestResult(TestResultType.Failure);
failedTestMembers.Add(member.Name, member);
break;
case TestResultType.Success:
passedTestMembers.Add(member.Name, member);
if (passedTestMembers.Count == Count) {
SetTestResult(TestResultType.Success);
} else if (passedTestMembers.Count + ignoredTestMembers.Count == Count) {
SetTestResult(TestResultType.Ignored);
}
break;
case TestResultType.Ignored:
ignoredTestMembers.Add(member.Name, member);
if (ignoredTestMembers.Count == Count ||
ignoredTestMembers.Count + passedTestMembers.Count == Count) {
SetTestResult(TestResultType.Ignored);
}
break;
}
}
void SetTestResult(TestResultType value)
{
TestResultType previousTestResult = testResult;
testResult = value;
if (testResult != previousTestResult) {
OnResultChanged();
}
}
void OnResultChanged()
{
if (ResultChanged != null) {
ResultChanged(this, new EventArgs());
}
}
/// <summary>
/// Removes the specified test member from the list of
/// failed, passed and ignored tests and updates the
/// test result state of the test members collection.
/// </summary>
void OnTestResultNone(string name)
{
passedTestMembers.Remove(name);
failedTestMembers.Remove(name);
ignoredTestMembers.Remove(name);
if (ignoredTestMembers.Count + failedTestMembers.Count == 0) {
SetTestResult(TestResultType.None);
}
}
}
}

30
src/AddIns/Analysis/UnitTesting/Service/TestMemberEventArgs.cs

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Represents the member that will handle the TestCollection's
/// TestMemberAdded or TestMemberRemoved events.
/// </summary>
public delegate void TestMemberEventHandler(object source, TestMemberEventArgs e);
/// <summary>
/// Provides data for the TestCollection's TestMemberAdded and TestMemberRemoved events.
/// </summary>
public class TestMemberEventArgs
{
TestMember testMember;
public TestMemberEventArgs(TestMember testMember)
{
this.testMember = testMember;
}
public TestMember TestMember {
get { return testMember; }
}
}
}

298
src/AddIns/Analysis/UnitTesting/Service/TestProject.cs

@ -0,0 +1,298 @@ @@ -0,0 +1,298 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Parser;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Represents a project that has a reference to a unit testing
/// framework assembly. Currently only NUnit is supported.
/// </summary>
public class TestProject
{
IProject project;
IProjectContent projectContent;
List<IUnresolvedTypeDefinition> testClasses;
List<string> rootNamespaces;
IRegisteredTestFrameworks testFrameworks;
public TestProject(IProject project)
{
this.project = project;
this.projectContent = project.ProjectContent;
this.testFrameworks = TestService.RegisteredTestFrameworks;
project.ProjectContent.GetAllTypeDefinitions().Where(td => td.Attributes.Select(a => a.CreateResolvedAttribute(project.ProjectContent.CreateCompilation().
}
public IProject Project {
get { return project; }
}
/// <summary>
/// Gets the test classes in this project.
/// </summary>
// public TestClassCollection TestClasses {
// get {
// if (testClasses == null) {
// GetTestClasses();
// }
// return testClasses;
// }
// }
/// <summary>
/// Gets the test classes that exist in the specified namespace.
/// </summary>
public TestClass[] GetTestClasses(string ns)
{
throw new NotImplementedException();
// return TestClass.GetTestClasses(TestClasses, ns);
}
/// <summary>
/// Gets the test classes whose namespaces start with the specified string.
/// </summary>
public TestClass[] GetAllTestClasses(string namespaceStartsWith)
{
throw new NotImplementedException();
// return TestClass.GetAllTestClasses(TestClasses, namespaceStartsWith);
}
/// <summary>
/// Gets all the child namespaces with the specified parent
/// namespace. The parent namespace can be one or more
/// namespaces separated with a period.
/// </summary>
public string[] GetChildNamespaces(string parentNamespace)
{
throw new NotImplementedException();
// return TestClass.GetChildNamespaces(TestClasses, parentNamespace);
}
/// <summary>
/// Gets the project's name.
/// </summary>
public string Name {
get { return project.Name; }
}
/// <summary>
/// Gets the distinct root namespaces for all this project.
/// </summary>
/// <remarks>
/// If one of the namespaces is 'ICSharpCode.XmlEditor' then this
/// method will return 'ICSharpCode' as one of the root namespaces.
/// </remarks>
public IList<string> RootNamespaces {
get {
if (rootNamespaces == null) {
GetRootNamespaces();
}
return rootNamespaces;
}
}
/// <summary>
/// Updates the test method based on the test result.
/// </summary>
public void UpdateTestResult(TestResult testResult)
{
// TestClasses.UpdateTestResult(testResult);
}
/// <summary>
/// Sets all the test results back to none.
/// </summary>
public void ResetTestResults()
{
// TestClasses.ResetTestResults();
}
/// <summary>
/// Updates the classes and methods based on the new parse information.
/// </summary>
/// <param name="oldUnit">The old compiliation unit
/// (ParseInformationEventArgs.ParseInformation.BestCompilationUnit as ICompilationUnit)</param>
/// <param name="newUnit">The new compilation unit
/// (ParseInformationEventArgs.CompilationUnit).</param>
public void UpdateParseInfo(IParsedFile oldUnit, IParsedFile newUnit)
{
if (!IsParseInfoForThisProject(oldUnit, newUnit)) {
return;
}
// RemovedClasses removedClasses = new RemovedClasses();
//
// if (oldUnit != null) {
// removedClasses.Add(oldUnit.Classes);
// }
// if (newUnit != null) {
// foreach (IClass c in newUnit.Classes) {
// UpdateTestClass(c);
// foreach (IClass innerClass in new InnerClassEnumerator(c)) {
// UpdateTestClass(innerClass);
// removedClasses.Remove(innerClass);
// }
// removedClasses.Remove(c);
// }
// }
//
// // Remove missing classes.
// foreach (IClass c in removedClasses.GetMissingClasses()) {
// IClass existingClass = GetExistingTestClassInProject(c);
// if (existingClass != null) {
// UpdateTestClass(existingClass);
// } else {
// TestClasses.Remove(c.DotNetName);
// }
// }
}
/// <summary>
/// Determines whether the new parse information is for this test
/// project.
/// </summary>
public bool IsParseInfoForThisProject(IParsedFile oldUnit, IParsedFile newUnit)
{
// ICompilationUnit unit = oldUnit;
// if (unit == null) {
// unit = newUnit;
// }
// if (unit != null) {
// return Object.ReferenceEquals(unit.ProjectContent, this.projectContent);
// }
return false;
}
/// <summary>
/// Adds a new class to the test project's classes only if
/// the class is a test class.
/// </summary>
void AddNewTestClass(IUnresolvedTypeDefinition c)
{
if (IsTestClass(c)) {
TestClass testClass = CreateTestClass(c);
// TestClasses.Add(testClass);
}
}
TestClass CreateTestClass(IUnresolvedTypeDefinition c)
{
throw new NotImplementedException();
// return new TestClass(c, testFrameworks);
}
/// <summary>
/// Updates the test class methods based on the newly parsed class
/// information.
/// </summary>
void UpdateTestClass(IUnresolvedTypeDefinition c)
{
// if (TestClasses.Contains(c.DotNetName))
// {
// if (IsTestClass(c))
// {
// TestClass testClass = TestClasses[c.DotNetName];
// testClass.UpdateClass(c);
// }
// else
// {
// // TestFixture attribute has been removed so
// // remove the class from the set of TestClasses.
// TestClasses.Remove(c.DotNetName);
// }
// }
// else
// {
// // TestFixture attribute may have been recently added to
// // this class so call AddNewTestClass. No need to
// // check if the class is actually a test class since
// // AddNewTestClass does this anyway.
// AddNewTestClass(c);
// }
//
// var derivedTestClasses = GetTestClassesDerivedFrom(c);
// if (derivedTestClasses.Any())
// UpdateClassesFromProjectContent(derivedTestClasses);
}
private IEnumerable<IUnresolvedTypeDefinition> GetTestClassesDerivedFrom(IUnresolvedTypeDefinition c)
{
throw new NotImplementedException();
// return TestClasses
// .Where(testClass => testClass.IsDerivedFrom(c))
// .Select(testClass => testClass.Class)
// .ToArray();
}
private void UpdateClassesFromProjectContent(IEnumerable<IUnresolvedTypeDefinition> classes)
{
// foreach (var c in classes)
// {
// var classInProjectContent = projectContent.GetClass(c.FullyQualifiedName, c.TypeParameters.Count);
// if (classInProjectContent != null)
// UpdateTestClass(classInProjectContent);
// }
}
void GetTestClasses()
{
// testClasses = new TestClassCollection();
// foreach (IClass c in projectContent.Classes) {
// if (IsTestClass(c)) {
// if (!testClasses.Contains(c.FullyQualifiedName)) {
// testClasses.Add(CreateTestClass(c));
// }
// }
// foreach (IClass innerClass in new InnerClassEnumerator(c)) {
// if (IsTestClass(innerClass)) {
// if (!testClasses.Contains(innerClass.DotNetName)) {
// testClasses.Add(CreateTestClass(innerClass));
// }
// }
// }
// }
}
bool IsTestClass(IUnresolvedTypeDefinition c)
{
return testFrameworks.IsTestClass(c);
}
void GetRootNamespaces()
{
// rootNamespaces = new List<string>();
// foreach (TestClass c in TestClasses) {
// string rootNamespace = c.RootNamespace;
// if ((rootNamespace.Length > 0) && !rootNamespaces.Contains(rootNamespace)) {
// rootNamespaces.Add(rootNamespace);
// }
// }
}
/// <summary>
/// Gets an existing test class with the same name in the project. This
/// method is used to check that we do not remove a class after an existing duplicate class name
/// is changed.
/// </summary>
TestClass GetExistingTestClassInProject(IUnresolvedTypeDefinition c)
{
// foreach (IClass existingClass in projectContent.Classes) {
// if (IsTestClass(existingClass)) {
// if (existingClass.DotNetName == c.DotNetName) {
// return existingClass;
// }
// }
// }
return null;
}
}
}

92
src/AddIns/Analysis/UnitTesting/Service/TestResult.cs

@ -0,0 +1,92 @@ @@ -0,0 +1,92 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.UnitTesting
{
public enum TestResultType {
/// <summary>
/// The test has not been run.
/// </summary>
None = 0,
/// <summary>
/// The test passed.
/// </summary>
Success = 1,
/// <summary>
/// The test failed.
/// </summary>
Failure = 2,
/// <summary>
/// The test was ignored.
/// </summary>
Ignored = 3
}
/// <summary>
/// Holds the information about a single test result.
/// </summary>
public class TestResult
{
string name = String.Empty;
string message = String.Empty;
string stackTrace = String.Empty;
TestResultType resultType = TestResultType.None;
DomRegion stackTraceFilePosition = DomRegion.Empty;
public TestResult(string name)
{
this.name = name;
}
public string Name {
get { return name; }
}
public bool IsSuccess {
get { return resultType == TestResultType.Success; }
}
public bool IsFailure {
get { return resultType == TestResultType.Failure; }
}
public bool IsIgnored {
get { return resultType == TestResultType.Ignored; }
}
public TestResultType ResultType {
get { return resultType; }
set { resultType = value; }
}
public string Message {
get { return message; }
set { message = value; }
}
public string StackTrace {
get { return stackTrace; }
set {
if (stackTrace != value) {
stackTrace = value;
OnStackTraceChanged();
}
}
}
protected virtual void OnStackTraceChanged()
{
}
public DomRegion StackTraceFilePosition {
get { return stackTraceFilePosition; }
set { stackTraceFilePosition = value; }
}
}
}

68
src/AddIns/Analysis/UnitTesting/Service/TestRunnerBase.cs

@ -0,0 +1,68 @@ @@ -0,0 +1,68 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Diagnostics;
namespace ICSharpCode.UnitTesting
{
public abstract class TestRunnerBase : ITestRunner
{
public TestRunnerBase()
{
}
protected virtual ProcessStartInfo GetProcessStartInfo(SelectedTests selectedTests)
{
return new ProcessStartInfo();
}
protected void LogCommandLine(ProcessStartInfo startInfo)
{
string commandLine = GetCommandLine(startInfo);
OnMessageReceived(commandLine);
}
protected string GetCommandLine(ProcessStartInfo startInfo)
{
return String.Format("\"{0}\" {1}", startInfo.FileName, startInfo.Arguments);
}
public event EventHandler AllTestsFinished;
protected void OnAllTestsFinished(object source, EventArgs e)
{
if (AllTestsFinished != null) {
AllTestsFinished(source, e);
}
}
public event TestFinishedEventHandler TestFinished;
protected void OnTestFinished(object source, TestFinishedEventArgs e)
{
if (TestFinished != null) {
TestResult testResult = CreateTestResultForTestFramework(e.Result);
TestFinished(source, new TestFinishedEventArgs(testResult));
}
}
protected virtual TestResult CreateTestResultForTestFramework(TestResult testResult)
{
return testResult;
}
public event MessageReceivedEventHandler MessageReceived;
protected virtual void OnMessageReceived(string message)
{
if (MessageReceived != null) {
MessageReceived(this, new MessageReceivedEventArgs(message));
}
}
public abstract void Dispose();
public abstract void Stop();
public abstract void Start(SelectedTests selectedTests);
}
}

47
src/AddIns/Analysis/UnitTesting/Service/TestService.cs

@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.SharpDevelop.Gui;
namespace ICSharpCode.UnitTesting
{
public static class TestService
{
static IRegisteredTestFrameworks testFrameworks;
static MessageViewCategory unitTestMessageView;
public static IRegisteredTestFrameworks RegisteredTestFrameworks {
get {
CreateRegisteredTestFrameworks();
return testFrameworks;
}
set { testFrameworks = value; }
}
static void CreateRegisteredTestFrameworks()
{
if (testFrameworks == null) {
UnitTestAddInTree addInTree = new UnitTestAddInTree();
testFrameworks = new RegisteredTestFrameworks(addInTree);
}
}
public static MessageViewCategory UnitTestMessageView {
get {
if (unitTestMessageView == null) {
CreateUnitTestCategory();
}
return unitTestMessageView;
}
}
static void CreateUnitTestCategory()
{
MessageViewCategory.Create(ref unitTestMessageView,
"UnitTesting",
"${res:ICSharpCode.NUnitPad.NUnitPadContent.PadName}");
}
}
}

121
src/AddIns/Analysis/UnitTesting/Service/TestableCondition.cs

@ -0,0 +1,121 @@ @@ -0,0 +1,121 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Bookmarks;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Supplies a "Unit test" menu item if the class is a test fixture.
/// </summary>
public class TestableCondition : IConditionEvaluator
{
IRegisteredTestFrameworks testFrameworks;
public TestableCondition(IRegisteredTestFrameworks testFrameworks)
{
this.testFrameworks = testFrameworks;
}
public TestableCondition()
: this(TestService.RegisteredTestFrameworks)
{
}
public static IMember GetMember(object caller)
{
ITestTreeView testTreeView = caller as ITestTreeView;
if (testTreeView != null) {
return testTreeView.SelectedMember;
}
MemberNode memberNode = caller as MemberNode;
if (memberNode != null) {
return memberNode.Member;
} else {
ClassMemberBookmark mbookmark = caller as ClassMemberBookmark;
if (mbookmark != null) {
return mbookmark.Member;
}
}
return null;
}
public static IClass GetClass(object caller)
{
ITestTreeView testTreeView = caller as ITestTreeView;
if (testTreeView != null) {
return testTreeView.SelectedClass;
}
ClassNode classNode = caller as ClassNode;
if (classNode != null) {
return classNode.Class;
} else {
ClassBookmark bookmark = caller as ClassBookmark;
if (bookmark != null) {
return bookmark.Class;
}
}
return null;
}
public static IProject GetProject(object caller)
{
ITestTreeView testTreeView = caller as ITestTreeView;
if (testTreeView != null) {
return testTreeView.SelectedProject;
}
IClass c = GetClassFromMemberOrCaller(caller);
return GetProject(c);
}
static IClass GetClassFromMemberOrCaller(object caller)
{
IMember m = GetMember(caller);
if (m != null) {
return m.DeclaringType;
}
return GetClass(caller);
}
static IProject GetProject(IClass c)
{
if (c != null) {
return (IProject)c.ProjectContent.Project;
}
return null;
}
/// <summary>
/// Returns the namespace selected if any.
/// </summary>
public static string GetNamespace(object caller)
{
ITestTreeView testTreeView = caller as ITestTreeView;
if (testTreeView != null) {
return testTreeView.SelectedNamespace;
}
return null;
}
public bool IsValid(object caller, Condition condition)
{
IMember m = GetMember(caller);
if (m != null) {
return testFrameworks.IsTestMember(m);
}
IClass c = GetClass(caller);
if (ClassHasProject(c)) {
return testFrameworks.IsTestClass(c);
}
return false;
}
static bool ClassHasProject(IClass c)
{
return (c != null) && (c.ProjectContent.Project != null);
}
}
}

50
src/AddIns/Analysis/UnitTesting/TestAttributeName.cs

@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.UnitTesting
{
public class NUnitTestAttributeName
{
string name = String.Empty;
string qualifiedName = String.Empty;
string fullName = String.Empty;
StringComparer nameComparer;
/// <summary>
/// Creates a new instance of the NUnit Test Attribute class.
/// </summary>
/// <param name="name">The name of the attribute (e.g. Test) not
/// the full name of the attribute (e.g. TestAttribute).</param>
/// <param name="nameComparer">The string comparer to use
/// when comparing attribute names.</param>
public NUnitTestAttributeName(string name, StringComparer nameComparer)
{
this.name = name;
this.nameComparer = nameComparer;
qualifiedName = String.Concat(name, "Attribute");
fullName = String.Concat("NUnit.Framework.", name, "Attribute");
}
/// <summary>
/// Determines whether the specified attribute name is a
/// match to this attribute.
/// </summary>
public bool IsEqual(string attributeName)
{
if (nameComparer.Equals(attributeName, name) ||
nameComparer.Equals(attributeName, qualifiedName) ||
nameComparer.Equals(attributeName, fullName)) {
return true;
}
return false;
}
public bool IsEqual(IAttribute attribute)
{
return IsEqual(attribute.AttributeType.FullName);
}
}
}

109
src/AddIns/Analysis/UnitTesting/TestProcessRunnerBase.cs

@ -0,0 +1,109 @@ @@ -0,0 +1,109 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Diagnostics;
using ICSharpCode.Core.WinForms;
using ICSharpCode.SharpDevelop.Util;
namespace ICSharpCode.UnitTesting
{
public class TestProcessRunnerBase : TestRunnerBase
{
IUnitTestProcessRunner processRunner;
ITestResultsMonitor testResultsMonitor;
IFileSystem fileSystem;
IUnitTestMessageService messageService;
public TestProcessRunnerBase()
: this(new UnitTestProcessRunner(),
new TestResultsMonitor(),
new UnitTestFileService(),
new UnitTestMessageService())
{
}
public TestProcessRunnerBase(TestProcessRunnerBaseContext context)
: this(context.TestProcessRunner,
context.TestResultsMonitor,
context.FileSystem,
context.MessageService)
{
}
public TestProcessRunnerBase(IUnitTestProcessRunner processRunner,
ITestResultsMonitor testResultsMonitor,
IFileSystem fileSystem,
IUnitTestMessageService messageService)
{
this.processRunner = processRunner;
this.testResultsMonitor = testResultsMonitor;
this.fileSystem = fileSystem;
this.messageService = messageService;
processRunner.LogStandardOutputAndError = false;
processRunner.OutputLineReceived += OutputLineReceived;
processRunner.ErrorLineReceived += OutputLineReceived;
processRunner.ProcessExited += OnAllTestsFinished;
testResultsMonitor.TestFinished += OnTestFinished;
}
protected ITestResultsMonitor TestResultsMonitor {
get { return testResultsMonitor; }
}
protected IUnitTestProcessRunner ProcessRunner {
get { return processRunner; }
}
void OutputLineReceived(object source, LineReceivedEventArgs e)
{
OnMessageReceived(e.Line);
}
public override void Start(SelectedTests selectedTests)
{
ProcessStartInfo startInfo = GetProcessStartInfo(selectedTests);
Start(startInfo);
}
void Start(ProcessStartInfo processStartInfo)
{
LogCommandLine(processStartInfo);
if (ApplicationFileNameExists(processStartInfo.FileName)) {
testResultsMonitor.Start();
processRunner.WorkingDirectory = processStartInfo.WorkingDirectory;
processRunner.Start(processStartInfo.FileName, processStartInfo.Arguments);
} else {
ShowApplicationDoesNotExistMessage(processStartInfo.FileName);
}
}
bool ApplicationFileNameExists(string fileName)
{
return fileSystem.FileExists(fileName);
}
void ShowApplicationDoesNotExistMessage(string fileName)
{
string resourceString = "${res:ICSharpCode.UnitTesting.TestRunnerNotFoundMessageFormat}";
messageService.ShowFormattedErrorMessage(resourceString, fileName);
}
public override void Stop()
{
processRunner.Kill();
testResultsMonitor.Stop();
testResultsMonitor.Read();
}
public override void Dispose()
{
testResultsMonitor.Dispose();
testResultsMonitor.TestFinished -= OnTestFinished;
processRunner.ErrorLineReceived -= OutputLineReceived;
processRunner.OutputLineReceived -= OutputLineReceived;
}
}
}

52
src/AddIns/Analysis/UnitTesting/TestProcessRunnerBaseContext.cs

@ -0,0 +1,52 @@ @@ -0,0 +1,52 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.Core.WinForms;
using ICSharpCode.SharpDevelop.Util;
namespace ICSharpCode.UnitTesting
{
public class TestProcessRunnerBaseContext
{
IUnitTestProcessRunner processRunner;
ITestResultsMonitor testResultsMonitor;
IFileSystem fileSystem;
IUnitTestMessageService messageService;
public TestProcessRunnerBaseContext()
: this(new UnitTestProcessRunner(),
new TestResultsMonitor(),
new UnitTestFileService(),
new UnitTestMessageService())
{
}
public TestProcessRunnerBaseContext(IUnitTestProcessRunner processRunner,
ITestResultsMonitor testResultsMonitor,
IFileSystem fileSystem,
IUnitTestMessageService messageService)
{
this.processRunner = processRunner;
this.testResultsMonitor = testResultsMonitor;
this.fileSystem = fileSystem;
this.messageService = messageService;
}
public IUnitTestProcessRunner TestProcessRunner {
get { return processRunner; }
}
public ITestResultsMonitor TestResultsMonitor {
get { return testResultsMonitor; }
}
public IFileSystem FileSystem {
get { return fileSystem; }
}
public IUnitTestMessageService MessageService {
get { return messageService; }
}
}
}

97
src/AddIns/Analysis/UnitTesting/TestResultTask.cs

@ -0,0 +1,97 @@ @@ -0,0 +1,97 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.IO;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Gui;
namespace ICSharpCode.UnitTesting
{
public class TestResultTask
{
TestResultTask()
{
}
public static SDTask Create(TestResult result, TestProject project)
{
TaskType taskType = TaskType.Warning;
FileLineReference lineRef = null;
string message = String.Empty;
if (result.IsFailure) {
taskType = TaskType.Error;
if (!result.StackTraceFilePosition.IsEmpty) {
lineRef = new FileLineReference(result.StackTraceFilePosition.FileName, result.StackTraceFilePosition.BeginLine - 1, result.StackTraceFilePosition.BeginColumn - 1);
}
message = GetTestFailedMessage(result);
} else if (result.IsIgnored) {
message = GetTestIgnoredMessage(result);
}
if (lineRef == null) {
lineRef = FindTest(result.Name, project);
}
FileName fileName = null;
if (lineRef != null) {
fileName = new FileName(Path.GetFullPath(lineRef.FileName));
int line = lineRef.Line + 1;
return new SDTask(fileName, message, lineRef.Column, line, taskType);
}
return new SDTask(fileName, message, 0, 0, taskType);
}
static string GetTestFailedMessage(TestResult result)
{
string resource = "${res:NUnitPad.NUnitPadContent.TestTreeView.TestFailedMessage}";
return GetTestResultMessage(result, resource);
}
static string GetTestIgnoredMessage(TestResult result)
{
string resource = "${res:NUnitPad.NUnitPadContent.TestTreeView.TestNotExecutedMessage}";
return GetTestResultMessage(result, resource);
}
/// <summary>
/// Returns the test result message if there is on otherwise
/// uses the string resource to create a message.
/// </summary>
static string GetTestResultMessage(TestResult result, string stringResource)
{
if (result.Message.Length > 0) {
return result.Message;
}
return GetTestResultMessageFromResource(result, stringResource);
}
static string GetTestResultMessageFromResource(TestResult result, string stringResource)
{
return StringParser.Parse(stringResource, new StringTagPair("TestCase", result.Name));
}
/// <summary>
/// Returns the location of the specified test member in the
/// project being tested.
/// </summary>
static FileLineReference FindTest(string memberName, TestProject testProject)
{
// if (testProject != null) {
// TestMember testMember = testProject.TestClasses.GetTestMember(memberName);
// if (testMember != null) {
// return FindTest(testMember);
// }
// }
return null;
}
// static FileLineReference FindTest(TestMember testMember)
// {
// MemberResolveResult resolveResult = new MemberResolveResult(null, null, testMember.Member);
// FilePosition filePos = resolveResult.GetDefinitionPosition();
// return new FileLineReference(filePos.FileName, filePos.Line - 1, filePos.Column - 1);
// }
}
}

178
src/AddIns/Analysis/UnitTesting/TestResultsMonitor.cs

@ -0,0 +1,178 @@ @@ -0,0 +1,178 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.IO;
using System.Text;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Watches for new test results as they occur. Test results
/// are written to a file and read in by this class.
/// </summary>
public class TestResultsMonitor : ITestResultsMonitor
{
FileInfo fileInfo;
TestResultsReader testResultsReader;
FileSystemWatcher fileSystemWatcher;
long initialFilePosition = 3;
long filePosition;
const int BytesBufferLength = 1024;
byte[] bytes = new byte[BytesBufferLength];
/// <summary>
/// Raised when a single test has been completed.
/// </summary>
public event TestFinishedEventHandler TestFinished;
public TestResultsMonitor(string fileName)
{
fileInfo = new FileInfo(fileName);
ResetFilePosition();
}
public TestResultsMonitor()
: this(Path.GetTempFileName())
{
ResetFilePosition();
}
public long InitialFilePosition {
get { return initialFilePosition; }
set { initialFilePosition = value; }
}
/// <summary>
/// Gets or sets the test results filename.
/// </summary>
public string FileName {
get { return fileInfo.FullName; }
set { fileInfo = new FileInfo(value); }
}
/// <summary>
/// Starts monitoring for test results.
/// </summary>
public void Start()
{
testResultsReader = new TestResultsReader();
ResetFilePosition();
string filter = fileInfo.Name;
fileSystemWatcher = new FileSystemWatcher(fileInfo.DirectoryName, filter);
if (File.Exists(fileInfo.FullName)) {
fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
fileSystemWatcher.Changed += FileChanged;
} else {
fileSystemWatcher.Created += FileCreated;
}
fileSystemWatcher.Error += FileSystemWatcherError;
fileSystemWatcher.EnableRaisingEvents = true;
}
/// <summary>
/// Stops monitoring.
/// </summary>
public void Stop()
{
if (fileSystemWatcher != null) {
fileSystemWatcher.Dispose();
fileSystemWatcher = null;
}
}
/// <summary>
/// Reads the rest of the file from the current position.
/// Raises the TestFinished event for each test result
/// still in the file.
/// </summary>
public void Read()
{
string text = ReadTextAdded();
if (text != null) {
TestResult[] results = testResultsReader.Read(text);
OnTestResultsReceived(results);
}
}
/// <summary>
/// Stops monitoring and releases any resources used
/// by the TestResultsMonitor.
/// </summary>
public void Dispose()
{
Stop();
try {
File.Delete(FileName);
} catch { }
}
void FileCreated(object source, FileSystemEventArgs e)
{
fileSystemWatcher.Created -= FileCreated;
fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
fileSystemWatcher.Changed += FileChanged;
}
void FileChanged(object source, FileSystemEventArgs e)
{
Read();
}
void OnTestResultsReceived(TestResult[] results)
{
if ((results.Length > 0) && (TestFinished != null)) {
foreach (TestResult result in results) {
TestFinished(this, new TestFinishedEventArgs(result));
}
}
}
/// <summary>
/// Reads the text added to the end of the file from the last
/// position we read from.
/// </summary>
string ReadTextAdded()
{
StringBuilder text = null;
try {
using (FileStream fs = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
if (fs.Length > 0) {
text = new StringBuilder();
int bytesRead = 0;
fs.Seek(filePosition, SeekOrigin.Begin);
do {
bytesRead = fs.Read(bytes, 0, BytesBufferLength);
if (bytesRead > 0) {
filePosition += bytesRead;
text.Append(UTF8Encoding.UTF8.GetString(bytes, 0, bytesRead));
}
} while ((bytesRead > 0) && (filePosition < fs.Length));
}
}
} catch (FileNotFoundException) {
// Test was aborted before it even started execution
return null;
}
if (text != null) {
return text.ToString();
}
return null;
}
void FileSystemWatcherError(object source, ErrorEventArgs e)
{
Console.WriteLine(e.GetException().ToString());
}
void ResetFilePosition()
{
filePosition = initialFilePosition;
}
}
}

155
src/AddIns/Analysis/UnitTesting/TestResultsReader.cs

@ -0,0 +1,155 @@ @@ -0,0 +1,155 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Text;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Reads the test results file produced by the custom
/// nunit-console application.
/// </summary>
public class TestResultsReader
{
StringBuilder nameBuilder = new StringBuilder();
StringBuilder valueBuilder = new StringBuilder();
bool firstNameChar = true;
TestResult result;
enum State {
WaitingForEndOfName = 0,
WaitingForStartOfValue = 1,
WaitingForEndOfValue = 2
}
State state = State.WaitingForEndOfName;
public TestResultsReader()
{
}
/// <summary>
/// Returns any TestResults that are in the text.
/// </summary>
/// <param name="text">The text read in from the
/// TestResults file.</param>
public TestResult[] Read(string text)
{
List<TestResult> results = new List<TestResult>();
foreach (char ch in text) {
if (ReadNameValuePair(ch)) {
if (ReadTestResult()) {
results.Add(result);
}
}
}
return results.ToArray();
}
/// <summary>
/// Reads a name-value pair of the form:
///
/// Name: Value
///
/// A value can span multiple lines:
///
/// Name1: ValueLine1
/// {SP}ValueLine2
/// {SP}ValueLine3
/// Name1: Value2
///
/// Each continued line of the value must start with
/// a single space character to distinguish it from
/// the start of a new name-value pair.
/// </summary>
/// <returns>True if a name-value pair has been
/// successfully read in</returns>
bool ReadNameValuePair(char ch)
{
if (state == State.WaitingForEndOfName) {
ReadNameChar(ch);
} else if (state == State.WaitingForStartOfValue) {
// Makes sure first space is ignored.
state = State.WaitingForEndOfValue;
} else {
return ReadValueChar(ch);
}
return false;
}
void ReadNameChar(char ch)
{
if (ch == ':') {
state = State.WaitingForStartOfValue;
} else if (ch == '\r' || ch == '\n') {
nameBuilder = new StringBuilder();
} else if (ch == ' ' && firstNameChar) {
state = State.WaitingForEndOfValue;
valueBuilder.Append("\r\n");
} else if (firstNameChar) {
firstNameChar = false;
nameBuilder = new StringBuilder();
valueBuilder = new StringBuilder();
nameBuilder.Append(ch);
} else {
nameBuilder.Append(ch);
}
}
bool ReadValueChar(char ch)
{
if (ch == '\r') {
// Ignore.
} else if (ch == '\n') {
state = State.WaitingForEndOfName;
firstNameChar = true;
return true;
} else {
valueBuilder.Append(ch);
}
return false;
}
/// <summary>
/// Creates and updates a TestResult based on the
/// name-value pair read in,
/// </summary>
/// <returns>True if a TestResult is ready to be returned
/// to the caller.</returns>
/// <remarks>
/// The first name-value pair for a test result is the
/// test name. The last name-value pair is the result of
/// the test (Success, Failure or Ignored).</remarks>
bool ReadTestResult()
{
string name = nameBuilder.ToString();
if (name == "Name") {
result = new TestResult(valueBuilder.ToString());
} else if (result != null) {
if (name == "Message") {
result.Message = valueBuilder.ToString();
} else if (name == "StackTrace") {
result.StackTrace = valueBuilder.ToString();
} else if (name == "Result") {
UpdateTestSuccess();
return true;
}
}
return false;
}
void UpdateTestSuccess()
{
string value = valueBuilder.ToString();
if (value == "Success") {
result.ResultType = TestResultType.Success;
} else if (value == "Failure") {
result.ResultType = TestResultType.Failure;
} else {
result.ResultType = TestResultType.Ignored;
}
}
}
}

434
src/AddIns/Analysis/UnitTesting/TestTreeView.cs

@ -0,0 +1,434 @@ @@ -0,0 +1,434 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Tree view that shows all the unit tests in a project.
/// </summary>
public class TestTreeView : sharptr, ITestTreeView, IOwnerState
{
/// <summary>
/// The current state of the tree view.
/// </summary>
[Flags]
public enum TestTreeViewState {
None = 0,
SourceCodeItemSelected = 1
}
/// <summary>
/// The All Tests tree root node that is added if multiple
/// test projects exist in the solution. If the solution contains
/// only one test project then no such node will be added.
/// </summary>
AllTestsTreeNode allTestsNode;
IRegisteredTestFrameworks testFrameworks;
public TestTreeView(IRegisteredTestFrameworks testFrameworks)
{
this.testFrameworks = testFrameworks;
ImageList = TestTreeViewImageList.ImageList;
CanClearSelection = false;
}
/// <summary>
/// Gets the current state of the test tree view.
/// </summary>
public Enum InternalState {
get {
TestTreeNode selectedNode = SelectedNode as TestTreeNode;
if ((selectedNode is TestClassTreeNode) || (selectedNode is TestMemberTreeNode)) {
return TestTreeViewState.SourceCodeItemSelected;
}
return TestTreeViewState.None;
}
}
/// <summary>
/// Adds the solution's projects to the test tree view.
/// Only test projects are displayed.
/// </summary>
public void AddSolution(Solution solution)
{
Clear();
AddProjects(solution.Projects);
}
/// <summary>
/// Adds the projects to the test tree view. Only those projects
/// which are determined to have a reference to a supported
/// test framework will be added to the tree.
/// </summary>
public void AddProjects(IEnumerable<IProject> projects)
{
foreach (IProject project in projects) {
AddProject(project);
}
}
/// <summary>
/// Adds the project to the test tree view if the project
/// is a test project.
/// </summary>
/// <remarks>
/// If the project is already in the tree then it will
/// not be added again. If a project is already in the tree then
/// an All Tests root node will be added.
/// </remarks>
public void AddProject(IProject project)
{
if (IsTestProject(project)) {
if (GetProjectTreeNode(project) == null) {
AddProjectTreeNode(project);
}
}
}
bool IsTestProject(IProject project)
{
return testFrameworks.IsTestProject(project);
}
void AddProjectTreeNode(IProject project)
{
TestProjectTreeNode node = CreateProjectTreeNode(project);
if (node != null) {
AddProjectTreeNodeToTree(node);
SortNodes(Nodes, true);
}
}
TestProjectTreeNode CreateProjectTreeNode(IProject project)
{
IProjectContent projectContent = GetProjectContent(project);
if (projectContent != null) {
TestProject testProject = new TestProject(project, projectContent, testFrameworks);
return new TestProjectTreeNode(testProject);
}
return null;
}
void AddProjectTreeNodeToTree(TestProjectTreeNode node)
{
if (Nodes.Count == 0) {
node.AddTo(this);
} else {
AllTestsTreeNode allTestsNode = GetAllTestsNode();
allTestsNode.AddProjectNode(node);
}
}
public void RemoveSolutionFolder(ISolutionFolder solutionFolder)
{
IProject project = solutionFolder as IProject;
if (project != null) {
RemoveProject(project);
}
ISolutionFolderContainer solutionFolderContainer = solutionFolder as ISolutionFolderContainer;
if (solutionFolderContainer != null) {
foreach (ISolutionFolder subSolutionFolder in solutionFolderContainer.Folders) {
RemoveSolutionFolder(subSolutionFolder);
}
}
}
/// <summary>
/// Removes the specified project from the test tree view.
/// </summary>
public void RemoveProject(IProject project)
{
TestProjectTreeNode projectNode = GetProjectTreeNode(project);
RemoveProjectNode(projectNode);
RemoveAllTestsNodeIfOnlyOneProjectLeft();
}
void RemoveAllTestsNodeIfOnlyOneProjectLeft()
{
if (allTestsNode != null) {
if (GetProjectNodes().Count == 1) {
RemoveAllTestsNode();
}
}
}
/// <summary>
/// Gets the projects displayed in the tree.
/// </summary>
public IProject[] GetProjects()
{
List<IProject> projects = new List<IProject>();
foreach (TestProjectTreeNode projectNode in GetProjectNodes()) {
projects.Add(projectNode.Project);
}
return projects.ToArray();
}
/// <summary>
/// Returns the TestProject associated with the specified project.
/// </summary>
public TestProject GetTestProject(IProject project)
{
TestProjectTreeNode node = GetProjectTreeNode(project);
if (node != null) {
return node.TestProject;
}
return null;
}
/// <summary>
/// Gets the member of the currently selected tree node.
/// </summary>
public IMember SelectedMember {
get {
TestMemberTreeNode memberNode = SelectedNode as TestMemberTreeNode;
if (memberNode != null) {
return memberNode.Member;
}
return null;
}
}
/// <summary>
/// Gets the class of the currently selected tree node.
/// </summary>
public IClass SelectedClass {
get {
TestClassTreeNode classNode = SelectedNode as TestClassTreeNode;
if (classNode == null) {
classNode = GetClassNodeFromSelectedMemberNode();
}
if (classNode != null) {
return classNode.Class;
}
return null;
}
}
TestClassTreeNode GetClassNodeFromSelectedMemberNode()
{
TestMemberTreeNode memberNode = SelectedNode as TestMemberTreeNode;
if (memberNode != null) {
return memberNode.Parent as TestClassTreeNode;
}
return null;
}
/// <summary>
/// Gets the project associated with the currently selected
/// tree node.
/// </summary>
public IProject SelectedProject {
get {
TestProject testProject = SelectedTestProject;
if (testProject != null) {
return testProject.Project;
}
return null;
}
}
/// <summary>
/// If a namespace node is selected then the fully qualified namespace
/// for this node is returned (i.e. includes the parent namespace prefixed
/// to it). For all other nodes this returns null.
/// </summary>
public string SelectedNamespace {
get {
TestNamespaceTreeNode selectedNode = SelectedNode as TestNamespaceTreeNode;
if (selectedNode != null) {
return selectedNode.FullNamespace;
}
return null;
}
}
/// <summary>
/// Gets the selected test project.
/// </summary>
public TestProject SelectedTestProject {
get {
TestTreeNode selectedNode = SelectedNode as TestTreeNode;
if (selectedNode != null) {
return selectedNode.TestProject;
}
return null;
}
}
/// <summary>
/// Updates the classes and members in the test tree view based on the
/// parse information.
/// </summary>
public void UpdateParseInfo(ICompilationUnit oldUnit, ICompilationUnit newUnit)
{
foreach (TestProjectTreeNode projectNode in GetProjectNodes()) {
TestProject testProject = projectNode.TestProject;
testProject.UpdateParseInfo(oldUnit, newUnit);
}
}
/// <summary>
/// Resets the test results for all the projects in the
/// test tree view.
/// </summary>
public void ResetTestResults()
{
foreach (TestProjectTreeNode projectNode in GetProjectNodes()) {
TestProject testProject = projectNode.TestProject;
testProject.ResetTestResults();
}
}
/// <summary>
/// Returns the project content for the specified project.
/// </summary>
public virtual IProjectContent GetProjectContent(IProject project)
{
return ParserService.GetProjectContent(project);
}
/// <summary>
/// A tree node has been selected. Here we make sure the tree node
/// uses the context menu strip that the tree view is using. This
/// ensures that if the user brings up the context menu using a keyboard
/// shortcut (Shift+F10) then it appears over the node rather than in
/// the middle of the Unit Tests window.
/// </summary>
protected override void OnBeforeSelect(TreeViewCancelEventArgs e)
{
TreeNode node = e.Node;
if (node.ContextMenuStrip == null) {
node.ContextMenuStrip = ContextMenuStrip;
}
}
/// <summary>
/// Returns the project tree node that is associated with the
/// specified project.
/// </summary>
TestProjectTreeNode GetProjectTreeNode(IProject project)
{
foreach (TestProjectTreeNode projectNode in GetProjectNodes()) {
if (Object.ReferenceEquals(projectNode.Project, project)) {
return projectNode;
}
}
return null;
}
/// <summary>
/// Returns the test project tree nodes taking into account
/// if the All Tests root node exists.
/// </summary>
TreeNodeCollection GetProjectNodes()
{
if (allTestsNode != null) {
return allTestsNode.Nodes;
}
return Nodes;
}
/// <summary>
/// Removes the project node from the tree.
/// </summary>
void RemoveProjectNode(TestProjectTreeNode projectNode)
{
if (projectNode != null) {
if (allTestsNode != null) {
allTestsNode.RemoveProjectNode(projectNode);
} else {
projectNode.Remove();
}
}
}
/// <summary>
/// Gets the All Tests root node which is added if the tree is
/// showing multiple test projects. The All Tests root node will
/// be added if it does not exist.
/// </summary>
AllTestsTreeNode GetAllTestsNode()
{
if (allTestsNode == null) {
AddAllTestsNode();
}
return allTestsNode;
}
/// <summary>
/// Adds a new All Tests root node.
/// </summary>
void AddAllTestsNode()
{
// Save existing nodes (should only be one) before
// clearing so we can add these to the new All Tests node.
TreeNode[] projectNodes = new TreeNode[Nodes.Count];
Nodes.CopyTo(projectNodes, 0);
Nodes.Clear();
allTestsNode = new AllTestsTreeNode();
allTestsNode.Disposed += AllTestsNodeDisposed;
Nodes.Add(allTestsNode);
// Add the original project nodes to the new
// All Tests node.
foreach (TestProjectTreeNode node in projectNodes) {
allTestsNode.AddProjectNode(node);
}
}
/// <summary>
/// Removes the all tests node.
/// </summary>
void RemoveAllTestsNode()
{
allTestsNode.Remove();
// Copy project nodes to the root.
foreach (TestTreeNode node in allTestsNode.Nodes) {
Nodes.Add(node);
}
AllTestsNodeDisposed(null, null);
}
/// <summary>
/// Ensures that if the TreeView's Clear member is called
/// directly the test tree does not think there is still
/// an All Tests node.
/// </summary>
void AllTestsNodeDisposed(object source, EventArgs e)
{
allTestsNode.Disposed -= AllTestsNodeDisposed;
allTestsNode = null;
}
/// <summary>
/// Adds the test project to the test tree view if it is now recognised as a
/// test project and is not already in the test tree.
/// </summary>
public void ProjectItemAdded(ProjectItem projectItem)
{
AddProject(projectItem.Project);
}
public void ProjectItemRemoved(ProjectItem projectItem)
{
if (!testFrameworks.IsTestProject(projectItem.Project)) {
RemoveProject(projectItem.Project);
}
}
}
}

221
src/AddIns/Analysis/UnitTesting/UnitTestApplicationStartHelper.cs

@ -0,0 +1,221 @@ @@ -0,0 +1,221 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Helper to run the unit testing console application.
/// </summary>
public class UnitTestApplicationStartHelper
{
/// <summary>
/// returns full/path/to/Tools/NUnit
/// </summary>
public static string UnitTestApplicationDirectory {
get {
return Path.Combine(FileUtility.ApplicationRootPath, @"bin\Tools\NUnit");
}
}
/// <summary>
/// returns full/path/to/Tools/NUnit/nunit-console.exe (or whichever nunit-console exe is right
/// for the project - there are different .exes for .NET 4.0 and for x86-only projects.
/// </summary>
public string UnitTestApplication {
get {
string exe = "nunit-console";
if (ProjectUsesDotnet20Runtime(project)) {
exe += "-dotnet2";
}
if (IsPlatformTarget32Bit(project)) {
exe += "-x86";
}
exe += ".exe";
return Path.Combine(UnitTestApplicationDirectory, exe);
}
}
public readonly List<string> Assemblies = new List<string>();
/// <summary>
/// Use shadow copy assemblies. Default = true.
/// </summary>
public bool ShadowCopy = true;
/// <summary>
/// Disables the use of a separate thread to run tests on separate thread. Default = false;
/// </summary>
public bool NoThread = false;
/// <summary>
/// Use /nologo directive.
/// </summary>
public bool NoLogo = false;
/// <summary>
/// Use /labels directive.
/// </summary>
public bool Labels = false;
/// <summary>
/// Use /nodots directive.
/// </summary>
public bool NoDots = false;
/// <summary>
/// File to write xml output to. Default = null.
/// </summary>
public string XmlOutputFile;
/// <summary>
/// Fixture to test. Null = test all fixtures.
/// </summary>
public string Fixture;
/// <summary>
/// Test to run. Null = run all tests. Only valid together with the Fixture property.
/// </summary>
public string Test;
/// <summary>
/// File to write test results to.
/// </summary>
public string Results;
/// <summary>
/// The namespace that tests need to be a part of if they are to
/// be run.
/// </summary>
public string NamespaceFilter;
IProject project;
public void Initialize(IProject project, IClass fixture, IMember test)
{
Initialize(project, null, fixture, test);
}
public void Initialize(IProject project, string namespaceFilter)
{
Initialize(project, namespaceFilter, null, null);
}
public void Initialize(IProject project, string namespaceFilter, IClass fixture, IMember test)
{
this.project = project;
Assemblies.Add(project.OutputAssemblyFullPath);
if (namespaceFilter != null) {
NamespaceFilter = namespaceFilter;
}
if (fixture != null) {
Fixture = fixture.DotNetName;
if (test != null) {
Test = test.Name;
}
}
}
public IProject Project {
get {
return project;
}
}
/// <summary>
/// Gets the full command line to run the unit test application.
/// This is the combination of the UnitTestApplication and
/// the command line arguments.
/// </summary>
public string GetCommandLine()
{
return String.Concat("\"", UnitTestApplication, "\" ", GetArguments());
}
/// <summary>
/// Gets the arguments to use on the command line to run NUnit.
/// </summary>
public string GetArguments()
{
StringBuilder b = new StringBuilder();
foreach (string assembly in Assemblies) {
if (b.Length > 0)
b.Append(' ');
b.Append('"');
b.Append(assembly);
b.Append('"');
}
if (!ShadowCopy)
b.Append(" /noshadow");
if (NoThread)
b.Append(" /nothread");
if (NoLogo)
b.Append(" /nologo");
if (Labels)
b.Append(" /labels");
if (NoDots)
b.Append(" /nodots");
if (XmlOutputFile != null) {
b.Append(" /xml=\"");
b.Append(XmlOutputFile);
b.Append('"');
}
if (Results != null) {
b.Append(" /results=\"");
b.Append(Results);
b.Append('"');
}
string run = null;
if (NamespaceFilter != null) {
run = NamespaceFilter;
} else if (Fixture != null) {
if (Test != null) {
run = Fixture + "." + Test;
} else {
run = Fixture;
}
} else if (Test != null) {
run = Test;
}
if (run != null) {
b.Append(" /run=\"");
b.Append(run);
b.Append('"');
}
return b.ToString();
}
/// <summary>
/// Checks that the project's PlatformTarget is x86 for the active configuration.
/// </summary>
bool IsPlatformTarget32Bit(IProject project)
{
MSBuildBasedProject msbuildProject = project as MSBuildBasedProject;
if (msbuildProject != null) {
string platformTarget = msbuildProject.GetEvaluatedProperty("PlatformTarget");
return String.Equals(platformTarget, "x86", StringComparison.OrdinalIgnoreCase);
}
return false;
}
bool ProjectUsesDotnet20Runtime(IProject project)
{
MSBuildBasedProject msbuildProject = project as MSBuildBasedProject;
if (msbuildProject != null) {
string targetFrameworkVersion = msbuildProject.GetEvaluatedProperty("TargetFrameworkVersion");
return !String.Equals(targetFrameworkVersion, "v4.0", StringComparison.OrdinalIgnoreCase);
}
return false;
}
}
}

118
src/AddIns/Analysis/UnitTesting/UnitTestCommands.cs

@ -0,0 +1,118 @@ @@ -0,0 +1,118 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
public class StopTestsCommand : AbstractMenuCommand
{
public override void Run()
{
AbstractRunTestCommand runTestCommand = AbstractRunTestCommand.RunningTestCommand;
if (runTestCommand != null) {
runTestCommand.Stop();
}
}
}
public class AddNUnitReferenceCommand : AbstractMenuCommand
{
public void Run(IProject project)
{
if (project != null) {
ReferenceProjectItem nunitRef = new ReferenceProjectItem(project, "nunit.framework");
ProjectService.AddProjectItem(project, nunitRef);
project.Save();
}
}
public override void Run()
{
Run(ProjectService.CurrentProject);
}
}
public class GotoDefinitionCommand : AbstractMenuCommand
{
IUnitTestFileService fileService;
public GotoDefinitionCommand()
: this(new UnitTestFileService())
{
}
public GotoDefinitionCommand(IUnitTestFileService fileService)
{
this.fileService = fileService;
}
public override void Run()
{
ITestTreeView treeView = Owner as ITestTreeView;
if (treeView != null) {
IMember member = GetMember(treeView);
IClass c = treeView.SelectedClass;
if (member != null) {
GotoMember(member);
} else if (c != null) {
GotoClass(c);
}
}
}
IMember GetMember(ITestTreeView treeView)
{
IMember member = treeView.SelectedMember;
if (member != null) {
BaseTestMember baseTestMethod = member as BaseTestMember;
if (baseTestMethod != null) {
return baseTestMethod.Member;
}
}
return member;
}
void GotoMember(IMember member)
{
MemberResolveResult resolveResult = new MemberResolveResult(null, null, member);
GotoFilePosition(resolveResult.GetDefinitionPosition());
}
void GotoClass(IClass c)
{
TypeResolveResult resolveResult = new TypeResolveResult(null, null, c);
GotoFilePosition(resolveResult.GetDefinitionPosition());
}
void GotoFilePosition(DomRegion filePosition)
{
if (filePosition.Position.IsEmpty) {
fileService.OpenFile(filePosition.FileName);
} else {
fileService.JumpToFilePosition(filePosition.FileName, filePosition.Line - 1, filePosition.Column - 1);
}
}
}
public class CollapseAllTestsCommand : AbstractMenuCommand
{
public override void Run()
{
if (!(this.Owner is TreeView))
return;
var treeView = (TreeView)this.Owner;
treeView.CollapseAll();
if (treeView.Nodes.Count > 0) {
treeView.Nodes[0].Expand();
}
}
}
}

58
src/AddIns/Analysis/UnitTesting/UnitTestProcessRunner.cs

@ -0,0 +1,58 @@ @@ -0,0 +1,58 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using ICSharpCode.SharpDevelop.Util;
namespace ICSharpCode.UnitTesting
{
public class UnitTestProcessRunner : IUnitTestProcessRunner
{
ProcessRunner runner;
public event LineReceivedEventHandler OutputLineReceived {
add { runner.OutputLineReceived += value; }
remove { runner.OutputLineReceived -= value; }
}
public event LineReceivedEventHandler ErrorLineReceived {
add { runner.ErrorLineReceived += value; }
remove { runner.ErrorLineReceived -= value; }
}
public event EventHandler ProcessExited {
add { runner.ProcessExited += value; }
remove { runner.ProcessExited -= value; }
}
public UnitTestProcessRunner()
{
runner = new ProcessRunner();
}
public bool LogStandardOutputAndError {
get { return runner.LogStandardOutputAndError; }
set { runner.LogStandardOutputAndError = value; }
}
public string WorkingDirectory {
get { return runner.WorkingDirectory; }
set { runner.WorkingDirectory = value; }
}
public Dictionary<string, string> EnvironmentVariables {
get { return runner.EnvironmentVariables; }
}
public void Start(string command, string arguments)
{
runner.Start(command, arguments);
}
public void Kill()
{
runner.Kill();
}
}
}

118
src/AddIns/Analysis/UnitTesting/UnitTestingOptions.cs

@ -0,0 +1,118 @@ @@ -0,0 +1,118 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Widgets;
namespace ICSharpCode.UnitTesting
{
public class UnitTestingOptions
{
/// <summary>
/// The name of the options as read from the PropertyService.
/// </summary>
public static readonly string AddInOptionsName = "UnitTesting.Options";
/// <summary>
/// The name of the noshadow property stored in SharpDevelop's options.
/// </summary>
public static readonly string NoShadowProperty = "NoShadow";
/// <summary>
/// The name of the nothread property stored in SharpDevelop's options.
/// </summary>
public static readonly string NoThreadProperty = "NoThread";
/// <summary>
/// The name of the nologo property stored in SharpDevelop's options.
/// </summary>
public static readonly string NoLogoProperty = "NoLogo";
/// <summary>
/// The name of the nodots property stored in SharpDevelop's options.
/// </summary>
public static readonly string NoDotsProperty = "NoDots";
/// <summary>
/// The name of the labels property stored in SharpDevelop's options.
/// </summary>
public static readonly string LabelsProperty = "Labels";
/// <summary>
/// The name of the create xml file property stored in SharpDevelop's options.
/// </summary>
public static readonly string CreateXmlOutputFileProperty = "CreateXmlOutputFile";
static readonly UnitTestingOptions instance = new UnitTestingOptions();
public static UnitTestingOptions Instance {
get { return instance; }
}
Properties properties;
UnitTestingOptions()
: this(PropertyService.NestedProperties(AddInOptionsName))
{
}
UnitTestingOptions(Properties properties)
{
this.properties = properties;
}
/// <summary>
/// Disables the use of a separate thread for running tests.
/// </summary>
public bool NoThread {
get { return properties.Get<bool>(NoThreadProperty, false); }
set { properties.Set<bool>(NoThreadProperty, value); }
}
/// <summary>
/// Disables shadow copying of the assemblies being tested.
/// </summary>
public bool NoShadow {
get { return properties.Get<bool>(NoShadowProperty, false); }
set { properties.Set<bool>(NoShadowProperty, value); }
}
/// <summary>
/// Disables the display of the NUnit logo when running NUnit-Console.
/// </summary>
public bool NoLogo {
get { return properties.Get<bool>(NoLogoProperty, false); }
set { properties.Set<bool>(NoLogoProperty, value); }
}
/// <summary>
/// Disables the display of progress when running the unit tests.
/// </summary>
public bool NoDots {
get { return properties.Get<bool>(NoDotsProperty, false); }
set { properties.Set<bool>(NoDotsProperty, value); }
}
/// <summary>
/// Labels each test in the console output.
/// </summary>
public bool Labels {
get { return properties.Get<bool>(LabelsProperty, false); }
set { properties.Set<bool>(LabelsProperty, value); }
}
/// <summary>
/// Creates an XML output file.
/// </summary>
public bool CreateXmlOutputFile {
get { return properties.Get<bool>(CreateXmlOutputFileProperty, false); }
set { properties.Set<bool>(CreateXmlOutputFileProperty, value); }
}
public UnitTestingOptions Clone()
{
return new UnitTestingOptions(properties.Clone());
}
}
}

4
src/AddIns/Analysis/UnitTesting/license.txt

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
Unit Testing Icons are taken from
Fugue Icons Library:
License: Creative Commons Attribution 3.0 License
http://p.yusukekamiyamane.com/
Loading…
Cancel
Save