Browse Source

implement class/namespace handling in Unit Testing

newNRvisualizers
Siegfried Pammer 13 years ago
parent
commit
4d9219bd81
  1. 36
      src/AddIns/Analysis/UnitTesting/Gui/UnitTestsPad.cs
  2. 157
      src/AddIns/Analysis/UnitTesting/Model/TestClass.cs
  3. 0
      src/AddIns/Analysis/UnitTesting/Model/TestMember.cs
  4. 85
      src/AddIns/Analysis/UnitTesting/Model/TestProject.cs
  5. 28
      src/AddIns/Analysis/UnitTesting/Nodes/ClassUnitTestNode.cs
  6. 70
      src/AddIns/Analysis/UnitTesting/Nodes/ProjectUnitTestNode.cs
  7. 72
      src/AddIns/Analysis/UnitTesting/Nodes/RootUnitTestNode.cs
  8. 298
      src/AddIns/Analysis/UnitTesting/Service/TestProject.cs
  9. 69
      src/AddIns/Analysis/UnitTesting/Service/TestService.cs
  10. 11
      src/AddIns/Analysis/UnitTesting/UnitTesting.csproj
  11. 2
      src/Main/Base/Project/Src/Util/ExtensionMethods.cs

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

@ -53,13 +53,7 @@ namespace ICSharpCode.UnitTesting
SD.ParserService.LoadSolutionProjectsThread.Finished += LoadSolutionProjectsThreadFinished; SD.ParserService.LoadSolutionProjectsThread.Finished += LoadSolutionProjectsThreadFinished;
OnAddedLoadSolutionProjectsThreadEndedHandler(); OnAddedLoadSolutionProjectsThreadEndedHandler();
// Display currently open solution. treeView.Root = new RootUnitTestNode();
if (!IsParserLoadingSolution) {
Solution openSolution = GetOpenSolution();
if (openSolution != null) {
SolutionLoaded(openSolution);
}
}
SD.ParserService.ParseInformationUpdated += ParseInformationUpdated; SD.ParserService.ParseInformationUpdated += ParseInformationUpdated;
ProjectService.SolutionClosed += SolutionClosed; ProjectService.SolutionClosed += SolutionClosed;
@ -133,22 +127,6 @@ namespace ICSharpCode.UnitTesting
// treeView.CollapseAll(); // treeView.CollapseAll();
// } // }
/// <summary>
/// Called when a solution has been loaded.
/// </summary>
protected void SolutionLoaded(Solution solution)
{
// SolutionLoaded will be invoked from another thread.
// The UnitTestsPad might be disposed by the time the event is processed by the main thread.
if (treeView != null) {
if (solution != null) {
treeView.Root = new RootUnitTestNode(solution);
} else {
treeView.Root = null;
}
}
}
/// <summary> /// <summary>
/// Called when a solution has been closed. /// Called when a solution has been closed.
/// </summary> /// </summary>
@ -161,15 +139,6 @@ namespace ICSharpCode.UnitTesting
// { // {
// treeView.RemoveSolutionFolder(solutionFolder); // treeView.RemoveSolutionFolder(solutionFolder);
// } // }
//
/// <summary>
/// The project is added to the tree view only if it has a
/// reference to a unit testing framework.
/// </summary>
protected void ProjectAdded(IProject project)
{
SolutionLoaded(GetOpenSolution());
}
/// <summary> /// <summary>
/// If the project item removed is a reference to a unit /// If the project item removed is a reference to a unit
@ -193,7 +162,7 @@ namespace ICSharpCode.UnitTesting
{ {
RootUnitTestNode root = (RootUnitTestNode)treeView.Root; RootUnitTestNode root = (RootUnitTestNode)treeView.Root;
if (root == null) { if (root == null) {
SolutionLoaded(GetOpenSolution()); // SolutionLoaded(GetOpenSolution());
root = (RootUnitTestNode)treeView.Root; root = (RootUnitTestNode)treeView.Root;
if (root == null) return; if (root == null) return;
} }
@ -271,7 +240,6 @@ namespace ICSharpCode.UnitTesting
void ProjectAdded(object source, ProjectEventArgs e) void ProjectAdded(object source, ProjectEventArgs e)
{ {
ProjectAdded(e.Project);
UpdateToolbar(); UpdateToolbar();
} }

157
src/AddIns/Analysis/UnitTesting/Service/TestClass.cs → src/AddIns/Analysis/UnitTesting/Model/TestClass.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.ObjectModel;
using System.Linq; using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using ICSharpCode.Core; using ICSharpCode.Core;
@ -14,7 +15,8 @@ namespace ICSharpCode.UnitTesting
/// </summary> /// </summary>
public class TestClass public class TestClass
{ {
List<IUnresolvedTypeDefinition> parts; string fullName;
ObservableCollection<IUnresolvedTypeDefinition> parts;
// TestMemberCollection testMembers; // TestMemberCollection testMembers;
TestResultType testResultType; TestResultType testResultType;
IRegisteredTestFrameworks testFrameworks; IRegisteredTestFrameworks testFrameworks;
@ -24,10 +26,11 @@ namespace ICSharpCode.UnitTesting
/// </summary> /// </summary>
public event EventHandler ResultChanged; public event EventHandler ResultChanged;
public TestClass(IRegisteredTestFrameworks testFrameworks, params IUnresolvedTypeDefinition[] parts) public TestClass(IRegisteredTestFrameworks testFrameworks, string fullName, IEnumerable<IUnresolvedTypeDefinition> parts)
{ {
this.parts = new List<IUnresolvedTypeDefinition>(parts); this.parts = new ObservableCollection<IUnresolvedTypeDefinition>(parts);
this.testFrameworks = testFrameworks; this.testFrameworks = testFrameworks;
this.fullName = fullName;
} }
/// <summary> /// <summary>
@ -37,6 +40,10 @@ namespace ICSharpCode.UnitTesting
get { return parts; } get { return parts; }
} }
public string FullName {
get { return fullName; }
}
/// <summary> /// <summary>
/// Gets the list of other (e.g. base types) classes where from which test members included in this test class come from. /// Gets the list of other (e.g. base types) classes where from which test members included in this test class come from.
/// </summary> /// </summary>
@ -71,70 +78,26 @@ namespace ICSharpCode.UnitTesting
return matchedClasses.ToArray(); return matchedClasses.ToArray();
} }
/// <summary>
/// Gets all child namespaces that starts with the specified string.
/// </summary>
/// <remarks>
/// If the starts with string is 'ICSharpCode' and there is a code coverage
/// method with a namespace of 'ICSharpCode.XmlEditor.Tests', then this
/// method will return 'XmlEditor' as one of its strings.
/// </remarks>
public static string[] GetChildNamespaces(ICollection<TestClass> classes, string parentNamespace) {
List<string> items = new List<string>();
foreach (TestClass c in classes) {
string ns = c.GetChildNamespace(parentNamespace);
if (ns.Length > 0) {
if (!items.Contains(ns)) {
items.Add(ns);
}
}
}
return items.ToArray();
}
/// <summary> /// <summary>
/// Gets the name of the class. /// Gets the name of the class.
/// </summary> /// </summary>
public string Name { public string Name {
get get { return parts.First().Name; }
{
// var currentClass = c;
// var name = c.Name;
// while(currentClass.DeclaringType != null)
// {
// name = String.Concat(currentClass.DeclaringType.Name, "+", name);
// currentClass = currentClass.DeclaringType;
// }
return "";//name;
}
} }
/// <summary> /// <summary>
/// Gets the fully qualified name of the class. /// Gets the fully qualified name of the class.
/// </summary> /// </summary>
public string QualifiedName { public string QualifiedName {
get { return ""; }// c.DotNetName; } get { return parts.First().ReflectionName; }
} }
/// <summary> /// <summary>
/// Gets the namespace of this class. /// Gets the namespace of this class.
/// </summary> /// </summary>
public string Namespace { public string Namespace {
get { get { return parts.First().Namespace; }
// var currentClass = c;
// while (currentClass.DeclaringType != null)
// currentClass = currentClass.DeclaringType;
// return currentClass.Namespace;
throw new NotImplementedException();
} }
}
/// <summary>
/// Gets the root namespace for this class.
/// </summary>
// public string RootNamespace {
// get { return GetRootNamespace(c.Namespace); }
// }
/// <summary> /// <summary>
/// Gets the test result for this class. /// Gets the test result for this class.
@ -150,59 +113,6 @@ namespace ICSharpCode.UnitTesting
} }
} }
/// <summary>
/// Gets the child namespace from the specified namespace
/// based on the parent namespace.
/// </summary>
/// <param name="parentNamespace">Can contain multiple namespaces
/// (e.g. ICSharpCode.XmlEditor).</param>
public static string GetChildNamespace(string ns, string parentNamespace)
{
if (parentNamespace.Length > 0) {
if (ns.StartsWith(String.Concat(parentNamespace, "."))) {
string end = ns.Substring(parentNamespace.Length + 1);
return GetRootNamespace(end);
}
return String.Empty;
}
return ns;
}
/// <summary>
/// Gets the child namespace based on the parent namespace
/// from this class.
/// </summary>
/// <param name="parentNamespace">Can contain multiple namespaces
/// (e.g. ICSharpCode.XmlEditor).</param>
public string GetChildNamespace(string parentNamespace)
{
return GetChildNamespace(Namespace, parentNamespace);
}
/// <summary>
/// Gets the test members in this class.
/// </summary>
// public TestMemberCollection TestMembers {
// get {
// if (testMembers == null) {
// GetTestMembers();
// }
// return testMembers;
// }
// }
/// <summary>
/// Gets the test member with the specified name.
/// </summary>
/// <returns>Null if the member cannot be found.</returns>
// public TestMember GetTestMember(string name)
// {
// if (TestMembers.Contains(name)) {
// return TestMembers[name];
// }
// return null;
// }
/// <summary> /// <summary>
/// Updates the test member with the specified test result. /// Updates the test member with the specified test result.
/// </summary> /// </summary>
@ -234,9 +144,8 @@ namespace ICSharpCode.UnitTesting
/// Updates the members and class based on the new class /// Updates the members and class based on the new class
/// information that has been parsed. /// information that has been parsed.
/// </summary> /// </summary>
public void UpdateClass(IUnresolvedTypeDefinition c) public void UpdateClass(ITypeDefinition definition)
{ {
#warning not implemented!
// this.c = c.GetCompoundClass(); // this.c = c.GetCompoundClass();
// //
// // Remove missing members. // // Remove missing members.
@ -281,44 +190,6 @@ namespace ICSharpCode.UnitTesting
// testMembers.ResultChanged += TestMembersResultChanged; // testMembers.ResultChanged += TestMembersResultChanged;
} }
/// <summary>
/// Gets the test members for the specified class.
/// </summary>
// TestMemberCollection GetTestMembers(IUnresolvedTypeDefinition c)
// {
// TestMemberCollection testMembers = new TestMemberCollection();
// foreach (var member in testFrameworks.GetTestMembersFor(c))
// if (!testMembers.Contains(member.Name)) {
// testMembers.Add(member);
// }
//
// // Add base class test members.
// IClass declaringType = c;
// while (c.BaseClass != null)
// {
// foreach (var testMember in testFrameworks.GetTestMembersFor(c.BaseClass)) {
// BaseTestMember baseTestMethod = new BaseTestMember(declaringType, testMember.Member);
// TestMember testMethod = new TestMember(c.BaseClass, baseTestMethod);
// if (testMember.Member.IsVirtual) {
// if (!testMembers.Contains(testMember.Name)) {
// testMembers.Add(testMethod);
// }
// } else {
// if (!testMembers.Contains(testMethod.Name)) {
// testMembers.Add(testMethod);
// }
// }
// }
// c = c.BaseClass;
// }
//
// baseClassesFQNames.Clear();
// foreach (var memberDeclaringClass in testMembers.Select(member => member.DeclaringType).Distinct())
// if (memberDeclaringClass.CompareTo(declaringType) != 0)
// baseClassesFQNames.Add(memberDeclaringClass.FullyQualifiedName);
// return testMembers;
// }
/// <summary> /// <summary>
/// Updates the test class's test result after the test member's /// Updates the test class's test result after the test member's
/// test result has changed. /// test result has changed.

