← Back to team overview

nunit-core team mailing list archive

[Merge] lp:~jterrell/nunitv2/action-attributes into lp:nunitv2

 

You have been requested to review the proposed merge of lp:~jterrell/nunitv2/action-attributes into lp:nunitv2.

Implements new Action Attributes feature.

-- 
https://code.launchpad.net/~jterrell/nunitv2/action-attributes/+merge/43316
Your team NUnit Core Developers is requested to review the proposed merge of lp:~jterrell/nunitv2/action-attributes into lp:nunitv2.
=== added file 'src/NUnitCore/core/ActionsHelper.cs'
--- src/NUnitCore/core/ActionsHelper.cs	1970-01-01 00:00:00 +0000
+++ src/NUnitCore/core/ActionsHelper.cs	2010-12-10 03:36:07 +0000
@@ -0,0 +1,127 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Text;
+
+namespace NUnit.Core
+{
+    internal static class ActionsHelper
+    {
+        private static Type _ActionInterfaceType = null;
+        private static Hashtable _ActionTypes = null;
+
+        static ActionsHelper()
+        {
+            _ActionInterfaceType = Type.GetType(NUnitFramework.ActionInterface);
+            _ActionTypes = new Hashtable();
+
+            _ActionTypes.Add(ActionLevel.Suite, Type.GetType(NUnitFramework.SuiteActionInterface));
+            _ActionTypes.Add(ActionLevel.Test, Type.GetType(NUnitFramework.TestActionInterface));
+        }
+
+        public static object[] GetActionsFromAttributes(ICustomAttributeProvider attributeProvider)
+        {
+            ArrayList resultList = new ArrayList();
+
+            object[] attributes = attributeProvider.GetCustomAttributes(true);
+
+            foreach (Attribute attribute in attributes)
+            {
+                if (_ActionInterfaceType.IsAssignableFrom(attribute.GetType()))
+                    resultList.Add(attribute);
+            }
+
+            object[] results = new object[resultList.Count];
+            resultList.CopyTo(results);
+
+            return results;
+        }
+
+        public static void ExecuteActions(ActionLevel level, ActionPhase phase, IEnumerable actions, object fixture, MethodInfo method)
+        {
+            if (actions == null)
+                throw new ArgumentNullException("actions");
+
+            Type actionType = GetActionType(level);
+            MethodInfo actionMethod = GetActionMethod(actionType, level, phase);
+
+            object[] filteredActions = GetFilteredAndSortedActions(actions, phase, actionType);
+
+            
+
+            foreach (object action in filteredActions)
+            {
+                if (action == null)
+                    continue;
+                
+                if(level == ActionLevel.Suite)
+                    Reflect.InvokeMethod(actionMethod, action, fixture);
+                else
+                    Reflect.InvokeMethod(actionMethod, action, fixture, method);
+            }
+        }
+
+        private static object[] GetFilteredAndSortedActions(IEnumerable actions, ActionPhase phase, Type actionType)
+        {
+            ArrayList filteredActions = new ArrayList();
+            foreach(object actionItem in actions)
+            {
+                if(actionItem == null)
+                    continue;
+
+                if(actionItem is IEnumerable)
+                {
+                    foreach(object nestedItem in ((IEnumerable)actionItem))
+                    {
+                        if(nestedItem == null)
+                            continue;
+
+                        if (actionType.IsAssignableFrom(nestedItem.GetType()) && filteredActions.Contains(nestedItem) != true)
+                            filteredActions.Add(nestedItem);
+                    }
+                }
+                else if(actionType.IsAssignableFrom(actionItem.GetType()) && filteredActions.Contains(actionItem) != true)
+                    filteredActions.Add(actionItem);
+            }
+
+            if(phase == ActionPhase.After)
+                filteredActions.Reverse();
+
+            return filteredActions.ToArray();
+        }
+
+        private static Type GetActionType(ActionLevel level)
+        {
+            return (Type) _ActionTypes[level];
+        }
+
+        private static MethodInfo GetActionMethod(Type actionType, ActionLevel level, ActionPhase phase)
+        {
+            if (phase == ActionPhase.Before)
+            {
+                if (level == ActionLevel.Suite)
+                    return Reflect.GetNamedMethod(actionType, "BeforeSuite");
+
+                return Reflect.GetNamedMethod(actionType, "BeforeTest");
+            }
+
+            if (level == ActionLevel.Suite)
+                return Reflect.GetNamedMethod(actionType, "AfterSuite");
+
+            return Reflect.GetNamedMethod(actionType, "AfterTest");
+        }
+    }
+
+    internal enum ActionLevel
+    {
+        Suite,
+        Test
+    }
+
+    internal enum ActionPhase
+    {
+        Before,
+        After
+    }
+}

=== modified file 'src/NUnitCore/core/Builders/TestAssemblyBuilder.cs'
--- src/NUnitCore/core/Builders/TestAssemblyBuilder.cs	2010-08-09 02:30:40 +0000
+++ src/NUnitCore/core/Builders/TestAssemblyBuilder.cs	2010-12-10 03:36:07 +0000
@@ -86,12 +86,12 @@
                 // a type, we handle it specially
                 Type testType = assembly.GetType(testName);
                 if (testType != null)
-                    return Build(assemblyName, testType, autoSuites);
+                    return Build(assembly, assemblyName, testType, autoSuites);
 
                 // Assume that testName is a namespace and get all fixtures in it
                 IList fixtures = GetFixtures(assembly, testName);
-                if (fixtures.Count > 0)
-                    return BuildTestAssembly(assemblyName, fixtures, autoSuites);
+                if (fixtures.Count > 0)
+                    return BuildTestAssembly(this.assembly, assemblyName, fixtures, autoSuites);
 
                 return null;
             }
