Browse Source

UnitTesting: rewrite tree creation logic to use nested nodes for namespaces

newNRvisualizers
Siegfried Pammer 13 years ago
parent
commit
542544314d
  1. 2
      SharpDevelop.sln
  2. 81
      src/AddIns/Analysis/UnitTesting/Extensions.cs
  3. 101
      src/AddIns/Analysis/UnitTesting/Model/TestClass.cs
  4. 142
      src/AddIns/Analysis/UnitTesting/Model/TestMember.cs
  5. 6
      src/AddIns/Analysis/UnitTesting/Model/TestProject.cs
  6. 102
      src/AddIns/Analysis/UnitTesting/Nodes/AllTestsTreeNode.cs
  7. 29
      src/AddIns/Analysis/UnitTesting/Nodes/ClassUnitTestNode.cs
  8. 26
      src/AddIns/Analysis/UnitTesting/Nodes/MemberUnitTestNode.cs
  9. 38
      src/AddIns/Analysis/UnitTesting/Nodes/NamespaceUnitTestNode.cs
  10. 112
      src/AddIns/Analysis/UnitTesting/Nodes/ProjectUnitTestNode.cs
  11. 50
      src/AddIns/Analysis/UnitTesting/Nodes/RootUnitTestNode.cs
  12. 98
      src/AddIns/Analysis/UnitTesting/Nodes/TestClassTreeNode.cs
  13. 53
      src/AddIns/Analysis/UnitTesting/Nodes/TestMemberTreeNode.cs
  14. 55
      src/AddIns/Analysis/UnitTesting/Nodes/TestMethodTreeNode.cs
  15. 336
      src/AddIns/Analysis/UnitTesting/Nodes/TestNamespaceTreeNode.cs
  16. 113
      src/AddIns/Analysis/UnitTesting/Nodes/TestProjectTreeNode.cs
  17. 103
      src/AddIns/Analysis/UnitTesting/Nodes/TestTreeNode.cs
  18. 1
      src/AddIns/Analysis/UnitTesting/Nodes/UnitTestBaseNode.cs
  19. 4
      src/AddIns/Analysis/UnitTesting/UnitTesting.csproj

2
SharpDevelop.sln

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
# SharpDevelop 4.2.0.8707-Beta 2
# SharpDevelop 4.2.0.8742-Beta 2
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Main", "Main", "{256F5C28-532C-44C0-8AB8-D8EC5E492E01}"
ProjectSection(SolutionItems) = postProject
EndProjectSection

81
src/AddIns/Analysis/UnitTesting/Extensions.cs

@ -0,0 +1,81 @@ @@ -0,0 +1,81 @@
// 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 System.Reflection;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Parser;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.TreeView;
using NUnit.Framework;
namespace ICSharpCode.UnitTesting
{
public static class Extensions
{
static readonly ITypeReference testAttribute = new GetClassTypeReference("NUnit.Framework", "TestAttribute", 0);
public static bool IsTestProject(this IProject project)
{
if (project == null)
throw new ArgumentNullException("project");
if (project.ProjectContent == null)
return false;
return testAttribute.Resolve(SD.ParserService.GetCompilation(project).TypeResolveContext).Kind != TypeKind.Unknown;
}
public static bool IsTestMethod(this IMethod method, ICompilation compilation)
{
if (method == null)
throw new ArgumentNullException("method");
var testAttribute = Extensions.testAttribute.Resolve(compilation.TypeResolveContext);
return method.Attributes.Any(a => a.AttributeType.Equals(testAttribute));
}
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);
}
}
}

101
src/AddIns/Analysis/UnitTesting/Model/TestClass.cs