0
src/AddIns/Analysis/UnitTesting/Service/TestMember.cs → src/AddIns/Analysis/UnitTesting/Model/TestMember.cs

85
src/AddIns/Analysis/UnitTesting/Model/TestProject.cs

@ -0,0 +1,85 @@
// 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.Diagnostics;
using System.Linq;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
using ICSharpCode.NRefactory.Utils;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Parser;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Represents a project that has a reference to a unit testing
/// framework assembly. Currently only NUnit is supported.
/// </summary>
public class TestProject
{
IProject project;
IRegisteredTestFrameworks testFrameworks;
readonly ObservableCollection<TestClass> testClasses;
public TestProject(IProject project)
{
this.project = project;
this.testFrameworks = TestService.RegisteredTestFrameworks;
project.ParseInformationUpdated += project_ParseInformationUpdated;
var compilation = SD.ParserService.GetCompilation(project);
var classes = project.ProjectContent
.Resolve(compilation.TypeResolveContext)
.GetAllTypeDefinitions()
.Where(td => td.HasTests(compilation))
.Select(g => new TestClass(testFrameworks, g.ReflectionName, g.Parts));
testClasses = new ObservableCollection<TestClass>(classes);
}
void project_ParseInformationUpdated(object sender, ParseInformationEventArgs e)
{
var context = new SimpleTypeResolveContext(SD.ParserService.GetCompilation(project).MainAssembly);
IEnumerable<ITypeDefinition> @new;
if (e.NewParsedFile != null)
@new = e.NewParsedFile.TopLevelTypeDefinitions.Select(utd => utd.Resolve(context).GetDefinition()).Where(x => x != null && x.HasTests(SD.ParserService.GetCompilation(project)));
else
@new = Enumerable.Empty<ITypeDefinition>();
UpdateTestClasses(testClasses.Where(tc => tc.Parts.Any(td => td.ParsedFile.FileName == e.OldParsedFile.FileName)).Select(tc => new DefaultResolvedTypeDefinition(context, tc.Parts.ToArray())).ToList(), @new.ToList());
}
void UpdateTestClasses(IReadOnlyList<ITypeDefinition> oldTypes, IReadOnlyList<ITypeDefinition> newTypes)
{
var mappings = oldTypes.FullOuterJoin(newTypes, t => t.ReflectionName, t => t.ReflectionName, Tuple.Create);
foreach (Tuple<ITypeDefinition, ITypeDefinition> mapping in mappings) {
if (mapping.Item2 == null)
testClasses.RemoveWhere(c => c.FullName == mapping.Item1.ReflectionName);
else if (mapping.Item1 == null)
testClasses.Add(new TestClass(testFrameworks, mapping.Item2.ReflectionName, mapping.Item2.Parts));
else {
var testClass = testClasses.SingleOrDefault(c => c.FullName == mapping.Item1.ReflectionName);
if (testClass == null)
testClasses.Add(new TestClass(testFrameworks, mapping.Item2.ReflectionName, mapping.Item2.Parts));
else
testClass.UpdateClass(mapping.Item2);
}
}
}
public IProject Project {
get { return project; }
}
public ObservableCollection<TestClass> TestClasses {
get { return testClasses; }
}
public void UpdateTestResult(TestResult result)
{
}
}
}