@@ -107,11 +107,11 @@
                 if (this.assembly == null) return null;
 
                 IList fixtures = GetFixtures(assembly, null);
-                return BuildTestAssembly(assemblyName, fixtures, autoSuites);
+                return BuildTestAssembly(this.assembly, assemblyName, fixtures, autoSuites);
             }
 		}
 
-		private Test Build( string assemblyName, Type testType, bool autoSuites )
+		private Test Build( Assembly assembly, string assemblyName, Type testType, bool autoSuites )
 		{
 			// TODO: This is the only situation in which we currently
 			// recognize and load legacy suites. We need to determine 
@@ -119,14 +119,14 @@
 			if ( legacySuiteBuilder.CanBuildFrom( testType ) )
 				return legacySuiteBuilder.BuildFrom( testType );
 			else if ( TestFixtureBuilder.CanBuildFrom( testType ) )
-				return BuildTestAssembly( assemblyName,
+				return BuildTestAssembly( assembly, assemblyName,
 					new Test[] { TestFixtureBuilder.BuildFrom( testType ) }, autoSuites );
 			return null;
 		}
 
-		private TestSuite BuildTestAssembly( string assemblyName, IList fixtures, bool autoSuites )
+		private TestSuite BuildTestAssembly( Assembly assembly, string assemblyName, IList fixtures, bool autoSuites )
 		{
-			TestSuite testAssembly = new TestAssembly( assemblyName );
+			TestSuite testAssembly = new TestAssembly( assembly, assemblyName );
 
 			if ( autoSuites )
 			{

=== modified file 'src/NUnitCore/core/NUnitFramework.cs'
--- src/NUnitCore/core/NUnitFramework.cs	2010-11-04 18:12:55 +0000
+++ src/NUnitCore/core/NUnitFramework.cs	2010-12-10 03:36:07 +0000
@@ -23,8 +23,8 @@
         #region Constants
 
 		#region Attribute Names
-		// NOTE: Attributes used in switch statements must be const
-
+		// NOTE: Attributes used in switch statements must be const
+
         // Attributes that apply to Assemblies, Classes and Methods
         public const string IgnoreAttribute = "NUnit.Framework.IgnoreAttribute";
 		public const string PlatformAttribute = "NUnit.Framework.PlatformAttribute";
@@ -54,7 +54,12 @@
         public static readonly string SuiteAttribute = "NUnit.Framework.SuiteAttribute";
         #endregion
 
-        #region Other Framework Types
+        #region Other Framework Types
+
+        public static readonly string SuiteActionInterface = "NUnit.Framework.ISuiteAction, NUnit.Framework";
+        public static readonly string TestActionInterface = "NUnit.Framework.ITestAction, NUnit.Framework";
+        public static readonly string ActionInterface = "NUnit.Framework.IAction, NUnit.Framework";
+
         public static readonly string AssertException = "NUnit.Framework.AssertionException";
         public static readonly string IgnoreException = "NUnit.Framework.IgnoreException";
         public static readonly string InconclusiveException = "NUnit.Framework.InconclusiveException";

=== modified file 'src/NUnitCore/core/NUnitTestFixture.cs'
--- src/NUnitCore/core/NUnitTestFixture.cs	2009-04-17 07:12:10 +0000
+++ src/NUnitCore/core/NUnitTestFixture.cs	2010-12-10 03:36:07 +0000
@@ -4,8 +4,9 @@
 // copyright ownership at http://nunit.org.
 // ****************************************************************
 
-using System;
-using System.Reflection;
+using System;
+using System.Reflection;
+using System.Collections;
 
 namespace NUnit.Core
 {
@@ -27,7 +28,19 @@
             this.setUpMethods = 
                 Reflect.GetMethodsWithAttribute(this.FixtureType, NUnitFramework.SetUpAttribute, true);
             this.tearDownMethods = 
-                Reflect.GetMethodsWithAttribute(this.FixtureType, NUnitFramework.TearDownAttribute, true);
+                Reflect.GetMethodsWithAttribute(this.FixtureType, NUnitFramework.TearDownAttribute, true);
+
+            ArrayList collectedActions = new ArrayList();
+
+            collectedActions.AddRange(ActionsHelper.GetActionsFromAttributes(fixtureType));
+
+            Type[] fixtureInterfaces = this.FixtureType.GetInterfaces();
+
+            foreach (Type fixtureInterface in fixtureInterfaces)
+                collectedActions.AddRange(ActionsHelper.GetActionsFromAttributes(fixtureInterface));
+
+            this.actions = new Attribute[collectedActions.Count];
+            collectedActions.CopyTo(this.actions);
         }
 
         protected override void DoOneTimeSetUp(TestResult suiteResult)

=== modified file 'src/NUnitCore/core/ParameterizedTestMethodSuite.cs'
--- src/NUnitCore/core/ParameterizedTestMethodSuite.cs	2010-09-21 19:07:23 +0000
+++ src/NUnitCore/core/ParameterizedTestMethodSuite.cs	2010-12-10 03:36:07 +0000
@@ -2,7 +2,8 @@
 // Copyright 2008, Charlie Poole
 // This is free software licensed under the NUnit license. You may
 // obtain a copy of the license at http://nunit.org.
-// ****************************************************************
+// ****************************************************************
+using System.Collections;
 using System.Reflection;
 using System.Text;
 
@@ -14,7 +15,8 @@
     /// </summary>
     public class ParameterizedMethodSuite : TestSuite
     {
-        private bool isTheory;
+        private bool isTheory;
+        private MethodInfo method;
 
         /// <summary>
         /// Construct from a MethodInfo
@@ -24,7 +26,8 @@
             : base(method.ReflectedType.FullName, method.Name)
         {
             this.maintainTestOrder = true;
-            this.isTheory = Reflect.HasAttribute(method, NUnitFramework.TheoryAttribute, true);
+            this.isTheory = Reflect.HasAttribute(method, NUnitFramework.TheoryAttribute, true);
+            this.method = method;
         }
 
         /// <summary>
@@ -56,9 +59,11 @@
                 if (suite != null)
                 {
                     this.setUpMethods = suite.GetSetUpMethods();
-                    this.tearDownMethods = suite.GetTearDownMethods();
+                    this.tearDownMethods = suite.GetTearDownMethods();
                 }
-            }
+            }
+
+            this.actions = ActionsHelper.GetActionsFromAttributes(this.method);
 
             // DYNAMIC: Get the parameters, and add the methods here.
             
@@ -74,7 +79,8 @@
 
 			this.Fixture = null;
 			this.setUpMethods = null;
-			this.tearDownMethods = null;
+			this.tearDownMethods = null;
+            this.actions = null;
 
             return result;
         }
@@ -95,6 +101,6 @@
         /// <param name="suiteResult"></param>
         protected override void DoOneTimeTearDown(TestResult suiteResult)
         {
-        }
+        }
     }
 }