@ -2,11 +2,13 @@ @@ -2,11 +2,13 @@
// 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.Linq;
using System.Collections.Generic;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop;
namespace ICSharpCode.UnitTesting
{
@ -17,7 +19,7 @@ namespace ICSharpCode.UnitTesting @@ -17,7 +19,7 @@ namespace ICSharpCode.UnitTesting
{
string fullName;
ObservableCollection<IUnresolvedTypeDefinition> parts;
// TestMemberCollection testMembers;
readonly ObservableCollection<TestMember> testMembers;
TestResultType testResultType;
IRegisteredTestFrameworks testFrameworks;
@ -26,11 +28,13 @@ namespace ICSharpCode.UnitTesting @@ -26,11 +28,13 @@ namespace ICSharpCode.UnitTesting
/// </summary>
public event EventHandler ResultChanged;
public TestClass(IRegisteredTestFrameworks testFrameworks, string fullName, IEnumerable<IUnresolvedTypeDefinition> parts)
public TestClass(IRegisteredTestFrameworks testFrameworks, string fullName, ITypeDefinition definition)
{
this.parts = new ObservableCollection<IUnresolvedTypeDefinition>(parts);
this.parts = new ObservableCollection<IUnresolvedTypeDefinition>();
this.testMembers = new ObservableCollection<TestMember>();
this.testFrameworks = testFrameworks;
this.fullName = fullName;
UpdateClass(definition);
}
/// <summary>
@ -40,44 +44,14 @@ namespace ICSharpCode.UnitTesting @@ -40,44 +44,14 @@ namespace ICSharpCode.UnitTesting
get { return parts; }
}
public ObservableCollection<TestMember> Members {
get { return testMembers; }
}
public string FullName {
get { return fullName; }
}
/// <summary>
/// Gets the list of other (e.g. base types) classes where from which test members included in this test class come from.
/// </summary>
private readonly ICollection<string> baseClassesFQNames = new List<string>();
/// <summary>
/// Gets the test classes that exist in the specified namespace.
/// </summary>
public static TestClass[] GetTestClasses(ICollection<TestClass> classes, string ns)
{
List<TestClass> matchedClasses = new List<TestClass>();
foreach (TestClass c in classes) {
if (c.Namespace == ns) {
matchedClasses.Add(c);
}
}
return matchedClasses.ToArray();
}
/// <summary>
/// Gets the test classes that namespaces starts with the specified
/// string.
/// </summary>
public static TestClass[] GetAllTestClasses(ICollection<TestClass> classes, string namespaceStartsWith)
{
List<TestClass> matchedClasses = new List<TestClass>();
foreach (TestClass c in classes) {
if (c.Namespace.StartsWith(namespaceStartsWith)) {
matchedClasses.Add(c);
}
}
return matchedClasses.ToArray();
}
/// <summary>
/// Gets the name of the class.
/// </summary>
@ -146,27 +120,21 @@ namespace ICSharpCode.UnitTesting @@ -146,27 +120,21 @@ namespace ICSharpCode.UnitTesting
/// </summary>
public void UpdateClass(ITypeDefinition definition)
{
// this.c = c.GetCompoundClass();
//
// // Remove missing members.
// TestMemberCollection newTestMembers = GetTestMembers(this.c);
// TestMemberCollection existingTestMembers = TestMembers;
// for (int i = existingTestMembers.Count - 1; i >= 0; --i) {
// TestMember member = existingTestMembers[i];
// if (newTestMembers.Contains(member.Name)) {
// member.Update(newTestMembers[member.Name].Member);
// } else {
// existingTestMembers.RemoveAt(i);
// }
// }
//
// // Add new members.
// foreach (TestMember member in newTestMembers) {
// if (existingTestMembers.Contains(member.Name)) {
// } else {
// existingTestMembers.Add(member);
// }
// }
int i = 0;
while (i < parts.Count) {
var part = parts[i];
if (!definition.Parts.Any(p => p.ParsedFile.FileName == part.ParsedFile.FileName && p.Region == part.Region))
parts.RemoveAt(i);
else
i++;
}
foreach (var part in definition.Parts) {
if (!parts.Any(p => p.ParsedFile.FileName == part.ParsedFile.FileName && p.Region == part.Region))
parts.Add(part);
}
testMembers.RemoveWhere(m => !definition.Methods.Any(dm => dm.ReflectionName == m.Method.ReflectionName && dm.IsTestMethod(definition.Compilation)));
testMembers.AddRange(definition.Methods.Where(m => m.IsTestMethod(definition.Compilation) && !testMembers.Any(dm => dm.Method.ReflectionName == m.ReflectionName)).Select(m => new TestMember((IUnresolvedMethod)m.UnresolvedMember)));
}
/// <summary>
@ -208,7 +176,7 @@ namespace ICSharpCode.UnitTesting @@ -208,7 +176,7 @@ namespace ICSharpCode.UnitTesting
ResultChanged(this, new EventArgs());
}
}
/// <summary>
/// This function adds the base class as a prefix and tries to find
/// the corresponding test member.
@ -218,9 +186,9 @@ namespace ICSharpCode.UnitTesting @@ -218,9 +186,9 @@ namespace ICSharpCode.UnitTesting
/// RootNamespace.TestFixture.TestFixtureBaseClass.TestMethod
/// </summary>
/// <remarks>
/// NUnit 2.4 uses the correct test method name when a test
/// class uses a base class with test methods. It does
/// not prefix the test method name with the base class name
/// NUnit 2.4 uses the correct test method name when a test
/// class uses a base class with test methods. It does
/// not prefix the test method name with the base class name
/// in the test results returned from nunit-console. It still
/// displays the name in the NUnit GUI with the base class
/// name prefixed. Older versions of NUnit-console (2.2.9) returned
@ -228,7 +196,7 @@ namespace ICSharpCode.UnitTesting @@ -228,7 +196,7 @@ namespace ICSharpCode.UnitTesting
///
/// RootNamespace.TestFixture.BaseTestFixture.TestMethod
///
/// The test method name would have the base class name prefixed
/// The test method name would have the base class name prefixed
/// to it.
/// </remarks>
// TestMember GetPrefixedTestMember(string testResultName)
@ -245,10 +213,5 @@ namespace ICSharpCode.UnitTesting @@ -245,10 +213,5 @@ namespace ICSharpCode.UnitTesting
// }
// return null;
// }
public bool IsDerivedFrom(IUnresolvedTypeDefinition c)
{
return baseClassesFQNames.Contains(c.FullName);
}
}
}

