Browse Source

[WIP] Major refactoring of the unit testing addin:

A lot of functionality gets moved from the main portion of the AddIn into the framework-specific providers.

We no longer deal with test classes and members, but instead use the new ITest interface.
A test can be a whole project, namespace, class, or just a single method.
This can be used to support features like the NUnit [TestCase] attribute, where a single method might result in multiple tests.
newNRvisualizers
Daniel Grunwald 14 years ago
parent
commit
dff21e821f
  1. 398
      src/AddIns/Analysis/UnitTesting/Commands/AbstractRunTestCommand.cs
  2. 25
      src/AddIns/Analysis/UnitTesting/Commands/IRunTestCommandContext.cs
  3. 56
      src/AddIns/Analysis/UnitTesting/Commands/PadCommands.cs
  4. 26
      src/AddIns/Analysis/UnitTesting/Commands/RunAllTestsInPadCommand.cs
  5. 42
      src/AddIns/Analysis/UnitTesting/Commands/RunProjectTestsInPadCommand.cs
  6. 68
      src/AddIns/Analysis/UnitTesting/Commands/RunTestCommandContext.cs
  7. 26
      src/AddIns/Analysis/UnitTesting/Commands/RunTestInPadCommand.cs
  8. 26
      src/AddIns/Analysis/UnitTesting/Commands/RunTestWithDebuggerCommand.cs
  9. 4
      src/AddIns/Analysis/UnitTesting/Commands/RunningTestsCondition.cs
  10. 88
      src/AddIns/Analysis/UnitTesting/Commands/TestableCondition.cs
  11. 70
      src/AddIns/Analysis/UnitTesting/Commands/UnitTestCommands.cs
  12. 23
      src/AddIns/Analysis/UnitTesting/Frameworks/IRegisteredTestFrameworks.cs
  13. 14
      src/AddIns/Analysis/UnitTesting/Frameworks/ITestFramework.cs
  14. 12
      src/AddIns/Analysis/UnitTesting/Frameworks/ITestFrameworkFactory.cs
  15. 51
      src/AddIns/Analysis/UnitTesting/Frameworks/ITestService.cs
  16. 99
      src/AddIns/Analysis/UnitTesting/Frameworks/RegisteredTestFrameworks.cs
  17. 88
      src/AddIns/Analysis/UnitTesting/Frameworks/SDTestService.cs
  18. 149
      src/AddIns/Analysis/UnitTesting/Frameworks/TestExecutionManager.cs
  19. 19
      src/AddIns/Analysis/UnitTesting/Frameworks/TestExecutionOptions.cs
  20. 8
      src/AddIns/Analysis/UnitTesting/Frameworks/TestFrameworkDescriptor.cs
  21. 7
      src/AddIns/Analysis/UnitTesting/Frameworks/TestFrameworkDoozer.cs
  22. 23
      src/AddIns/Analysis/UnitTesting/Frameworks/TestFrameworkFactory.cs
  23. 57
      src/AddIns/Analysis/UnitTesting/Frameworks/TestService.cs
  24. 2
      src/AddIns/Analysis/UnitTesting/Interfaces/IBuildProjectFactory.cs
  25. 1
      src/AddIns/Analysis/UnitTesting/Interfaces/IUnitTestTaskService.cs
  26. 15
      src/AddIns/Analysis/UnitTesting/Interfaces/IUnitTestWorkbench.cs
  27. 2
      src/AddIns/Analysis/UnitTesting/Interfaces/UnitTestBuildProjectFactory.cs
  28. 5
      src/AddIns/Analysis/UnitTesting/Interfaces/UnitTestTaskService.cs
  29. 27
      src/AddIns/Analysis/UnitTesting/Interfaces/UnitTestWorkbench.cs
  30. 55
      src/AddIns/Analysis/UnitTesting/Model/ITest.cs
  31. 47
      src/AddIns/Analysis/UnitTesting/Model/ITestProject.cs
  32. 33
      src/AddIns/Analysis/UnitTesting/Model/ITestSolution.cs
  33. 143
      src/AddIns/Analysis/UnitTesting/Model/TestBase.cs
  34. 199
      src/AddIns/Analysis/UnitTesting/Model/TestCollection.cs
  35. 46
      src/AddIns/Analysis/UnitTesting/Model/TestNamespace.cs
  36. 58
      src/AddIns/Analysis/UnitTesting/Model/TestProjectBase.cs
  37. 30
      src/AddIns/Analysis/UnitTesting/Model/TestResultTypeChangedEventArgs.cs
  38. 80
      src/AddIns/Analysis/UnitTesting/Model/TestSolution.cs
  39. 16
      src/AddIns/Analysis/UnitTesting/NUnit/NUnitConsoleApplication.cs
  40. 4
      src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestDebugger.cs
  41. 39
      src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestFramework.cs
  42. 26
      src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestProject.cs
  43. 4
      src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestRunner.cs
  44. 53
      src/AddIns/Analysis/UnitTesting/Pad/EmptyUnitTestsPad.cs
  45. 18
      src/AddIns/Analysis/UnitTesting/Pad/IUnitTestsPad.cs
  46. 236
      src/AddIns/Analysis/UnitTesting/Pad/UnitTestsPad.cs
  47. 3
      src/AddIns/Analysis/UnitTesting/TestRunner/ITestRunner.cs
  48. 91
      src/AddIns/Analysis/UnitTesting/TestRunner/SelectedTests.cs
  49. 4
      src/AddIns/Analysis/UnitTesting/TestRunner/TestDebuggerBase.cs
  50. 4
      src/AddIns/Analysis/UnitTesting/TestRunner/TestProcessRunnerBase.cs
  51. 4
      src/AddIns/Analysis/UnitTesting/TestRunner/TestResultTask.cs
  52. 5
      src/AddIns/Analysis/UnitTesting/TestRunner/TestRunnerBase.cs
  53. 2
      src/AddIns/Analysis/UnitTesting/TreeView/ClassUnitTestNode.cs
  54. 20
      src/AddIns/Analysis/UnitTesting/TreeView/ITestTreeView.cs
  55. 2
      src/AddIns/Analysis/UnitTesting/TreeView/MemberUnitTestNode.cs
  56. 8
      src/AddIns/Analysis/UnitTesting/TreeView/NamespaceUnitTestNode.cs
  57. 10
      src/AddIns/Analysis/UnitTesting/TreeView/ProjectUnitTestNode.cs
  58. 8
      src/AddIns/Analysis/UnitTesting/TreeView/RootUnitTestNode.cs
  59. 104
      src/AddIns/Analysis/UnitTesting/TreeView/TestTreeView.cs
  60. 38
      src/AddIns/Analysis/UnitTesting/TreeView/UnitTestBaseNode.cs
  61. 128
      src/AddIns/Analysis/UnitTesting/TreeView/UnitTestNode.cs
  62. 21
      src/AddIns/Analysis/UnitTesting/UnitTesting.addin
  63. 56
      src/AddIns/Analysis/UnitTesting/UnitTesting.csproj
  64. 20
      src/Libraries/NRefactory/ICSharpCode.NRefactory/Utils/MultiDictionary.cs
  65. 1
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  66. 23
      src/Main/Base/Project/Src/Commands/BuildCommands.cs
  67. 4
      src/Main/Base/Project/Src/Project/MultipleProjectBuildable.cs
  68. 22
      src/Main/Base/Project/Src/Services/Tasks/TaskService.cs

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

@ -1,398 +0,0 @@
// 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;
}
}
}
}
}

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

@ -1,25 +0,0 @@
// 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; }
}
}

56
src/AddIns/Analysis/UnitTesting/Commands/PadCommands.cs

@ -0,0 +1,56 @@
// 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.Core;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.TreeView;
namespace ICSharpCode.UnitTesting
{
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
{
public override void Run()
{
ITestTreeView treeView = Owner as ITestTreeView;
if (treeView != null) {
ITest test = treeView.SelectedTests.FirstOrDefault();
if (test != null && test.SupportsGoToDefinition)
test.GoToDefinition();
}
}
}
public class CollapseAllTestsCommand : AbstractMenuCommand
{
public override void Run()
{
if (!(this.Owner is SharpTreeView))
return;
var treeView = (SharpTreeView)this.Owner;
if (treeView.Root != null) {
foreach (var n in treeView.Root.Descendants())
n.IsExpanded = false;
}
}
}
}

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

@ -1,26 +0,0 @@
// 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();
}
}
}

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

@ -1,42 +0,0 @@
// 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 TestMember SelectedMember {
get { return null; }
}
public TestClass SelectedClass {
get { return null; }
}
public TestProject SelectedProject {
get { return TestService.Solution.GetTestProject(ProjectService.CurrentProject); }
}
public string SelectedNamespace {
get { return null; }
}
}
}

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

@ -1,68 +0,0 @@
// 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; }
}
}
}

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

@ -1,26 +0,0 @@
// 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

@ -1,26 +0,0 @@
// 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);
}
}
}

4
src/AddIns/Analysis/UnitTesting/Commands/RunningTestsCondition.cs

@ -3,6 +3,7 @@
using System; using System;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
namespace ICSharpCode.UnitTesting namespace ICSharpCode.UnitTesting
{ {
@ -13,7 +14,8 @@ namespace ICSharpCode.UnitTesting
{ {
public bool IsValid(object caller, Condition condition) public bool IsValid(object caller, Condition condition)
{ {
return AbstractRunTestCommand.IsRunningTest; ITestService testService = SD.GetRequiredService<ITestService>();
return testService.IsRunningTests;
} }
} }
} }

88
src/AddIns/Analysis/UnitTesting/Commands/TestableCondition.cs

@ -2,13 +2,13 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System; using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Bookmarks;
using ICSharpCode.SharpDevelop.Editor.Commands; using ICSharpCode.SharpDevelop.Editor.Commands;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting namespace ICSharpCode.UnitTesting
{ {
@ -17,90 +17,36 @@ namespace ICSharpCode.UnitTesting
/// </summary> /// </summary>
public class TestableCondition : IConditionEvaluator public class TestableCondition : IConditionEvaluator
{ {
IRegisteredTestFrameworks testFrameworks; readonly ITestService testService;
public TestableCondition(IRegisteredTestFrameworks testFrameworks)
{
this.testFrameworks = testFrameworks;
}
public TestableCondition() public TestableCondition()
: this(TestService.RegisteredTestFrameworks)
{
}
public static IMember GetMember(object caller)
{
ITestTreeView testTreeView = caller as ITestTreeView;
if (testTreeView != null && testTreeView.SelectedMember != null) {
return testTreeView.SelectedMember.Resolve(testTreeView.SelectedProject);
}
IEntity entity = ResolveResultMenuCommand.GetEntity(caller);
return entity as IMember;
}
public static ITypeDefinition GetClass(object caller)
{
ITestTreeView testTreeView = caller as ITestTreeView;
if (testTreeView != null && testTreeView.SelectedClass != null) {
return testTreeView.SelectedClass.Resolve(testTreeView.SelectedProject);
}
IEntity entity = ResolveResultMenuCommand.GetEntity(caller);
return entity as ITypeDefinition;
}
public static IProject GetProject(object caller)
{ {
ITestTreeView testTreeView = caller as ITestTreeView; this.testService = SD.GetRequiredService<ITestService>();
if (testTreeView != null) {
return testTreeView.SelectedProject != null ? testTreeView.SelectedProject.Project : null;
}
ITypeDefinition c = GetClassFromMemberOrCaller(caller);
return GetProject(c);
} }
public static ITypeDefinition GetClassFromMemberOrCaller(object caller) public TestableCondition(ITestService testService)
{ {
IMember m = GetMember(caller); this.testService = testService;
if (m != null) {
return m.DeclaringTypeDefinition;
}
return GetClass(caller);
} }
static IProject GetProject(ITypeDefinition c) public bool IsValid(object caller, Condition condition)
{ {
return c != null ? c.ParentAssembly.GetProject() : null; return GetTests(testService.OpenSolution, caller).Any();
} }
/// <summary> public static IEnumerable<ITest> GetTests(ITestSolution testSolution, object caller)
/// Returns the namespace selected if any.
/// </summary>
public static string GetNamespace(object caller)
{ {
ITestTreeView testTreeView = caller as ITestTreeView; ITestTreeView testTreeView = caller as ITestTreeView;
if (testTreeView != null) { if (testTreeView != null) {
return testTreeView.SelectedNamespace; return testTreeView.SelectedTests;
} }
return null; if (testSolution != null) {
} IEntity entity = ResolveResultMenuCommand.GetEntity(caller);
ITest test = testSolution.GetTestForEntity(entity);
public bool IsValid(object caller, Condition condition) if (test != null)
{ return new[] { test };
IMember m = GetMember(caller);
if (m != null) {
return testFrameworks.IsTestMember(m);
}
ITypeDefinition c = GetClass(caller);
if (ClassHasProject(c)) {
return testFrameworks.IsTestClass(c);
} }
return false; return Enumerable.Empty<ITest>();
}
static bool ClassHasProject(ITypeDefinition c)
{
return (c != null) && (c.ParentAssembly.GetProject() != null);
} }
} }
} }