=== modified file 'src/NUnitCore/core/SetUpFixture.cs'
--- src/NUnitCore/core/SetUpFixture.cs	2010-01-31 21:39:24 +0000
+++ src/NUnitCore/core/SetUpFixture.cs	2010-12-10 03:36:07 +0000
@@ -27,7 +27,9 @@
                 this.TestName.Name = this.TestName.Name.Substring(index + 1);
             
 			this.fixtureSetUpMethods = Reflect.GetMethodsWithAttribute( type, NUnitFramework.SetUpAttribute, true );
-			this.fixtureTearDownMethods = Reflect.GetMethodsWithAttribute( type, NUnitFramework.TearDownAttribute, true );
+			this.fixtureTearDownMethods = Reflect.GetMethodsWithAttribute( type, NUnitFramework.TearDownAttribute, true );
+
+		    this.actions = ActionsHelper.GetActionsFromAttributes(type);
 		}
 		#endregion
 

=== modified file 'src/NUnitCore/core/TestAssembly.cs'
--- src/NUnitCore/core/TestAssembly.cs	2010-01-31 21:39:24 +0000
+++ src/NUnitCore/core/TestAssembly.cs	2010-12-10 03:36:07 +0000
@@ -4,8 +4,9 @@
 // copyright ownership at http://nunit.org.
 // ****************************************************************
 