142
src/AddIns/Analysis/UnitTesting/Model/TestMember.cs

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

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

@ -36,7 +36,7 @@ namespace ICSharpCode.UnitTesting @@ -36,7 +36,7 @@ namespace ICSharpCode.UnitTesting
.Resolve(compilation.TypeResolveContext)
.GetAllTypeDefinitions()
.Where(td => td.HasTests(compilation))
.Select(g => new TestClass(testFrameworks, g.ReflectionName, g.Parts));
.Select(g => new TestClass(testFrameworks, g.ReflectionName, g));
testClasses = new ObservableCollection<TestClass>(classes);
}
@ -58,11 +58,11 @@ namespace ICSharpCode.UnitTesting @@ -58,11 +58,11 @@ namespace ICSharpCode.UnitTesting
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));
testClasses.Add(new TestClass(testFrameworks, mapping.Item2.ReflectionName, mapping.Item2));
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));
testClasses.Add(new TestClass(testFrameworks, mapping.Item2.ReflectionName, mapping.Item2));
else
testClass.UpdateClass(mapping.Item2);
}

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

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

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

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Specialized;
namespace ICSharpCode.UnitTesting
{
@ -19,6 +20,34 @@ namespace ICSharpCode.UnitTesting @@ -19,6 +20,34 @@ namespace ICSharpCode.UnitTesting
public ClassUnitTestNode(TestClass testClass)
{
this.testClass = testClass;
testClass.Members.CollectionChanged += TestMembersCollectionChanged;
LazyLoading = true;
}
void TestMembersCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action) {
case NotifyCollectionChangedAction.Add:
foreach (TestMember m in e.NewItems) {
Children.OrderedInsert(new MemberUnitTestNode(m), (a, b) => string.CompareOrdinal(a.Text.ToString(), b.Text.ToString()));
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (TestMember m in e.OldItems) {
Children.RemoveAll(n => n is MemberUnitTestNode && ((MemberUnitTestNode)n).TestMember.Method.ReflectionName == m.Method.ReflectionName);
}
break;
case NotifyCollectionChangedAction.Reset:
LoadChildren();
break;
}
}
protected override void LoadChildren()
{
Children.Clear();
foreach (TestMember m in testClass.Members)
Children.Add(new MemberUnitTestNode(m));
}
public override object Text {

26
src/AddIns/Analysis/UnitTesting/Nodes/MemberUnitTestNode.cs

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Specialized;
namespace ICSharpCode.UnitTesting
{
public class MemberUnitTestNode : UnitTestBaseNode
{
TestMember testMember;
public TestMember TestMember {
get { return testMember; }
}
public MemberUnitTestNode(TestMember testMember)
{
this.testMember = testMember;
}
public override object Text {
get { return testMember.Method.Name; }
}
}
}

38
src/AddIns/Analysis/UnitTesting/Nodes/NamespaceUnitTestNode.cs

@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Specialized;
using System.Linq;
using System.Reflection;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.TreeView;
namespace ICSharpCode.UnitTesting
{
public class NamespaceUnitTestNode : UnitTestBaseNode
{
string name;
public string Namespace {
get { return name; }
}
public NamespaceUnitTestNode(string name)
{
if (string.IsNullOrEmpty(name))
throw new ArgumentException("name");
this.name = name;
}
public override object Text {
get { return name.Substring(name.LastIndexOf('.') + 1); }
}
}
}

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

@ -36,21 +36,37 @@ namespace ICSharpCode.UnitTesting @@ -36,21 +36,37 @@ namespace ICSharpCode.UnitTesting
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()));
if (c.Namespace == "<invalid>") continue;
string namespaceSuffix = GetNamespaceSuffix(c.Namespace, project.Project.RootNamespace);
if (string.IsNullOrEmpty(namespaceSuffix)) {
Children.OrderedInsert(new ClassUnitTestNode(c), (a, b) => string.CompareOrdinal(a.Text.ToString(), b.Text.ToString()));
} else {
var node = FindNamespace(namespaceSuffix);
if (node == null) {
node = new NamespaceUnitTestNode(namespaceSuffix);
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()));
}
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);
if (c.Namespace == "<invalid>") continue;
string namespaceSuffix = GetNamespaceSuffix(c.Namespace, project.Project.RootNamespace);
if (string.IsNullOrEmpty(namespaceSuffix)) {
Children.RemoveWhere(n => n is ClassUnitTestNode && ((ClassUnitTestNode)n).TestClass.FullName == c.FullName);
} else {
UnitTestBaseNode node = FindNamespace(namespaceSuffix);
if (node == null) continue;
node.Children.RemoveWhere(n => n is ClassUnitTestNode && ((ClassUnitTestNode)n).TestClass.FullName == c.FullName);
while (node.Children.Count == 0) {
var parent = (UnitTestBaseNode)node.Parent;
if (parent == null) break;
parent.Children.Remove(node);
node = parent;
}
}
}
break;
case NotifyCollectionChangedAction.Reset:
@ -59,46 +75,72 @@ namespace ICSharpCode.UnitTesting @@ -59,46 +75,72 @@ namespace ICSharpCode.UnitTesting
}
}
NamespaceUnitTestNode FindNamespace(string @namespace)
static string GetNamespaceSuffix(string @namespace, string rootNamespace)
{
foreach (var node in Children.OfType<NamespaceUnitTestNode>()) {
// TODO use language-specific StringComparer
if (string.Equals(node.Namespace, @namespace, StringComparison.Ordinal))
return node;
}
return null;
if (@namespace.StartsWith(rootNamespace + ".", StringComparison.Ordinal))
return @namespace.Substring(rootNamespace.Length + 1);
if (@namespace.Equals(rootNamespace, StringComparison.Ordinal))
return "";
return @namespace;
}
protected override void LoadChildren()
NamespaceUnitTestNode FindNamespace(string @namespace)
{
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);
var parent = FindNamespace(@namespace, this);
NamespaceUnitTestNode newNode;
string rootNamespace = "";
if (parent is NamespaceUnitTestNode) {
var namespaceNode = (NamespaceUnitTestNode)parent;
rootNamespace = namespaceNode.Namespace;
if (namespaceNode.Namespace.Equals(@namespace, StringComparison.OrdinalIgnoreCase))
return namespaceNode;
}
parent.Children.Add(CreateMissingNamespaceNodes(out newNode, @namespace, rootNamespace));
return newNode;
}
public override object Text {
get { return project.Project.Name; }
NamespaceUnitTestNode CreateMissingNamespaceNodes(out NamespaceUnitTestNode newNode, string @namespace, string rootNamespace)
{
newNode = new NamespaceUnitTestNode(@namespace);
NamespaceUnitTestNode result = newNode;
int dot = @namespace.LastIndexOf('.');
@namespace = dot > -1 ? @namespace.Substring(0, dot) : @namespace;
while (dot > -1 && !rootNamespace.Equals(@namespace, StringComparison.OrdinalIgnoreCase)) {
var node = result;
result = new NamespaceUnitTestNode(@namespace);
result.Children.Add(node);
dot = @namespace.LastIndexOf('.');
@namespace = dot > -1 ? @namespace.Substring(0, dot) : @namespace;
}
return result;
}
}
public class NamespaceUnitTestNode : UnitTestBaseNode
{
string name;
public string Namespace {
get { return name; }
UnitTestBaseNode FindNamespace(string @namespace, UnitTestBaseNode parent)
{
foreach (var node in parent.Children.OfType<NamespaceUnitTestNode>()) {
if (@namespace.StartsWith(node.Namespace + ".", StringComparison.OrdinalIgnoreCase) || @namespace.Equals(node.Namespace, StringComparison.OrdinalIgnoreCase))
return FindNamespace(@namespace, node);
}
return parent;
}
public NamespaceUnitTestNode(string name)
protected override void LoadChildren()
{
this.name = name;
Children.Clear();
foreach (var g in project.TestClasses.Select(c => new ClassUnitTestNode(c)).GroupBy(tc => tc.TestClass.Namespace)) {
UnitTestBaseNode node;
if (g.Key == "<invalid>") continue;
string namespaceName = GetNamespaceSuffix(g.Key, project.Project.RootNamespace);
if (string.IsNullOrEmpty(namespaceName))
node = this;
else
node = FindNamespace(namespaceName);
node.Children.AddRange(g);
}
}
public override object Text {
get { return string.IsNullOrEmpty(name) ? "<default>" : name; }
get { return project.Project.Name; }
}
}
}

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