28
src/AddIns/Analysis/UnitTesting/Nodes/ClassUnitTestNode.cs

@ -0,0 +1,28 @@
// 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>
/// Description of ClassUnitTestNode.
/// </summary>
public class ClassUnitTestNode : UnitTestBaseNode
{
TestClass testClass;
public TestClass TestClass {
get { return testClass; }
}
public ClassUnitTestNode(TestClass testClass)
{
this.testClass = testClass;
}
public override object Text {
get { return testClass.Name; }
}
}
}

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

@ -2,12 +2,15 @@
// 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.Specialized;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Project; using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.TreeView; using ICSharpCode.TreeView;
@ -24,15 +27,78 @@ namespace ICSharpCode.UnitTesting
public ProjectUnitTestNode(TestProject project) public ProjectUnitTestNode(TestProject project)
{ {
this.project = project; this.project = project;
project.TestClasses.CollectionChanged += TestClassesCollectionChanged;
LazyLoading = true;
}
void TestClassesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action) {
case NotifyCollectionChangedAction.Add:
foreach (TestClass c in e.NewItems) {
var node = FindNamespace(c.Namespace);
if (node == null) {
node = new NamespaceUnitTestNode(c.Namespace);
Children.OrderedInsert(node, (a, b) => string.CompareOrdinal(a.Text.ToString(), b.Text.ToString()));
}
node.Children.OrderedInsert(new ClassUnitTestNode(c), (a, b) => string.CompareOrdinal(a.Text.ToString(), b.Text.ToString()));
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (TestClass c in e.OldItems) {
var node = FindNamespace(c.Namespace);
if (node == null) continue;
node.Children.RemoveWhere(n => n is ClassUnitTestNode && ((ClassUnitTestNode)n).TestClass.FullName == c.FullName);
if (node.Children.Count == 0)
Children.Remove(node);
}
break;
case NotifyCollectionChangedAction.Reset:
LoadChildren();
break;
}
}
NamespaceUnitTestNode FindNamespace(string @namespace)
{
foreach (var node in Children.OfType<NamespaceUnitTestNode>()) {
// TODO use language-specific StringComparer
if (string.Equals(node.Namespace, @namespace, StringComparison.Ordinal))
return node;
}
return null;
} }
protected override void LoadChildren() protected override void LoadChildren()
{ {
base.LoadChildren(); Children.Clear();
foreach (var g in project.TestClasses.Select(c => new ClassUnitTestNode(c)).GroupBy(tc => tc.TestClass.Namespace)) {
var namespaceNode = new NamespaceUnitTestNode(g.Key);
namespaceNode.Children.AddRange(g);
Children.Add(namespaceNode);
}
}
public override object Text {
get { return project.Project.Name; }
}
}
public class NamespaceUnitTestNode : UnitTestBaseNode
{
string name;
public string Namespace {
get { return name; }
}
public NamespaceUnitTestNode(string name)
{
this.name = name;
} }
public override object Text { public override object Text {
get { return project.Name; } get { return string.IsNullOrEmpty(name) ? "<default>" : name; }
} }
} }
} }

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

