diff --git a/src/AddIns/Analysis/UnitTesting/Gui/UnitTestsPad.cs b/src/AddIns/Analysis/UnitTesting/Gui/UnitTestsPad.cs index 3a782a7abc..b725b0dc10 100644 --- a/src/AddIns/Analysis/UnitTesting/Gui/UnitTestsPad.cs +++ b/src/AddIns/Analysis/UnitTesting/Gui/UnitTestsPad.cs @@ -53,13 +53,7 @@ namespace ICSharpCode.UnitTesting SD.ParserService.LoadSolutionProjectsThread.Finished += LoadSolutionProjectsThreadFinished; OnAddedLoadSolutionProjectsThreadEndedHandler(); - // Display currently open solution. - if (!IsParserLoadingSolution) { - Solution openSolution = GetOpenSolution(); - if (openSolution != null) { - SolutionLoaded(openSolution); - } - } + treeView.Root = new RootUnitTestNode(); SD.ParserService.ParseInformationUpdated += ParseInformationUpdated; ProjectService.SolutionClosed += SolutionClosed; @@ -133,22 +127,6 @@ namespace ICSharpCode.UnitTesting // treeView.CollapseAll(); // } - /// - /// Called when a solution has been loaded. - /// - protected void SolutionLoaded(Solution solution) - { - // SolutionLoaded will be invoked from another thread. - // The UnitTestsPad might be disposed by the time the event is processed by the main thread. - if (treeView != null) { - if (solution != null) { - treeView.Root = new RootUnitTestNode(solution); - } else { - treeView.Root = null; - } - } - } - /// /// Called when a solution has been closed. /// @@ -161,15 +139,6 @@ namespace ICSharpCode.UnitTesting // { // treeView.RemoveSolutionFolder(solutionFolder); // } -// - /// - /// The project is added to the tree view only if it has a - /// reference to a unit testing framework. - /// - protected void ProjectAdded(IProject project) - { - SolutionLoaded(GetOpenSolution()); - } /// /// If the project item removed is a reference to a unit @@ -193,7 +162,7 @@ namespace ICSharpCode.UnitTesting { RootUnitTestNode root = (RootUnitTestNode)treeView.Root; if (root == null) { - SolutionLoaded(GetOpenSolution()); +// SolutionLoaded(GetOpenSolution()); root = (RootUnitTestNode)treeView.Root; if (root == null) return; } @@ -271,7 +240,6 @@ namespace ICSharpCode.UnitTesting void ProjectAdded(object source, ProjectEventArgs e) { - ProjectAdded(e.Project); UpdateToolbar(); } diff --git a/src/AddIns/Analysis/UnitTesting/Service/TestClass.cs b/src/AddIns/Analysis/UnitTesting/Model/TestClass.cs similarity index 59% rename from src/AddIns/Analysis/UnitTesting/Service/TestClass.cs rename to src/AddIns/Analysis/UnitTesting/Model/TestClass.cs index d7424aa54a..4d582eb3f9 100644 --- a/src/AddIns/Analysis/UnitTesting/Service/TestClass.cs +++ b/src/AddIns/Analysis/UnitTesting/Model/TestClass.cs @@ -2,6 +2,7 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; +using System.Collections.ObjectModel; using System.Linq; using System.Collections.Generic; using ICSharpCode.Core; @@ -14,7 +15,8 @@ namespace ICSharpCode.UnitTesting /// public class TestClass { - List parts; + string fullName; + ObservableCollection parts; // TestMemberCollection testMembers; TestResultType testResultType; IRegisteredTestFrameworks testFrameworks; @@ -24,10 +26,11 @@ namespace ICSharpCode.UnitTesting /// public event EventHandler ResultChanged; - public TestClass(IRegisteredTestFrameworks testFrameworks, params IUnresolvedTypeDefinition[] parts) + public TestClass(IRegisteredTestFrameworks testFrameworks, string fullName, IEnumerable parts) { - this.parts = new List(parts); + this.parts = new ObservableCollection(parts); this.testFrameworks = testFrameworks; + this.fullName = fullName; } /// @@ -36,6 +39,10 @@ namespace ICSharpCode.UnitTesting public IEnumerable Parts { get { return parts; } } + + public string FullName { + get { return fullName; } + } /// /// Gets the list of other (e.g. base types) classes where from which test members included in this test class come from. @@ -71,71 +78,27 @@ namespace ICSharpCode.UnitTesting return matchedClasses.ToArray(); } - /// - /// Gets all child namespaces that starts with the specified string. - /// - /// - /// If the starts with string is 'ICSharpCode' and there is a code coverage - /// method with a namespace of 'ICSharpCode.XmlEditor.Tests', then this - /// method will return 'XmlEditor' as one of its strings. - /// - public static string[] GetChildNamespaces(ICollection classes, string parentNamespace) { - List items = new List(); - foreach (TestClass c in classes) { - string ns = c.GetChildNamespace(parentNamespace); - if (ns.Length > 0) { - if (!items.Contains(ns)) { - items.Add(ns); - } - } - } - return items.ToArray(); - } - /// /// Gets the name of the class. /// public string Name { - get - { -// var currentClass = c; -// var name = c.Name; -// while(currentClass.DeclaringType != null) -// { -// name = String.Concat(currentClass.DeclaringType.Name, "+", name); -// currentClass = currentClass.DeclaringType; -// } - return "";//name; - } + get { return parts.First().Name; } } /// /// Gets the fully qualified name of the class. /// public string QualifiedName { - get { return ""; }// c.DotNetName; } + get { return parts.First().ReflectionName; } } /// /// Gets the namespace of this class. /// public string Namespace { - get { -// var currentClass = c; -// while (currentClass.DeclaringType != null) -// currentClass = currentClass.DeclaringType; -// return currentClass.Namespace; - throw new NotImplementedException(); - } + get { return parts.First().Namespace; } } - /// - /// Gets the root namespace for this class. - /// -// public string RootNamespace { -// get { return GetRootNamespace(c.Namespace); } -// } - /// /// Gets the test result for this class. /// @@ -150,59 +113,6 @@ namespace ICSharpCode.UnitTesting } } - /// - /// Gets the child namespace from the specified namespace - /// based on the parent namespace. - /// - /// Can contain multiple namespaces - /// (e.g. ICSharpCode.XmlEditor). - public static string GetChildNamespace(string ns, string parentNamespace) - { - if (parentNamespace.Length > 0) { - if (ns.StartsWith(String.Concat(parentNamespace, "."))) { - string end = ns.Substring(parentNamespace.Length + 1); - return GetRootNamespace(end); - } - return String.Empty; - } - return ns; - } - - /// - /// Gets the child namespace based on the parent namespace - /// from this class. - /// - /// Can contain multiple namespaces - /// (e.g. ICSharpCode.XmlEditor). - public string GetChildNamespace(string parentNamespace) - { - return GetChildNamespace(Namespace, parentNamespace); - } - - /// - /// Gets the test members in this class. - /// -// public TestMemberCollection TestMembers { -// get { -// if (testMembers == null) { -// GetTestMembers(); -// } -// return testMembers; -// } -// } - - /// - /// Gets the test member with the specified name. - /// - /// Null if the member cannot be found. -// public TestMember GetTestMember(string name) -// { -// if (TestMembers.Contains(name)) { -// return TestMembers[name]; -// } -// return null; -// } - /// /// Updates the test member with the specified test result. /// @@ -234,9 +144,8 @@ namespace ICSharpCode.UnitTesting /// Updates the members and class based on the new class /// information that has been parsed. /// - public void UpdateClass(IUnresolvedTypeDefinition c) + public void UpdateClass(ITypeDefinition definition) { - #warning not implemented! // this.c = c.GetCompoundClass(); // // // Remove missing members. @@ -281,44 +190,6 @@ namespace ICSharpCode.UnitTesting // testMembers.ResultChanged += TestMembersResultChanged; } - /// - /// Gets the test members for the specified class. - /// -// TestMemberCollection GetTestMembers(IUnresolvedTypeDefinition c) -// { -// TestMemberCollection testMembers = new TestMemberCollection(); -// foreach (var member in testFrameworks.GetTestMembersFor(c)) -// if (!testMembers.Contains(member.Name)) { -// testMembers.Add(member); -// } -// -// // Add base class test members. -// IClass declaringType = c; -// while (c.BaseClass != null) -// { -// foreach (var testMember in testFrameworks.GetTestMembersFor(c.BaseClass)) { -// BaseTestMember baseTestMethod = new BaseTestMember(declaringType, testMember.Member); -// TestMember testMethod = new TestMember(c.BaseClass, baseTestMethod); -// if (testMember.Member.IsVirtual) { -// if (!testMembers.Contains(testMember.Name)) { -// testMembers.Add(testMethod); -// } -// } else { -// if (!testMembers.Contains(testMethod.Name)) { -// testMembers.Add(testMethod); -// } -// } -// } -// c = c.BaseClass; -// } -// -// baseClassesFQNames.Clear(); -// foreach (var memberDeclaringClass in testMembers.Select(member => member.DeclaringType).Distinct()) -// if (memberDeclaringClass.CompareTo(declaringType) != 0) -// baseClassesFQNames.Add(memberDeclaringClass.FullyQualifiedName); -// return testMembers; -// } - /// /// Updates the test class's test result after the test member's /// test result has changed. diff --git a/src/AddIns/Analysis/UnitTesting/Service/TestMember.cs b/src/AddIns/Analysis/UnitTesting/Model/TestMember.cs similarity index 100% rename from src/AddIns/Analysis/UnitTesting/Service/TestMember.cs rename to src/AddIns/Analysis/UnitTesting/Model/TestMember.cs diff --git a/src/AddIns/Analysis/UnitTesting/Model/TestProject.cs b/src/AddIns/Analysis/UnitTesting/Model/TestProject.cs new file mode 100644 index 0000000000..68a4c4b6a2 --- /dev/null +++ b/src/AddIns/Analysis/UnitTesting/Model/TestProject.cs @@ -0,0 +1,85 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq; +using ICSharpCode.Core; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.Utils; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Parser; +using ICSharpCode.SharpDevelop.Project; + +namespace ICSharpCode.UnitTesting +{ + /// + /// Represents a project that has a reference to a unit testing + /// framework assembly. Currently only NUnit is supported. + /// + public class TestProject + { + IProject project; + IRegisteredTestFrameworks testFrameworks; + readonly ObservableCollection testClasses; + + public TestProject(IProject project) + { + this.project = project; + this.testFrameworks = TestService.RegisteredTestFrameworks; + project.ParseInformationUpdated += project_ParseInformationUpdated; + var compilation = SD.ParserService.GetCompilation(project); + var classes = project.ProjectContent + .Resolve(compilation.TypeResolveContext) + .GetAllTypeDefinitions() + .Where(td => td.HasTests(compilation)) + .Select(g => new TestClass(testFrameworks, g.ReflectionName, g.Parts)); + testClasses = new ObservableCollection(classes); + } + + void project_ParseInformationUpdated(object sender, ParseInformationEventArgs e) + { + var context = new SimpleTypeResolveContext(SD.ParserService.GetCompilation(project).MainAssembly); + IEnumerable @new; + if (e.NewParsedFile != null) + @new = e.NewParsedFile.TopLevelTypeDefinitions.Select(utd => utd.Resolve(context).GetDefinition()).Where(x => x != null && x.HasTests(SD.ParserService.GetCompilation(project))); + else + @new = Enumerable.Empty(); + UpdateTestClasses(testClasses.Where(tc => tc.Parts.Any(td => td.ParsedFile.FileName == e.OldParsedFile.FileName)).Select(tc => new DefaultResolvedTypeDefinition(context, tc.Parts.ToArray())).ToList(), @new.ToList()); + } + + void UpdateTestClasses(IReadOnlyList oldTypes, IReadOnlyList newTypes) + { + var mappings = oldTypes.FullOuterJoin(newTypes, t => t.ReflectionName, t => t.ReflectionName, Tuple.Create); + foreach (Tuple mapping in mappings) { + if (mapping.Item2 == null) + testClasses.RemoveWhere(c => c.FullName == mapping.Item1.ReflectionName); + else if (mapping.Item1 == null) + testClasses.Add(new TestClass(testFrameworks, mapping.Item2.ReflectionName, mapping.Item2.Parts)); + else { + var testClass = testClasses.SingleOrDefault(c => c.FullName == mapping.Item1.ReflectionName); + if (testClass == null) + testClasses.Add(new TestClass(testFrameworks, mapping.Item2.ReflectionName, mapping.Item2.Parts)); + else + testClass.UpdateClass(mapping.Item2); + } + } + } + + public IProject Project { + get { return project; } + } + + public ObservableCollection TestClasses { + get { return testClasses; } + } + + public void UpdateTestResult(TestResult result) + { + + } + } +} diff --git a/src/AddIns/Analysis/UnitTesting/Nodes/ClassUnitTestNode.cs b/src/AddIns/Analysis/UnitTesting/Nodes/ClassUnitTestNode.cs new file mode 100644 index 0000000000..89ca050aa2 --- /dev/null +++ b/src/AddIns/Analysis/UnitTesting/Nodes/ClassUnitTestNode.cs @@ -0,0 +1,28 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.UnitTesting +{ + /// + /// Description of ClassUnitTestNode. + /// + public class ClassUnitTestNode : UnitTestBaseNode + { + TestClass testClass; + + public TestClass TestClass { + get { return testClass; } + } + + public ClassUnitTestNode(TestClass testClass) + { + this.testClass = testClass; + } + + public override object Text { + get { return testClass.Name; } + } + } +} diff --git a/src/AddIns/Analysis/UnitTesting/Nodes/ProjectUnitTestNode.cs b/src/AddIns/Analysis/UnitTesting/Nodes/ProjectUnitTestNode.cs index a77a30dc26..cb96a9b28e 100644 --- a/src/AddIns/Analysis/UnitTesting/Nodes/ProjectUnitTestNode.cs +++ b/src/AddIns/Analysis/UnitTesting/Nodes/ProjectUnitTestNode.cs @@ -2,12 +2,15 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) 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; @@ -24,15 +27,78 @@ namespace ICSharpCode.UnitTesting public ProjectUnitTestNode(TestProject project) { this.project = project; + project.TestClasses.CollectionChanged += TestClassesCollectionChanged; + LazyLoading = true; + } + + void TestClassesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + switch (e.Action) { + case NotifyCollectionChangedAction.Add: + foreach (TestClass c in e.NewItems) { + var node = FindNamespace(c.Namespace); + if (node == null) { + node = new NamespaceUnitTestNode(c.Namespace); + Children.OrderedInsert(node, (a, b) => string.CompareOrdinal(a.Text.ToString(), b.Text.ToString())); + } + node.Children.OrderedInsert(new ClassUnitTestNode(c), (a, b) => string.CompareOrdinal(a.Text.ToString(), b.Text.ToString())); + } + break; + case NotifyCollectionChangedAction.Remove: + foreach (TestClass c in e.OldItems) { + var node = FindNamespace(c.Namespace); + if (node == null) continue; + node.Children.RemoveWhere(n => n is ClassUnitTestNode && ((ClassUnitTestNode)n).TestClass.FullName == c.FullName); + if (node.Children.Count == 0) + Children.Remove(node); + } + break; + case NotifyCollectionChangedAction.Reset: + LoadChildren(); + break; + } + } + + NamespaceUnitTestNode FindNamespace(string @namespace) + { + foreach (var node in Children.OfType()) { + // TODO use language-specific StringComparer + if (string.Equals(node.Namespace, @namespace, StringComparison.Ordinal)) + return node; + } + return null; } protected override void LoadChildren() { - base.LoadChildren(); + Children.Clear(); + foreach (var g in project.TestClasses.Select(c => new ClassUnitTestNode(c)).GroupBy(tc => tc.TestClass.Namespace)) { + var namespaceNode = new NamespaceUnitTestNode(g.Key); + namespaceNode.Children.AddRange(g); + Children.Add(namespaceNode); + } + } + + public override object Text { + get { return project.Project.Name; } + } + } + + public class NamespaceUnitTestNode : UnitTestBaseNode + { + string name; + + public string Namespace { + get { return name; } + } + + public NamespaceUnitTestNode(string name) + { + this.name = name; } public override object Text { - get { return project.Name; } + get { return string.IsNullOrEmpty(name) ? "" : name; } } } } diff --git a/src/AddIns/Analysis/UnitTesting/Nodes/RootUnitTestNode.cs b/src/AddIns/Analysis/UnitTesting/Nodes/RootUnitTestNode.cs index d7cd39000c..d3efabbb2c 100644 --- a/src/AddIns/Analysis/UnitTesting/Nodes/RootUnitTestNode.cs +++ b/src/AddIns/Analysis/UnitTesting/Nodes/RootUnitTestNode.cs @@ -2,11 +2,14 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) 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; @@ -23,32 +26,31 @@ namespace ICSharpCode.UnitTesting /// public class RootUnitTestNode : UnitTestBaseNode { - Solution solution; - - public RootUnitTestNode(Solution solution) + public RootUnitTestNode() { - this.solution = solution; - ProjectService.ProjectAdded += OnProjectAdded; - ProjectService.ProjectRemoved += OnProjectRemoved; - SD.ParserService.LoadSolutionProjectsThread.Finished += delegate { LoadChildren(); }; + TestService.TestableProjects.CollectionChanged += TestService_TestableProjects_CollectionChanged; LazyLoading = true; } - void OnProjectRemoved(object sender, ProjectEventArgs e) - { - LoadChildren(); - } - - void OnProjectAdded(object sender, ProjectEventArgs e) + void TestService_TestableProjects_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { - LoadChildren(); + switch (e.Action) { + case NotifyCollectionChangedAction.Add: + Children.AddRange(e.NewItems.OfType().Select(p => new ProjectUnitTestNode(p))); + break; + case NotifyCollectionChangedAction.Remove: + Children.RemoveWhere(node => node is ProjectUnitTestNode && e.OldItems.OfType().Any(p => p.Project == ((ProjectUnitTestNode)node).Project)); + break; + case NotifyCollectionChangedAction.Reset: + LoadChildren(); + break; + } } protected override void LoadChildren() { - this.Children.Clear(); - if (!SD.ParserService.LoadSolutionProjectsThread.IsRunning) - this.Children.AddRange(solution.Projects.Where(p => p.IsTestProject()).Select(p => new ProjectUnitTestNode(new TestProject(p)))); + Children.Clear(); + Children.AddRange(TestService.TestableProjects.Select(p => new ProjectUnitTestNode(p))); } public override object Text { @@ -58,13 +60,51 @@ 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 SD.ParserService.GetCompilation(project).FindType("NUnit.Framework.TestAttribute").Kind != TypeKind.Unknown; + return testAttribute.Resolve(SD.ParserService.GetCompilation(project).TypeResolveContext).Kind != TypeKind.Unknown; + } + + public static bool HasTests(this ITypeDefinition type, ICompilation compilation) + { + if (type == null) + throw new ArgumentNullException("type"); + var testAttribute = Extensions.testAttribute.Resolve(compilation.TypeResolveContext); + return type.Methods.Any(m => m.Attributes.Any(a => a.AttributeType.Equals(testAttribute))); + } + + public static IEnumerable FullOuterJoin(this IEnumerable outer, IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func 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(this IList list, T item, Func comparer) + { + int index = 0; + while (index < list.Count && comparer(list[index], item) < 0) + index++; + list.Insert(index, item); } } } diff --git a/src/AddIns/Analysis/UnitTesting/Service/TestProject.cs b/src/AddIns/Analysis/UnitTesting/Service/TestProject.cs deleted file mode 100644 index ba712be570..0000000000 --- a/src/AddIns/Analysis/UnitTesting/Service/TestProject.cs +++ /dev/null @@ -1,298 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System; -using System.Collections.Generic; -using System.Linq; -using ICSharpCode.Core; -using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.SharpDevelop.Parser; -using ICSharpCode.SharpDevelop.Project; - -namespace ICSharpCode.UnitTesting -{ - /// - /// Represents a project that has a reference to a unit testing - /// framework assembly. Currently only NUnit is supported. - /// - public class TestProject - { - IProject project; - IProjectContent projectContent; - List testClasses; - List rootNamespaces; - IRegisteredTestFrameworks testFrameworks; - - public TestProject(IProject project) - { - this.project = project; - this.projectContent = project.ProjectContent; - this.testFrameworks = TestService.RegisteredTestFrameworks; -// project.ProjectContent.GetAllTypeDefinitions().Where(td => td.Attributes.Select(a => a.CreateResolvedAttribute(project.ProjectContent.CreateCompilation(). - } - - public IProject Project { - get { return project; } - } - - /// - /// Gets the test classes in this project. - /// -// public TestClassCollection TestClasses { -// get { -// if (testClasses == null) { -// GetTestClasses(); -// } -// return testClasses; -// } -// } - - /// - /// Gets the test classes that exist in the specified namespace. - /// - public TestClass[] GetTestClasses(string ns) - { - throw new NotImplementedException(); -// return TestClass.GetTestClasses(TestClasses, ns); - } - - /// - /// Gets the test classes whose namespaces start with the specified string. - /// - public TestClass[] GetAllTestClasses(string namespaceStartsWith) - { - throw new NotImplementedException(); -// return TestClass.GetAllTestClasses(TestClasses, namespaceStartsWith); - } - - /// - /// Gets all the child namespaces with the specified parent - /// namespace. The parent namespace can be one or more - /// namespaces separated with a period. - /// - public string[] GetChildNamespaces(string parentNamespace) - { - throw new NotImplementedException(); -// return TestClass.GetChildNamespaces(TestClasses, parentNamespace); - } - - /// - /// Gets the project's name. - /// - public string Name { - get { return project.Name; } - } - - /// - /// Gets the distinct root namespaces for all this project. - /// - /// - /// If one of the namespaces is 'ICSharpCode.XmlEditor' then this - /// method will return 'ICSharpCode' as one of the root namespaces. - /// - public IList RootNamespaces { - get { - if (rootNamespaces == null) { - GetRootNamespaces(); - } - return rootNamespaces; - } - } - - /// - /// Updates the test method based on the test result. - /// - public void UpdateTestResult(TestResult testResult) - { -// TestClasses.UpdateTestResult(testResult); - } - - /// - /// Sets all the test results back to none. - /// - public void ResetTestResults() - { -// TestClasses.ResetTestResults(); - } - - /// - /// Updates the classes and methods based on the new parse information. - /// - /// The old compiliation unit - /// (ParseInformationEventArgs.ParseInformation.BestCompilationUnit as ICompilationUnit) - /// The new compilation unit - /// (ParseInformationEventArgs.CompilationUnit). - public void UpdateParseInfo(IParsedFile oldUnit, IParsedFile newUnit) - { - if (!IsParseInfoForThisProject(oldUnit, newUnit)) { - return; - } - -// RemovedClasses removedClasses = new RemovedClasses(); -// -// if (oldUnit != null) { -// removedClasses.Add(oldUnit.Classes); -// } -// if (newUnit != null) { -// foreach (IClass c in newUnit.Classes) { -// UpdateTestClass(c); -// foreach (IClass innerClass in new InnerClassEnumerator(c)) { -// UpdateTestClass(innerClass); -// removedClasses.Remove(innerClass); -// } -// removedClasses.Remove(c); -// } -// } -// -// // Remove missing classes. -// foreach (IClass c in removedClasses.GetMissingClasses()) { -// IClass existingClass = GetExistingTestClassInProject(c); -// if (existingClass != null) { -// UpdateTestClass(existingClass); -// } else { -// TestClasses.Remove(c.DotNetName); -// } -// } - } - - /// - /// Determines whether the new parse information is for this test - /// project. - /// - public bool IsParseInfoForThisProject(IParsedFile oldUnit, IParsedFile newUnit) - { -// ICompilationUnit unit = oldUnit; -// if (unit == null) { -// unit = newUnit; -// } -// if (unit != null) { -// return Object.ReferenceEquals(unit.ProjectContent, this.projectContent); -// } - return false; - } - - /// - /// Adds a new class to the test project's classes only if - /// the class is a test class. - /// - void AddNewTestClass(IUnresolvedTypeDefinition c) - { - if (IsTestClass(c)) { - TestClass testClass = CreateTestClass(c); -// TestClasses.Add(testClass); - } - } - - TestClass CreateTestClass(IUnresolvedTypeDefinition c) - { - throw new NotImplementedException(); -// return new TestClass(c, testFrameworks); - } - - /// - /// Updates the test class methods based on the newly parsed class - /// information. - /// - void UpdateTestClass(IUnresolvedTypeDefinition c) - { -// if (TestClasses.Contains(c.DotNetName)) -// { -// if (IsTestClass(c)) -// { -// TestClass testClass = TestClasses[c.DotNetName]; -// testClass.UpdateClass(c); -// } -// else -// { -// // TestFixture attribute has been removed so -// // remove the class from the set of TestClasses. -// TestClasses.Remove(c.DotNetName); -// } -// } -// else -// { -// // TestFixture attribute may have been recently added to -// // this class so call AddNewTestClass. No need to -// // check if the class is actually a test class since -// // AddNewTestClass does this anyway. -// AddNewTestClass(c); -// } -// -// var derivedTestClasses = GetTestClassesDerivedFrom(c); -// if (derivedTestClasses.Any()) -// UpdateClassesFromProjectContent(derivedTestClasses); - - } - - private IEnumerable GetTestClassesDerivedFrom(IUnresolvedTypeDefinition c) - { - throw new NotImplementedException(); -// return TestClasses -// .Where(testClass => testClass.IsDerivedFrom(c)) -// .Select(testClass => testClass.Class) -// .ToArray(); - } - - private void UpdateClassesFromProjectContent(IEnumerable classes) - { -// foreach (var c in classes) -// { -// var classInProjectContent = projectContent.GetClass(c.FullyQualifiedName, c.TypeParameters.Count); -// if (classInProjectContent != null) -// UpdateTestClass(classInProjectContent); -// } - } - - void GetTestClasses() - { -// testClasses = new TestClassCollection(); -// foreach (IClass c in projectContent.Classes) { -// if (IsTestClass(c)) { -// if (!testClasses.Contains(c.FullyQualifiedName)) { -// testClasses.Add(CreateTestClass(c)); -// } -// } -// foreach (IClass innerClass in new InnerClassEnumerator(c)) { -// if (IsTestClass(innerClass)) { -// if (!testClasses.Contains(innerClass.DotNetName)) { -// testClasses.Add(CreateTestClass(innerClass)); -// } -// } -// } -// } - } - - bool IsTestClass(IUnresolvedTypeDefinition c) - { - return testFrameworks.IsTestClass(c); - } - - void GetRootNamespaces() - { -// rootNamespaces = new List(); -// foreach (TestClass c in TestClasses) { -// string rootNamespace = c.RootNamespace; -// if ((rootNamespace.Length > 0) && !rootNamespaces.Contains(rootNamespace)) { -// rootNamespaces.Add(rootNamespace); -// } -// } - } - - /// - /// Gets an existing test class with the same name in the project. This - /// method is used to check that we do not remove a class after an existing duplicate class name - /// is changed. - /// - TestClass GetExistingTestClassInProject(IUnresolvedTypeDefinition c) - { -// foreach (IClass existingClass in projectContent.Classes) { -// if (IsTestClass(existingClass)) { -// if (existingClass.DotNetName == c.DotNetName) { -// return existingClass; -// } -// } -// } - return null; - } - } -} diff --git a/src/AddIns/Analysis/UnitTesting/Service/TestService.cs b/src/AddIns/Analysis/UnitTesting/Service/TestService.cs index 20d0aa36e2..87104a2a26 100644 --- a/src/AddIns/Analysis/UnitTesting/Service/TestService.cs +++ b/src/AddIns/Analysis/UnitTesting/Service/TestService.cs @@ -2,7 +2,13 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Gui; +using ICSharpCode.SharpDevelop.Project; namespace ICSharpCode.UnitTesting { @@ -28,7 +34,7 @@ namespace ICSharpCode.UnitTesting } public static MessageViewCategory UnitTestMessageView { - get { + get { if (unitTestMessageView == null) { CreateUnitTestCategory(); } @@ -38,10 +44,73 @@ namespace ICSharpCode.UnitTesting static void CreateUnitTestCategory() { - MessageViewCategory.Create(ref unitTestMessageView, - "UnitTesting", - "${res:ICSharpCode.NUnitPad.NUnitPadContent.PadName}"); + MessageViewCategory.Create(ref unitTestMessageView, + "UnitTesting", + "${res:ICSharpCode.NUnitPad.NUnitPadContent.PadName}"); + } + + static readonly ObservableCollection testableProjects = new ObservableCollection(); + + public static ObservableCollection TestableProjects { + get { return testableProjects; } + } + + static TestService() + { + ProjectService.SolutionCreated += ProjectService_SolutionCreated; + ProjectService.SolutionLoaded += ProjectService_SolutionLoaded; + ProjectService.SolutionClosed += ProjectService_SolutionClosed; + ProjectService.ProjectCreated += ProjectService_ProjectCreated; + ProjectService.ProjectAdded += ProjectService_ProjectAdded; + ProjectService.ProjectRemoved += ProjectService_ProjectRemoved; + SD.ParserService.LoadSolutionProjectsThread.Finished += SD_ParserService_LoadSolutionProjectsThread_Finished; } + static void SD_ParserService_LoadSolutionProjectsThread_Finished(object sender, EventArgs e) + { + testableProjects.Clear(); + testableProjects.AddRange(GetTestableProjects()); + } + + static void ProjectService_ProjectCreated(object sender, ProjectEventArgs e) + { + if (e.Project.IsTestProject()) + testableProjects.Add(new TestProject(e.Project)); + } + + static void ProjectService_ProjectAdded(object sender, ProjectEventArgs e) + { + if (e.Project.IsTestProject()) + testableProjects.Add(new TestProject(e.Project)); + } + + static void ProjectService_ProjectRemoved(object sender, ProjectEventArgs e) + { + testableProjects.RemoveWhere(test => test.Project == e.Project); + } + + static void ProjectService_SolutionCreated(object sender, SolutionEventArgs e) + { + testableProjects.Clear(); + testableProjects.AddRange(GetTestableProjects()); + } + + static void ProjectService_SolutionClosed(object sender, EventArgs e) + { + testableProjects.Clear(); + } + + static void ProjectService_SolutionLoaded(object sender, SolutionEventArgs e) + { + testableProjects.Clear(); + testableProjects.AddRange(GetTestableProjects()); + } + + static IEnumerable GetTestableProjects() + { + if (ProjectService.OpenSolution == null) + return Enumerable.Empty(); + return ProjectService.OpenSolution.Projects.Where(p => p.IsTestProject()).Select(p => new TestProject(p)); + } } } diff --git a/src/AddIns/Analysis/UnitTesting/UnitTesting.csproj b/src/AddIns/Analysis/UnitTesting/UnitTesting.csproj index bf8350692e..fca20029a7 100644 --- a/src/AddIns/Analysis/UnitTesting/UnitTesting.csproj +++ b/src/AddIns/Analysis/UnitTesting/UnitTesting.csproj @@ -17,7 +17,7 @@ 4096 4 false - v4.0 + v4.5 @@ -37,8 +37,7 @@ - ..\..\..\..\bin\Tools\NUnit\nunit.framework.dll - False + ..\..\..\Tools\NUnit\nunit.framework.dll 3.0 @@ -103,7 +102,10 @@ + + + @@ -113,12 +115,10 @@ - - @@ -179,6 +179,7 @@ + diff --git a/src/Main/Base/Project/Src/Util/ExtensionMethods.cs b/src/Main/Base/Project/Src/Util/ExtensionMethods.cs index a9ae133b98..edd80bf93f 100644 --- a/src/Main/Base/Project/Src/Util/ExtensionMethods.cs +++ b/src/Main/Base/Project/Src/Util/ExtensionMethods.cs @@ -113,7 +113,7 @@ namespace ICSharpCode.SharpDevelop /// /// Adds all to . /// - public static void AddRange(this WinForms.ComboBox.ObjectCollection list, IEnumerable elements) + internal static void AddRange(this WinForms.ComboBox.ObjectCollection list, IEnumerable elements) { foreach (var o in elements) list.Add(o);