70
src/AddIns/Analysis/UnitTesting/Commands/UnitTestCommands.cs

@ -2,78 +2,64 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System; using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project; using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.TreeView;
namespace ICSharpCode.UnitTesting namespace ICSharpCode.UnitTesting
{ {
public class StopTestsCommand : AbstractMenuCommand public class RunAllTestsInProjectCommand : AbstractMenuCommand
{ {
public override void Run() public override void Run()
{ {
AbstractRunTestCommand runTestCommand = AbstractRunTestCommand.RunningTestCommand; ITestService testService = SD.GetRequiredService<ITestService>();
if (runTestCommand != null) { if (testService.OpenSolution != null) {
runTestCommand.Stop(); ITestProject project = testService.OpenSolution.GetTestProject(ProjectService.CurrentProject);
if (project != null)
testService.RunTestsAsync(new [] { project }, new TestExecutionOptions()).FireAndForget();
} }
} }
} }
public class AddNUnitReferenceCommand : AbstractMenuCommand public class RunAllTestsInSolutionCommand : AbstractMenuCommand
{ {
public void Run(IProject project) public override void Run()
{ {
if (project != null) { ITestService testService = SD.GetRequiredService<ITestService>();
ReferenceProjectItem nunitRef = new ReferenceProjectItem(project, "nunit.framework"); if (testService.OpenSolution != null)
ProjectService.AddProjectItem(project, nunitRef); testService.RunTestsAsync(new [] { testService.OpenSolution }, new TestExecutionOptions()).FireAndForget();
project.Save();
}
} }
}
public class RunSelectedTestCommand : AbstractMenuCommand
{
public override void Run() public override void Run()
{ {
Run(ProjectService.CurrentProject); ITestService testService = SD.GetRequiredService<ITestService>();
IEnumerable<ITest> tests = TestableCondition.GetTests(testService.OpenSolution, Owner);
testService.RunTestsAsync(tests, new TestExecutionOptions()).FireAndForget();
} }
} }
public class GotoDefinitionCommand : AbstractMenuCommand public class RunSelectedTestWithDebuggerCommand : AbstractMenuCommand
{ {
public override void Run() public override void Run()
{ {
ITestTreeView treeView = Owner as ITestTreeView; ITestService testService = SD.GetRequiredService<ITestService>();
if (treeView != null) { IEnumerable<ITest> tests = TestableCondition.GetTests(testService.OpenSolution, Owner);
var member = treeView.SelectedMember; testService.RunTestsAsync(tests, new TestExecutionOptions { UseDebugger = true }).FireAndForget();
var c = treeView.SelectedClass;
IEntity entity;
if (member != null) {
entity = member.Resolve(treeView.SelectedProject);
} else if (c != null) {
entity = c.Resolve(treeView.SelectedProject);
} else {
entity = null;
}
if (entity != null) {
NavigationService.NavigateTo(entity);
}
}
} }
} }
public class CollapseAllTestsCommand : AbstractMenuCommand public class StopTestsCommand : AbstractMenuCommand
{ {
public override void Run() public override void Run()
{ {
if (!(this.Owner is SharpTreeView)) ITestService testService = SD.GetRequiredService<ITestService>();
return; testService.CancelRunningTests();
var treeView = (SharpTreeView)this.Owner;
if (treeView.Root != null) {
foreach (var n in treeView.Root.Descendants())
n.IsExpanded = false;
}
} }
} }
} }

23
src/AddIns/Analysis/UnitTesting/Frameworks/IRegisteredTestFrameworks.cs

@ -1,23 +0,0 @@
// 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(IMember member);
bool IsTestClass(ITypeDefinition typeDefinition);
bool IsTestProject(IProject project);
bool IsBuildNeededBeforeTestRunForProject(IProject project);
}
}

14
src/AddIns/Analysis/UnitTesting/Frameworks/ITestFramework.cs

@ -10,15 +10,25 @@ namespace ICSharpCode.UnitTesting
{ {
public interface ITestFramework public interface ITestFramework
{ {
/// <summary>
/// Gets whether this test framework supports the specified project.
/// </summary>
bool IsTestProject(IProject project);
/// <summary>
/// Creates a new test project based on the specified project.
/// </summary>
ITestProject CreateTestProject(ITestSolution parentSolution, IProject project);
/*
bool IsTestMember(IMember member); bool IsTestMember(IMember member);
bool IsTestClass(ITypeDefinition testClass); bool IsTestClass(ITypeDefinition testClass);
bool IsTestProject(IProject project);
IEnumerable<TestMember> GetTestMembersFor(ITypeDefinition typeDefinition); IEnumerable<TestMember> GetTestMembersFor(ITypeDefinition typeDefinition);
ITestRunner CreateTestRunner(); ITestRunner CreateTestRunner();
ITestRunner CreateTestDebugger(); ITestRunner CreateTestDebugger();
bool IsBuildNeededBeforeTestRun { get; } bool IsBuildNeededBeforeTestRun { get; }*/
} }
} }

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

@ -1,12 +0,0 @@
// 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);
}
}

51
src/AddIns/Analysis/UnitTesting/Frameworks/ITestService.cs

@ -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;
using System.Threading.Tasks;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
[SDService]
public interface ITestService
{
/// <summary>
/// Gets the test framework that supports the specified project.
/// </summary>
ITestFramework GetTestFrameworkForProject(IProject project);
MessageViewCategory UnitTestMessageView { get; }
/// <summary>
/// Gets the current test solution.
/// This property should only be accessed by the main thread.
/// </summary>
ITestSolution OpenSolution { get; }
/// <summary>
/// Occurs when the open test solution has changed.
/// </summary>
event EventHandler OpenSolutionChanged;
/// <summary>
/// Builds the project (if necessary) and runs the specified tests.
/// If tests are already running, the existing run is cancelled.
/// </summary>
Task RunTestsAsync(IEnumerable<ITest> selectedTests, TestExecutionOptions options);
/// <summary>
/// Gets whether tests are currently running.
/// </summary>
bool IsRunningTests { get; }
/// <summary>
/// Aborts the current test run.
/// This method has no effect if no tests are running.
/// </summary>
void CancelRunningTests();
}
}

99
src/AddIns/Analysis/UnitTesting/Frameworks/RegisteredTestFrameworks.cs

@ -1,99 +0,0 @@
// 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;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
public class RegisteredTestFrameworks : IRegisteredTestFrameworks
{
IReadOnlyList<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(IMember member)
{
ITestFramework testFramework = GetTestFramework(member);
if (testFramework != null) {
return testFramework.IsTestMember(member);
}
return false;
}
ITestFramework GetTestFramework(IEntity entity)
{
if (entity != null) {
return GetTestFrameworkForProject(entity.ParentAssembly.GetProject());
}
return null;
}
public bool IsTestClass(ITypeDefinition 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;
}
}
}

88
src/AddIns/Analysis/UnitTesting/Frameworks/SDTestService.cs

@ -0,0 +1,88 @@
// 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.Threading;
using System.Threading.Tasks;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.UnitTesting.Frameworks;
namespace ICSharpCode.UnitTesting
{
sealed class SDTestService : ITestService
{
#region Test Framework Management
const string AddInPath = "/SharpDevelop/UnitTesting/TestFrameworks";
IReadOnlyList<TestFrameworkDescriptor> testFrameworkDescriptors = SD.AddInTree.BuildItems<TestFrameworkDescriptor>(AddInPath, null);
public ITestFramework GetTestFrameworkForProject(IProject project)
{
if (project != null) {
foreach (TestFrameworkDescriptor descriptor in testFrameworkDescriptors) {
if (descriptor.IsSupportedProject(project)) {
return descriptor.TestFramework;
}
}
}
return null;
}
#endregion
#region UnitTestMessageView
MessageViewCategory unitTestMessageView;
public MessageViewCategory UnitTestMessageView {
get {
if (unitTestMessageView == null) {
MessageViewCategory.Create(ref unitTestMessageView,
"UnitTesting",
"${res:ICSharpCode.NUnitPad.NUnitPadContent.PadName}");
}
return unitTestMessageView;
}
}
#endregion
#region OpenSolution
TestSolution solution;
public ITestSolution OpenSolution {
get {
SD.MainThread.VerifyAccess();
if (solution == null)
solution = new TestSolution(this, SD.ResourceService);
return solution;
}
}
public event EventHandler OpenSolutionChanged { add {} remove {} }
#endregion
#region RunTests
CancellationTokenSource runTestsCancellationTokenSource;
public bool IsRunningTests {
get { return runTestsCancellationTokenSource != null; }
}
public Task RunTestsAsync(IEnumerable<ITest> selectedTests, TestExecutionOptions options)
{
CancelRunningTests();
runTestsCancellationTokenSource = new CancellationTokenSource();
var executionManager = new TestExecutionManager();
return executionManager.RunTestsAsync(selectedTests, options, runTestsCancellationTokenSource.Token);
}
public void CancelRunningTests()
{
if (runTestsCancellationTokenSource != null) {
runTestsCancellationTokenSource.Cancel();
runTestsCancellationTokenSource = null;
}
}
#endregion
}
}

149
src/AddIns/Analysis/UnitTesting/Frameworks/TestExecutionManager.cs

@ -0,0 +1,149 @@
// 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.Utils;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting.Frameworks
{
/// <summary>
/// Manages the execution of tests across multiple projects.
/// Takes care of building the projects (if necessary) and showing progress in the UI.
/// </summary>
public class TestExecutionManager
{
readonly IBuildProjectFactory buildProjectFactory;
readonly IUnitTestTaskService taskService;
readonly IUnitTestSaveAllFilesCommand saveAllFilesCommand;
readonly ITestService testService;
readonly IWorkbench workbench;
readonly IStatusBarService statusBarService;
readonly IBuildOptions buildOptions;
public TestExecutionManager()
{
this.buildProjectFactory = new UnitTestBuildProjectFactory();
this.taskService = new UnitTestTaskService();
this.saveAllFilesCommand = new UnitTestSaveAllFilesCommand();
this.testService = SD.GetRequiredService<ITestService>();
this.workbench = SD.Workbench;
this.statusBarService = SD.StatusBar;
this.buildOptions = new UnitTestBuildOptions();
}
readonly MultiDictionary<ITestProject, ITest> testsByProject = new MultiDictionary<ITestProject, ITest>();
CancellationToken cancellationToken;
public async Task RunTestsAsync(IEnumerable<ITest> selectedTests, TestExecutionOptions options, CancellationToken cancellationToken)
{
this.cancellationToken = cancellationToken;
GroupTestsByProject(selectedTests);
ClearTasks();
ShowUnitTestsPad();
ShowOutputPad();
ResetTestResults();
saveAllFilesCommand.SaveAllFiles();
// Run the build, if necessary:
var projectsToBuild = testsByProject.Keys.Select(p => p.GetBuildableForTesting()).Where(b => b != null).ToList();
if (projectsToBuild.Count > 0) {
var buildCommand = buildProjectFactory.CreateBuildProjectBeforeTestRun(projectsToBuild);
var buildResults = await buildCommand.BuildAsync(cancellationToken);
if (buildResults.Result != BuildResultCode.Success)
return;
}
cancellationToken.ThrowIfCancellationRequested();
using (IProgressMonitor progressMonitor = statusBarService.CreateProgressMonitor(cancellationToken)) {
int projectsLeftToRun = testsByProject.Count;
foreach (IGrouping<ITestProject, ITest> g in testsByProject) {
ITestProject project = g.Key;
progressMonitor.TaskName = GetProgressMonitorLabel(project);
progressMonitor.Progress = GetProgress(projectsLeftToRun);
using (IProgressMonitor nested = progressMonitor.CreateSubTask(1.0 / testsByProject.Count)) {
await project.RunTestsAsync(g, options, nested);
}
projectsLeftToRun--;
progressMonitor.CancellationToken.ThrowIfCancellationRequested();
}
}
ShowErrorList();
}
void GroupTestsByProject(IEnumerable<ITest> selectedTests)
{
foreach (ITest test in selectedTests) {
if (test == null)
continue;
if (test.ParentProject == null) {
// When a solution is selected, select all its projects individually
foreach (ITest project in test.NestedTests) {
Debug.Assert(project == project.ParentProject);
testsByProject.Add(project.ParentProject, project);
}
} else {
testsByProject.Add(test.ParentProject, test);
}
cancellationToken.ThrowIfCancellationRequested();
}
}
void ClearTasks()
{
taskService.BuildMessageViewCategory.ClearText();
taskService.ClearExceptCommentTasks();
testService.UnitTestMessageView.ClearText();
}
void ShowUnitTestsPad()
{
workbench.GetPad(typeof(UnitTestsPad)).BringPadToFront();
}
void ShowOutputPad()
{
workbench.GetPad(typeof(CompilerMessageView)).BringPadToFront();
}
void ResetTestResults()
{
cancellationToken.ThrowIfCancellationRequested();
foreach (ITest test in testsByProject.Values) {
test.ResetTestResults();
}
cancellationToken.ThrowIfCancellationRequested();
}
string GetProgressMonitorLabel(ITestProject project)
{
StringTagPair tagPair = new StringTagPair("Name", project.DisplayName);
return StringParser.Parse("${res:ICSharpCode.UnitTesting.StatusBarProgressLabel}", tagPair);
}
double GetProgress(int projectsLeftToRunCount)
{
int totalProjectCount = testsByProject.Count;
return (double)(totalProjectCount - projectsLeftToRunCount) / totalProjectCount;
}
void ShowErrorList()
{
if (taskService.SomethingWentWrong && buildOptions.ShowErrorListAfterBuild) {
workbench.GetPad(typeof(ErrorListPad)).BringPadToFront();
}
}
}
}