@ -2,11 +2,14 @@
// 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.Collections.Specialized;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation; using ICSharpCode.NRefactory.TypeSystem.Implementation;
@ -23,32 +26,31 @@ namespace ICSharpCode.UnitTesting
/// </summary> /// </summary>
public class RootUnitTestNode : UnitTestBaseNode public class RootUnitTestNode : UnitTestBaseNode
{ {
Solution solution; public RootUnitTestNode()
public RootUnitTestNode(Solution solution)
{ {
this.solution = solution; TestService.TestableProjects.CollectionChanged += TestService_TestableProjects_CollectionChanged;
ProjectService.ProjectAdded += OnProjectAdded;
ProjectService.ProjectRemoved += OnProjectRemoved;
SD.ParserService.LoadSolutionProjectsThread.Finished += delegate { LoadChildren(); };
LazyLoading = true; LazyLoading = true;
} }
void OnProjectRemoved(object sender, ProjectEventArgs e) void TestService_TestableProjects_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{ {
switch (e.Action) {
case NotifyCollectionChangedAction.Add:
Children.AddRange(e.NewItems.OfType<TestProject>().Select(p => new ProjectUnitTestNode(p)));
break;
case NotifyCollectionChangedAction.Remove:
Children.RemoveWhere(node => node is ProjectUnitTestNode && e.OldItems.OfType<TestProject>().Any(p => p.Project == ((ProjectUnitTestNode)node).Project));
break;
case NotifyCollectionChangedAction.Reset:
LoadChildren(); LoadChildren();
break;
} }
void OnProjectAdded(object sender, ProjectEventArgs e)
{
LoadChildren();
} }
protected override void LoadChildren() protected override void LoadChildren()
{ {
this.Children.Clear(); Children.Clear();
if (!SD.ParserService.LoadSolutionProjectsThread.IsRunning) Children.AddRange(TestService.TestableProjects.Select(p => new ProjectUnitTestNode(p)));
this.Children.AddRange(solution.Projects.Where(p => p.IsTestProject()).Select(p => new ProjectUnitTestNode(new TestProject(p))));
} }
public override object Text { public override object Text {
@ -58,13 +60,51 @@ namespace ICSharpCode.UnitTesting
public static class Extensions public static class Extensions
{ {
static readonly ITypeReference testAttribute = new GetClassTypeReference("NUnit.Framework", "TestAttribute", 0);
public static bool IsTestProject(this IProject project) public static bool IsTestProject(this IProject project)
{ {
if (project == null) if (project == null)
throw new ArgumentNullException("project"); throw new ArgumentNullException("project");
if (project.ProjectContent == null) if (project.ProjectContent == null)
return false; return false;
return SD.ParserService.GetCompilation(project).FindType("NUnit.Framework.TestAttribute").Kind != TypeKind.Unknown; return testAttribute.Resolve(SD.ParserService.GetCompilation(project).TypeResolveContext).Kind != TypeKind.Unknown;
}
public static bool HasTests(this ITypeDefinition type, ICompilation compilation)
{
if (type == null)
throw new ArgumentNullException("type");
var testAttribute = Extensions.testAttribute.Resolve(compilation.TypeResolveContext);
return type.Methods.Any(m => m.Attributes.Any(a => a.AttributeType.Equals(testAttribute)));
}
public static IEnumerable<TResult> FullOuterJoin<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter,TKey> outerKeySelector, Func<TInner,TKey> innerKeySelector, Func<TOuter,TInner,TResult> resultSelector)
where TInner : class
where TOuter : class
{
var innerLookup = inner.ToLookup(innerKeySelector);
var outerLookup = outer.ToLookup(outerKeySelector);
var innerJoinItems = inner
.Where(innerItem => !outerLookup.Contains(innerKeySelector(innerItem)))
.Select(innerItem => resultSelector(null, innerItem));
return outer
.SelectMany(outerItem => {
var innerItems = innerLookup[outerKeySelector(outerItem)];
return innerItems.Any() ? innerItems : new TInner[] { null };
}, resultSelector)
.Concat(innerJoinItems);
}
public static void OrderedInsert<T>(this IList<T> list, T item, Func<T, T, int> comparer)
{
int index = 0;
while (index < list.Count && comparer(list[index], item) < 0)
index++;
list.Insert(index, item);
} }
} }
} }

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

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

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

@ -2,7 +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.Collections.ObjectModel;
using System.Linq;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting namespace ICSharpCode.UnitTesting
{ {
@ -43,5 +49,68 @@ namespace ICSharpCode.UnitTesting
"${res:ICSharpCode.NUnitPad.NUnitPadContent.PadName}"); "${res:ICSharpCode.NUnitPad.NUnitPadContent.PadName}");
} }
static readonly ObservableCollection<TestProject> testableProjects = new ObservableCollection<TestProject>();
public static ObservableCollection<TestProject> TestableProjects {
get { return testableProjects; }
}
static TestService()
{
ProjectService.SolutionCreated += ProjectService_SolutionCreated;
ProjectService.SolutionLoaded += ProjectService_SolutionLoaded;
ProjectService.SolutionClosed += ProjectService_SolutionClosed;
ProjectService.ProjectCreated += ProjectService_ProjectCreated;
ProjectService.ProjectAdded += ProjectService_ProjectAdded;
ProjectService.ProjectRemoved += ProjectService_ProjectRemoved;
SD.ParserService.LoadSolutionProjectsThread.Finished += SD_ParserService_LoadSolutionProjectsThread_Finished;
}
static void SD_ParserService_LoadSolutionProjectsThread_Finished(object sender, EventArgs e)
{
testableProjects.Clear();
testableProjects.AddRange(GetTestableProjects());
}
static void ProjectService_ProjectCreated(object sender, ProjectEventArgs e)
{
if (e.Project.IsTestProject())
testableProjects.Add(new TestProject(e.Project));
}
static void ProjectService_ProjectAdded(object sender, ProjectEventArgs e)
{
if (e.Project.IsTestProject())
testableProjects.Add(new TestProject(e.Project));
}
static void ProjectService_ProjectRemoved(object sender, ProjectEventArgs e)
{
testableProjects.RemoveWhere(test => test.Project == e.Project);
}
static void ProjectService_SolutionCreated(object sender, SolutionEventArgs e)
{
testableProjects.Clear();
testableProjects.AddRange(GetTestableProjects());
}
static void ProjectService_SolutionClosed(object sender, EventArgs e)
{
testableProjects.Clear();
}
static void ProjectService_SolutionLoaded(object sender, SolutionEventArgs e)
{
testableProjects.Clear();
testableProjects.AddRange(GetTestableProjects());
}
static IEnumerable<TestProject> GetTestableProjects()
{
if (ProjectService.OpenSolution == null)
return Enumerable.Empty<TestProject>();
return ProjectService.OpenSolution.Projects.Where(p => p.IsTestProject()).Select(p => new TestProject(p));
}
} }
} }

