diff --git a/src/AddIns/Analysis/UnitTesting/Frameworks/ITestFramework.cs b/src/AddIns/Analysis/UnitTesting/Frameworks/ITestFramework.cs index af59cc24a9..badce6aa83 100644 --- a/src/AddIns/Analysis/UnitTesting/Frameworks/ITestFramework.cs +++ b/src/AddIns/Analysis/UnitTesting/Frameworks/ITestFramework.cs @@ -19,16 +19,5 @@ namespace ICSharpCode.UnitTesting /// Creates a new test project based on the specified project. /// ITestProject CreateTestProject(ITestSolution parentSolution, IProject project); - - /* - bool IsTestMember(IMember member); - bool IsTestClass(ITypeDefinition testClass); - - IEnumerable GetTestMembersFor(ITypeDefinition typeDefinition); - - ITestRunner CreateTestRunner(); - ITestRunner CreateTestDebugger(); - - bool IsBuildNeededBeforeTestRun { get; }*/ } } diff --git a/src/AddIns/Analysis/UnitTesting/Model/ITest.cs b/src/AddIns/Analysis/UnitTesting/Model/ITest.cs index a86d28494c..dbffdac68d 100644 --- a/src/AddIns/Analysis/UnitTesting/Model/ITest.cs +++ b/src/AddIns/Analysis/UnitTesting/Model/ITest.cs @@ -16,6 +16,14 @@ namespace ICSharpCode.UnitTesting /// TestCollection NestedTests { get; } + /// + /// Gets whether this test allows expanding the list of nested tests. + /// If possible, this property should return the same value as NestedTests.Count. + /// However, when doing so is expensive (e.g. due to lazy initialization), this + /// property may return true even if there are no nested tests. + /// + bool CanExpandNestedTests { get; } + /// /// Gets the parent project that owns this test. /// diff --git a/src/AddIns/Analysis/UnitTesting/Model/ITestSolution.cs b/src/AddIns/Analysis/UnitTesting/Model/ITestSolution.cs index e5d867cee0..ae81bc6e1c 100644 --- a/src/AddIns/Analysis/UnitTesting/Model/ITestSolution.cs +++ b/src/AddIns/Analysis/UnitTesting/Model/ITestSolution.cs @@ -13,11 +13,6 @@ namespace ICSharpCode.UnitTesting /// public interface ITestSolution : ITest { -// /// -// /// Gets the list of all test projects. -// /// -// ObservableCollection TestableProjects { get; } - /// /// Gets the test project for the specified project. /// Returns null if the project is not a test project. diff --git a/src/AddIns/Analysis/UnitTesting/Model/TestBase.cs b/src/AddIns/Analysis/UnitTesting/Model/TestBase.cs index 7a2df43104..39f373c17f 100644 --- a/src/AddIns/Analysis/UnitTesting/Model/TestBase.cs +++ b/src/AddIns/Analysis/UnitTesting/Model/TestBase.cs @@ -86,18 +86,30 @@ namespace ICSharpCode.UnitTesting public TestCollection NestedTests { get { - if (nestedTests == null) { - nestedTests = InitializeNestedTests(); - OnNestedTestsInitialized(); - } + EnsureNestedTestsInitialized(); return nestedTests; } } + public virtual bool CanExpandNestedTests { + get { + EnsureNestedTestsInitialized(); + return nestedTests.Count != 0; + } + } + protected bool NestedTestsInitialized { get { return nestedTests != null; } } + protected void EnsureNestedTestsInitialized() + { + if (nestedTests == null) { + nestedTests = InitializeNestedTests(); + OnNestedTestsInitialized(); + } + } + protected virtual TestCollection InitializeNestedTests() { return new TestCollection(); @@ -130,7 +142,7 @@ namespace ICSharpCode.UnitTesting get { return false; } } - public void GoToDefinition() + public virtual void GoToDefinition() { throw new NotSupportedException(); } diff --git a/src/AddIns/Analysis/UnitTesting/Model/TestClass.cs b/src/AddIns/Analysis/UnitTesting/Model/TestClass.cs deleted file mode 100644 index c88afeb2cc..0000000000 --- a/src/AddIns/Analysis/UnitTesting/Model/TestClass.cs +++ /dev/null @@ -1,198 +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.Collections.ObjectModel; -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.Widgets; - -namespace ICSharpCode.UnitTesting -{ - /// - /// Represents a class that can be tested. - /// - public class TestClass - { - readonly ITestFramework testFramework; - readonly ObservableCollection testMembers; - readonly ObservableCollection nestedClasses; - IList parts; - - public TestClass(ITypeDefinition definition, ITestFramework testFramework) - { - if (definition == null) - throw new ArgumentNullException("definition"); - if (testFramework == null) - throw new ArgumentNullException("testFramework"); - this.testFramework = testFramework; - this.parts = new ObservableCollection(); - this.testMembers = new ObservableCollection(); - this.nestedClasses = new ObservableCollection(); - UpdateClass(definition); - } - - /// - /// Gets the underlying IUnresolvedTypeDefinitions for this test class. - /// - public IList Parts { - get { return parts; } - } - - public ObservableCollection Members { - get { return testMembers; } - } - - public ObservableCollection NestedClasses { - get { return nestedClasses; } - } - - /// - /// Gets the name of the class. - /// - public string Name { - get { return parts[0].Name; } - } - - /// - /// Gets the fully qualified name of the class. - /// - public string QualifiedName { - get { return parts[0].ReflectionName; } - } - - /// - /// Gets the namespace of this class. - /// - public string Namespace { - get { return parts[0].Namespace; } - } - - TestResultType testResult; - - public event EventHandler TestResultChanged; - - /// - /// Gets the test result for this class. - /// - public TestResultType TestResult { - get { return testResult; } - set { - if (testResult != value) { - testResult = value; - if (TestResultChanged != null) - TestResultChanged(this, EventArgs.Empty); - } - } - } - - static TestResultType GetTestResult(TestClass testClass) - { - if (testClass.nestedClasses.Count == 0 && testClass.testMembers.Count == 0) - return TestResultType.None; - if (testClass.nestedClasses.Any(c => c.TestResult == TestResultType.Failure) - || testClass.testMembers.Any(m => m.TestResult == TestResultType.Failure)) - return TestResultType.Failure; - if (testClass.nestedClasses.Any(c => c.TestResult == TestResultType.None) - || testClass.testMembers.Any(m => m.TestResult == TestResultType.None)) - return TestResultType.None; - if (testClass.nestedClasses.Any(c => c.TestResult == TestResultType.Ignored) - || testClass.testMembers.Any(m => m.TestResult == TestResultType.Ignored)) - return TestResultType.Ignored; - return TestResultType.Success; - } - - /// - /// Updates the test member with the specified test result. - /// - public void UpdateTestResult(TestResult testResult) - { - var member = testMembers.SingleOrDefault(m => m.Member.ReflectionName == testResult.Name); - member.TestResult = testResult.ResultType; - this.TestResult = GetTestResult(this); - } - - /// - /// Resets all the test results back to none. - /// - public void ResetTestResults() - { - foreach (var testClass in this.NestedClasses) { - testClass.ResetTestResults(); - } - foreach (var member in this.Members) { - member.ResetTestResults(); - } - } - - /// - /// Updates the members and class based on the new class - /// information that has been parsed. - /// - public void UpdateClass(ITypeDefinition typeDefinition) - { - this.parts = typeDefinition.Parts; - - testMembers.Clear(); - testMembers.AddRange(testFramework.GetTestMembersFor(typeDefinition)); - - - /*var oldParts = this.parts; - nestedClasses.UpdateTestClasses(testFrameworks, - - int i = 0; - while (i < parts.Count) { - var part = parts[i]; - if (!definition.Parts.Any(p => p.UnresolvedFile.FileName == part.UnresolvedFile.FileName && p.Region == part.Region)) - parts.RemoveAt(i); - else - i++; - } - - foreach (var part in definition.Parts) { - if (!parts.Any(p => p.UnresolvedFile.FileName == part.UnresolvedFile.FileName && p.Region == part.Region)) - parts.Add(part); - } - testMembers.RemoveWhere(m => !definition.Methods.Any(dm => dm.ReflectionName == m.Member.ReflectionName && testFrameworks.IsTestMethod(dm))); - testMembers.AddRange( - definition.Methods.Where(m => testFrameworks.IsTestMethod(m, definition.Compilation) - && !testMembers.Any(dm => dm.Member.ReflectionName == m.ReflectionName)) - .Select(m => new TestMember(Project, (IUnresolvedMethod)m.UnresolvedMember, testFrameworks.IsTestCase(m, definition.Compilation)))); - - var context = new SimpleTypeResolveContext(definition); - nestedClasses.UpdateTestClasses(testFrameworks, nestedClasses.Select(tc => new DefaultResolvedTypeDefinition(context, tc.Parts.ToArray())).ToList(), definition.NestedTypes.Where(nt => testFrameworks.IsTestClass(nt, definition.Compilation)).ToList(), this, Project); - */ - } - - /// - /// Gets the first dotted part of the namespace. - /// - static string GetRootNamespace(string ns) - { - int index = ns.IndexOf('.'); - if (index > 0) { - return ns.Substring(0, index); - } - return ns; - } - - public ITypeDefinition Resolve(TestProject project) - { - if (project == null) - return null; - ICompilation compilation = SD.ParserService.GetCompilation(project.Project); - return parts[0].Resolve(new SimpleTypeResolveContext(compilation.MainAssembly)).GetDefinition(); - } - - public override string ToString() - { - return string.Format("[TestClass TestResult={0}, Name={1}]", testResult, this.QualifiedName); - } - } -} \ No newline at end of file diff --git a/src/AddIns/Analysis/UnitTesting/Model/TestMember.cs b/src/AddIns/Analysis/UnitTesting/Model/TestMember.cs deleted file mode 100644 index 01f3f44975..0000000000 --- a/src/AddIns/Analysis/UnitTesting/Model/TestMember.cs +++ /dev/null @@ -1,67 +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.NRefactory.TypeSystem; -using ICSharpCode.NRefactory.TypeSystem.Implementation; -using ICSharpCode.SharpDevelop; -using ICSharpCode.SharpDevelop.Widgets; - -namespace ICSharpCode.UnitTesting -{ - /// - /// Represents a member that can be tested. - /// - public class TestMember - { - readonly IUnresolvedMember member; - - public IUnresolvedMember Member { - get { return member; } - } - - public TestMember(IUnresolvedMember member) - { - if (member == null) - throw new ArgumentNullException("member"); - this.member = member; - } - - public string Name { - get { return member.Name; } - } - - public event EventHandler TestResultChanged; - - TestResultType testResult; - - public TestResultType TestResult { - get { return testResult; } - set { - if (testResult != value) { - testResult = value; - if (TestResultChanged != null) - TestResultChanged(this, EventArgs.Empty); - } - } - } - - public virtual void ResetTestResults() - { - testResult = TestResultType.None; - } - - public IMember Resolve(TestProject project) - { - if (project == null) - return null; - ICompilation compilation = SD.ParserService.GetCompilation(project.Project); - return member.Resolve(new SimpleTypeResolveContext(compilation.MainAssembly)); - } - - public override string ToString() - { - return string.Format("[TestMember Method={0}, TestResult={1}]", member, testResult); - } - } -} diff --git a/src/AddIns/Analysis/UnitTesting/Model/TestProject.cs b/src/AddIns/Analysis/UnitTesting/Model/TestProject.cs deleted file mode 100644 index 446ce4011c..0000000000 --- a/src/AddIns/Analysis/UnitTesting/Model/TestProject.cs +++ /dev/null @@ -1,196 +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.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; -using ICSharpCode.SharpDevelop.Widgets; - -namespace ICSharpCode.UnitTesting -{ - /// - /// Represents a project that has a reference to a unit testing - /// framework assembly. Currently only NUnit is supported. - /// - public class TestProject : ViewModelBase - { - readonly IProject project; - readonly ITestFramework testFramework; - readonly ObservableCollection testClasses; - - public TestProject(IProject project, ITestFramework testFramework) - { - this.testFramework = testFramework; - this.project = project; - var compilation = SD.ParserService.GetCompilation(project); - testClasses = new ObservableCollection( - from td in compilation.MainAssembly.TopLevelTypeDefinitions - where this.testFramework.IsTestClass(td) - select new TestClass(td, this.testFramework) - ); - } - - HashSet dirtyTypeDefinitions = new HashSet(); - - public void NotifyParseInformationChanged(IUnresolvedFile oldUnresolvedFile, IUnresolvedFile newUnresolvedFile) - { - AddToDirtyList(oldUnresolvedFile); - AddToDirtyList(newUnresolvedFile); - ProcessUpdates(); - } - - void AddToDirtyList(IUnresolvedFile unresolvedFile) - { - if (unresolvedFile != null) { - foreach (var td in unresolvedFile.TopLevelTypeDefinitions) { - dirtyTypeDefinitions.Add(new FullNameAndTypeParameterCount(td.Namespace, td.Name, td.TypeParameters.Count)); - } - } - } - - void ProcessUpdates() - { - var compilation = SD.ParserService.GetCompilation(project); - var context = new SimpleTypeResolveContext(compilation.MainAssembly); - - var testClassNameToIndex = new Dictionary(); - for (int i = 0; i < testClasses.Count; i++) { - var primaryPart = testClasses[i].Parts[0]; - testClassNameToIndex[new FullNameAndTypeParameterCount(primaryPart.Namespace, primaryPart.Name, primaryPart.TypeParameters.Count)] = i; - } - - List testClassesToRemove = new List(); - foreach (var dirtyTypeDef in dirtyTypeDefinitions) { - ITypeDefinition typeDef = compilation.MainAssembly.GetTypeDefinition(dirtyTypeDef.Namespace, dirtyTypeDef.Name, dirtyTypeDef.TypeParameterCount); - int pos; - if (testClassNameToIndex.TryGetValue(dirtyTypeDef, out pos)) { - if (typeDef == null) { - // Test class was removed completely (no parts left) - - // Removing the class messes up the indices stored in the dictionary, - // so we'll just remember that we need to remove the class - testClassesToRemove.Add(pos); - } else { - // Test class was modified - // Check if it's still a test class: - if (testFramework.IsTestClass(typeDef)) - testClasses[pos].UpdateClass(typeDef); - else - testClassesToRemove.Add(pos); - } - } else if (typeDef != null && testFramework.IsTestClass(typeDef)) { - // Test class was added - testClasses.Add(new TestClass(typeDef, testFramework)); - } - } - dirtyTypeDefinitions.Clear(); - // Now remove the outdated test classes - testClassesToRemove.Sort(); - foreach (int index in testClassesToRemove) { - testClasses.RemoveAt(index); - } - } - - public ITestFramework TestFramework { - get { return testFramework; } - } - - public IProject Project { - get { return project; } - } - - public ObservableCollection TestClasses { - get { return testClasses; } - } - - public TestMember GetTestMember(IMember member) - { - if (member != null) - return GetTestMember(member.ReflectionName); - else - return null; - } - - public TestMember GetTestMember(string reflectionName) - { - return TreeTraversal.PostOrder(testClasses, c => c.NestedClasses) - .SelectMany(c => c.Members) - .FirstOrDefault(m => reflectionName.Equals(m.Member.ReflectionName, StringComparison.Ordinal)); - } - - public TestClass GetTestClass(ITypeDefinition typeDefinition) - { - if (typeDefinition != null) - return GetTestClass(typeDefinition.ReflectionName); - else - return null; - } - - public TestClass GetTestClass(string reflectionName) - { - int pos = reflectionName.LastIndexOf('+'); - if (pos < 0) { - // top-level class - foreach (var tc in testClasses) { - if (tc.QualifiedName == reflectionName) - return tc; - } - } else { - // nested class - TestClass declaringClass = GetTestClass(reflectionName.Substring(0, pos)); - if (declaringClass != null) { - return declaringClass.NestedClasses.FirstOrDefault(t => t.QualifiedName == reflectionName); - } - } - return null; - } - - public void UpdateTestResult(TestResult result) - { - TestClass testClass = GetTestClass(result.Name); - if (testClass != null) { - testClass.UpdateTestResult(result); - TestResult = GetTestResult(testClasses); - } - } - - public void ResetTestResults() - { - foreach (var testClass in testClasses) - testClass.ResetTestResults(); - TestResult = TestResultType.None; - } - - TestResultType testResult; - - /// - /// Gets the test result for this project. - /// - public TestResultType TestResult { - get { return testResult; } - set { SetAndNotifyPropertyChanged(ref testResult, value); } - } - - static TestResultType GetTestResult(IList testClasses) - { - if (testClasses.Count == 0) - return TestResultType.None; - if (testClasses.Any(c => c.TestResult == TestResultType.Failure)) - return TestResultType.Failure; - if (testClasses.Any(c => c.TestResult == TestResultType.None)) - return TestResultType.None; - if (testClasses.Any(c => c.TestResult == TestResultType.Ignored)) - return TestResultType.Ignored; - return TestResultType.Success; - } - } -} diff --git a/src/AddIns/Analysis/UnitTesting/Model/TestProjectBase.cs b/src/AddIns/Analysis/UnitTesting/Model/TestProjectBase.cs index e86404b281..3065bac016 100644 --- a/src/AddIns/Analysis/UnitTesting/Model/TestProjectBase.cs +++ b/src/AddIns/Analysis/UnitTesting/Model/TestProjectBase.cs @@ -3,8 +3,12 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; using System.Threading.Tasks; using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Project; @@ -12,10 +16,14 @@ namespace ICSharpCode.UnitTesting { /// /// Base class for implementations. + /// + /// This implementation will show a tree of namespaces, with each namespace + /// containing a list of test fixtures (ITests created from type definitions). /// public abstract class TestProjectBase : TestBase, ITestProject { IProject project; + Dictionary topLevelTestClasses = new Dictionary(); public TestProjectBase(IProject project) { @@ -25,6 +33,14 @@ namespace ICSharpCode.UnitTesting BindResultToCompositeResultOfNestedTests(); } + public abstract Task RunTestsAsync(IEnumerable tests, TestExecutionOptions options, IProgressMonitor progressMonitor); + public abstract ITest GetTestForEntity(IEntity entity); + + // Test class management methods + public abstract bool IsTestClass(ITypeDefinition typeDefinition); + public abstract ITest CreateTestClass(ITypeDefinition typeDefinition); + public abstract void UpdateTestClass(ITest test, ITypeDefinition typeDefinition); + public IProject Project { get { return project; } } @@ -37,22 +53,177 @@ namespace ICSharpCode.UnitTesting get { return project.Name; } } - public virtual ITest GetTestForEntity(IEntity entity) - { - return null; - } - public virtual IBuildable GetBuildableForTesting() { return project; } + #region NotifyParseInformationChanged public void NotifyParseInformationChanged(IUnresolvedFile oldUnresolvedFile, IUnresolvedFile newUnresolvedFile) { + // We use delay-loading: the nested tests of a project are + // initializedhmm if (!NestedTestsInitialized) return; + var dirtyTypeDefinitions = new HashSet(); + AddToDirtyList(oldUnresolvedFile, dirtyTypeDefinitions); + AddToDirtyList(newUnresolvedFile, dirtyTypeDefinitions); + ProcessUpdates(dirtyTypeDefinitions); } - public abstract Task RunTestsAsync(IEnumerable tests, TestExecutionOptions options, IProgressMonitor progressMonitor); + public override bool CanExpandNestedTests { + get { return true; } + } + + protected override void OnNestedTestsInitialized() + { + var compilation = SD.ParserService.GetCompilation(project); + foreach (var typeDef in compilation.MainAssembly.TopLevelTypeDefinitions) { + UpdateType(new FullNameAndTypeParameterCount(typeDef.Namespace, typeDef.Name, typeDef.TypeParameterCount), typeDef); + } + base.OnNestedTestsInitialized(); + } + + void AddToDirtyList(IUnresolvedFile unresolvedFile, HashSet dirtyTypeDefinitions) + { + if (unresolvedFile != null) { + foreach (var td in unresolvedFile.TopLevelTypeDefinitions) { + dirtyTypeDefinitions.Add(new FullNameAndTypeParameterCount(td.Namespace, td.Name, td.TypeParameters.Count)); + } + } + } + + void ProcessUpdates(HashSet dirtyTypeDefinitions) + { + var compilation = SD.ParserService.GetCompilation(project); + var context = new SimpleTypeResolveContext(compilation.MainAssembly); + + foreach (var dirtyTypeDef in dirtyTypeDefinitions) { + ITypeDefinition typeDef = compilation.MainAssembly.GetTypeDefinition(dirtyTypeDef.Namespace, dirtyTypeDef.Name, dirtyTypeDef.TypeParameterCount); + UpdateType(dirtyTypeDef, typeDef); + } + } + + /// + /// Adds/Updates/Removes the test class for the type definition. + /// + void UpdateType(FullNameAndTypeParameterCount dirtyTypeDef, ITypeDefinition typeDef) + { + ITest test; + if (topLevelTestClasses.TryGetValue(dirtyTypeDef, out test)) { + if (typeDef == null) { + // Test class was removed completely (no parts left) + RemoveTestClass(dirtyTypeDef, test); + } else { + // Test class was modified + // Check if it's still a test class: + if (IsTestClass(typeDef)) + UpdateTestClass(test, typeDef); + else + RemoveTestClass(dirtyTypeDef, test); + } + } else if (typeDef != null) { + // Test class was added + var testClass = CreateTestClass(typeDef); + if (testClass != null) + AddTestClass(dirtyTypeDef, testClass); + } + } + #endregion + + #region Namespace Management + protected ITest GetTestClass(FullNameAndTypeParameterCount fullName) + { + EnsureNestedTestsInitialized(); + return topLevelTestClasses.GetOrDefault(fullName); + } + + void AddTestClass(FullNameAndTypeParameterCount fullName, ITest test) + { + topLevelTestClasses.Add(fullName, test); + ITest testNamespace = FindOrCreateNamespace(this, project.RootNamespace, fullName.Namespace); + testNamespace.NestedTests.Add(test); + } + + void RemoveTestClass(FullNameAndTypeParameterCount fullName, ITest test) + { + topLevelTestClasses.Remove(fullName); + ITest testNamespace = FindNamespace(this, project.RootNamespace, fullName.Namespace); + if (testNamespace != null) { + testNamespace.NestedTests.Remove(test); + if (testNamespace.NestedTests.Count == 0) { + // Remove the namespace + RemoveTestNamespace(this, project.RootNamespace, fullName.Namespace); + } + } + } + + ITest FindOrCreateNamespace(ITest parent, string parentNamespace, string @namespace) + { + if (parentNamespace == @namespace) + return parent; + foreach (var node in parent.NestedTests.OfType()) { + if (@namespace == node.NamespaceName) + return node; + if (@namespace.StartsWith(node.NamespaceName + ".", StringComparison.Ordinal)) { + return FindOrCreateNamespace(node, node.NamespaceName, @namespace); + } + } + // Create missing namespace node: + + // Figure out which part of the namespace we can remove due to the parent namespace: + int startPos = 0; + if (@namespace.StartsWith(parentNamespace + ".", StringComparison.Ordinal)) { + startPos = parentNamespace.Length + 1; + } + // Get the next dot + int dotPos = @namespace.IndexOf('.', startPos); + if (dotPos < 0) { + var newNode = new TestNamespace(this, @namespace); + parent.NestedTests.Add(newNode); + return newNode; + } else { + var newNode = new TestNamespace(this, @namespace.Substring(0, dotPos)); + parent.NestedTests.Add(newNode); + return FindOrCreateNamespace(newNode, newNode.NamespaceName, @namespace); + } + } + + static ITest FindNamespace(ITest parent, string parentNamespace, string @namespace) + { + if (parentNamespace == @namespace) + return parent; + foreach (var node in parent.NestedTests.OfType()) { + if (@namespace == node.NamespaceName) + return node; + if (@namespace.StartsWith(node.NamespaceName + ".", StringComparison.Ordinal)) { + return FindNamespace(node, node.NamespaceName, @namespace); + } + } + return null; + } + + /// + /// Removes the target namespace and all parent namespaces that are empty after the removal. + /// + static void RemoveTestNamespace(ITest parent, string parentNamespace, string @namespace) + { + if (parentNamespace == @namespace) + return; + foreach (var node in parent.NestedTests.OfType()) { + if (@namespace == node.NamespaceName) { + parent.NestedTests.Remove(node); + return; + } + if (@namespace.StartsWith(node.NamespaceName + ".", StringComparison.Ordinal)) { + RemoveTestNamespace(node, node.NamespaceName, @namespace); + if (node.NestedTests.Count == 0) { + parent.NestedTests.Remove(node); + } + return; + } + } + } + #endregion } } diff --git a/src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestClass.cs b/src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestClass.cs new file mode 100644 index 0000000000..b39dd37534 --- /dev/null +++ b/src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestClass.cs @@ -0,0 +1,83 @@ +// 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.NRefactory.TypeSystem; +using ICSharpCode.SharpDevelop; + +namespace ICSharpCode.UnitTesting +{ + /// + /// NUnit test fixture. + /// + public class NUnitTestClass : TestBase + { + ITestProject parentProject; + IUnresolvedTypeDefinition primaryPart; + + public NUnitTestClass(ITestProject parentProject, ITypeDefinition typeDefinition) + { + this.parentProject = parentProject; + UpdateTestClass(typeDefinition); + BindResultToCompositeResultOfNestedTests(); + } + + public override ITestProject ParentProject { + get { return parentProject; } + } + + public override string DisplayName { + get { return primaryPart.Name; } + } + + public string ReflectionName { + get { return primaryPart.ReflectionName; } + } + + public override bool SupportsGoToDefinition { + get { return true; } + } + + ITypeDefinition Resolve() + { + ICompilation compilation = SD.ParserService.GetCompilation(parentProject.Project); + IType type = primaryPart.Resolve(new SimpleTypeResolveContext(compilation.MainAssembly)); + return type.GetDefinition(); + } + + public override void GoToDefinition() + { + ITypeDefinition typeDefinition = Resolve(); + if (typeDefinition != null) + NavigationService.NavigateTo(typeDefinition); + } + + public void UpdateTestClass(ITypeDefinition typeDefinition) + { + primaryPart = typeDefinition.Parts[0]; + if (this.NestedTestsInitialized) { + this.NestedTests.Clear(); + this.NestedTests.AddRange(from nt in typeDefinition.NestedTypes + where NUnitTestFramework.IsTestClass(nt) + select new NUnitTestClass(parentProject, nt)); + this.NestedTests.AddRange(from m in typeDefinition.Methods + where NUnitTestFramework.IsTestMember(m) + select new NUnitTestMethod(parentProject, (IUnresolvedMethod)m.UnresolvedMember)); + } + } + + public override bool CanExpandNestedTests { + get { return true; } + } + + protected override void OnNestedTestsInitialized() + { + ITypeDefinition typeDefinition = Resolve(); + if (typeDefinition != null) { + UpdateTestClass(typeDefinition); + } + base.OnNestedTestsInitialized(); + } + } +} diff --git a/src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestFramework.cs b/src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestFramework.cs index a556cbc641..83cbd820d1 100644 --- a/src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestFramework.cs +++ b/src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestFramework.cs @@ -14,9 +14,9 @@ namespace ICSharpCode.UnitTesting { public class NUnitTestFramework : ITestFramework { - readonly ITypeReference testAttributeRef = new GetClassTypeReference("NUnit.Framework", "TestAttribute", 0); - readonly ITypeReference testCaseAttributeRef = new GetClassTypeReference("NUnit.Framework", "TestCaseAttribute", 0); - readonly ITypeReference testFixtureAttributeRef = new GetClassTypeReference("NUnit.Framework", "TestFixtureAttribute", 0); + 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); /// /// Determines whether the project is a test project. A project @@ -35,19 +35,7 @@ namespace ICSharpCode.UnitTesting return new NUnitTestProject(project); } - /* - public ITestRunner CreateTestRunner() - { - return new NUnitTestRunner(); - } - - public ITestRunner CreateTestDebugger() - { - return new NUnitTestDebugger(); - } - - - public bool IsTestMember(IMember member) + public static bool IsTestMember(IMember member) { if (member == null || member.EntityType != EntityType.Method) return false; @@ -60,21 +48,18 @@ namespace ICSharpCode.UnitTesting return false; } - public bool IsTestClass(ITypeDefinition type) + public static bool IsTestClass(ITypeDefinition type) { - if (type == null || type.IsAbstract) + if (type == null) + return false; + if (type.NestedTypes.Any(IsTestClass)) + return true; + if (type.IsAbstract) return false; var testFixtureAttribute = testFixtureAttributeRef.Resolve(type.Compilation); if (type.Attributes.Any(attr => attr.AttributeType.Equals(testFixtureAttributeRef))) return true; - else - return type.Methods.Any(IsTestMember); + return type.Methods.Any(IsTestMember); } - - public IEnumerable GetTestMembersFor(ITypeDefinition typeDefinition) - { - var project = typeDefinition.ParentAssembly.GetProject(); - return typeDefinition.Methods.Where(IsTestMember).Select(m => new TestMember(m.UnresolvedMember)); - }*/ } } diff --git a/src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestMethod.cs b/src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestMethod.cs new file mode 100644 index 0000000000..7909fcae6f --- /dev/null +++ b/src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestMethod.cs @@ -0,0 +1,35 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.UnitTesting +{ + /// + /// NUnit test method. + /// + public class NUnitTestMethod : TestBase + { + ITestProject parentProject; + IUnresolvedMethod method; + + public NUnitTestMethod(ITestProject parentProject, IUnresolvedMethod method) + { + if (parentProject == null) + throw new ArgumentNullException("parentProject"); + if (method == null) + throw new ArgumentNullException("method"); + this.parentProject = parentProject; + this.method = method; + } + + public override ITestProject ParentProject { + get { return parentProject; } + } + + public override string DisplayName { + get { return method.Name; } + } + } +} diff --git a/src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestProject.cs b/src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestProject.cs index 818eb2b65b..c3a9af304d 100644 --- a/src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestProject.cs +++ b/src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestProject.cs @@ -4,6 +4,8 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Project; @@ -22,5 +24,37 @@ namespace ICSharpCode.UnitTesting { throw new NotImplementedException(); } + + public override void UpdateTestClass(ITest test, ITypeDefinition typeDefinition) + { + ((NUnitTestClass)test).UpdateTestClass(typeDefinition); + } + + public override bool IsTestClass(ITypeDefinition typeDefinition) + { + return NUnitTestFramework.IsTestClass(typeDefinition); + } + + public override ITest GetTestForEntity(IEntity entity) + { + if (entity.DeclaringTypeDefinition != null) { + ITest testClass = GetTestForEntity(entity.DeclaringTypeDefinition); + throw new NotImplementedException(); + } else if (entity is ITypeDefinition) { + // top-level type definition + ITypeDefinition typeDef = (ITypeDefinition)entity; + return GetTestClass(new FullNameAndTypeParameterCount(typeDef.Namespace, typeDef.Name, typeDef.TypeParameterCount)); + } else { + return null; + } + } + + public override ITest CreateTestClass(ITypeDefinition typeDefinition) + { + if (NUnitTestFramework.IsTestClass(typeDefinition)) + return new NUnitTestClass(this, typeDefinition); + else + return null; + } } } diff --git a/src/AddIns/Analysis/UnitTesting/TreeView/ClassUnitTestNode.cs b/src/AddIns/Analysis/UnitTesting/TreeView/ClassUnitTestNode.cs deleted file mode 100644 index 68df61d772..0000000000 --- a/src/AddIns/Analysis/UnitTesting/TreeView/ClassUnitTestNode.cs +++ /dev/null @@ -1,100 +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.Specialized; -using System.ComponentModel; - -using ICSharpCode.SharpDevelop; - -namespace ICSharpCode.UnitTesting -{ - /// - /// Represents a TestClass in the tree view. - /// - public class ClassUnitTestNode : UnitTestNode - { - TestClass testClass; - - public TestClass TestClass { - get { return testClass; } - } - - public ClassUnitTestNode(TestClass testClass) - { - this.testClass = testClass; - this.testClass.TestResultChanged += delegate { - RaisePropertyChanged("Icon"); - RaisePropertyChanged("ExpandedIcon"); - var parentNode = Parent; - while (parentNode is NamespaceUnitTestNode) { - parentNode.RaisePropertyChanged("Icon"); - parentNode.RaisePropertyChanged("ExpandedIcon"); - parentNode = parentNode.Parent; - } - }; - testClass.Members.CollectionChanged += TestMembersCollectionChanged; - testClass.NestedClasses.CollectionChanged += NestedClassesCollectionChanged; - LazyLoading = true; - } - - void NestedClassesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - switch (e.Action) { - case NotifyCollectionChangedAction.Add: - foreach (TestClass c in e.NewItems) { - Children.OrderedInsert(new ClassUnitTestNode(c), NodeTextComparer); - } - break; - case NotifyCollectionChangedAction.Remove: - foreach (TestClass c in e.OldItems) { - Children.RemoveAll(n => n is ClassUnitTestNode && ((ClassUnitTestNode)n).TestClass == c); - } - break; - case NotifyCollectionChangedAction.Reset: - LoadChildren(); - break; - default: - throw new NotSupportedException(); - } - } - - void TestMembersCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - switch (e.Action) { - case NotifyCollectionChangedAction.Add: - foreach (TestMember m in e.NewItems) { - Children.OrderedInsert(new MemberUnitTestNode(m), NodeTextComparer); - } - break; - case NotifyCollectionChangedAction.Remove: - foreach (TestMember m in e.OldItems) { - Children.RemoveAll(n => n is MemberUnitTestNode && ((MemberUnitTestNode)n).TestMember == m); - } - break; - case NotifyCollectionChangedAction.Reset: - LoadChildren(); - break; - default: - throw new NotSupportedException(); - } - } - - protected override void LoadChildren() - { - Children.Clear(); - foreach (TestClass c in testClass.NestedClasses) - Children.Add(new ClassUnitTestNode(c)); - foreach (TestMember m in testClass.Members) - Children.Add(new MemberUnitTestNode(m)); - } - - public override object Text { - get { return testClass.Name; } - } - - internal override TestResultType TestResultType { - get { return testClass.TestResult; } - } - } -} diff --git a/src/AddIns/Analysis/UnitTesting/TreeView/MemberUnitTestNode.cs b/src/AddIns/Analysis/UnitTesting/TreeView/MemberUnitTestNode.cs deleted file mode 100644 index 80b1aadeb1..0000000000 --- a/src/AddIns/Analysis/UnitTesting/TreeView/MemberUnitTestNode.cs +++ /dev/null @@ -1,44 +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.Specialized; -using System.ComponentModel; -using ICSharpCode.Core; -using ICSharpCode.SharpDevelop; -using ICSharpCode.TreeView; - -namespace ICSharpCode.UnitTesting -{ - public class MemberUnitTestNode : UnitTestNode - { - TestMember testMember; - - public TestMember TestMember { - get { return testMember; } - } - - public MemberUnitTestNode(TestMember testMember) - { - this.testMember = testMember; - this.testMember.TestResultChanged += delegate { - RaisePropertyChanged("Icon"); - RaisePropertyChanged("ExpandedIcon"); - }; - } - - internal override TestResultType TestResultType { - get { return this.testMember.TestResult; } - } - - public override object Text { - get { return testMember.Member.Name; } - } - - public override void ActivateItem(System.Windows.RoutedEventArgs e) - { - var region = testMember.Member.Region; - SD.FileService.JumpToFilePosition(new FileName(region.FileName), region.BeginLine, region.BeginColumn); - } - } -} diff --git a/src/AddIns/Analysis/UnitTesting/TreeView/NamespaceUnitTestNode.cs b/src/AddIns/Analysis/UnitTesting/TreeView/NamespaceUnitTestNode.cs deleted file mode 100644 index cf8e846981..0000000000 --- a/src/AddIns/Analysis/UnitTesting/TreeView/NamespaceUnitTestNode.cs +++ /dev/null @@ -1,63 +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.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 : UnitTestNode - { - readonly string shortName; - readonly string namespaceName; - - /// - /// Gets the namespace suffix (namespace portion after the root namespace) - /// - public string ShortName { - get { return shortName; } - } - - /// - /// Gets the full namespace - /// - public string NamespaceName { - get { return namespaceName; } - } - - public NamespaceUnitTestNode(string namespaceName) - { - if (namespaceName == null) - throw new ArgumentNullException("namespaceName"); - this.namespaceName = namespaceName; - this.shortName = namespaceName.Substring(namespaceName.IndexOf('.') + 1); - } - - public override object Text { - get { return shortName; } - } - - internal override TestResultType TestResultType { - get { - if (Children.Count == 0) return TestResultType.None; - if (Children.OfType().Any(node => node.TestResultType == TestResultType.Failure)) - return TestResultType.Failure; - if (Children.OfType().Any(node => node.TestResultType == TestResultType.None)) - return TestResultType.None; - if (Children.OfType().Any(node => node.TestResultType == TestResultType.Ignored)) - return TestResultType.Ignored; - return TestResultType.Success; - } - } - } -} diff --git a/src/AddIns/Analysis/UnitTesting/TreeView/ProjectUnitTestNode.cs b/src/AddIns/Analysis/UnitTesting/TreeView/ProjectUnitTestNode.cs deleted file mode 100644 index c5b073843f..0000000000 --- a/src/AddIns/Analysis/UnitTesting/TreeView/ProjectUnitTestNode.cs +++ /dev/null @@ -1,136 +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.Specialized; -using System.ComponentModel; -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 ProjectUnitTestNode : UnitTestNode - { - TestProject project; - - public TestProject Project { - get { return project; } - } - - public ProjectUnitTestNode(TestProject project) - { - this.project = project; - project.TestClasses.CollectionChanged += TestClassesCollectionChanged; - project.PropertyChanged += ProjectPropertyChanged; - LazyLoading = true; - } - - void ProjectPropertyChanged(object sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == "TestResult") { - RaisePropertyChanged("Icon"); - RaisePropertyChanged("ExpandedIcon"); - } - } - - void TestClassesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - switch (e.Action) { - case NotifyCollectionChangedAction.Add: - foreach (TestClass c in e.NewItems) { - if (c.Namespace == "") continue; - UnitTestNode node = FindOrCreateNamespace(this, project.Project.RootNamespace, c.Namespace); - node.Children.OrderedInsert(new ClassUnitTestNode(c), NodeTextComparer); - } - break; - case NotifyCollectionChangedAction.Remove: - foreach (TestClass c in e.OldItems) { - if (c.Namespace == "") continue; - SharpTreeNode node = FindNamespace(this, project.Project.RootNamespace, c.Namespace); - node.Children.RemoveAll(n => n is ClassUnitTestNode && ((ClassUnitTestNode)n).TestClass == c); - while (node is NamespaceUnitTestNode && node.Children.Count == 0) { - var parent = node.Parent; - if (parent == null) break; - parent.Children.Remove(node); - node = parent; - } - } - break; - case NotifyCollectionChangedAction.Reset: - LoadChildren(); - break; - } - } - - static UnitTestNode FindOrCreateNamespace(UnitTestNode parent, string parentNamespace, string @namespace) - { - if (parentNamespace == @namespace) - return parent; - foreach (var node in parent.Children.OfType()) { - if (@namespace == node.NamespaceName) - return node; - if (@namespace.StartsWith(node.NamespaceName + ".", StringComparison.Ordinal)) { - return FindOrCreateNamespace(node, node.NamespaceName, @namespace); - } - } - // Create missing namespace node: - - // Figure out which part of the namespace we can remove due to the parent namespace: - int startPos = 0; - if (@namespace.StartsWith(parentNamespace + ".", StringComparison.Ordinal)) { - startPos = parentNamespace.Length + 1; - } - // Get the next dot - int dotPos = @namespace.IndexOf('.', startPos); - if (dotPos < 0) { - var newNode = new NamespaceUnitTestNode(@namespace); - parent.Children.OrderedInsert(newNode, NodeTextComparer); - return newNode; - } else { - var newNode = new NamespaceUnitTestNode(@namespace.Substring(0, dotPos)); - parent.Children.OrderedInsert(newNode, NodeTextComparer); - return FindOrCreateNamespace(newNode, newNode.NamespaceName, @namespace); - } - } - - static UnitTestNode FindNamespace(UnitTestNode parent, string parentNamespace, string @namespace) - { - if (parentNamespace == @namespace) - return parent; - foreach (var node in parent.Children.OfType()) { - if (@namespace == node.NamespaceName) - return node; - if (@namespace.StartsWith(node.NamespaceName + ".", StringComparison.Ordinal)) { - return FindNamespace(node, node.NamespaceName, @namespace); - } - } - return null; - } - - protected override void LoadChildren() - { - Children.Clear(); - foreach (var g in project.TestClasses.Select(c => new ClassUnitTestNode(c)).GroupBy(tc => tc.TestClass.Namespace)) { - if (g.Key == "") continue; - UnitTestNode node = FindOrCreateNamespace(this, project.Project.RootNamespace, g.Key); - node.Children.AddRange(g.OrderBy(NodeTextComparer)); - } - } - - public override object Text { - get { return project.Project.Name; } - } - - internal override TestResultType TestResultType { - get { return project.TestResult; } - } - } -} diff --git a/src/AddIns/Analysis/UnitTesting/TreeView/RootUnitTestNode.cs b/src/AddIns/Analysis/UnitTesting/TreeView/RootUnitTestNode.cs deleted file mode 100644 index b300d52c2a..0000000000 --- a/src/AddIns/Analysis/UnitTesting/TreeView/RootUnitTestNode.cs +++ /dev/null @@ -1,76 +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.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 -{ - /// - /// Description of RootUnitTestNode. - /// - public class RootUnitTestNode : UnitTestNode - { - readonly TestSolution testSolution; - - public RootUnitTestNode(TestSolution testSolution) - { - this.testSolution = testSolution; - testSolution.TestableProjects.CollectionChanged += TestSolution_TestableProjects_CollectionChanged; - LazyLoading = true; - } - - void TestSolution_TestableProjects_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - switch (e.Action) { - case NotifyCollectionChangedAction.Add: - Children.AddRange(e.NewItems.OfType().Select(p => new ProjectUnitTestNode(p))); - break; - case NotifyCollectionChangedAction.Remove: - Children.RemoveAll(node => node is ProjectUnitTestNode && e.OldItems.OfType().Any(p => p.Project == ((ProjectUnitTestNode)node).Project)); - break; - case NotifyCollectionChangedAction.Reset: - LoadChildren(); - break; - } - } - - protected override void LoadChildren() - { - Children.Clear(); - Children.AddRange(testSolution.TestableProjects.Select(p => new ProjectUnitTestNode(p))); - } - - public override object Text { - get { return ResourceService.GetString("ICSharpCode.UnitTesting.AllTestsTreeNode.Text"); } - } - - internal override TestResultType TestResultType { - get { - if (Children.Count == 0) return TestResultType.None; - if (Children.OfType().Any(node => node.TestResultType == TestResultType.Failure)) - return TestResultType.Failure; - if (Children.OfType().Any(node => node.TestResultType == TestResultType.None)) - return TestResultType.None; - if (Children.OfType().Any(node => node.TestResultType == TestResultType.Ignored)) - return TestResultType.Ignored; - return TestResultType.Success; - } - } - } -} diff --git a/src/AddIns/Analysis/UnitTesting/TreeView/UnitTestNode.cs b/src/AddIns/Analysis/UnitTesting/TreeView/UnitTestNode.cs index df565a586d..3bf62c0e85 100644 --- a/src/AddIns/Analysis/UnitTesting/TreeView/UnitTestNode.cs +++ b/src/AddIns/Analysis/UnitTesting/TreeView/UnitTestNode.cs @@ -30,6 +30,7 @@ namespace ICSharpCode.UnitTesting void DetachEventHandlers() { + // TODO: figure out when we can call this method test.DisplayNameChanged -= test_NameChanged; test.ResultChanged -= test_ResultChanged; // If children loaded, also detach the collection change event handler @@ -43,6 +44,10 @@ namespace ICSharpCode.UnitTesting } #region Manage Children + public override bool ShowExpander { + get { return test.CanExpandNestedTests && base.ShowExpander; } + } + protected override void LoadChildren() { Children.Clear(); diff --git a/src/AddIns/Analysis/UnitTesting/UnitTesting.csproj b/src/AddIns/Analysis/UnitTesting/UnitTesting.csproj index f2c713e547..a068bc35cd 100644 --- a/src/AddIns/Analysis/UnitTesting/UnitTesting.csproj +++ b/src/AddIns/Analysis/UnitTesting/UnitTesting.csproj @@ -92,6 +92,8 @@ + + diff --git a/src/Main/Base/Project/Src/Util/AtomicBoolean.cs b/src/Main/Base/Project/Src/Util/AtomicBoolean.cs index 637c133454..4258bd7484 100644 --- a/src/Main/Base/Project/Src/Util/AtomicBoolean.cs +++ b/src/Main/Base/Project/Src/Util/AtomicBoolean.cs @@ -54,7 +54,7 @@ namespace ICSharpCode.SharpDevelop /// public override bool Equals(object obj) { - return (obj is AtomicBoolean) && val == ((AtomicBoolean)obj).val; + return (obj is AtomicBoolean) && this.Value == ((AtomicBoolean)obj).Value; } } }