19
src/AddIns/Analysis/UnitTesting/Frameworks/TestExecutionOptions.cs

@ -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;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Options used for test execution.
/// </summary>
public class TestExecutionOptions
{
/// <summary>
/// Gets/Sets whether to debug the test execution.
/// The default is <c>false</c>.
/// </summary>
public bool UseDebugger { get; set; }
}
}

8
src/AddIns/Analysis/UnitTesting/Frameworks/TestFrameworkDescriptor.cs

@ -12,14 +12,14 @@ namespace ICSharpCode.UnitTesting
public class TestFrameworkDescriptor public class TestFrameworkDescriptor
{ {
Properties properties; Properties properties;
ITestFrameworkFactory factory; Func<string, object> objectFactory;
ITestFramework testFramework; ITestFramework testFramework;
List<string> supportedProjectFileExtensions = new List<string>(); List<string> supportedProjectFileExtensions = new List<string>();
public TestFrameworkDescriptor(Properties properties, ITestFrameworkFactory factory) public TestFrameworkDescriptor(Properties properties, Func<string, object> objectFactory)
{ {
this.properties = properties; this.properties = properties;
this.factory = factory; this.objectFactory = objectFactory;
GetSupportedProjectFileExtensions(); GetSupportedProjectFileExtensions();
} }
@ -47,7 +47,7 @@ namespace ICSharpCode.UnitTesting
void CreateTestFrameworkIfNotCreated() void CreateTestFrameworkIfNotCreated()
{ {
if (testFramework == null) { if (testFramework == null) {
testFramework = factory.Create(ClassName); testFramework = (ITestFramework)objectFactory(ClassName);
} }
} }

7
src/AddIns/Analysis/UnitTesting/Frameworks/TestFrameworkDoozer.cs

@ -19,12 +19,7 @@ namespace ICSharpCode.UnitTesting
public object BuildItem(BuildItemArgs args) public object BuildItem(BuildItemArgs args)
{ {
return BuildItem(args.Codon, new TestFrameworkFactory(args.AddIn)); return new TestFrameworkDescriptor(args.Codon.Properties, args.AddIn.CreateObject);
}
public TestFrameworkDescriptor BuildItem(Codon codon, ITestFrameworkFactory factory)
{
return new TestFrameworkDescriptor(codon.Properties, factory);
} }
} }
} }

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

@ -1,23 +0,0 @@
// 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;
}
}
}

57
src/AddIns/Analysis/UnitTesting/Frameworks/TestService.cs

@ -1,57 +0,0 @@
// 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 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) {
testFrameworks = new RegisteredTestFrameworks(SD.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}");
}
static TestSolution solution;
public static TestSolution Solution {
get {
SD.MainThread.VerifyAccess();
if (solution == null)
solution = new TestSolution(RegisteredTestFrameworks);
return solution;
}
}
}
}

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

@ -10,6 +10,6 @@ namespace ICSharpCode.UnitTesting
{ {
public interface IBuildProjectFactory public interface IBuildProjectFactory
{ {
BuildProject CreateBuildProjectBeforeTestRun(IEnumerable<IProject> projects); BuildProject CreateBuildProjectBeforeTestRun(IEnumerable<IBuildable> projects);
} }
} }

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

@ -11,7 +11,6 @@ namespace ICSharpCode.UnitTesting
public interface IUnitTestTaskService public interface IUnitTestTaskService
{ {
MessageViewCategory BuildMessageViewCategory { get; } MessageViewCategory BuildMessageViewCategory { get; }
bool InUpdate { get; set; }
void ClearExceptCommentTasks(); void ClearExceptCommentTasks();
void Add(SDTask task); void Add(SDTask task);
bool SomethingWentWrong { get; } bool SomethingWentWrong { get; }

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

@ -1,15 +0,0 @@
// 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);
}
}

2
src/AddIns/Analysis/UnitTesting/Interfaces/UnitTestBuildProjectFactory.cs

@ -10,7 +10,7 @@ namespace ICSharpCode.UnitTesting
{ {
public class UnitTestBuildProjectFactory : IBuildProjectFactory public class UnitTestBuildProjectFactory : IBuildProjectFactory
{ {
public BuildProject CreateBuildProjectBeforeTestRun(IEnumerable<IProject> projects) public BuildProject CreateBuildProjectBeforeTestRun(IEnumerable<IBuildable> projects)
{ {
return new BuildProjectBeforeExecute(new MultipleProjectBuildable(projects)); return new BuildProjectBeforeExecute(new MultipleProjectBuildable(projects));
} }

5
src/AddIns/Analysis/UnitTesting/Interfaces/UnitTestTaskService.cs

@ -14,11 +14,6 @@ namespace ICSharpCode.UnitTesting
TaskService.ClearExceptCommentTasks(); TaskService.ClearExceptCommentTasks();
} }
public bool InUpdate {
get { return TaskService.InUpdate; }
set { TaskService.InUpdate = value; }
}
public MessageViewCategory BuildMessageViewCategory { public MessageViewCategory BuildMessageViewCategory {
get { return TaskService.BuildMessageViewCategory; } get { return TaskService.BuildMessageViewCategory; }
} }

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

@ -1,27 +0,0 @@
// 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);
}
}
}

55
src/AddIns/Analysis/UnitTesting/Model/ITest.cs

@ -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 System.Collections.ObjectModel;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Represents a unit test or a group of unit tests.
/// </summary>
public interface ITest
{
/// <summary>
/// Gets the collection of nested tests.
/// </summary>
TestCollection NestedTests { get; }
/// <summary>
/// Gets the parent project that owns this test.
/// </summary>
ITestProject ParentProject { get; }
/// <summary>
/// Name to be displayed in the tests tree view.
/// </summary>
string DisplayName { get; }
/// <summary>
/// Raised when the <see cref="Name"/> property changes.
/// </summary>
event EventHandler DisplayNameChanged;
/// <summary>
/// Gets the result of the previous run of this test.
/// </summary>
TestResultType Result { get; }
/// <summary>
/// Raised when the <see cref="Result"/> property changes.
/// </summary>
event EventHandler<TestResultTypeChangedEventArgs> ResultChanged;
/// <summary>
/// Resets the test results for this test and all nested tests.
/// </summary>
void ResetTestResults();
bool SupportsGoToDefinition { get; }
void GoToDefinition();
UnitTestNode CreateTreeNode();
}
}

47
src/AddIns/Analysis/UnitTesting/Model/ITestProject.cs

@ -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.Threading;
using System.Threading.Tasks;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Represents the root node of a test project.
/// </summary>
public interface ITestProject : ITest
{
/// <summary>
/// Gets the SharpDevelop project on which this test project is based.
/// </summary>
IProject Project { get; }
/// <summary>
/// Gets the test for the specified entity.
/// Returns null if the entity is not a unit test.
/// </summary>
ITest GetTestForEntity(IEntity entity);
/// <summary>
/// Returns a SharpDevelop <see cref="IBuildable"/> that builds the project
/// for test execution.
/// May return null if the project does not require compilation.
/// </summary>
IBuildable GetBuildableForTesting();
/// <summary>
/// Notifies the project that the parse information was changed.
/// </summary>
void NotifyParseInformationChanged(IUnresolvedFile oldUnresolvedFile, IUnresolvedFile newUnresolvedFile);
/// <summary>
/// Runs the specified tests. The specified tests must belong to this project.
/// </summary>
Task RunTestsAsync(IEnumerable<ITest> tests, TestExecutionOptions options, IProgressMonitor progressMonitor);
}
}

33
src/AddIns/Analysis/UnitTesting/Model/ITestSolution.cs

@ -0,0 +1,33 @@
// 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.ObjectModel;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// A test that represents the whole solution.
/// </summary>
public interface ITestSolution : ITest
{
// /// <summary>
// /// Gets the list of all test projects.
// /// </summary>
// ObservableCollection<ITestProject> TestableProjects { get; }
/// <summary>
/// Gets the test project for the specified project.
/// Returns null if the project is not a test project.
/// </summary>
ITestProject GetTestProject(IProject project);
/// <summary>
/// Gets the test for the specified entity.
/// Returns null if the entity is not a unit test.
/// </summary>
ITest GetTestForEntity(IEntity entity);
}
}

143
src/AddIns/Analysis/UnitTesting/Model/TestBase.cs

@ -0,0 +1,143 @@
// 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.ObjectModel;
using System.ComponentModel;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Base class for <see cref="ITest"/> implementations.
/// </summary>
public abstract class TestBase : ITest
{
public abstract string DisplayName { get; }
public virtual event EventHandler DisplayNameChanged { add {} remove {} }
public abstract ITestProject ParentProject { get; }
#region Result
TestResultType result;
bool useCompositeResultsOfNestedTests;
public event EventHandler<TestResultTypeChangedEventArgs> ResultChanged;
/// <summary>
/// Gets/Sets the test result.
/// </summary>
/// <remarks>
/// If the result is bound to the composite of the nested tests, setting this
/// property will remove the binding.
/// </remarks>
public TestResultType Result {
get { return result; }
protected set {
if (useCompositeResultsOfNestedTests) {
nestedTests.PropertyChanged -= nestedTestsCollection_PropertyChanged;
useCompositeResultsOfNestedTests = false;
}
ChangeResult(value);
}
}
void ChangeResult(TestResultType newResult)
{
TestResultType oldResult = result;
if (oldResult != newResult) {
result = newResult;
OnResultChanged(new TestResultTypeChangedEventArgs(oldResult, newResult));
}
}
/// <summary>
/// Binds the result to the composite result of the nested tests.
/// </summary>
protected void BindResultToCompositeResultOfNestedTests()
{
if (!useCompositeResultsOfNestedTests) {
useCompositeResultsOfNestedTests = true;
if (nestedTests != null) {
nestedTests.PropertyChanged += nestedTestsCollection_PropertyChanged;
ChangeResult(nestedTests.CompositeResult);
} else {
ChangeResult(TestResultType.None);
}
}
}
void nestedTestsCollection_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == null || e.PropertyName == "CompositeResult") {
ChangeResult(nestedTests.CompositeResult);
}
}
protected virtual void OnResultChanged(TestResultTypeChangedEventArgs e)
{
if (ResultChanged != null) {
ResultChanged(this, e);
}
}
#endregion
#region NestedTests
TestCollection nestedTests;
public TestCollection NestedTests {
get {
if (nestedTests == null) {
nestedTests = InitializeNestedTests();
OnNestedTestsInitialized();
}
return nestedTests;
}
}
protected bool NestedTestsInitialized {
get { return nestedTests != null; }
}
protected virtual TestCollection InitializeNestedTests()
{
return new TestCollection();
}
protected virtual void OnNestedTestsInitialized()
{
// If we're supposed to be bound to the composite result of the nested tests,
// but the nested test collection wasn't initialized yet,
// we will recreate the binding.
if (useCompositeResultsOfNestedTests) {
useCompositeResultsOfNestedTests = false;
BindResultToCompositeResultOfNestedTests();
}
}
#endregion
public virtual void ResetTestResults()
{
if (nestedTests != null) {
foreach (ITest test in nestedTests) {
test.ResetTestResults();
}
}
if (!useCompositeResultsOfNestedTests)
this.Result = TestResultType.None;
}
public virtual bool SupportsGoToDefinition {
get { return false; }
}
public void GoToDefinition()
{
throw new NotSupportedException();
}
public virtual UnitTestNode CreateTreeNode()
{
return new UnitTestNode(this);
}
}
}

199
src/AddIns/Analysis/UnitTesting/Model/TestCollection.cs

