// 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.Core; using ICSharpCode.SharpDevelop.Dom; namespace ICSharpCode.UnitTesting { /// /// Represents a class that can be tested. /// public class TestClass { IClass c; TestMethodCollection testMethods; TestResultType testResultType; IRegisteredTestFrameworks testFrameworks; /// /// Raised when the test class result is changed. /// public event EventHandler ResultChanged; public TestClass(IClass c, IRegisteredTestFrameworks testFrameworks) { this.c = c; this.testFrameworks = testFrameworks; } /// /// Gets the underlying IClass for this test class. /// public IClass Class { get { return c; } } /// /// Gets the test classes that exist in the specified namespace. /// public static TestClass[] GetTestClasses(ICollection classes, string ns) { List matchedClasses = new List(); foreach (TestClass c in classes) { if (c.Namespace == ns) { matchedClasses.Add(c); } } return matchedClasses.ToArray(); } /// /// Gets the test classes that namespaces starts with the specified /// string. /// public static TestClass[] GetAllTestClasses(ICollection classes, string namespaceStartsWith) { List matchedClasses = new List(); foreach (TestClass c in classes) { if (c.Namespace.StartsWith(namespaceStartsWith)) { matchedClasses.Add(c); } } 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; } } /// /// Gets the fully qualified name of the class. /// public string QualifiedName { get { return c.DotNetName; } } /// /// Gets the namespace of this class. /// public string Namespace { get { var currentClass = c; while (currentClass.DeclaringType != null) currentClass = currentClass.DeclaringType; return currentClass.Namespace; } } /// /// Gets the root namespace for this class. /// public string RootNamespace { get { return GetRootNamespace(c.Namespace); } } /// /// Gets the test result for this class. /// public TestResultType Result { get { return testResultType; } set { TestResultType previousTestResultType = testResultType; testResultType = value; if (previousTestResultType != testResultType) { OnResultChanged(); } } } /// /// 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 methods in this class. /// public TestMethodCollection TestMethods { get { if (testMethods == null) { GetTestMethods(); } return testMethods; } } /// /// Gets the test method with the specified name. /// /// Null if the method cannot be found. public TestMethod GetTestMethod(string name) { if (TestMethods.Contains(name)) { return TestMethods[name]; } return null; } /// /// Updates the test method with the specified test result. /// public void UpdateTestResult(TestResult testResult) { TestMethod method = null; string methodName = TestMethod.GetMethodName(testResult.Name); if (methodName != null) { method = GetTestMethod(methodName); if (method == null) { method = GetPrefixedTestMethod(testResult.Name); } } if (method != null) { method.Result = testResult.ResultType; } } /// /// Resets all the test results back to none. /// public void ResetTestResults() { Result = TestResultType.None; TestMethods.ResetTestResults(); } /// /// Updates the methods and class based on the new class /// information that has been parsed. /// public void UpdateClass(IClass c) { this.c = c.GetCompoundClass(); // Remove missing methods. TestMethodCollection newTestMethods = GetTestMethods(this.c); TestMethodCollection existingTestMethods = TestMethods; for (int i = existingTestMethods.Count - 1; i >= 0; --i) { TestMethod method = existingTestMethods[i]; if (newTestMethods.Contains(method.Name)) { method.Update(newTestMethods[method.Name].Method); } else { existingTestMethods.RemoveAt(i); } } // Add new methods. foreach (TestMethod method in newTestMethods) { if (existingTestMethods.Contains(method.Name)) { } else { existingTestMethods.Add(method); } } } /// /// 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; } /// /// Gets the test methods for the class. /// void GetTestMethods() { testMethods = GetTestMethods(c); testMethods.ResultChanged += TestMethodsResultChanged; } /// /// Gets the test methods for the specified class. /// TestMethodCollection GetTestMethods(IClass c) { TestMethodCollection testMethods = new TestMethodCollection(); foreach (var method in c.AllMembers) { if (IsTestMethod(method)) { if (!testMethods.Contains(method.Name)) { testMethods.Add(new TestMethod(method)); } } } // Add base class test methods. IClass declaringType = c; while (c.BaseClass != null) { foreach (var method in c.BaseClass.AllMembers) { if (IsTestMethod(method)) { BaseTestMethod baseTestMethod = new BaseTestMethod(declaringType, method); TestMethod testMethod = new TestMethod(c.BaseClass.Name, baseTestMethod); if (method.IsVirtual) { if (!testMethods.Contains(method.Name)) { testMethods.Add(testMethod); } } else { if (!testMethods.Contains(testMethod.Name)) { testMethods.Add(testMethod); } } } } c = c.BaseClass; } return testMethods; } bool IsTestMethod(IMember method) { return testFrameworks.IsTestMethod(method); } /// /// Updates the test class's test result after the test method's /// test result has changed. /// void TestMethodsResultChanged(object source, EventArgs e) { Result = testMethods.Result; } /// /// Raises the ResultChanged event. /// void OnResultChanged() { if (ResultChanged != null) { ResultChanged(this, new EventArgs()); } } /// /// This function adds the base class as a prefix and tries to find /// the corresponding test method. /// /// Actual method name: /// /// RootNamespace.TestFixture.TestFixtureBaseClass.TestMethod /// /// /// 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 /// the test result with the test method name as follows: /// /// RootNamespace.TestFixture.BaseTestFixture.TestMethod /// /// The test method name would have the base class name prefixed /// to it. /// TestMethod GetPrefixedTestMethod(string testResultName) { IClass baseClass = c.BaseClass; while (baseClass != null) { string methodName = TestMethod.GetMethodName(testResultName); string actualMethodName = String.Concat(baseClass.Name, ".", methodName); TestMethod method = GetTestMethod(actualMethodName); if (method != null) { return method; } baseClass = baseClass.BaseClass; } return null; } } }