@ -57,54 +57,4 @@ namespace ICSharpCode.UnitTesting @@ -57,54 +57,4 @@ namespace ICSharpCode.UnitTesting
get { return ResourceService.GetString("ICSharpCode.UnitTesting.AllTestsTreeNode.Text"); }
}
}
public static class Extensions
{
static readonly ITypeReference testAttribute = new GetClassTypeReference("NUnit.Framework", "TestAttribute", 0);
public static bool IsTestProject(this IProject project)
{
if (project == null)
throw new ArgumentNullException("project");
if (project.ProjectContent == null)
return false;
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);
}
}
}

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

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

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

@ -1,53 +0,0 @@ @@ -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 ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Represents a member that has the [Test] attribute associated with it.
/// </summary>
public class TestMemberTreeNode : TestTreeNode
{
TestMember testMember;
public TestMemberTreeNode(TestProject project, TestMember testMember)
: base(project, testMember.Name)
{
this.testMember = testMember;
testMember.ResultChanged += TestMemberResultChanged;
UpdateImageListIndex(testMember.Result);
}
/// <summary>
/// Gets the underlying IMember for this test member.
/// </summary>
public IMember Member {
get { return testMember.Member; }
}
/// <summary>
/// Removes the TestMember.ResultChanged event handler.
/// </summary>
public override void Dispose()
{
if (!IsDisposed) {
testMember.ResultChanged -= TestMemberResultChanged;
}
base.Dispose();
}
/// <summary>
/// Updates the node's icon after the test member result
/// has changed.
/// </summary>
void TestMemberResultChanged(object source, EventArgs e)
{
UpdateImageListIndex(testMember.Result);
}
}
}

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