@ -0,0 +1,199 @@
// 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 System.Collections.Specialized;
using System.ComponentModel;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Collection of tests that monitors the amount of tests for each result type,
/// allowing efficient updates of the overall result.
/// </summary>
public class TestCollection : ObservableCollection<ITest>
{
#region Struct TestCounts
struct TestCounts
{
internal int indeterminate;
internal int successful;
internal int failed;
internal int ignored;
public TestResultType CompositeResult {
get {
if (failed > 0)
return TestResultType.Failure;
if (indeterminate > 0)
return TestResultType.None;
if (ignored > 0)
return TestResultType.Ignored;
if (successful > 0)
return TestResultType.Success;
return TestResultType.None;
}
}
public void Add(TestResultType result)
{
switch (result) {
case TestResultType.None:
indeterminate++;
break;
case TestResultType.Success:
successful++;
break;
case TestResultType.Failure:
failed++;
break;
case TestResultType.Ignored:
ignored++;
break;
default:
throw new NotSupportedException("Invalid value for TestResultType");
}
}
public void Remove(TestResultType result)
{
switch (result) {
case TestResultType.None:
indeterminate--;
break;
case TestResultType.Success:
successful--;
break;
case TestResultType.Failure:
failed--;
break;
case TestResultType.Ignored:
ignored--;
break;
default:
throw new NotSupportedException("Invalid value for TestResultType");
}
}
}
#endregion
public static TestResultType GetCompositeResult(IEnumerable<ITest> tests)
{
TestCounts c = new TestCounts();
foreach (ITest test in tests) {
c.Add(test.Result);
}
return c.CompositeResult;
}
#region Properties
TestCounts counts;
public int IndeterminateNestedTestCount {
get { return counts.indeterminate; }
}
public int SuccessfulNestedTestCount {
get { return counts.successful; }
}
public int FailedNestedTestCount {
get { return counts.failed; }
}
public int IgnoredNestedTestCount {
get { return counts.ignored; }
}
public TestResultType CompositeResult {
get { return counts.CompositeResult; }
}
static readonly PropertyChangedEventArgs indeterminateArgs = new PropertyChangedEventArgs("IndeterminateNestedTestCount");
static readonly PropertyChangedEventArgs successfulArgs = new PropertyChangedEventArgs("SuccessfulNestedTestCount");
static readonly PropertyChangedEventArgs failedArgs = new PropertyChangedEventArgs("FailedNestedTestCount");
static readonly PropertyChangedEventArgs ignoredArgs = new PropertyChangedEventArgs("IgnoredNestedTestCount");
static readonly PropertyChangedEventArgs compositeResultArgs = new PropertyChangedEventArgs("CompositeResult");
void RaiseCountChangeEvents(TestCounts old)
{
if (old.indeterminate != counts.indeterminate)
OnPropertyChanged(indeterminateArgs);
if (old.successful != counts.successful)
OnPropertyChanged(successfulArgs);
if (old.failed != counts.failed)
OnPropertyChanged(failedArgs);
if (old.ignored != counts.ignored)
OnPropertyChanged(ignoredArgs);
if (old.CompositeResult != counts.CompositeResult)
OnPropertyChanged(compositeResultArgs);
}
// change visibility of PropertyChanged event to public
public new event PropertyChangedEventHandler PropertyChanged {
add { base.PropertyChanged += value; }
remove { base.PropertyChanged -= value; }
}
#endregion
void item_ResultChanged(object sender, TestResultTypeChangedEventArgs e)
{
TestCounts old = counts;
counts.Remove(e.OldResult);
counts.Add(e.NewResult);
RaiseCountChangeEvents(old);
}
protected override void ClearItems()
{
TestCounts old = counts;
counts = default(TestCounts);
foreach (ITest test in Items) {
counts.Remove(test.Result);
test.ResultChanged -= item_ResultChanged;
}
base.ClearItems();
RaiseCountChangeEvents(old);
}
protected override void InsertItem(int index, ITest item)
{
if (item == null)
throw new ArgumentNullException("item");
TestCounts old = counts;
counts.Add(item.Result);
base.InsertItem(index, item);
item.ResultChanged += item_ResultChanged;
RaiseCountChangeEvents(old);
}
protected override void RemoveItem(int index)
{
TestCounts old = counts;
ITest oldItem = Items[index];
counts.Remove(oldItem.Result);
oldItem.ResultChanged -= item_ResultChanged;
base.RemoveItem(index);
RaiseCountChangeEvents(old);
}
protected override void SetItem(int index, ITest item)
{
if (item == null)
throw new ArgumentNullException("item");
TestCounts old = counts;
ITest oldItem = Items[index];
counts.Remove(oldItem.Result);
oldItem.ResultChanged -= item_ResultChanged;
counts.Add(item.Result);
item.ResultChanged += item_ResultChanged;
base.SetItem(index, item);
RaiseCountChangeEvents(old);
}
}
}

46
src/AddIns/Analysis/UnitTesting/Model/TestNamespace.cs

@ -0,0 +1,46 @@
// 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.ObjectModel;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Represents a namespace of unit tests.
/// </summary>
public class TestNamespace : TestBase
{
readonly ITestProject project;
readonly string namespaceName;
readonly string displayName;
public TestNamespace(ITestProject project, string namespaceName, string displayName = null)
{
if (project == null)
throw new ArgumentNullException("project");
if (namespaceName == null)
throw new ArgumentNullException("namespaceName");
this.project = project;
this.namespaceName = namespaceName;
if (displayName != null) {
this.displayName = displayName;
} else {
this.displayName = namespaceName.Substring(namespaceName.IndexOf('.') + 1);
}
BindResultToCompositeResultOfNestedTests();
}
public override ITestProject ParentProject {
get { return project; }
}
public string NamespaceName {
get { return namespaceName; }
}
public override string DisplayName {
get { return displayName; }
}
}
}

58
src/AddIns/Analysis/UnitTesting/Model/TestProjectBase.cs

@ -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 System.Threading.Tasks;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Base class for <see cref="ITestProject"/> implementations.
/// </summary>
public abstract class TestProjectBase : TestBase, ITestProject
{
IProject project;
public TestProjectBase(IProject project)
{
if (project == null)
throw new ArgumentNullException("project");
this.project = project;
BindResultToCompositeResultOfNestedTests();
}
public IProject Project {
get { return project; }
}
public override ITestProject ParentProject {
get { return this; }
}
public override string DisplayName {
get { return project.Name; }
}
public virtual ITest GetTestForEntity(IEntity entity)
{
return null;
}
public virtual IBuildable GetBuildableForTesting()
{
return project;
}
public void NotifyParseInformationChanged(IUnresolvedFile oldUnresolvedFile, IUnresolvedFile newUnresolvedFile)
{
if (!NestedTestsInitialized)
return;
}
public abstract Task RunTestsAsync(IEnumerable<ITest> tests, TestExecutionOptions options, IProgressMonitor progressMonitor);
}
}

30
src/AddIns/Analysis/UnitTesting/Model/TestResultTypeChangedEventArgs.cs

@ -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>
/// EventArgs for ITest.ResultChanged.
/// </summary>
public class TestResultTypeChangedEventArgs : EventArgs
{
readonly TestResultType oldResult;
readonly TestResultType newResult;
public TestResultTypeChangedEventArgs(TestResultType oldResult, TestResultType newResult)
{
this.oldResult = oldResult;
this.newResult = newResult;
}
public TestResultType NewResult {
get { return newResult; }
}
public TestResultType OldResult {
get { return oldResult; }
}
}
}

80
src/AddIns/Analysis/UnitTesting/Model/TestSolution.cs

@ -5,7 +5,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Parser; using ICSharpCode.SharpDevelop.Parser;
using ICSharpCode.SharpDevelop.Project; using ICSharpCode.SharpDevelop.Project;
@ -15,21 +16,20 @@ namespace ICSharpCode.UnitTesting
/// <summary> /// <summary>
/// Manages the collection of TestProjects. /// Manages the collection of TestProjects.
/// </summary> /// </summary>
public class TestSolution sealed class TestSolution : TestBase, ITestSolution
{ {
readonly IRegisteredTestFrameworks registeredTestFrameworks; readonly IResourceService resourceService;
readonly ITestService testService;
readonly List<ProjectChangeListener> changeListeners = new List<ProjectChangeListener>(); readonly List<ProjectChangeListener> changeListeners = new List<ProjectChangeListener>();
readonly ObservableCollection<TestProject> testableProjects = new ObservableCollection<TestProject>();
public ObservableCollection<TestProject> TestableProjects {
get { return testableProjects; }
}
public TestSolution(IRegisteredTestFrameworks registeredTestFrameworks) public TestSolution(ITestService testService, IResourceService resourceService)
{ {
if (registeredTestFrameworks == null) if (testService == null)
throw new ArgumentNullException("registeredTestFrameworks"); throw new ArgumentNullException("testService");
this.registeredTestFrameworks = registeredTestFrameworks; if (resourceService == null)
throw new ArgumentNullException("resourceService");
this.testService = testService;
this.resourceService = resourceService;
ProjectService.SolutionLoaded += ProjectService_SolutionLoaded; ProjectService.SolutionLoaded += ProjectService_SolutionLoaded;
ProjectService.SolutionClosed += ProjectService_SolutionClosed; ProjectService.SolutionClosed += ProjectService_SolutionClosed;
ProjectService.ProjectAdded += ProjectService_ProjectAdded; ProjectService.ProjectAdded += ProjectService_ProjectAdded;
@ -40,13 +40,39 @@ namespace ICSharpCode.UnitTesting
SD_ParserService_LoadSolutionProjectsThread_Finished(null, null); SD_ParserService_LoadSolutionProjectsThread_Finished(null, null);
} }
} }
/// <summary> public override string DisplayName {
/// Retrieves the TestProject for the specified project. get { return resourceService.GetString("ICSharpCode.UnitTesting.AllTestsTreeNode.Text"); }
/// </summary> }
public TestProject GetTestProject(IProject currentProject)
public override event EventHandler DisplayNameChanged {
add { resourceService.LanguageChanged += value; }
remove { resourceService.LanguageChanged -= value; }
}
public override ITestProject ParentProject {
get { return null; }
}
public ITestProject GetTestProject(IProject project)
{
for (int i = 0; i < changeListeners.Count; i++) {
if (changeListeners[i].project == project) {
return changeListeners[i].testProject;
}
}
return null;
}
public ITest GetTestForEntity(IEntity entity)
{ {
return testableProjects.FirstOrDefault(p => p.Project == currentProject); if (entity == null)
return null;
ITestProject testProject = GetTestProject(entity.ParentAssembly.GetProject());
if (testProject != null)
return testProject.GetTestForEntity(entity);
else
return null;
} }
/// <summary> /// <summary>
@ -57,8 +83,9 @@ namespace ICSharpCode.UnitTesting
class ProjectChangeListener class ProjectChangeListener
{ {
readonly TestSolution testSolution; readonly TestSolution testSolution;
ITestFramework oldTestFramework;
internal readonly IProject project; internal readonly IProject project;
TestProject testProject; internal ITestProject testProject;
public ProjectChangeListener(TestSolution testSolution, IProject project) public ProjectChangeListener(TestSolution testSolution, IProject project)
{ {
@ -77,7 +104,7 @@ namespace ICSharpCode.UnitTesting
project.ParseInformationUpdated -= project_ParseInformationUpdated; project.ParseInformationUpdated -= project_ParseInformationUpdated;
// Remove old testProject // Remove old testProject
if (testProject != null) { if (testProject != null) {
testSolution.testableProjects.Remove(testProject); testSolution.NestedTests.Remove(testProject);
testProject = null; testProject = null;
} }
} }
@ -91,20 +118,23 @@ namespace ICSharpCode.UnitTesting
internal void CheckTestFramework() internal void CheckTestFramework()
{ {
ITestFramework newTestFramework = testSolution.registeredTestFrameworks.GetTestFrameworkForProject(project); ITestFramework newTestFramework = testSolution.testService.GetTestFrameworkForProject(project);
if (newTestFramework != null && testProject != null && testProject.TestFramework == newTestFramework) if (newTestFramework == oldTestFramework)
return; // test framework is unchanged return; // test framework is unchanged
// Remove old testProject // Remove old testProject
if (testProject != null) { if (testProject != null) {
testSolution.testableProjects.Remove(testProject); testSolution.NestedTests.Remove(testProject);
testProject = null; testProject = null;
} }
// Create new testProject // Create new testProject
if (newTestFramework != null) { if (newTestFramework != null) {
testProject = new TestProject(project, newTestFramework); testProject = newTestFramework.CreateTestProject(testSolution, project);
testSolution.testableProjects.Add(testProject); if (testProject == null)
throw new InvalidOperationException("CreateTestProject() returned null");
testSolution.NestedTests.Add(testProject);
} }
oldTestFramework = newTestFramework;
} }
} }

16
src/AddIns/Analysis/UnitTesting/NUnit/NUnitConsoleApplication.cs