11
src/AddIns/Analysis/UnitTesting/UnitTesting.csproj

@ -17,7 +17,7 @@
<FileAlignment>4096</FileAlignment> <FileAlignment>4096</FileAlignment>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors> <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkProfile> <TargetFrameworkProfile>
</TargetFrameworkProfile> </TargetFrameworkProfile>
</PropertyGroup> </PropertyGroup>
@ -37,8 +37,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="nunit.framework"> <Reference Include="nunit.framework">
<HintPath>..\..\..\..\bin\Tools\NUnit\nunit.framework.dll</HintPath> <HintPath>..\..\..\Tools\NUnit\nunit.framework.dll</HintPath>
<Private>False</Private>
</Reference> </Reference>
<Reference Include="PresentationCore"> <Reference Include="PresentationCore">
<RequiredTargetFramework>3.0</RequiredTargetFramework> <RequiredTargetFramework>3.0</RequiredTargetFramework>
@ -103,7 +102,10 @@
<Compile Include="Interfaces\IUnitTestsPad.cs" /> <Compile Include="Interfaces\IUnitTestsPad.cs" />
<Compile Include="Interfaces\IUnitTestTaskService.cs" /> <Compile Include="Interfaces\IUnitTestTaskService.cs" />
<Compile Include="Interfaces\IUnitTestWorkbench.cs" /> <Compile Include="Interfaces\IUnitTestWorkbench.cs" />
<Compile Include="Model\TestClass.cs" />
<Compile Include="Model\TestProject.cs" />
<Compile Include="MultipleProjectBuildable.cs" /> <Compile Include="MultipleProjectBuildable.cs" />
<Compile Include="Nodes\ClassUnitTestNode.cs" />
<Compile Include="Nodes\ProjectUnitTestNode.cs" /> <Compile Include="Nodes\ProjectUnitTestNode.cs" />
<Compile Include="Nodes\RootUnitTestNode.cs" /> <Compile Include="Nodes\RootUnitTestNode.cs" />
<Compile Include="Nodes\UnitTestBaseNode.cs" /> <Compile Include="Nodes\UnitTestBaseNode.cs" />
@ -113,12 +115,10 @@
<Compile Include="Service\RegisteredTestFrameworks.cs" /> <Compile Include="Service\RegisteredTestFrameworks.cs" />
<Compile Include="Service\RunningTestsCondition.cs" /> <Compile Include="Service\RunningTestsCondition.cs" />
<Compile Include="Service\SelectedTests.cs" /> <Compile Include="Service\SelectedTests.cs" />
<Compile Include="Service\TestClass.cs" />
<Compile Include="Service\TestFinishedEventArgs.cs" /> <Compile Include="Service\TestFinishedEventArgs.cs" />
<Compile Include="Service\TestFrameworkDescriptor.cs" /> <Compile Include="Service\TestFrameworkDescriptor.cs" />
<Compile Include="Service\TestFrameworkDoozer.cs" /> <Compile Include="Service\TestFrameworkDoozer.cs" />
<Compile Include="Service\TestFrameworkFactory.cs" /> <Compile Include="Service\TestFrameworkFactory.cs" />
<Compile Include="Service\TestProject.cs" />
<Compile Include="Service\TestResult.cs" /> <Compile Include="Service\TestResult.cs" />
<Compile Include="Service\TestRunnerBase.cs" /> <Compile Include="Service\TestRunnerBase.cs" />
<Compile Include="Service\TestService.cs" /> <Compile Include="Service\TestService.cs" />
@ -179,6 +179,7 @@
<Folder Include="Gui" /> <Folder Include="Gui" />
<Folder Include="Implementation" /> <Folder Include="Implementation" />
<Folder Include="Interfaces" /> <Folder Include="Interfaces" />
<Folder Include="Model" />
<Folder Include="Service" /> <Folder Include="Service" />
<Folder Include="Nodes" /> <Folder Include="Nodes" />
</ItemGroup> </ItemGroup>

2
src/Main/Base/Project/Src/Util/ExtensionMethods.cs

@ -113,7 +113,7 @@ namespace ICSharpCode.SharpDevelop
/// <summary> /// <summary>
/// Adds all <paramref name="elements"/> to <paramref name="list"/>. /// Adds all <paramref name="elements"/> to <paramref name="list"/>.
/// </summary> /// </summary>
public static void AddRange(this WinForms.ComboBox.ObjectCollection list, IEnumerable elements) internal static void AddRange(this WinForms.ComboBox.ObjectCollection list, IEnumerable elements)
{ {
foreach (var o in elements) foreach (var o in elements)
list.Add(o); list.Add(o);

Loading…
Cancel
Save