@ -1,55 +0,0 @@ @@ -1,55 +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.Dom;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.UnitTesting
{
/// <summary>
/// Represents a method that has the [Test] attribute associated with it.
/// </summary>
public class TestMethodTreeNode : TestTreeNode
{
TestMethod testMethod;
public TestMethodTreeNode(TestProject project, TestMethod testMethod)
: base(project, testMethod.Name)
{
this.testMethod = testMethod;
testMethod.ResultChanged += TestMethodResultChanged;
UpdateImageListIndex(testMethod.Result);
}
/// <summary>
/// Gets the underlying IMethod for this test method.
/// </summary>
public IMember Method {
get {
return testMethod.Method;
}
}
/// <summary>
/// Removes the TestMethod.ResultChanged event handler.
/// </summary>
public override void Dispose()
{
if (!IsDisposed) {
testMethod.ResultChanged -= TestMethodResultChanged;
}
base.Dispose();
}
/// <summary>
/// Updates the node's icon after the test method result
/// has changed.
/// </summary>
void TestMethodResultChanged(object source, EventArgs e)
{
UpdateImageListIndex(testMethod.Result);
}
}
}

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

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

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

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

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

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

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

@ -21,6 +21,7 @@ namespace ICSharpCode.UnitTesting @@ -21,6 +21,7 @@ namespace ICSharpCode.UnitTesting
return TestResultType.Success;
}
}
public override object Icon {
get { return GetIcon(TestResultType); }
}