@ -16,18 +16,19 @@ namespace ICSharpCode.UnitTesting
{ {
public class NUnitConsoleApplication public class NUnitConsoleApplication
{ {
public NUnitConsoleApplication(SelectedTests selectedTests, UnitTestingOptions options) /*
public NUnitConsoleApplication(ITestProject project, IEnumerable<ITest> selectedTests, UnitTestingOptions options)
{ {
Initialize(selectedTests); Initialize(project, selectedTests);
InitializeOptions(options); InitializeOptions(options);
} }
public NUnitConsoleApplication(SelectedTests selectedTests) public NUnitConsoleApplication(ITestProject project, IEnumerable<ITest> selectedTests)
{ {
Initialize(selectedTests); Initialize(project, selectedTests);
} }
void Initialize(SelectedTests selectedTests) void Initialize(ITestProject project, IEnumerable<ITest> selectedTests)
{ {
this.selectedTests = selectedTests; this.selectedTests = selectedTests;
this.project = selectedTests.Project; this.project = selectedTests.Project;
@ -42,6 +43,7 @@ namespace ICSharpCode.UnitTesting
} }
} }
} }
*/
void InitializeOptions(UnitTestingOptions options) void InitializeOptions(UnitTestingOptions options)
{ {
@ -150,9 +152,9 @@ namespace ICSharpCode.UnitTesting
public string NamespaceFilter; public string NamespaceFilter;
IProject project; IProject project;
SelectedTests selectedTests; IEnumerable<ITest> selectedTests;
public SelectedTests SelectedTests { public IEnumerable<ITest> SelectedTests {
get { return selectedTests; } get { return selectedTests; }
} }

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

@ -2,7 +2,9 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Debugging; using ICSharpCode.SharpDevelop.Debugging;
@ -30,7 +32,7 @@ namespace ICSharpCode.UnitTesting
this.options = options; this.options = options;
} }
protected override ProcessStartInfo GetProcessStartInfo(SelectedTests selectedTests) protected override ProcessStartInfo GetProcessStartInfo(IEnumerable<ITest> selectedTests)
{ {
NUnitConsoleApplication app = new NUnitConsoleApplication(selectedTests, options); NUnitConsoleApplication app = new NUnitConsoleApplication(selectedTests, options);
app.Results = base.TestResultsMonitor.FileName; app.Results = base.TestResultsMonitor.FileName;

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

@ -14,23 +14,9 @@ namespace ICSharpCode.UnitTesting
{ {
public class NUnitTestFramework : ITestFramework public class NUnitTestFramework : ITestFramework
{ {
public bool IsBuildNeededBeforeTestRun { readonly ITypeReference testAttributeRef = new GetClassTypeReference("NUnit.Framework", "TestAttribute", 0);
get { return true; } readonly ITypeReference testCaseAttributeRef = new GetClassTypeReference("NUnit.Framework", "TestCaseAttribute", 0);
} readonly ITypeReference testFixtureAttributeRef = new GetClassTypeReference("NUnit.Framework", "TestFixtureAttribute", 0);
public ITestRunner CreateTestRunner()
{
return new NUnitTestRunner();
}
public ITestRunner CreateTestDebugger()
{
return new NUnitTestDebugger();
}
static readonly ITypeReference testAttributeRef = new GetClassTypeReference("NUnit.Framework", "TestAttribute", 0);
static readonly ITypeReference testCaseAttributeRef = new GetClassTypeReference("NUnit.Framework", "TestCaseAttribute", 0);
static readonly ITypeReference testFixtureAttributeRef = new GetClassTypeReference("NUnit.Framework", "TestFixtureAttribute", 0);
/// <summary> /// <summary>
/// Determines whether the project is a test project. A project /// Determines whether the project is a test project. A project
@ -44,6 +30,23 @@ namespace ICSharpCode.UnitTesting
return testAttributeRef.Resolve(SD.ParserService.GetCompilation(project).TypeResolveContext).Kind != TypeKind.Unknown; return testAttributeRef.Resolve(SD.ParserService.GetCompilation(project).TypeResolveContext).Kind != TypeKind.Unknown;
} }
public ITestProject CreateTestProject(ITestSolution parentSolution, IProject project)
{
return new NUnitTestProject(project);
}
/*
public ITestRunner CreateTestRunner()
{
return new NUnitTestRunner();
}
public ITestRunner CreateTestDebugger()
{
return new NUnitTestDebugger();
}
public bool IsTestMember(IMember member) public bool IsTestMember(IMember member)
{ {
if (member == null || member.EntityType != EntityType.Method) if (member == null || member.EntityType != EntityType.Method)
@ -72,6 +75,6 @@ namespace ICSharpCode.UnitTesting
{ {
var project = typeDefinition.ParentAssembly.GetProject(); var project = typeDefinition.ParentAssembly.GetProject();
return typeDefinition.Methods.Where(IsTestMember).Select(m => new TestMember(m.UnresolvedMember)); return typeDefinition.Methods.Where(IsTestMember).Select(m => new TestMember(m.UnresolvedMember));
} }*/
} }
} }

26
src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestProject.cs

@ -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.Collections.Generic;
using System.Threading.Tasks;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// NUnit test project.
/// </summary>
public class NUnitTestProject : TestProjectBase
{
public NUnitTestProject(IProject project) : base(project)
{
}
public override Task RunTestsAsync(IEnumerable<ITest> tests, TestExecutionOptions options, IProgressMonitor progressMonitor)
{
throw new NotImplementedException();
}
}
}

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

@ -2,7 +2,9 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using ICSharpCode.SharpDevelop.Util; using ICSharpCode.SharpDevelop.Util;
namespace ICSharpCode.UnitTesting namespace ICSharpCode.UnitTesting
@ -23,7 +25,7 @@ namespace ICSharpCode.UnitTesting
this.options = options; this.options = options;
} }
protected override ProcessStartInfo GetProcessStartInfo(SelectedTests selectedTests) protected override ProcessStartInfo GetProcessStartInfo(IEnumerable<ITest> selectedTests)
{ {
NUnitConsoleApplication app = new NUnitConsoleApplication(selectedTests, options); NUnitConsoleApplication app = new NUnitConsoleApplication(selectedTests, options);
app.Results = base.TestResultsMonitor.FileName; app.Results = base.TestResultsMonitor.FileName;

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

@ -1,53 +0,0 @@
// 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()
{
}
}
}

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

@ -1,18 +0,0 @@
// 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();
}
}

236
src/AddIns/Analysis/UnitTesting/Pad/UnitTestsPad.cs

@ -19,23 +19,20 @@ namespace ICSharpCode.UnitTesting
{ {
public class UnitTestsPad : AbstractPadContent public class UnitTestsPad : AbstractPadContent
{ {
TestSolution testSolution; ITestService testService;
TestTreeView treeView; TestTreeView treeView;
bool disposed;
DockPanel panel; DockPanel panel;
ToolBar toolBar; ToolBar toolBar;
List<Tuple<IUnresolvedFile, IUnresolvedFile>> pending = new List<Tuple<IUnresolvedFile, IUnresolvedFile>>(); List<Tuple<IUnresolvedFile, IUnresolvedFile>> pending = new List<Tuple<IUnresolvedFile, IUnresolvedFile>>();
static UnitTestsPad instance;
public UnitTestsPad() public UnitTestsPad()
: this(TestService.RegisteredTestFrameworks, TestService.Solution) : this(SD.GetRequiredService<ITestService>())
{ {
} }
public UnitTestsPad(IRegisteredTestFrameworks testFrameworks, TestSolution testSolution) public UnitTestsPad(ITestService testService)
{ {
instance = this; this.testService = testService;
this.testSolution = testSolution;
panel = new DockPanel(); panel = new DockPanel();
@ -43,131 +40,28 @@ namespace ICSharpCode.UnitTesting
panel.Children.Add(toolBar); panel.Children.Add(toolBar);
DockPanel.SetDock(toolBar, Dock.Top); DockPanel.SetDock(toolBar, Dock.Top);
treeView = new TestTreeView(testFrameworks, testSolution); treeView = new TestTreeView();
panel.Children.Add(treeView); 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.
SD.ParserService.LoadSolutionProjectsThread.Finished += LoadSolutionProjectsThreadFinished;
OnAddedLoadSolutionProjectsThreadEndedHandler();
ProjectService.SolutionClosed += SolutionClosed;
ProjectService.SolutionFolderRemoved += SolutionFolderRemoved;
ProjectService.ProjectAdded += ProjectAdded;
ProjectService.ProjectItemAdded += ProjectItemAdded;
ProjectService.ProjectItemRemoved += ProjectItemRemoved;
treeView.ContextMenu = CreateContextMenu("/SharpDevelop/Pads/UnitTestsPad/ContextMenu"); treeView.ContextMenu = CreateContextMenu("/SharpDevelop/Pads/UnitTestsPad/ContextMenu");
}
testService.OpenSolutionChanged += testService_OpenSolutionChanged;
public static UnitTestsPad Instance { testService_OpenSolutionChanged(null, null);
get { return instance; }
}
public override object Control {
get { return panel; }
} }
public override void Dispose() public override void Dispose()
{ {
if (!disposed) { testService.OpenSolutionChanged -= testService_OpenSolutionChanged;
disposed = true; base.Dispose();
ProjectService.ProjectItemRemoved -= ProjectItemRemoved;
ProjectService.ProjectItemAdded -= ProjectItemAdded;
ProjectService.ProjectAdded -= ProjectAdded;
ProjectService.SolutionFolderRemoved -= SolutionFolderRemoved;
ProjectService.SolutionClosed -= SolutionClosed;
SD.ParserService.LoadSolutionProjectsThread.Finished -= LoadSolutionProjectsThreadFinished;
}
} }
// public TestTreeView TestTreeView { public override object Control {
// get { return treeView; } get { return panel; }
// }
// public void ResetTestResults()
// {
// treeView.ResetTestResults();
// }
//
public IProject[] GetProjects()
{
return testSolution.TestableProjects.Select(tp => tp.Project).ToArray();
} }
// public TestProject GetTestProject(IProject project) void testService_OpenSolutionChanged(object sender, EventArgs e)
// {
// 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); treeView.TestSolution = testService.OpenSolution;
}
/// <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 closed.
/// </summary>
// protected void SolutionClosed()
// {
// treeView.Clear();
// }
//
// protected void SolutionFolderRemoved(ISolutionFolder solutionFolder)
// {
// treeView.RemoveSolutionFolder(solutionFolder);
// }
/// <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(IUnresolvedFile oldFile, IUnresolvedFile 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> /// <summary>
@ -187,107 +81,5 @@ namespace ICSharpCode.UnitTesting
{ {
return MenuService.CreateContextMenu(treeView, 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 SD.ParserService.LoadSolutionProjectsThread.IsRunning; }
}
/// <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)
{
UpdateToolbar();
}
void LoadSolutionProjectsThreadFinished(object source, EventArgs e)
{
WorkbenchSingleton.SafeThreadAsyncCall(UpdateToolbar);
Solution solution = ProjectService.OpenSolution;
// WorkbenchSingleton.SafeThreadAsyncCall(SolutionLoaded, solution);
}
// 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);
}
public void ResetTestResults()
{
foreach (var testProject in testSolution.TestableProjects)
testProject.ResetTestResults();
}
} }
} }

3
src/AddIns/Analysis/UnitTesting/TestRunner/ITestRunner.cs

@ -2,6 +2,7 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System; using System;
using System.Collections.Generic;
using ICSharpCode.SharpDevelop.Util; using ICSharpCode.SharpDevelop.Util;
namespace ICSharpCode.UnitTesting namespace ICSharpCode.UnitTesting
@ -11,7 +12,7 @@ namespace ICSharpCode.UnitTesting
event EventHandler<TestFinishedEventArgs> TestFinished; event EventHandler<TestFinishedEventArgs> TestFinished;
event EventHandler AllTestsFinished; event EventHandler AllTestsFinished;
event EventHandler<MessageReceivedEventArgs> MessageReceived; event EventHandler<MessageReceivedEventArgs> MessageReceived;
void Start(SelectedTests selectedTests); void Start(IEnumerable<ITest> selectedTests);
void Stop(); void Stop();
} }
} }

91
src/AddIns/Analysis/UnitTesting/TestRunner/SelectedTests.cs

@ -1,91 +0,0 @@
// 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);
}
TestProject testProject = TestService.Solution.GetTestProject(project);
if (testProject != null) {
member = testProject.GetTestMember(TestableCondition.GetMember(owner));
c = testProject.GetTestClass(TestableCondition.GetClassFromMemberOrCaller(owner));
}
namespaceFilter = TestableCondition.GetNamespace(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 TestClass Class {
get { return c; }
}
public TestMember Member {
get { return member; }
}
}
}

4
src/AddIns/Analysis/UnitTesting/TestRunner/TestDebuggerBase.cs

