//
//
//
//
// $Revision$
//
using System;
using System.Collections.Generic;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Dom;
namespace ICSharpCode.UnitTesting
{
///
/// Represents a class that can be tested. In order for a
/// class to be considered to be testable it needs to have the
/// [TestFixture] attribute.
///
public class TestClass
{
IClass c;
TestMethodCollection testMethods;
TestResultType testResultType;
///
/// Raised when the test class result is changed.
///
public event EventHandler ResultChanged;
public TestClass(IClass c)
{
this.c = c;
}
///
/// Gets the underlying IClass for this test class.
///
public IClass Class {
get {
return c;
}
}
///
/// Determines whether the class is a test fixture. A class
/// is considered to be a test class if it contains certain
/// test attributes.
///
public static bool IsTestClass(IClass c)
{
StringComparer nameComparer = GetNameComparer(c);
if (nameComparer != null) {
TestAttributeName testAttributeName = new TestAttributeName("TestFixture", nameComparer);
foreach (IAttribute attribute in c.Attributes) {
if (testAttributeName.IsEqual(attribute.Name)) {
return true;
}
}
}
return false;
}
///
/// Returns the name comparer for the specified class.
///
public static StringComparer GetNameComparer(IClass c)
{
if (c != null) {
IProjectContent projectContent = c.ProjectContent;
if (projectContent != null) {
LanguageProperties language = projectContent.Language;
if (language != null) {
return language.NameComparer;
}
}
}
return null;
}
///
/// 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 {
return c.Name;
}
}
///
/// Gets the fully qualified name of the class.
///
public string QualifiedName {
get {
return c.FullyQualifiedName;
}
}
///
/// Gets the namespace of this class.
///
public string Namespace {
get {
return c.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.
///
static TestMethodCollection GetTestMethods(IClass c)
{
TestMethodCollection testMethods = new TestMethodCollection();
foreach (IMethod method in c.Methods) {
if (TestMethod.IsTestMethod(method)) {
if (!testMethods.Contains(method.Name)) {
testMethods.Add(new TestMethod(method));
}
}
}
// Add base class test methods.
if (c.BaseClass != null) {
foreach (IMethod method in c.BaseClass.Methods) {
if (TestMethod.IsTestMethod(method)) {
TestMethod testMethod = new TestMethod(c.BaseClass.Name, method);
if (!testMethods.Contains(testMethod.Name)) {
testMethods.Add(testMethod);
}
}
}
}
return testMethods;
}
///
/// 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());
}
}
///
/// First tries the last dotted part of the test result name as the
/// method name. If there is no matching method the preceding dotted
/// part is prefixed to the method name until a match is found.
///
/// Given a test result of:
///
/// RootNamespace.ClassName.BaseClass1.BaseClass2.TestMethod
///
/// The method names tried are:
///
/// TestMethod
/// BaseClass2.TestMethod
/// BaseClass2.BaseClass1.TestMethod
/// etc.
///
TestMethod GetPrefixedTestMethod(string testResultName)
{
int index = 0;
string methodName = TestMethod.GetMethodName(testResultName);
string className = TestMethod.GetQualifiedClassName(testResultName);
do {
index = className.LastIndexOf('.');
if (index > 0) {
methodName = String.Concat(className.Substring(index + 1), ".", methodName);
TestMethod method = GetTestMethod(methodName);
if (method != null) {
return method;
}
className = className.Substring(0, index);
}
} while (index > 0);
return null;
}
}
}