4
src/AddIns/Analysis/UnitTesting/UnitTesting.csproj

@ -71,6 +71,7 @@ @@ -71,6 +71,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Commands\AbstractRunTestCommand.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="Gui\EmptyUnitTestsPad.cs" />
<Compile Include="Gui\UnitTestingOptionsPanel.xaml.cs">
<DependentUpon>UnitTestingOptionsPanel.xaml</DependentUpon>
@ -103,9 +104,12 @@ @@ -103,9 +104,12 @@
<Compile Include="Interfaces\IUnitTestTaskService.cs" />
<Compile Include="Interfaces\IUnitTestWorkbench.cs" />
<Compile Include="Model\TestClass.cs" />
<Compile Include="Model\TestMember.cs" />
<Compile Include="Model\TestProject.cs" />
<Compile Include="MultipleProjectBuildable.cs" />
<Compile Include="Nodes\ClassUnitTestNode.cs" />
<Compile Include="Nodes\MemberUnitTestNode.cs" />
<Compile Include="Nodes\NamespaceUnitTestNode.cs" />
<Compile Include="Nodes\ProjectUnitTestNode.cs" />
<Compile Include="Nodes\RootUnitTestNode.cs" />
<Compile Include="Nodes\UnitTestBaseNode.cs" />

Loading…
Cancel
Save