-using System;
-
+using System;
+using System.Reflection;
+
 namespace NUnit.Core
 {
     /// <summary>
@@ -18,7 +19,10 @@
         /// Initializes a new instance of the <see cref="TestAssembly"/> class.
         /// </summary>
         /// <param name="path">The path.</param>
-        public TestAssembly(string path) : base(path) { }
+        public TestAssembly(Assembly assembly, string path) : base(path)
+        {
+            this.actions = ActionsHelper.GetActionsFromAttributes(assembly);
+        }
 
         /// <summary>
         /// Gets the type of the test.

=== modified file 'src/NUnitCore/core/TestMethod.cs'
--- src/NUnitCore/core/TestMethod.cs	2010-10-10 02:04:39 +0000
+++ src/NUnitCore/core/TestMethod.cs	2010-12-10 03:36:07 +0000
@@ -2,12 +2,11 @@
 // This is free software licensed under the NUnit license. You
 // may obtain a copy of the license as well as information regarding
 // copyright ownership at http://nunit.org.
-// ****************************************************************
-
+// ****************************************************************
 namespace NUnit.Core
 {
 	using System;
-    using System.Collections;
+    using System.Collections;
     using System.Runtime.Remoting.Messaging;
     using System.Threading;
 	using System.Text;
@@ -42,7 +41,17 @@
 		/// <summary>
 		/// The teardown method
 		/// </summary>
-		protected MethodInfo[] tearDownMethods;
+		protected MethodInfo[] tearDownMethods;
+
+        /// <summary>
+        /// The actions
+        /// </summary>
+	    protected object[] actions;
+
+        /// <summary>
+        /// The parent suite's actions
+        /// </summary>
+	    protected object[] suiteActions;
 
         /// <summary>
         /// The ExpectedExceptionProcessor for this test, if any
@@ -210,22 +219,25 @@
             ContextDictionary context = Context;
             context._ec = TestExecutionContext.CurrentContext;
 
-            CallContext.SetData("NUnit.Framework.TestContext", context);
-
-            if (this.Parent != null)
-            {
-                this.Fixture = this.Parent.Fixture;
-                TestSuite suite = this.Parent as TestSuite;
-                if (suite != null)
-                {
-                    this.setUpMethods = suite.GetSetUpMethods();
-                    this.tearDownMethods = suite.GetTearDownMethods();
-                }
-            }
-
-            try
-            {
-                // Temporary... to allow for tests that directly execute a test case
+            CallContext.SetData("NUnit.Framework.TestContext", context);
+
+            if (this.Parent != null)
+            {
+                this.Fixture = this.Parent.Fixture;
+                TestSuite suite = this.Parent as TestSuite;
+                if (suite != null)
+                {
+                    this.setUpMethods = suite.GetSetUpMethods();
+                    this.tearDownMethods = suite.GetTearDownMethods();
+                    this.suiteActions = suite.GetTestActions();
+                }
+            }
+
+            try
+            {
+                this.actions = ActionsHelper.GetActionsFromAttributes(method);
+
+                // Temporary... to allow for tests that directly execute a test case);
                 if (Fixture == null && !method.IsStatic)
                     Fixture = Reflect.Construct(this.FixtureType);
 
@@ -300,9 +312,10 @@
 			TestResult testResult = new TestResult(this);
 			TestExecutionContext.CurrentContext.CurrentResult =  testResult;
 			
-			try 
-			{
+			try
+			{
                 RunSetUp();
+			    RunBeforeActions();
 
 				RunTestCase( testResult );
 			}
@@ -317,7 +330,8 @@
 			}
 			finally 
 			{
-				RunTearDown( testResult );
+			    RunAfterActions(testResult);
+				RunTearDown( testResult );
 
 				DateTime stop = DateTime.Now;
 				TimeSpan span = stop.Subtract(start);
@@ -354,7 +368,29 @@
 
 		#region Invoke Methods by Reflection, Recording Errors
 
-        private void RunSetUp()
+        private void RunBeforeActions()
+        {
+            object[][] targetActions = new object[][] {this.suiteActions, this.actions};
+            ActionsHelper.ExecuteActions(ActionLevel.Test, ActionPhase.Before, targetActions, this.Fixture, this.Method);
+        }
+
+        private void RunAfterActions(TestResult testResult)
+        {
+            try
+            {
+                object[][] targetActions = new object[][] { this.suiteActions, this.actions };
+                ActionsHelper.ExecuteActions(ActionLevel.Test, ActionPhase.After, targetActions, this.Fixture, this.Method);
+            }
+            catch(Exception ex)
+            {
+                if (ex is NUnitException)
+                    ex = ex.InnerException;
+                // TODO: What about ignore exceptions in teardown?
+                testResult.Error(ex, FailureSite.TearDown);
+            }
+        }
+
+	    private void RunSetUp()
         {
             if (setUpMethods != null)
                 foreach( MethodInfo setUpMethod in setUpMethods )

=== modified file 'src/NUnitCore/core/TestSuite.cs'
--- src/NUnitCore/core/TestSuite.cs	2010-09-19 22:47:12 +0000
+++ src/NUnitCore/core/TestSuite.cs	2010-12-10 03:36:07 +0000
@@ -46,7 +46,12 @@
         /// <summary>
         /// The teardown methods for this suite
         /// </summary>
-        protected MethodInfo[] tearDownMethods;
+        protected MethodInfo[] tearDownMethods;
+
+        /// <summary>
+        /// The actions for this suite
+        /// </summary>
+	    protected object[] actions;
 
         /// <summary>
         /// Set to true to suppress sorting this suite's contents
@@ -187,6 +192,24 @@
         {
             return tearDownMethods;
         }
+
+        internal virtual object[] GetTestActions()
+        {
+            ArrayList allActions = new ArrayList();
+
+            if (this.Parent != null && this.Parent is TestSuite)
+            {
+                object[] parentActions = ((TestSuite)this.Parent).GetTestActions();
+
+                if (parentActions != null)
+                    allActions.AddRange(parentActions);
+            }
+
+            if (this.actions != null)
+                allActions.AddRange(this.actions);
+
+            return allActions.ToArray();
+        }
         #endregion
 
 		#region Test Overrides
@@ -268,7 +291,8 @@
         {
 			TestResult suiteResult = new TestResult(this);
 			
-            DoOneTimeSetUp(suiteResult);
+            DoOneTimeSetUp(suiteResult);
+            DoOneTimeBeforeTestSuiteActions(suiteResult);
 
             if (this.Properties["_SETCULTURE"] != null)
                 TestExecutionContext.CurrentContext.CurrentCulture =
@@ -292,8 +316,9 @@
                     {
                         RunAllTests(suiteResult, listener, filter);
                     }
-                    finally
-                    {
+                    finally
+                    {
+                        DoOneTimeAfterTestSuiteActions(suiteResult);
                         DoOneTimeTearDown(suiteResult);
                     }
                     break;
@@ -342,6 +367,36 @@
             }
         }
 
+        protected virtual void DoOneTimeBeforeTestSuiteActions(TestResult suiteResult)
+        {
+            try
+            {
+                if (this.actions != null)
+                    ActionsHelper.ExecuteActions(ActionLevel.Suite, ActionPhase.Before, this.actions, this.Fixture, null);
+
+                TestExecutionContext.CurrentContext.Update();
+            }
+            catch (Exception ex)
+            {
+                if (ex is NUnitException || ex is System.Reflection.TargetInvocationException)
+                    ex = ex.InnerException;
+
+                if (ex is InvalidTestFixtureException)
+                    suiteResult.Invalid(ex.Message);
+                else if (IsIgnoreException(ex))
+                {
+                    this.RunState = RunState.Ignored;
+                    suiteResult.Ignore(ex.Message);
+                    suiteResult.StackTrace = ex.StackTrace;
+                    this.IgnoreReason = ex.Message;
+                }
+                else if (IsAssertException(ex))
+                    suiteResult.Failure(ex.Message, ex.StackTrace, FailureSite.SetUp);
+                else
+                    suiteResult.Error(ex, FailureSite.SetUp);
+            }
+        }
+
 		protected virtual void CreateUserFixture()
 		{
             if (arguments != null && arguments.Length > 0)
@@ -387,6 +442,26 @@
             }
         }
 
+        protected virtual void DoOneTimeAfterTestSuiteActions(TestResult suiteResult)
+        {
+            try
+            {
+                if (this.actions != null)
+                    ActionsHelper.ExecuteActions(ActionLevel.Suite, ActionPhase.After, this.actions, this.Fixture, null);
+            }
+            catch (Exception ex)
+            {
+                // Error in TestFixtureTearDown or Dispose causes the
+                // suite to be marked as a failure, even if
+                // all the contained tests passed.
+                NUnitException nex = ex as NUnitException;
+                if (nex != null)
+                    ex = nex.InnerException;
+
+                suiteResult.Failure(ex.Message, ex.StackTrace, FailureSite.TearDown);
+            }
+        }
+
         protected virtual bool IsAssertException(Exception ex)
         {
             return ex.GetType().FullName == NUnitFramework.AssertException;
@@ -422,9 +497,9 @@
                     {
                         test.RunState = this.RunState;
                         test.IgnoreReason = this.IgnoreReason;
-                    }
-
-                    TestResult result = test.Run(listener, filter);
+                    }
+
+                    TestResult result = test.Run(listener, filter);
 
 					log.Debug("Test result = " + result.ResultState);
 					

=== modified file 'src/NUnitCore/core/nunit.core.build'
--- src/NUnitCore/core/nunit.core.build	2010-12-09 05:10:05 +0000
+++ src/NUnitCore/core/nunit.core.build	2010-12-10 03:36:07 +0000
@@ -3,6 +3,7 @@
 
   <patternset id="source-files">
     <include name="AbstractTestCaseDecoration.cs"/>
+    <include name="ActionsHelper.cs" />
     <include name="AssemblyInfo.cs"/>
     <include name="AssemblyHelper.cs"/>
     <include name="AssemblyReader.cs"/>

=== modified file 'src/NUnitCore/core/nunit.core.dll.csproj'
--- src/NUnitCore/core/nunit.core.dll.csproj	2010-11-07 15:49:05 +0000
+++ src/NUnitCore/core/nunit.core.dll.csproj	2010-12-10 03:36:07 +0000
@@ -1,8 +1,8 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"; ToolsVersion="3.5">
   <PropertyGroup>
     <ProjectType>Local</ProjectType>
-    <ProductVersion>9.0.21022</ProductVersion>
+    <ProductVersion>9.0.30729</ProductVersion>
     <SchemaVersion>2.0</SchemaVersion>
     <ProjectGuid>{EBD43A7F-AFCA-4281-BB53-5CDD91F966A3}</ProjectGuid>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -117,6 +117,7 @@
     <Compile Include="AssemblyInfo.cs" />
     <Compile Include="AssemblyReader.cs" />
     <Compile Include="AssemblyResolver.cs" />
+    <Compile Include="ActionsHelper.cs" />
     <Compile Include="Builders\CombinatorialStrategy.cs" />
     <Compile Include="Builders\CombinatorialTestCaseProvider.cs" />
     <Compile Include="Builders\CombiningStrategy.cs" />

=== added file 'src/NUnitCore/tests/ActionAttributeTests.cs'
--- src/NUnitCore/tests/ActionAttributeTests.cs	1970-01-01 00:00:00 +0000
+++ src/NUnitCore/tests/ActionAttributeTests.cs	2010-12-10 03:36:07 +0000
@@ -0,0 +1,209 @@
+using System;
+using System.Collections;
+using System.Collections.Specialized;
+using NUnit.Framework;
+using NUnit.TestData.ActionAttributeTests;
+
+namespace NUnit.Core.Tests
+{
+    [TestFixture]
+    public class ActionAttributeTests
+    {
+        private class ActionAttributeFixtureFilter : TestFilter
+        {
+            public override bool Match(ITest test)
+            {
+                return test.TestName.FullName.StartsWith(typeof(ActionAttributeFixture).FullName);
+            }
+        }
+
+        private TestResult _result = null;
+        private readonly string[] _definitionSites = new string[]
+        {
+            "Assembly",
+            "SetUpFixture",
+            "Fixture",
+            "Interface",
+            "Method"
+        };
+
+        [TestFixtureSetUp]
+        public void Setup()
+        {
+            ActionAttributeFixture.Results = new StringCollection();
+
+            TestSuiteBuilder builder = new TestSuiteBuilder();
+            TestPackage package = new TestPackage(AssemblyHelper.GetAssemblyPath(typeof(ActionAttributeFixture)));
+            package.TestName = typeof(ActionAttributeFixture).Namespace;
+
+            Test suite = builder.Build(package);
+            _result = suite.Run(new NullListener(), new ActionAttributeFixtureFilter());
+        }
+
+        [Test]
+        public void TestsRunsSuccessfully()
+        {
+            Assert.IsTrue(_result.IsSuccess, "Test run was not successful.");
+            Assert.Contains("SomeTest-Case1", ActionAttributeFixture.Results, "Test Case 1 was not run.");
+            Assert.Contains("SomeTest-Case2", ActionAttributeFixture.Results, "Test Case 2 was not run.");
+            Assert.Contains("SomeOtherTest", ActionAttributeFixture.Results, "SomeOtherTest was not run.");
+
+            foreach(string message in ActionAttributeFixture.Results)
+                Console.WriteLine(message);
+        }
+
+        [Test]
+        public void FirstFourDefinitionSites_BeforeSuite_ExecuteFirst_InOrder()
+        {
+            for(int i = 0; i < 4; i++)
+            {
+                string prefix = string.Format("{0}.BeforeSuite-", _definitionSites[i]);
+                
+                Assert.IsTrue(
+                    ActionAttributeFixture.Results[i].StartsWith(prefix),
+                    string.Format("Did not find prefix '{0}' at index {1}", prefix, i));
+            }
+        }
+
+
+        [Test]
+        public void FirstFourDefinitionSites_AfterSuite_ExecuteLast_InOrder()
+        {
+            int lastIndex = ActionAttributeFixture.Results.Count - 1;
+            for (int i = lastIndex; i > lastIndex - 4; i--)
+            {
+                string prefix = string.Format("{0}.AfterSuite-", _definitionSites[lastIndex - i]);
+
+                Assert.IsTrue(
+                    ActionAttributeFixture.Results[i].StartsWith(prefix),
+                    string.Format("Did not find prefix '{0}' at index {1}", prefix, i));
+            }
+        }
+
+        [Test]
+        public void FirstFourDefinitionSites_BeforeTest_ExecuteInOrder_ForSomeOtherTest()
+        {
+            int startIndex = ActionAttributeFixture.Results.IndexOf("SomeOtherTest") - 4;
+            for (int i = startIndex; i < startIndex; i++)
+            {
+                string prefix = string.Format("{0}.BeforeTest-", _definitionSites[i - startIndex]);
+
+                Assert.IsTrue(
+                    ActionAttributeFixture.Results[i].StartsWith(prefix),
+                    string.Format("Did not find prefix '{0}' at index {1}", prefix, i));
+            }
+        }
+
+        [Test]
+        public void FirstFourDefinitionSites_AfterTest_ExecuteInOrder_ForSomeOtherTest()
+        {
+            int startIndex = ActionAttributeFixture.Results.IndexOf("SomeOtherTest");
+            for (int i = 1; i <= 4; i++)
+            {
+                string prefix = string.Format("{0}.AfterTest-", _definitionSites[4 - i]);
+
+                Assert.IsTrue(
+                    ActionAttributeFixture.Results[startIndex + i].StartsWith(prefix),
+                    string.Format("Did not find prefix '{0}' at index {1}", prefix, i));
+            }
+        }
+
+        [Test]
+        public void AllDefinitionSites_BeforeTest_ExecuteInOrder_ForSomeTestCase1()
+        {
+            int startIndex = ActionAttributeFixture.Results.IndexOf("SomeTest-Case1") - 5;
+            for (int i = startIndex; i < startIndex; i++)
+            {
+                string prefix = string.Format("{0}.BeforeTest-", _definitionSites[i - startIndex]);
+
+                Assert.IsTrue(
+                    ActionAttributeFixture.Results[i].StartsWith(prefix),
+                    string.Format("Did not find prefix '{0}' at index {1}", prefix, i));
+            }
+        }
+
+        [Test]
+        public void AllDefinitionSites_AfterTest_ExecuteInOrder_ForSomeTestCase1()
+        {
+            int startIndex = ActionAttributeFixture.Results.IndexOf("SomeTest-Case1");
+            for (int i = 1; i <= 5; i++)
+            {
+                string prefix = string.Format("{0}.AfterTest-", _definitionSites[5 - i]);
+
+                Assert.IsTrue(
+                    ActionAttributeFixture.Results[startIndex + i].StartsWith(prefix),
+                    string.Format("Did not find prefix '{0}' at index {1}", prefix, i));
+            }
+        }
+
+        [Test]
+        public void AllDefinitionSites_BeforeTest_ExecuteInOrder_ForSomeTestCase2()
+        {
+            int startIndex = ActionAttributeFixture.Results.IndexOf("SomeTest-Case2") - 5;
+            for (int i = startIndex; i < startIndex; i++)
+            {
+                string prefix = string.Format("{0}.BeforeTest-", _definitionSites[i - startIndex]);
+
+                Assert.IsTrue(
+                    ActionAttributeFixture.Results[i].StartsWith(prefix),
+                    string.Format("Did not find prefix '{0}' at index {1}", prefix, i));
+            }
+        }
+
+        [Test]
+        public void AllDefinitionSites_AfterTest_ExecuteInOrder_ForSomeTestCase2()
+        {
+            int startIndex = ActionAttributeFixture.Results.IndexOf("SomeTest-Case2");
+            for (int i = 1; i <= 5; i++)
+            {
+                string prefix = string.Format("{0}.AfterTest-", _definitionSites[5 - i]);
+
+                Assert.IsTrue(
+                    ActionAttributeFixture.Results[startIndex + i].StartsWith(prefix),
+                    string.Format("Did not find prefix '{0}' at index {1}", prefix, i));
+            }
+        }
+
+        [Test]
+        public void MethodDefinedSite_BeforeSuite_BeforeSomeTestCase1()
+        {
+            int testCase = ActionAttributeFixture.Results.IndexOf("SomeTest-Case1");
+            Assert.IsTrue(testCase > ActionAttributeFixture.Results.IndexOf("Method.BeforeSuite-ActionAttributeFixture"));
+        }
+
+        [Test]
+        public void MethodDefinedSite_AfterSuite_BeforeSomeTestCase2()
+        {
+            int testCase = ActionAttributeFixture.Results.IndexOf("SomeTest-Case2");
+            Assert.IsTrue(testCase < ActionAttributeFixture.Results.IndexOf("Method.AfterSuite-ActionAttributeFixture"));
+        }
+
+        [Test]
+        public void AllActions_BeforeAndAfterTest_HasAccessToFixture()
+        {
+            foreach(string message in ActionAttributeFixture.Results)
+            {
+                if (message.Contains("BeforeTest") || message.Contains("AfterTest"))
+                    Assert.IsTrue(message.Contains(typeof(ActionAttributeFixture).Name), string.Format("'{0}' shows action does not have access to fixture.", message));
+            }
+        }
+
+        [Test]
+        public void AllActions_BeforeAndAfterTest_HasAccessToMethodInfo()
+        {
+            StringCollection validEndSegments = new StringCollection();
+            validEndSegments.AddRange(new string[] {"SomeOtherTest", "SomeTest"});
+
+            foreach (string message in ActionAttributeFixture.Results)
+            {
+                if (message.Contains("BeforeTest") || message.Contains("AfterTest"))
+                {
+                    string endSegment = message.Substring(message.LastIndexOf('-') + 1);
+
+                    Assert.IsTrue(validEndSegments.Contains(endSegment),
+                                  string.Format("'{0}' shows action does not have access to method info.", message));
+                }
+            }
+        }
+    }
+}

=== modified file 'src/NUnitCore/tests/nunit.core.tests.csproj'
--- src/NUnitCore/tests/nunit.core.tests.csproj	2010-12-09 05:10:05 +0000
+++ src/NUnitCore/tests/nunit.core.tests.csproj	2010-12-10 03:36:07 +0000
@@ -1,8 +1,8 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"; ToolsVersion="3.5">
   <PropertyGroup>
     <ProjectType>Local</ProjectType>
-    <ProductVersion>9.0.21022</ProductVersion>
+    <ProductVersion>9.0.30729</ProductVersion>
     <SchemaVersion>2.0</SchemaVersion>
     <ProjectGuid>{DD758D21-E5D5-4D40-9450-5F65A32F359C}</ProjectGuid>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -141,6 +141,7 @@
     <Compile Include="AttributeDescriptionFixture.cs" />
     <Compile Include="AttributeInheritance.cs" />
     <Compile Include="BasicRunnerTests.cs" />
+    <Compile Include="ActionAttributeTests.cs" />
     <Compile Include="CallContextTests.cs" />
     <Compile Include="CategoryAttributeTests.cs" />
     <Compile Include="CombinatorialTests.cs" />

=== added directory 'src/NUnitFramework/framework/Interfaces'
=== added file 'src/NUnitFramework/framework/Interfaces/IAction.cs'
--- src/NUnitFramework/framework/Interfaces/IAction.cs	1970-01-01 00:00:00 +0000
+++ src/NUnitFramework/framework/Interfaces/IAction.cs	2010-12-10 03:36:07 +0000
@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NUnit.Framework
+{
+    public interface IAction
+    {
+    }
+}

=== added file 'src/NUnitFramework/framework/Interfaces/ISuiteAction.cs'
--- src/NUnitFramework/framework/Interfaces/ISuiteAction.cs	1970-01-01 00:00:00 +0000
+++ src/NUnitFramework/framework/Interfaces/ISuiteAction.cs	2010-12-10 03:36:07 +0000
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NUnit.Framework
+{
+    public interface ISuiteAction : IAction
+    {
+        void BeforeSuite(object fixture);
+        void AfterSuite(object fixture);
+    }
+}

=== added file 'src/NUnitFramework/framework/Interfaces/ITestAction.cs'
--- src/NUnitFramework/framework/Interfaces/ITestAction.cs	1970-01-01 00:00:00 +0000
+++ src/NUnitFramework/framework/Interfaces/ITestAction.cs	2010-12-10 03:36:07 +0000
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Text;
+
+namespace NUnit.Framework
+{
+    public interface ITestAction : IAction
+    {
+        void BeforeTest(object fixture, MethodInfo method);
+        void AfterTest(object fixture, MethodInfo method);
+    }
+}

=== modified file 'src/NUnitFramework/framework/nunit.framework.build'
--- src/NUnitFramework/framework/nunit.framework.build	2010-12-09 18:18:45 +0000
+++ src/NUnitFramework/framework/nunit.framework.build	2010-12-10 03:36:07 +0000
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <project name="NUnitFramework" default="build" basedir=".">
 
-  <patternset id="source-files">
+    <patternset id="source-files">
         <include name="Attributes/CategoryAttribute.cs"/>
         <include name="Attributes/DatapointAttributes.cs"/>
         <include name="Attributes/DescriptionAttribute.cs"/>
@@ -75,6 +75,9 @@
         <include name="Exceptions/IgnoreException.cs"/>
         <include name="Exceptions/InconclusiveException.cs"/>
         <include name="Exceptions/SuccessException.cs"/>
+        <include name="Interfaces/IAction.cs" />
+        <include name="Interfaces/ISuiteAction.cs" />
+        <include name="Interfaces/ITestAction.cs" />
         <include name="AssemblyInfo.cs"/>
         <include name="Assert.cs"/>
         <include name="AssertionHelper.cs"/>
@@ -101,6 +104,7 @@
         <include name="Text.cs"/>
         <include name="TextMessageWriter.cs"/>
         <include name="Throws.cs"/>
+<<<<<<< TREE
   </patternset>
 
   <target name="build">
@@ -141,4 +145,46 @@
     </copy>
   </target>
 
-</project>
\ No newline at end of file
+</project>=======
+    </patternset>
+
+    <target name="build">
+
+        <csc target="library"
+            output="${current.framework.dir}/nunit.framework.dll"
+            doc="${current.framework.dir}/nunit.framework.xml"
+            debug="${build.debug}"
+            define="${build.defines}">
+            <nowarn>
+                <warning number="618,672"/>
+                <warning number="1699" if="${runtime.version=='2.0'}"/>
+            </nowarn>
+            <sources basedir=".">
+                <patternset refid="source-files"/>
+                <include name="../../GeneratedAssemblyInfo.cs" />
+            </sources>
+        </csc>
+
+        <!-- Needed locally by some NUnit tests -->
+        <copy file="${current.framework.dir}/nunit.framework.dll"
+          todir="${current.test.dir}"/>
+
+        <!-- Needed in base dir by pnunit tests -->
+        <copy file="${current.framework.dir}/nunit.framework.dll"
+          todir="${current.build.dir}"/>
+
+    </target>
+
+    <target name="package">
+        <copy todir="${package.src.dir}/NUnitFramework/framework">
+            <fileset>
+                <patternset refid="source-files"/>
+                <include name="nunit.framework.build"/>
+                <include name="nunit.framework.dll.csproj"/>
+                <include name="SyntaxElements.txt"/>
+            </fileset>
+        </copy>
+    </target>
+
+</project>
+>>>>>>> MERGE-SOURCE

=== modified file 'src/NUnitFramework/framework/nunit.framework.dll.csproj'
--- src/NUnitFramework/framework/nunit.framework.dll.csproj	2010-11-07 15:49:05 +0000
+++ src/NUnitFramework/framework/nunit.framework.dll.csproj	2010-12-10 03:36:07 +0000
@@ -1,8 +1,8 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"; ToolsVersion="3.5">
   <PropertyGroup>
     <ProjectType>Local</ProjectType>
-    <ProductVersion>9.0.21022</ProductVersion>
+    <ProductVersion>9.0.30729</ProductVersion>
     <SchemaVersion>2.0</SchemaVersion>
     <ProjectGuid>{83DD7E12-A705-4DBA-9D71-09C8973D9382}</ProjectGuid>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -155,9 +155,12 @@
     <Compile Include="FileAssert.cs" />
     <Compile Include="GlobalSettings.cs" />
     <Compile Include="Has.cs" />
+    <Compile Include="Interfaces\IAction.cs" />
     <Compile Include="IExpectException.cs" />
     <Compile Include="Is.cs" />
+    <Compile Include="Interfaces\ITestAction.cs" />
     <Compile Include="ITestCaseData.cs" />
+    <Compile Include="Interfaces\ISuiteAction.cs" />
     <Compile Include="Iz.cs" />
     <Compile Include="List.cs" />
     <Compile Include="ListMapper.cs" />

=== modified file 'src/NUnitFramework/tests/nunit.framework.tests.csproj'
--- src/NUnitFramework/tests/nunit.framework.tests.csproj	2010-11-07 15:49:05 +0000
+++ src/NUnitFramework/tests/nunit.framework.tests.csproj	2010-12-10 03:36:07 +0000
@@ -1,8 +1,8 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"; ToolsVersion="3.5">
   <PropertyGroup>
     <ProjectType>Local</ProjectType>
-    <ProductVersion>9.0.21022</ProductVersion>
+    <ProductVersion>9.0.30729</ProductVersion>
     <SchemaVersion>2.0</SchemaVersion>
     <ProjectGuid>{8C326431-AE57-4645-ACC1-A90A0B425129}</ProjectGuid>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>

=== added file 'src/tests/test-assembly/ActionAttributeFixture.cs'
--- src/tests/test-assembly/ActionAttributeFixture.cs	1970-01-01 00:00:00 +0000
+++ src/tests/test-assembly/ActionAttributeFixture.cs	2010-12-10 03:36:07 +0000
@@ -0,0 +1,97 @@
+using System;
+using System.Collections.Specialized;
+using NUnit.Framework;
+using System.Diagnostics;
+using System.Reflection;
+using NUnit.TestData.ActionAttributeTests;
+
+[assembly: SampleAction("Assembly")]
+
+namespace NUnit.TestData.ActionAttributeTests
+{
+    [SetUpFixture]
+    [SampleAction("SetUpFixture")]
+    public class SetupFixture
+    {
+    }
+
+    [TestFixture]
+    [SampleAction("Fixture")]
+    public class ActionAttributeFixture : IWithAction
+    {
+        private static StringCollection _Results = null;
+        public static StringCollection Results
+        {
+            get { return _Results; }
+            set { _Results = value; }
+        }
+
+        StringCollection IWithAction.Results { get { return Results; } }
+
+        [Test, TestCase("SomeTest-Case1"), TestCase("SomeTest-Case2")]
+        [SampleAction("Method")]
+        public void SomeTest(string message)
+        {
+            ((IWithAction)this).Results.Add(message);
+        }
+
+        [Test]
+        public void SomeOtherTest()
+        {
+            ((IWithAction)this).Results.Add("SomeOtherTest");
+        }
+
+    }
+
+    [SampleAction("Interface")]
+    public interface IWithAction
+    {
+        StringCollection Results { get; }
+    }
+
+    [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Method | AttributeTargets.Module, AllowMultiple = true, Inherited = true)]
+    public class SampleActionAttribute : Attribute, ISuiteAction, ITestAction
+    {
+        private string _Prefix = null;
+
+        public SampleActionAttribute(string prefix)
+        {
+            _Prefix = prefix;
+        }
+
+        void ISuiteAction.BeforeSuite(object fixture)
+        {
+            AddResult(fixture, null);
+        }
+
+        void ISuiteAction.AfterSuite(object fixture)
+        {
+            AddResult(fixture, null);
+        }
+
+        void ITestAction.BeforeTest(object fixture, MethodInfo method)
+        {
+            AddResult(fixture, method);
+        }
+
+        void ITestAction.AfterTest(object fixture, MethodInfo method)
+        {
+            AddResult(fixture, method);
+        }
+
+        private void AddResult(object fixture, MethodInfo method)
+        {
+            StackFrame frame = new StackFrame(1);
+            MethodBase actionMethod = frame.GetMethod();
+
+            string actionMethodName = actionMethod.Name.Substring(actionMethod.Name.LastIndexOf('.') + 1);
+            string message = string.Format("{0}.{1}-{2}" + (method != null ? "-{3}" : ""),
+                                           _Prefix,
+                                           actionMethodName,
+                                           fixture == null ? "{no-fixture}" : fixture.GetType().Name,
+                                           method != null ? method.Name : "");
+
+            ActionAttributeFixture.Results.Add(message);
+        }
+    }
+}

=== modified file 'src/tests/test-assembly/test-assembly.csproj'
--- src/tests/test-assembly/test-assembly.csproj	2010-11-07 15:49:05 +0000
+++ src/tests/test-assembly/test-assembly.csproj	2010-12-10 03:36:07 +0000
@@ -1,8 +1,8 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"; ToolsVersion="3.5">
   <PropertyGroup>
     <ProjectType>Local</ProjectType>
-    <ProductVersion>9.0.21022</ProductVersion>
+    <ProductVersion>9.0.30729</ProductVersion>
     <SchemaVersion>2.0</SchemaVersion>
     <ProjectGuid>{1960CAC4-9A82-47C5-A9B3-55BC37572C3C}</ProjectGuid>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -126,6 +126,7 @@
     </Compile>
     <Compile Include="AttributeDescriptionTests.cs" />
     <Compile Include="AttributeInheritance.cs" />
+    <Compile Include="ActionAttributeFixture.cs" />
     <Compile Include="CategoryAttributeTests.cs" />
     <Compile Include="ConsoleRunnerTest.cs" />
     <Compile Include="CultureAttributeTests.cs" />