@ -2,7 +2,9 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Debugging; using ICSharpCode.SharpDevelop.Debugging;
@ -39,7 +41,7 @@ namespace ICSharpCode.UnitTesting
get { return testResultsMonitor; } get { return testResultsMonitor; }
} }
public override void Start(SelectedTests selectedTests) public override void Start(IEnumerable<ITest> selectedTests)
{ {
ProcessStartInfo startInfo = GetProcessStartInfo(selectedTests); ProcessStartInfo startInfo = GetProcessStartInfo(selectedTests);
if (IsDebuggerRunning) { if (IsDebuggerRunning) {

4
src/AddIns/Analysis/UnitTesting/TestRunner/TestProcessRunnerBase.cs

@ -2,7 +2,9 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Util; using ICSharpCode.SharpDevelop.Util;
@ -62,7 +64,7 @@ namespace ICSharpCode.UnitTesting
OnMessageReceived(e.Line); OnMessageReceived(e.Line);
} }
public override void Start(SelectedTests selectedTests) public override void Start(IEnumerable<ITest> selectedTests)
{ {
ProcessStartInfo startInfo = GetProcessStartInfo(selectedTests); ProcessStartInfo startInfo = GetProcessStartInfo(selectedTests);
Start(startInfo); Start(startInfo);

4
src/AddIns/Analysis/UnitTesting/TestRunner/TestResultTask.cs

@ -16,7 +16,7 @@ namespace ICSharpCode.UnitTesting
{ {
} }
public static SDTask Create(TestResult result, TestProject project) public static SDTask Create(TestResult result, ITestProject project)
{ {
TaskType taskType = TaskType.Warning; TaskType taskType = TaskType.Warning;
FileLineReference lineRef = null; FileLineReference lineRef = null;
@ -76,7 +76,7 @@ namespace ICSharpCode.UnitTesting
/// Returns the location of the specified test member in the /// Returns the location of the specified test member in the
/// project being tested. /// project being tested.
/// </summary> /// </summary>
static FileLineReference FindTest(string memberName, TestProject testProject) static FileLineReference FindTest(string memberName, ITestProject testProject)
{ {
// if (testProject != null) { // if (testProject != null) {
// TestMember testMember = testProject.TestClasses.GetTestMember(memberName); // TestMember testMember = testProject.TestClasses.GetTestMember(memberName);

5
src/AddIns/Analysis/UnitTesting/TestRunner/TestRunnerBase.cs

@ -2,6 +2,7 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
namespace ICSharpCode.UnitTesting namespace ICSharpCode.UnitTesting
@ -12,7 +13,7 @@ namespace ICSharpCode.UnitTesting
{ {
} }
protected virtual ProcessStartInfo GetProcessStartInfo(SelectedTests selectedTests) protected virtual ProcessStartInfo GetProcessStartInfo(IEnumerable<ITest> selectedTests)
{ {
return new ProcessStartInfo(); return new ProcessStartInfo();
} }
@ -63,6 +64,6 @@ namespace ICSharpCode.UnitTesting
public abstract void Dispose(); public abstract void Dispose();
public abstract void Stop(); public abstract void Stop();
public abstract void Start(SelectedTests selectedTests); public abstract void Start(IEnumerable<ITest> selectedTests);
} }
} }

2
src/AddIns/Analysis/UnitTesting/TreeView/ClassUnitTestNode.cs

@ -12,7 +12,7 @@ namespace ICSharpCode.UnitTesting
/// <summary> /// <summary>
/// Represents a TestClass in the tree view. /// Represents a TestClass in the tree view.
/// </summary> /// </summary>
public class ClassUnitTestNode : UnitTestBaseNode public class ClassUnitTestNode : UnitTestNode
{ {
TestClass testClass; TestClass testClass;

20
src/AddIns/Analysis/UnitTesting/TreeView/ITestTreeView.cs

@ -2,6 +2,7 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System.Collections.Generic;
using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Project; using ICSharpCode.SharpDevelop.Project;
using System; using System;
@ -11,24 +12,13 @@ namespace ICSharpCode.UnitTesting
public interface ITestTreeView public interface ITestTreeView
{ {
/// <summary> /// <summary>
/// Gets the selected member in the test tree view. /// Gets the test solution that is being displayed in this tree view.
/// </summary> /// </summary>
TestMember SelectedMember {get;} ITestSolution TestSolution { get; }
/// <summary> /// <summary>
/// Gets the selected class in the test tree view. /// Gets the selected tests.
/// </summary> /// </summary>
TestClass SelectedClass {get;} IEnumerable<ITest> SelectedTests { get; }
/// <summary>
/// Gets the selected project for the selected node
/// in the test tree view.
/// </summary>
TestProject SelectedProject {get;}
/// <summary>
/// Gets the namespace for the selected namespace node.
/// </summary>
string SelectedNamespace {get;}
} }
} }

2
src/AddIns/Analysis/UnitTesting/TreeView/MemberUnitTestNode.cs

@ -10,7 +10,7 @@ using ICSharpCode.TreeView;
namespace ICSharpCode.UnitTesting namespace ICSharpCode.UnitTesting
{ {
public class MemberUnitTestNode : UnitTestBaseNode public class MemberUnitTestNode : UnitTestNode
{ {
TestMember testMember; TestMember testMember;

8
src/AddIns/Analysis/UnitTesting/TreeView/NamespaceUnitTestNode.cs

@ -16,7 +16,7 @@ using ICSharpCode.TreeView;
namespace ICSharpCode.UnitTesting namespace ICSharpCode.UnitTesting
{ {
public class NamespaceUnitTestNode : UnitTestBaseNode public class NamespaceUnitTestNode : UnitTestNode
{ {
readonly string shortName; readonly string shortName;
readonly string namespaceName; readonly string namespaceName;
@ -50,11 +50,11 @@ namespace ICSharpCode.UnitTesting
internal override TestResultType TestResultType { internal override TestResultType TestResultType {
get { get {
if (Children.Count == 0) return TestResultType.None; if (Children.Count == 0) return TestResultType.None;
if (Children.OfType<UnitTestBaseNode>().Any(node => node.TestResultType == TestResultType.Failure)) if (Children.OfType<UnitTestNode>().Any(node => node.TestResultType == TestResultType.Failure))
return TestResultType.Failure; return TestResultType.Failure;
if (Children.OfType<UnitTestBaseNode>().Any(node => node.TestResultType == TestResultType.None)) if (Children.OfType<UnitTestNode>().Any(node => node.TestResultType == TestResultType.None))
return TestResultType.None; return TestResultType.None;
if (Children.OfType<UnitTestBaseNode>().Any(node => node.TestResultType == TestResultType.Ignored)) if (Children.OfType<UnitTestNode>().Any(node => node.TestResultType == TestResultType.Ignored))
return TestResultType.Ignored; return TestResultType.Ignored;
return TestResultType.Success; return TestResultType.Success;
} }

10
src/AddIns/Analysis/UnitTesting/TreeView/ProjectUnitTestNode.cs

@ -17,7 +17,7 @@ using ICSharpCode.TreeView;
namespace ICSharpCode.UnitTesting namespace ICSharpCode.UnitTesting
{ {
public class ProjectUnitTestNode : UnitTestBaseNode public class ProjectUnitTestNode : UnitTestNode
{ {
TestProject project; TestProject project;
@ -47,7 +47,7 @@ namespace ICSharpCode.UnitTesting
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
foreach (TestClass c in e.NewItems) { foreach (TestClass c in e.NewItems) {
if (c.Namespace == "<invalid>") continue; if (c.Namespace == "<invalid>") continue;
UnitTestBaseNode node = FindOrCreateNamespace(this, project.Project.RootNamespace, c.Namespace); UnitTestNode node = FindOrCreateNamespace(this, project.Project.RootNamespace, c.Namespace);
node.Children.OrderedInsert(new ClassUnitTestNode(c), NodeTextComparer); node.Children.OrderedInsert(new ClassUnitTestNode(c), NodeTextComparer);
} }
break; break;
@ -70,7 +70,7 @@ namespace ICSharpCode.UnitTesting
} }
} }
static UnitTestBaseNode FindOrCreateNamespace(UnitTestBaseNode parent, string parentNamespace, string @namespace) static UnitTestNode FindOrCreateNamespace(UnitTestNode parent, string parentNamespace, string @namespace)
{ {
if (parentNamespace == @namespace) if (parentNamespace == @namespace)
return parent; return parent;
@ -101,7 +101,7 @@ namespace ICSharpCode.UnitTesting
} }
} }
static UnitTestBaseNode FindNamespace(UnitTestBaseNode parent, string parentNamespace, string @namespace) static UnitTestNode FindNamespace(UnitTestNode parent, string parentNamespace, string @namespace)
{ {
if (parentNamespace == @namespace) if (parentNamespace == @namespace)
return parent; return parent;
@ -120,7 +120,7 @@ namespace ICSharpCode.UnitTesting
Children.Clear(); Children.Clear();
foreach (var g in project.TestClasses.Select(c => new ClassUnitTestNode(c)).GroupBy(tc => tc.TestClass.Namespace)) { foreach (var g in project.TestClasses.Select(c => new ClassUnitTestNode(c)).GroupBy(tc => tc.TestClass.Namespace)) {
if (g.Key == "<invalid>") continue; if (g.Key == "<invalid>") continue;
UnitTestBaseNode node = FindOrCreateNamespace(this, project.Project.RootNamespace, g.Key); UnitTestNode node = FindOrCreateNamespace(this, project.Project.RootNamespace, g.Key);
node.Children.AddRange(g.OrderBy(NodeTextComparer)); node.Children.AddRange(g.OrderBy(NodeTextComparer));
} }
} }

8
src/AddIns/Analysis/UnitTesting/TreeView/RootUnitTestNode.cs

@ -24,7 +24,7 @@ namespace ICSharpCode.UnitTesting
/// <summary> /// <summary>
/// Description of RootUnitTestNode. /// Description of RootUnitTestNode.
/// </summary> /// </summary>
public class RootUnitTestNode : UnitTestBaseNode public class RootUnitTestNode : UnitTestNode
{ {
readonly TestSolution testSolution; readonly TestSolution testSolution;
@ -63,11 +63,11 @@ namespace ICSharpCode.UnitTesting
internal override TestResultType TestResultType { internal override TestResultType TestResultType {
get { get {
if (Children.Count == 0) return TestResultType.None; if (Children.Count == 0) return TestResultType.None;
if (Children.OfType<UnitTestBaseNode>().Any(node => node.TestResultType == TestResultType.Failure)) if (Children.OfType<UnitTestNode>().Any(node => node.TestResultType == TestResultType.Failure))
return TestResultType.Failure; return TestResultType.Failure;
if (Children.OfType<UnitTestBaseNode>().Any(node => node.TestResultType == TestResultType.None)) if (Children.OfType<UnitTestNode>().Any(node => node.TestResultType == TestResultType.None))
return TestResultType.None; return TestResultType.None;
if (Children.OfType<UnitTestBaseNode>().Any(node => node.TestResultType == TestResultType.Ignored)) if (Children.OfType<UnitTestNode>().Any(node => node.TestResultType == TestResultType.Ignored))
return TestResultType.Ignored; return TestResultType.Ignored;
return TestResultType.Success; return TestResultType.Success;
} }

104
src/AddIns/Analysis/UnitTesting/TreeView/TestTreeView.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
@ -23,100 +24,49 @@ namespace ICSharpCode.UnitTesting
[Flags] [Flags]
public enum TestTreeViewState { public enum TestTreeViewState {
None = 0, None = 0,
SourceCodeItemSelected = 1 SupportsGoToDefinition = 1
} }
IRegisteredTestFrameworks testFrameworks; ITestSolution testSolution;
public TestTreeView(IRegisteredTestFrameworks testFrameworks, TestSolution testSolution) public ITestSolution TestSolution {
{ get { return testSolution; }
this.testFrameworks = testFrameworks; set {
// Show 'All Tests' root node only if there is more than one test project in the solution if (testSolution == value)
testSolution.TestableProjects.CollectionChanged += delegate { ShowRoot = testSolution.TestableProjects.Count > 1; }; return;
this.Root = new RootUnitTestNode(testSolution); testSolution = value;
} if (testSolution != null) {
this.Root = new UnitTestNode(testSolution);
/// <summary> this.Root.Children.CollectionChanged += delegate {
/// Gets the current state of the test tree view. this.ShowRoot = this.Root != null && this.Root.Children.Count > 1;
/// </summary> };
public Enum InternalState { } else {
get { this.Root = null;
if (SelectedItem is ClassUnitTestNode || SelectedItem is MemberUnitTestNode)
return TestTreeViewState.SourceCodeItemSelected;
return TestTreeViewState.None;
}
}
bool IsTestProject(IProject project)
{
return testFrameworks.IsTestProject(project);
}
/// <summary>
/// Gets the member of the currently selected tree node.
/// </summary>
public TestMember SelectedMember {
get {
MemberUnitTestNode memberNode = SelectedItem as MemberUnitTestNode;
if (memberNode != null) {
return memberNode.TestMember;
}
return null;
}
}
/// <summary>
/// Gets the class of the currently selected tree node.
/// </summary>
public TestClass SelectedClass {
get {
ClassUnitTestNode classNode = SelectedItem as ClassUnitTestNode;
if (classNode == null) {
classNode = GetClassNodeFromSelectedMemberNode();
} }
if (classNode != null) {
return classNode.TestClass;
}
return null;
} }
} }
ClassUnitTestNode GetClassNodeFromSelectedMemberNode() public TestTreeView()
{ {
MemberUnitTestNode memberNode = SelectedItem as MemberUnitTestNode; this.ShowRoot = false;
if (memberNode != null) {
return memberNode.Parent as ClassUnitTestNode;
}
return null;
} }
/// <summary> /// <summary>
/// If a namespace node is selected then the fully qualified namespace /// Gets the current state of the test tree view.
/// for this node is returned (i.e. includes the parent namespace prefixed
/// to it). For all other nodes this returns null.
/// </summary> /// </summary>
public string SelectedNamespace { public Enum InternalState {
get { get {
NamespaceUnitTestNode selectedNode = SelectedItem as NamespaceUnitTestNode; var node = SelectedItem as UnitTestNode;
if (selectedNode != null) { if (node != null && node.Test.SupportsGoToDefinition)
return selectedNode.NamespaceName; return TestTreeViewState.SupportsGoToDefinition;
} else
return null; return TestTreeViewState.None;
} }
} }
/// <summary> public IEnumerable<ITest> SelectedTests {
/// Gets the selected test project.
/// </summary>
public TestProject SelectedProject {
get { get {
for (var node = SelectedItem as SharpTreeNode; node != null; node = node.Parent) { return this.GetTopLevelSelection().OfType<UnitTestNode>().Select(n => n.Test);
var projectNode = node as ProjectUnitTestNode;
if (projectNode != null)
return projectNode.Project;
}
return null;
} }
} }
} }

38
src/AddIns/Analysis/UnitTesting/TreeView/UnitTestBaseNode.cs

@ -1,38 +0,0 @@
// 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.NRefactory.Utils;
using ICSharpCode.TreeView;
namespace ICSharpCode.UnitTesting
{
public abstract class UnitTestBaseNode : SharpTreeNode
{
protected static readonly KeyComparer<SharpTreeNode, string> NodeTextComparer = KeyComparer.Create((SharpTreeNode n) => n.Text.ToString(), StringComparer.OrdinalIgnoreCase, StringComparer.OrdinalIgnoreCase);
internal abstract TestResultType TestResultType { get; }
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");
}
}
}
}

128
src/AddIns/Analysis/UnitTesting/TreeView/UnitTestNode.cs

@ -0,0 +1,128 @@
// 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.Specialized;
using System.Linq;
using ICSharpCode.NRefactory.Utils;
using ICSharpCode.SharpDevelop;
using ICSharpCode.TreeView;
namespace ICSharpCode.UnitTesting
{
public class UnitTestNode : SharpTreeNode
{
protected static readonly IComparer<SharpTreeNode> NodeTextComparer = KeyComparer.Create((SharpTreeNode n) => n.Text.ToString(), StringComparer.OrdinalIgnoreCase, StringComparer.OrdinalIgnoreCase);
readonly ITest test;
public UnitTestNode(ITest test)
{
if (test == null)
throw new ArgumentNullException("test");
this.test = test;
test.DisplayNameChanged += test_NameChanged;
test.ResultChanged += test_ResultChanged;
LazyLoading = true;
}
void DetachEventHandlers()
{
test.DisplayNameChanged -= test_NameChanged;
test.ResultChanged -= test_ResultChanged;
// If children loaded, also detach the collection change event handler
if (!LazyLoading) {
test.NestedTests.CollectionChanged -= test_NestedTests_CollectionChanged;
}
}
public ITest Test {
get { return test; }
}
#region Manage Children
protected override void LoadChildren()
{
Children.Clear();
InsertNestedTests(test.NestedTests);
test.NestedTests.CollectionChanged += test_NestedTests_CollectionChanged;
}
void InsertNestedTests(IEnumerable<ITest> nestedTests)
{
foreach (var test in nestedTests) {
Children.OrderedInsert(test.CreateTreeNode(), NodeTextComparer);
}
}
void test_NestedTests_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (!IsVisible) {
SwitchBackToLazyLoading();
return;
}
switch (e.Action) {
case NotifyCollectionChangedAction.Add:
InsertNestedTests(e.NewItems.Cast<ITest>());
break;
case NotifyCollectionChangedAction.Remove:
Children.RemoveAll(n => e.OldItems.Contains(((UnitTestNode)n).Test));
break;
case NotifyCollectionChangedAction.Reset:
if (IsExpanded) {
Children.Clear();
InsertNestedTests(test.NestedTests);
} else {
SwitchBackToLazyLoading();
}
break;
default:
throw new NotSupportedException("Invalid value for NotifyCollectionChangedAction");
}
}
void SwitchBackToLazyLoading()
{
test.NestedTests.CollectionChanged -= test_NestedTests_CollectionChanged;
Children.Clear();
LazyLoading = true;
}
#endregion
#region Icon + Text
public override object Icon {
get {
switch (test.Result) {
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 NotSupportedException("Invalid value for TestResultType");
}
}
}
void test_ResultChanged(object sender, EventArgs e)
{
RaisePropertyChanged("Icon");
RaisePropertyChanged("ExpandedIcon");
}
public override object Text {
get { return test.DisplayName; }
}
void test_NameChanged(object sender, EventArgs e)
{
RaisePropertyChanged("Text");
}
#endregion
}
}

21
src/AddIns/Analysis/UnitTesting/UnitTesting.addin

@ -17,6 +17,11 @@
</Import> </Import>
</Runtime> </Runtime>
<Path name = "/SharpDevelop/Services">
<Service id="ICSharpCode.UnitTesting.ITestService"
class="ICSharpCode.UnitTesting.SDTestService"/>
</Path>
<Path name="/SharpDevelop/UnitTesting/TestFrameworks"> <Path name="/SharpDevelop/UnitTesting/TestFrameworks">
<TestFramework id="nunit" <TestFramework id="nunit"
class="ICSharpCode.UnitTesting.NUnitTestFramework" class="ICSharpCode.UnitTesting.NUnitTestFramework"
@ -45,7 +50,7 @@
<MenuItem id="RunInPad" <MenuItem id="RunInPad"
label="${res:ICSharpCode.UnitTesting.RunInTestPad}" label="${res:ICSharpCode.UnitTesting.RunInTestPad}"
icon="PadIcons.NUnitTest" icon="PadIcons.NUnitTest"
class="ICSharpCode.UnitTesting.RunTestInPadCommand"/> class="ICSharpCode.UnitTesting.RunSelectedTestCommand"/>
</ComplexCondition> </ComplexCondition>
<Include id="CommonTestCommands" path="/SharpDevelop/Pads/UnitTestsPad/CommonTestCommands"/> <Include id="CommonTestCommands" path="/SharpDevelop/Pads/UnitTestsPad/CommonTestCommands"/>
</MenuItem> </MenuItem>
@ -63,7 +68,7 @@
<MenuItem id="RunWithDebugger" <MenuItem id="RunWithDebugger"
label="${res:ICSharpCode.UnitTesting.RunWithDebugger}" label="${res:ICSharpCode.UnitTesting.RunWithDebugger}"
icon="Icons.16x16.RunProgramIcon" icon="Icons.16x16.RunProgramIcon"
class="ICSharpCode.UnitTesting.RunTestWithDebuggerCommand"/> class="ICSharpCode.UnitTesting.RunSelectedTestWithDebuggerCommand"/>
</ComplexCondition> </ComplexCondition>
</Path> </Path>
@ -78,12 +83,12 @@
<ToolbarItem id="RunAll" <ToolbarItem id="RunAll"
icon="Icons.16x16.RunAllIcon" icon="Icons.16x16.RunAllIcon"
tooltip="${res:NUnitPad.NUnitPadContent.RunAllTests}" tooltip="${res:NUnitPad.NUnitPadContent.RunAllTests}"
class="ICSharpCode.UnitTesting.RunAllTestsInPadCommand"/> class="ICSharpCode.UnitTesting.RunAllTestsInSolutionCommand"/>
<ToolbarItem id="Run" <ToolbarItem id="Run"
insertafter="RunAll" insertafter="RunAll"
icon="Icons.16x16.RunProgramIcon" icon="Icons.16x16.RunProgramIcon"
tooltip="${res:NUnitPad.NUnitPadContent.RunItem}" tooltip="${res:NUnitPad.NUnitPadContent.RunItem}"
class="ICSharpCode.UnitTesting.RunTestInPadCommand"/> class="ICSharpCode.UnitTesting.RunSelectedTestCommand"/>
</ComplexCondition> </ComplexCondition>
<ComplexCondition action="Disable"> <ComplexCondition action="Disable">
<And> <And>
@ -120,7 +125,7 @@
<MenuItem id="Run" <MenuItem id="Run"
icon="Icons.16x16.RunProgramIcon" icon="Icons.16x16.RunProgramIcon"
label="${res:NUnitPad.NUnitPadContent.RunTestsContextMenuLabel}" label="${res:NUnitPad.NUnitPadContent.RunTestsContextMenuLabel}"
class="ICSharpCode.UnitTesting.RunTestInPadCommand"/> class="ICSharpCode.UnitTesting.RunSelectedTestCommand"/>
</ComplexCondition> </ComplexCondition>
<Include id="CommonTestCommands" path="/SharpDevelop/Pads/UnitTestsPad/CommonTestCommands"/> <Include id="CommonTestCommands" path="/SharpDevelop/Pads/UnitTestsPad/CommonTestCommands"/>
<ComplexCondition action="Disable"> <ComplexCondition action="Disable">
@ -133,7 +138,7 @@
label="${res:NUnitPad.NUnitPadContent.StopTests}" label="${res:NUnitPad.NUnitPadContent.StopTests}"
class="ICSharpCode.UnitTesting.StopTestsCommand"/> class="ICSharpCode.UnitTesting.StopTestsCommand"/>
</ComplexCondition> </ComplexCondition>
<Condition name="Ownerstate" ownerstate="SourceCodeItemSelected" action="Disable"> <Condition name="Ownerstate" ownerstate="SupportsGoToDefinition" action="Disable">
<MenuItem id="GotoDefinition" <MenuItem id="GotoDefinition"
label="${res:NUnitPad.NUnitPadContent.GotoDefinitionContextMenuLabel}" label="${res:NUnitPad.NUnitPadContent.GotoDefinitionContextMenuLabel}"
class="ICSharpCode.UnitTesting.GotoDefinitionCommand"/> class="ICSharpCode.UnitTesting.GotoDefinitionCommand"/>
@ -152,7 +157,7 @@
<MenuItem id="RunAll" <MenuItem id="RunAll"
icon="Icons.16x16.RunAllIcon" icon="Icons.16x16.RunAllIcon"
label="${res:NUnitPad.NUnitPadContent.RunAllTests}" label="${res:NUnitPad.NUnitPadContent.RunAllTests}"
class="ICSharpCode.UnitTesting.RunAllTestsInPadCommand"/> class="ICSharpCode.UnitTesting.RunAllTestsInSolutionCommand"/>
</ComplexCondition> </ComplexCondition>
<ComplexCondition action="Disable"> <ComplexCondition action="Disable">
<And> <And>
@ -165,7 +170,7 @@
<MenuItem id="RunProjectTests" <MenuItem id="RunProjectTests"
icon="Icons.16x16.RunProgramIcon" icon="Icons.16x16.RunProgramIcon"
label="${res:ICSharpCode.UnitTesting.RunProjectTests}" label="${res:ICSharpCode.UnitTesting.RunProjectTests}"
class="ICSharpCode.UnitTesting.RunProjectTestsInPadCommand"/> class="ICSharpCode.UnitTesting.RunAllTestsInProjectCommand"/>
</ComplexCondition> </ComplexCondition>
<ComplexCondition action="Disable"> <ComplexCondition action="Disable">
<And> <And>

56
src/AddIns/Analysis/UnitTesting/UnitTesting.csproj

@ -66,79 +66,54 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Commands\AbstractRunTestCommand.cs" /> <Compile Include="Commands\PadCommands.cs" />
<Compile Include="Commands\IRunTestCommandContext.cs" />
<Compile Include="Commands\MultipleProjectBuildable.cs" />
<Compile Include="Commands\RunAllTestsInPadCommand.cs" />
<Compile Include="Commands\RunningTestsCondition.cs" /> <Compile Include="Commands\RunningTestsCondition.cs" />
<Compile Include="Commands\RunProjectTestsInPadCommand.cs" />
<Compile Include="Commands\RunTestCommandContext.cs" />
<Compile Include="Commands\RunTestInPadCommand.cs" />
<Compile Include="Commands\RunTestWithDebuggerCommand.cs" />
<Compile Include="Commands\TestableCondition.cs" /> <Compile Include="Commands\TestableCondition.cs" />
<Compile Include="Commands\UnitTestCommands.cs" /> <Compile Include="Commands\UnitTestCommands.cs" />
<Compile Include="Frameworks\ITestService.cs" />
<Compile Include="Frameworks\TestExecutionManager.cs" />
<Compile Include="Frameworks\TestExecutionOptions.cs" />
<Compile Include="Interfaces\IUnitTestProcessRunner.cs" /> <Compile Include="Interfaces\IUnitTestProcessRunner.cs" />
<Compile Include="Interfaces\UnitTestProcessRunner.cs" /> <Compile Include="Interfaces\UnitTestProcessRunner.cs" />
<Compile Include="TestRunner\ITestRunner.cs" /> <Compile Include="Model\ITest.cs" />
<Compile Include="TestRunner\TestResultsMonitor.cs" /> <Compile Include="Model\ITestProject.cs" />
<Compile Include="TestRunner\TestResultsReader.cs" /> <Compile Include="Model\ITestSolution.cs" />
<Compile Include="TestRunner\TestResultTask.cs" /> <Compile Include="Model\TestBase.cs" />
<Compile Include="Frameworks\IRegisteredTestFrameworks.cs" />
<Compile Include="Frameworks\ITestFramework.cs" /> <Compile Include="Frameworks\ITestFramework.cs" />
<Compile Include="Frameworks\ITestFrameworkFactory.cs" />
<Compile Include="Interfaces\IBuildOptions.cs" /> <Compile Include="Interfaces\IBuildOptions.cs" />
<Compile Include="Interfaces\IBuildProjectFactory.cs" /> <Compile Include="Interfaces\IBuildProjectFactory.cs" />
<Compile Include="Interfaces\IFileSystem.cs" /> <Compile Include="Interfaces\IFileSystem.cs" />
<Compile Include="Interfaces\IUnitTestDebuggerService.cs" /> <Compile Include="Interfaces\IUnitTestDebuggerService.cs" />
<Compile Include="Interfaces\IUnitTestSaveAllFilesCommand.cs" /> <Compile Include="Interfaces\IUnitTestSaveAllFilesCommand.cs" />
<Compile Include="Interfaces\IUnitTestTaskService.cs" /> <Compile Include="Interfaces\IUnitTestTaskService.cs" />
<Compile Include="Interfaces\IUnitTestWorkbench.cs" />
<Compile Include="Interfaces\UnitTestBuildOptions.cs" /> <Compile Include="Interfaces\UnitTestBuildOptions.cs" />
<Compile Include="Model\TestCollection.cs" />
<Compile Include="Model\TestNamespace.cs" />
<Compile Include="Model\TestProjectBase.cs" />
<Compile Include="Model\TestResult.cs" /> <Compile Include="Model\TestResult.cs" />
<Compile Include="Pad\EmptyUnitTestsPad.cs" /> <Compile Include="Model\TestResultTypeChangedEventArgs.cs" />
<Compile Include="Pad\IUnitTestsPad.cs" /> <Compile Include="NUnit\NUnitTestProject.cs" />
<Compile Include="Pad\UnitTestsPad.cs" /> <Compile Include="Pad\UnitTestsPad.cs" />
<Compile Include="Interfaces\UnitTestBuildProjectFactory.cs" /> <Compile Include="Interfaces\UnitTestBuildProjectFactory.cs" />
<Compile Include="Interfaces\UnitTestDebuggerService.cs" /> <Compile Include="Interfaces\UnitTestDebuggerService.cs" />
<Compile Include="Interfaces\UnitTestFileService.cs" /> <Compile Include="Interfaces\UnitTestFileService.cs" />
<Compile Include="Interfaces\UnitTestSaveAllFilesCommand.cs" /> <Compile Include="Interfaces\UnitTestSaveAllFilesCommand.cs" />
<Compile Include="Interfaces\UnitTestTaskService.cs" /> <Compile Include="Interfaces\UnitTestTaskService.cs" />
<Compile Include="Interfaces\UnitTestWorkbench.cs" />
<Compile Include="Model\TestClass.cs" />
<Compile Include="Model\TestMember.cs" />
<Compile Include="Model\TestProject.cs" />
<Compile Include="Model\TestSolution.cs" /> <Compile Include="Model\TestSolution.cs" />
<Compile Include="Frameworks\ITestResultsMonitor.cs" />
<Compile Include="Frameworks\RegisteredTestFrameworks.cs" />
<Compile Include="Frameworks\TestFrameworkDescriptor.cs" /> <Compile Include="Frameworks\TestFrameworkDescriptor.cs" />
<Compile Include="Frameworks\TestFrameworkDoozer.cs" /> <Compile Include="Frameworks\TestFrameworkDoozer.cs" />
<Compile Include="Frameworks\TestFrameworkFactory.cs" /> <Compile Include="Frameworks\SDTestService.cs" />
<Compile Include="Frameworks\TestService.cs" />
<Compile Include="TreeView\ClassUnitTestNode.cs" />
<Compile Include="TreeView\ITestTreeView.cs" /> <Compile Include="TreeView\ITestTreeView.cs" />
<Compile Include="TreeView\MemberUnitTestNode.cs" />
<Compile Include="TreeView\NamespaceUnitTestNode.cs" />
<Compile Include="TreeView\ProjectUnitTestNode.cs" />
<Compile Include="TreeView\RootUnitTestNode.cs" />
<Compile Include="TreeView\TestTreeView.cs" /> <Compile Include="TreeView\TestTreeView.cs" />
<Compile Include="TreeView\UnitTestBaseNode.cs" /> <Compile Include="TreeView\UnitTestNode.cs" />
<Compile Include="NUnit\NUnitConsoleApplication.cs" /> <Compile Include="NUnit\NUnitConsoleApplication.cs" />
<Compile Include="NUnit\NUnitTestDebugger.cs" />
<Compile Include="NUnit\NUnitTestFramework.cs" /> <Compile Include="NUnit\NUnitTestFramework.cs" />
<Compile Include="NUnit\NUnitTestResult.cs" /> <Compile Include="NUnit\NUnitTestResult.cs" />
<Compile Include="NUnit\NUnitTestRunner.cs" />
<Compile Include="Options\UnitTestingOptions.cs" /> <Compile Include="Options\UnitTestingOptions.cs" />
<Compile Include="Options\UnitTestingOptionsPanel.xaml.cs"> <Compile Include="Options\UnitTestingOptionsPanel.xaml.cs">
<DependentUpon>UnitTestingOptionsPanel.xaml</DependentUpon> <DependentUpon>UnitTestingOptionsPanel.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Resources\Images.cs" /> <Compile Include="Resources\Images.cs" />
<Compile Include="TestRunner\MessageReceivedEventArgs.cs" />
<Compile Include="TestRunner\SelectedTests.cs" />
<Compile Include="TestRunner\TestDebuggerBase.cs" />
<Compile Include="TestRunner\TestFinishedEventArgs.cs" />
<Compile Include="TestRunner\TestProcessRunnerBase.cs" />
<Compile Include="TestRunner\TestProcessRunnerBaseContext.cs" />
<Compile Include="TestRunner\TestRunnerBase.cs" />
<None Include="PostBuildEvent.proj" /> <None Include="PostBuildEvent.proj" />
<None Include="UnitTesting.addin"> <None Include="UnitTesting.addin">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
@ -197,7 +172,6 @@
<Folder Include="Model" /> <Folder Include="Model" />
<Folder Include="Options" /> <Folder Include="Options" />
<Folder Include="NUnit" /> <Folder Include="NUnit" />
<Folder Include="TestRunner" />
<Folder Include="TreeView" /> <Folder Include="TreeView" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

20
src/Libraries/NRefactory/ICSharpCode.NRefactory/Utils/MultiDictionary.cs

@ -62,6 +62,15 @@ namespace ICSharpCode.NRefactory.Utils
return false; return false;
} }
/// <summary>
/// Removes all entries with the specified key.
/// </summary>
/// <returns>Returns true if at least one entry was removed.</returns>
public bool RemoveAll(TKey key)
{
return dict.Remove(key);
}
public void Clear() public void Clear()
{ {
dict.Clear(); dict.Clear();
@ -81,10 +90,21 @@ namespace ICSharpCode.NRefactory.Utils
} }
} }
/// <summary>
/// Returns the number of different keys.
/// </summary>
public int Count { public int Count {
get { return dict.Count; } get { return dict.Count; }
} }
public ICollection<TKey> Keys {
get { return dict.Keys; }
}
public IEnumerable<TValue> Values {
get { return dict.Values.SelectMany(list => list); }
}
IEnumerable<TValue> ILookup<TKey, TValue>.this[TKey key] { IEnumerable<TValue> ILookup<TKey, TValue>.this[TKey key] {
get { return this[key]; } get { return this[key]; }
} }

1
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -372,6 +372,7 @@
<Compile Include="Src\Project\MSBuildEngine\WorkerProcess.cs" /> <Compile Include="Src\Project\MSBuildEngine\WorkerProcess.cs" />
<Compile Include="Src\Project\MSBuildFileProject.cs" /> <Compile Include="Src\Project\MSBuildFileProject.cs" />
<Compile Include="Src\Project\MSBuildItemWrapper.cs" /> <Compile Include="Src\Project\MSBuildItemWrapper.cs" />
<Compile Include="Src\Project\MultipleProjectBuildable.cs" />
<Compile Include="Src\Project\PortableLibrary\CheckPortableLibraryInstalled.cs" /> <Compile Include="Src\Project\PortableLibrary\CheckPortableLibraryInstalled.cs" />
<Compile Include="Src\Project\PortableLibrary\ConvertToPortableLibraryProjectBehavior.cs" /> <Compile Include="Src\Project\PortableLibrary\ConvertToPortableLibraryProjectBehavior.cs" />
<Compile Include="Src\Project\PortableLibrary\PickPortableTargetFramework.cs" /> <Compile Include="Src\Project\PortableLibrary\PickPortableTargetFramework.cs" />

23
src/Main/Base/Project/Src/Commands/BuildCommands.cs

@ -3,9 +3,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.Core.Presentation; using ICSharpCode.Core.Presentation;
using ICSharpCode.SharpDevelop.Debugging; using ICSharpCode.SharpDevelop.Debugging;
@ -76,6 +77,26 @@ namespace ICSharpCode.SharpDevelop.Project.Commands
} }
} }
public Task<BuildResults> BuildAsync(CancellationToken cancellationToken)
{
var registration = cancellationToken.Register(BuildEngine.CancelGuiBuild, true);
var tcs = new TaskCompletionSource<BuildResults>();
this.BuildComplete += delegate {
registration.Dispose();
if (cancellationToken.IsCancellationRequested)
tcs.TrySetCanceled();
else
tcs.TrySetResult(this.LastBuildResults);
};
try {
StartBuild();
} catch (Exception ex) {
registration.Dispose();
tcs.TrySetException(ex);
}
return tcs.Task;
}
/// <summary> /// <summary>
/// Notifies the user that #develp's internal MSBuildEngine /// Notifies the user that #develp's internal MSBuildEngine
/// implementation only supports compiling solutions and projects; /// implementation only supports compiling solutions and projects;

4
src/AddIns/Analysis/UnitTesting/Commands/MultipleProjectBuildable.cs → src/Main/Base/Project/Src/Project/MultipleProjectBuildable.cs

@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using ICSharpCode.SharpDevelop.Project; using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting namespace ICSharpCode.SharpDevelop.Project
{ {
/// <summary> /// <summary>
/// IBuildable implementation that builds several projects. /// IBuildable implementation that builds several projects.
@ -17,7 +17,7 @@ namespace ICSharpCode.UnitTesting
public MultipleProjectBuildable(IEnumerable<IBuildable> projects) public MultipleProjectBuildable(IEnumerable<IBuildable> projects)
{ {
this.projects = projects.ToArray(); this.projects = projects.Where(p => p != null).ToArray();
} }
public string Name { public string Name {

22
src/Main/Base/Project/Src/Services/Tasks/TaskService.cs

@ -110,10 +110,16 @@ namespace ICSharpCode.SharpDevelop
public static void ClearExceptCommentTasks() public static void ClearExceptCommentTasks()
{ {
List<SDTask> commentTasks = new List<SDTask>(CommentTasks); bool wasInUpdate = InUpdate;
Clear(); InUpdate = true;
foreach (SDTask t in commentTasks) { try {
Add(t); List<SDTask> commentTasks = new List<SDTask>(CommentTasks);
Clear();
foreach (SDTask t in commentTasks) {
Add(t);
}
} finally {
InUpdate = wasInUpdate;
} }
} }
@ -149,10 +155,10 @@ namespace ICSharpCode.SharpDevelop
List<SDTask> newTasks = new List<SDTask>(); List<SDTask> newTasks = new List<SDTask>();
foreach (TagComment tag in tagComments) { foreach (TagComment tag in tagComments) {
newTasks.Add(new SDTask(fileName, newTasks.Add(new SDTask(fileName,
tag.Key + tag.CommentString, tag.Key + tag.CommentString,
tag.Region.BeginColumn, tag.Region.BeginColumn,
tag.Region.BeginLine, tag.Region.BeginLine,
TaskType.Comment)); TaskType.Comment));
} }
List<SDTask> oldTasks = new List<SDTask>(); List<SDTask> oldTasks = new List<SDTask>();

Loading…
Cancel
Save