//---------------------------------------------------------------------- // Private Methods //---------------------------------------------------------------------- /// <summary> /// Will asyncronously start the execution of the call. /// </summary> private void StartCall() { _callComplete = new ManualResetEvent(false); _thread = new Thread(this.RealInvoke); _thread.Name = string.Format( CultureInfo.InvariantCulture, "{0}.{1}", _method.DeclaringType.Name, _method.Name); TestStepAttribute attrib = (TestStepAttribute) TestServices.GetFirstAttribute( typeof(TestStepAttribute), Method); _thread.SetApartmentState(attrib.ApartmentState); _thread.Start(); }
/// <summary> /// Will return all the dependencies (as defined by WaitFor) on a call. /// </summary> /// <remarks> /// Current implementation assumes a default plan where calls map 1:1 to /// methods marked as test steps. This will need to change for XML /// plans. /// </remarks> /// <param name="attribute">The attribute for which we would like the /// dependent calls.</param> /// <param name="knownCalls">A dictionary of known calls with the name /// of the test step as the key.</param> /// <returns>A list of the dependent calls from the knownCalls /// parameter.</returns> private static Call[] GetDependencies( TestStepAttribute attribute, Dictionary<string, Call> knownCalls) { string waitForList = attribute.WaitFor; // PreCondition: if there is no list, return an empty array if (string.IsNullOrEmpty(waitForList)) { return new Call[0]; } // turn the comman seperated list into an array string[] callNames = waitForList.Split( CultureInfo.InvariantCulture .TextInfo.ListSeparator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries); // return array size matches the list Call[] dependencies = new Call[callNames.Length]; // look up the dependent call and store it in the array for (int i = 0; i < callNames.Length; i++) { string callName = callNames[i].Trim(); if (knownCalls.ContainsKey(callName)) { dependencies[i] = knownCalls[callName]; } else { // fail if there is an unknown dependency TestServices.Assert( false, "WaitFor call named {0} not found.", callName); } } return dependencies; }
//---------------------------------------------------------------------- // Constructors //---------------------------------------------------------------------- /// <summary> /// Creates a Call that will use the MethodInvoker to invoke the Method /// with the specified arguments on the object. /// </summary> /// <param name="invoker">The MethodInvoker to use for the call.</param> /// <param name="method">The Method to call.</param> /// <param name="target">The Target of the call.</param> /// <param name="args">The Parameters use for the call.</param> /// <param name="dependencies">The calls this call will wait on prior to /// invoking.</param> public Call( MethodInvoker invoker, MethodInfo method, object target, object[] args, Call[] dependencies) { _invoker = invoker; Method = method; Target = target; Parameters = args; _dependencies.AddRange(dependencies); TestStepAttribute attrib = TestServices.GetFirstAttribute( typeof(TestStepAttribute), Method) as TestStepAttribute; if (attrib != null) { _timeout = attrib.Timeout; _delay = attrib.Delay; _async = attrib.Async; _order = attrib.Order; } else { TestServices.Assert( false, "Method does not have the TestStep attribute."); } if (_forceNoTimeout & (_timeout != Timeout.Infinite)) { TestServices.Trace( "Forcing Timeout.Infinite on {0} was {1}.", method.Name, _timeout); _timeout = Timeout.Infinite; } }
/// <summary> /// Will construct the default execution plan. The default plan orders /// test steps by Order parameter, then by phsyical order in the class. /// </summary> /// <param name="invoker">The invoker to use in the plan.</param> /// <param name="test">The test to generate the plan for.</param> /// <returns>An execution plan.</returns> private static ExecutionPlan BuildDefaultExecutionPlan( MethodInvoker invoker, object test) { // all methods in the test MethodInfo[] methods = test.GetType().GetMethods(); int count = methods.Length; // use to store all the calls in the execution plan // default plan will only execute a test step method once so the // number of calls <= the count of methods Dictionary<string, Call> calls = new Dictionary<string, Call>(count); // use to get the TestStep attribute for the call // testSteps are attributes on methods thus they are <= the count // of methods. Dictionary<Call, TestStepAttribute> testSteps = new Dictionary<Call, TestStepAttribute>(count); // use to get the orginal physical order of the call // default plan will only execute a test step method once so the // number of calls <= the count of methods Dictionary<Call, int> physicalOrder = new Dictionary<Call, int>(count); // for every method in the test object for (int i = 0; i < count; i++) { MethodInfo method = methods[i]; TestStepAttribute attrib = TestServices.GetFirstAttribute(typeof(TestStepAttribute), method) as TestStepAttribute; // if an attribute was found, it is a TestStep if (attrib != null) { // again in the default plan calls map 1:1 to methods that // are test steps, so create a call for the method Call call = new Call( invoker, method, test, new object[method.GetParameters().Length], GetDependencies(attrib, calls)); if (!calls.ContainsKey(method.Name)) { calls.Add(method.Name, call); physicalOrder.Add(call, i); testSteps.Add(call, attrib); } else { TestServices.Warning( "Skipping TestStep \"{0}\".\n" + "Overloads are not allowed for methods marked with TestStep attribute.", method.Name); } } } // goal: order calls by oder parameter, then by position List<Call> sorted = new List<Call>(calls.Values); sorted.Sort(delegate(Call left, Call right) { int result = left.Order.CompareTo(right.Order); // if the order is the same sub sort by physical order if (result == 0) { result = physicalOrder[left] .CompareTo(physicalOrder[right]); } return result; }); // build the execution plan ExecutionPlan plan; plan.TestSteps = testSteps; plan.Calls = sorted; return plan; }