// 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;
}
}
}