public TestResultArgs(Process process, Activity activity, TestResultStage resultStage, string extraDetails) { CurrentProcess = process; CurrentActivity = activity; ResultStage = resultStage; ExtraDetails = extraDetails; }
private void CheckActivityStatus(Process p, Activity a, out bool breakFromLoop) { breakFromLoop = false; char[] squareBrackets = { '[', ']' }; string statusToCheck = a.Action.Trim(squareBrackets); int processInstanceIDToUse = p.ProcessInstanceID; //This is a system event and we just want to check if it is there and finished. if (!p.ProcessName.Equals(a.ProcessName)) {//TODO: doesnot cater for recursive IPCs processInstanceIDToUse = SmartObjectHelper.GetIPCProcessInstanceByFolio(K2Server, p.ProcessInstanceID, a.ProcessName); //////SendResult(new TestResultArgs(p, a, TestResultStage.DebugMessage, string.Format("IPC detected pid:{0} originalProcessName:{1} newProcessName{2}",processInstanceIDToUse,p.ProcessName, a.ProcessName))); //Check for IPC and if so get the child IPC pid if (processInstanceIDToUse == (int)SmartObjectHelper.ReturnCodes.NoProcessInstanceFound) { string IPCMessage = string.Format("'{0}' ({1})- '{2}':IPC child not found", p.ProcessName, a.ProcessName, a.Name); //////a.TestStatus = IPCMessage; SendResult(new TestResultArgs(p, a, TestResultStage.ActivityNotFoundRetrying, IPCMessage)); //////break; } } if (statusToCheck == "NotTaken") { //check path taken or not taken as appropriate. if (string.IsNullOrEmpty(SmartObjectHelper.GetActivityStatus(K2Server, processInstanceIDToUse, a.Name).Trim())) { a.Retry = false; a.TestStatus = string.Format("'{0}' - '{1}':Activity Not Taken Correctly: '", a.ProcessName, a.Name); SendResult(new TestResultArgs(p, a, TestResultStage.ActivityActioned, a.TestStatus + a.Name + "' ")); breakFromLoop = true; return; } else { string err = string.Format("'{0}' - '{1}':Activity Taken Incorrectly: ", a.ProcessName, a.Name); FailTest(err, TestResultStage.ActivityExecutionError, p, a); breakFromLoop = true; return; } } else //Completed, Active etc. { string actualStatus = SmartObjectHelper.GetActivityStatus(K2Server, processInstanceIDToUse, a.Name).Trim(); //////SendResult(new TestResultArgs(p, a, TestResultStage.DebugMessage, string.Format("statusToCheck:{0} actualStatus:{1} newProcessName{2}", statusToCheck, actualStatus, a.ProcessName))); if (actualStatus == statusToCheck) { a.Retry = false; a.TestStatus = string.Format("'{0}' - '{1}':Activity {2}: '", a.ProcessName, a.Name, actualStatus); SendResult(new TestResultArgs(p, a, TestResultStage.ActivityActioned, a.TestStatus + a.Name + "' ")); bool breakOut; //process the datafields to determine if we have to save a dfs value processDataFields(p, a, out breakOut); breakFromLoop = true; return; } else { // string err = string.Format("'{0}' - '{1}':[{2}] action expected but actual status was [{3}]", a.ProcessName, a.Name, statusToCheck, actualStatus); FailTest(err, TestResultStage.ActivityNotFoundRetrying, p, a); } } }
private void ProcessActivity(Process p, Activity a) { //Ok so we have an activity to action. //try and get the item from the worklist, but only a limited number of times. bool actioned = false; for (int i = 0; i < a.MaxRetryCount; i++) { bool breakFromLoop = false; retryLoopIteration(i, p, a, out breakFromLoop, out actioned); if (breakFromLoop) { break; } }//retry loop if (a.Retry) { p.ActivityExecutionError = "Could not find activity event: '" + a.Name + "'"; ////////SendResult(new TestResultArgs(p, a, TestResultStage.DebugMessage, string.Format("a.teststatus blank {0}",a.TestStatus))); SendResult(new TestResultArgs(p, a, TestResultStage.ActivityNotFoundGivingUp, "Error: Activity Name: '" + a.Name + "' could not be found after " + a.MaxRetryCount.ToString() + " retries.")); p.ProcessStatus = "Error"; a.TestStatus = "Could not find activity"; p.ProcessHasUnexpectedErrors = true; a.Retry = false; } }
private void ProcessActivities(Process p) { foreach (Activity a in p.Activities) { currentActivity = a; //Break out of this test if there is an error. if (p.ProcessHasUnexpectedErrors) break; if (a.Name.Equals("start", StringComparison.OrdinalIgnoreCase)) { ProcessStartActivity(p, a); }//END OF START ACTIVITY else if (a.Name.Equals("SubProcess", StringComparison.OrdinalIgnoreCase)) { foreach (Process subProcess in a.SubProcesses) { ProcessProcess(subProcess); } } else //Not start { ProcessActivity(p, a); }//Not Start if (!this.ProceedWithTest) { break; } }//foreach activity }
private void ProcessProcess(Process p) { currentProcess = p; try { string message = string.Format("Starting test {0,30}", p.Description); SendResult(new TestResultArgs(p, null, TestResultStage.NewProcessTestStarting, message)); p.TestStartDate = DateTime.Now; ProcessActivities(p); p.TestEndDate = DateTime.Now; SendResult(new TestResultArgs(p, null, TestResultStage.NewProcessTestFinished, "Test " + p.Description + " ended")); var servItems = k2helper.WorkflowServer().GetProcessInstances(p.Folio); if (servItems.Count == 1) { var err = k2helper.WorkflowServer().GetError(servItems[0].ProcID, servItems[0].ID); if (!string.IsNullOrEmpty(err)) { if (err.Count() > 0) p.ProcessError = err.ToString(); p.ProcessStatus = "In Error"; } else { p.ProcessStatus = "Running"; } } else { p.ProcessStatus = "Complete"; } } catch (Exception ex) { if (ex.IsFatal()) { throw; } string innerExceptionStackTrace = null; if (ex.InnerException != null) { innerExceptionStackTrace = ex.InnerException.StackTrace; } SendResult(new TestResultArgs(currentProcess, currentActivity, TestResultStage.FatalError, string.Format(new NullFormat(),"Process : {0}{1}{2} innerexception:{3}", currentActivity.ProcessName, ex.Message, ex.StackTrace, innerExceptionStackTrace))); p.ProcessStatus = "In Error"; } }
private void InvokeMethod(Process process, Activity activity, string assembly, string className, string method, List<string> parameters) { object[] para = new object[parameters.Count]; for (int i = 0; i < parameters.Count; i++) { switch (parameters[i]) { case "[Process]": para[i] = process; break; case "[Activity]": para[i] = activity; break; default: para[i] = parameters[i]; break; } } DynamicCodeExecution.InvokeMethod(assembly, className, method, para); }
private Process ConstructProcessFromXmlNodeListOldStyle(XmlNode processNode, Process newprocess, string xmlRootDir, string parentFilename) { newprocess.Description = XmlHelper.GetAttributeValue(processNode, "description", XmlHelper.NameCaseSensitive.No); //Now lets get all Activities listed in the XML newprocess.Activities.AddRange(ConstructActivitiesFromXMLNodeList(processNode.ChildNodes, xmlRootDir, newprocess, parentFilename)); return newprocess; }
private void processDataFields(Process p, Activity a, out bool breakFromLoop) { bool needsUpdate = false; breakFromLoop = false; foreach (var df in a.DataFields) { string dfValue = null; int i = 0; while (dfValue == null) { dfValue = SmartObjectHelper.GetProcessDataFieldValue(K2Server, p.ProcessInstanceID, df.Key); System.Threading.Thread.Sleep(1000); i++; if (i > 10) { break; } } var mockDF = new CoreDataField(); mockDF.Name = df.Key; SendResult(new TestResultArgs(p,a, TestResultStage.DebugMessage, string.Format(new NullFormat(), "dfKey:{0} - dfvalue:{0}",df.Key, dfValue))); if (dfValue == null) { dfValue = "NULL"; } mockDF.Value = dfValue; processDataField(df.Value, null, mockDF, out needsUpdate, out breakFromLoop); } }
private void RunMethod(Process p, Activity a, MethodType type) { //Do a quick check switch (type) { case MethodType.PreMethod: if (!a.PreMethodCall.NeedToInvoke) return; break; case MethodType.PostMethod: if (!a.PostMethodCall.NeedToInvoke) return; break; } string sAssembly = (type == MethodType.PreMethod) ? a.PreMethodCall.Assembly : a.PostMethodCall.Assembly; string sClass = (type == MethodType.PreMethod) ? a.PreMethodCall.Class : a.PostMethodCall.Class; string sMethod = (type == MethodType.PreMethod) ? a.PreMethodCall.Method : a.PostMethodCall.Method; List<string> sParameters = (type == MethodType.PreMethod) ? a.PreMethodCall.Parameters : a.PostMethodCall.Parameters; //Run method. try { InvokeMethod(p, a, sAssembly, sClass, sMethod, sParameters); SendResult(new TestResultArgs(p, a, (type == MethodType.PreMethod) ? TestResultStage.ActivityPreMethodExecuted : TestResultStage.ActivityPostMethodExecuted)); } catch (Exception ex) { if (ex.IsFatal()) { throw; } //TODO: refactor to use FailTest p.ActivityExecutionError = a.ActivityExecutionError = ex.Message.ToString(); SendResult(new TestResultArgs(p, a, TestResultStage.ActivityExecutionError, type.ToString() + " execution error : " + p.ActivityExecutionError)); p.ProcessHasUnexpectedErrors = true; } }
private void ConstructProcessesFromXMLNodeList(XmlNodeList xmlnodelist, List<Process> processes, string xmlRootDir, string parentFilename) { //List<Process> newProcesses = new List<Process>(); foreach (XmlNode processNode in xmlnodelist) { string processTypeString = XmlHelper.GetAttributeValue(processNode, "type", "Assert").ToLower(); ProcessType processType = (ProcessType)Enum.Parse(typeof(ProcessType), processTypeString); string processName = XmlHelper.GetAttributeValue(processNode, "processName"); string processUniqueID = XmlHelper.GetAttributeValue(processNode, "uniqueID", "NoUniqueID"); string fileName = XmlHelper.GetAttributeValue(processNode, "fileName"); bool runTest = bool.Parse(XmlHelper.GetAttributeValue(processNode, "TestEnabled", "True")); if (!runTest) continue; // Skip the remainder of this iteration.; if (!Regex.Match(this.TestsTypesToRun.ToString(), processTypeString, RegexOptions.IgnoreCase).Success) { //These are not the process types you are looking for. #jedimindtrick continue; } if (string.IsNullOrEmpty(fileName)) {//This is an actual process to test and not a pointer to another file if (processUniqueID == "NoUniqueID" || !uniqueProcessIdList.Contains(processUniqueID)) { uniqueProcessIdList.Add(processUniqueID); Process newProcess = new Process(); if (string.IsNullOrEmpty(processName)) {//old style Process Name is a node of the child nodes newProcess = ConstructProcessFromXmlNodeListOldStyle(processNode, xmlRootDir, parentFilename); } else { newProcess = ConstructProcessFromXmlNodeListNewStyle(processNode, xmlRootDir, parentFilename); } newProcess.ProcessType = processType; processes.Add(newProcess); } else { SendResult(new TestResultArgs(null, null, TestResultStage.DebugMessage, string.Format("Constructed Process already exists in test file, so skipping it {0} - {1}", processName, processUniqueID))); } } } }
private bool ActionActivity(Process p, Activity a, SourceCode.Workflow.Client.WorklistItem item, string actionuser, bool impersonateUser) { try { ////////item.ActivityInstanceDestination.DataFields["Data.Actioner"].Value = "bob"; ////////item.ProcessInstance.Update(); bool fieldToSaveFound = false; foreach (KeyValuePair<string, CoreDataField> kvp in a.DataFields) { if (kvp.Value.Check == "set") { //try to set the datafield if (kvp.Value.Type == CoreDataFieldType.Activity) { item.ActivityInstanceDestination.DataFields[kvp.Value.Name].Value = kvp.Value.Value; } else { item.ProcessInstance.DataFields[kvp.Value.Name].Value = kvp.Value.Value; } fieldToSaveFound = true; } } if (fieldToSaveFound) { item.ProcessInstance.Update(); } if (impersonateUser) { k2helper.WorkflowClient().connection.ImpersonateUser(actionuser); } if (this.AutoActionTasks) { item.Actions[a.Action].Execute(); } else { this.CurrentTaskURL = item.Data; SendResult(new TestResultArgs(p,a, TestResultStage.CreateTask, item.Data)); try { Thread.Sleep(Timeout.Infinite); } catch(ThreadInterruptedException) { } } string actionMsg = string.Format("'{0}' - '{1}': Activity '{2}' as user {3}", a.ProcessName, a.Name, a.Action, actionuser); a.Retry = false; a.TestStatus = actionMsg; SendResult(new TestResultArgs(p, a, TestResultStage.ActivityActioned, actionMsg)); if (impersonateUser) { k2helper.WorkflowClient().connection.RevertUser(); } return true; } catch (Exception ex) { if (ex.IsFatal()) { throw; } if (impersonateUser) { k2helper.WorkflowClient().connection.RevertUser(); } string err = string.Format("'{0}' - '{1}': Execution failed : {2}, {3}", a.ProcessName, a.Name, ex.Message, ex.StackTrace); //Some errors are temporary, so retry FailTest(err, TestResultStage.ActivityNotFoundRetrying, p, a); return false; } }
private void ProcessStartActivity(Process p, Activity a) { RunMethod(p, a, MethodType.PreMethod); //Start Process SendResult(new TestResultArgs(p, a, TestResultStage.ProcessStarting, "Process : " + a.ProcessName)); p.Folio = string.Format("{0}", "TEST : " + DateTime.Now.ToString()); //If we need to set a datafield to an existing variable, we need to modify the a.DataFields collection prepareDataFields(a.DataFields); try { p.ProcessInstanceID = k2helper.StartK2Process(a.ProcessName, p.Folio, a.DataFields); } catch (Exception ex) { if (ex.IsFatal()) { throw; } SendResult(new TestResultArgs(p, a, TestResultStage.FatalError, string.Format("Process : {0}{1}{2}", a.ProcessName, ex.Message, ex.StackTrace))); /// throw } if (p.ProcessInstanceID < 0) { //TODO: refactor to use FailTest p.ActivityExecutionError = "Process did not start : "; a.ActivityExecutionError = p.ActivityExecutionError; SendResult(new TestResultArgs(p, a, TestResultStage.FatalError, "Process did not start : " + p.ActivityExecutionError)); p.ProcessHasUnexpectedErrors = true; } else { //the p.Processname only gets assigned once the start activity has been initiated. p.ProcessName = a.ProcessName; a.Retry = false; a.TestStatus = "Created"; //////SendResult(new TestResultArgs(p, a, TestResultStage.DebugMessage, "a.teststatus set to created")); SendResult(new TestResultArgs(p, a, TestResultStage.ProcessStarted)); } //Post Method RunMethod(p, a, MethodType.PostMethod); }
private void FailTest(string reasonText, TestResultStage tesResultStage, Process p, Activity a) { p.ActivityExecutionError = reasonText; a.ActivityExecutionError = p.ActivityExecutionError; if (tesResultStage != TestResultStage.ActivityNotFoundRetrying) { p.ProcessHasUnexpectedErrors = true; a.Retry = false; a.TestStatus = "Failed"; } SendResult(new TestResultArgs(p, a, TestResultStage.ActivityExecutionError, reasonText)); }
private void ActionWorklistItemsIfFound(WorklistItems items, Process p, Activity a, out bool breakFromLoop, out bool actioned, out bool IPCeventFound) { actioned = false; breakFromLoop = false; IPCeventFound = false; string currentUser = "******" + System.Security.Principal.WindowsIdentity.GetCurrent().Name; for (int x = 0; x < items.Count; x++) { //Loop through and see if there is an item for this activity assigned to the current user. if (string.Equals(currentUser, items[x].Destination, StringComparison.OrdinalIgnoreCase) && a.Name.Equals(items[x].ActivityName)) { //if we are here then maybe we have an item in an IPC event, //set the a.ProcInstID which will be called on the next round. // important to note that IPC retry counts should be set higher to //cope with this hack! a.ProcessInstanceID = items[x].ProcInstID; IPCeventFound = true; breakFromLoop = true; return; } if (!this.ProceedWithTest) { breakFromLoop = true; return; } } for (int x = 0; x < items.Count; x++) { //we have a matching activity, but it is not assigned to the current user. if (string.Equals(items[x].ActivityName, a.Name, StringComparison.OrdinalIgnoreCase)) { try { //If we are on the last loop, lets impersonate the destination user and action the item. string impersonatedUser = string.Empty; Dictionary<string, object> filter = new Dictionary<string, object>(); switch (items[x].Actioner.ActionerType) { case ActionerType.User: impersonatedUser = items[x].Actioner.Name; break; case ActionerType.Groups: filter.Add("Group_name", items[x].Actioner.Name); filter.Add("LabelName", "K2:"); //get the first user in the group. var group = k2helper.SmartObjectClient().SmartObjectGetList(filter, "UMUSer", "Get_Group_Users"); impersonatedUser = group.Rows[0]["Name"].ToString(); break; case ActionerType.Role: filter.Add("Role_Name", items[x].Actioner.Name); //get the first user in the group. var role = k2helper.SmartObjectClient().SmartObjectGetList(filter, "UMUSer", "Get_Role_Users"); impersonatedUser = role.Rows[0]["Name"].ToString(); break; } //now impersonate them and action the worklist item. var wlitem = k2helper.WorkflowClient().GetWorkListItem(items[x].ProcInstID + "_ " + items[x].ActInstDestID, impersonatedUser); actioned = ActionActivity(p, a, wlitem, impersonatedUser, true); if (!actioned) { breakFromLoop = true; return; } //wlitem.Actions[a.Action].Execute(); //k2helper.WorkflowServer().RedirectWorklistItem(items[x].Destination, currentUser, items[x].ProcInstID, items[x].ActInstDestID, items[x].ID); } catch (Exception ex) { if (ex.IsFatal()) { throw; } string err = "Error getting worklist item : " + ex.Message; FailTest(err, TestResultStage.ActivityExecutionError, p, a); breakFromLoop = true; return; } if (!this.ProceedWithTest) { breakFromLoop = true; return; } Thread.Sleep(1000); } }//end for }
private List<Activity> ConstructActivitiesFromXMLNodeList(XmlNodeList processChildrenNodeList, string xmlRootDir, Process newProcess, string parentFilename) { //Old format contained Activities node for every activity //new format - not every nodes may be Activity node //for each activity in the process List<Activity> activities = new List<Activity>(); foreach (XmlNode activityNode in processChildrenNodeList) { if (activityNode.Name == "Activities") { string activitiesFileName = XmlHelper.GetAttributeValue(activityNode, "fileName"); //if Node contains a filename attribute and it is populated if (string.IsNullOrEmpty(activitiesFileName)) { List<Activity> newactivities = ConstructActivityFromXMLNode(activityNode, xmlRootDir, parentFilename); activities.AddRange(newactivities); } else { List<Activity> subFileActivities = ConstructActivitiesFromXMLFile(activitiesFileName, xmlRootDir, parentFilename); activities.AddRange(subFileActivities); } } else if (activityNode.Name == "Activity") { //We have found that this activity is of type "StartProcess" XmlNodeList xmlnodelistProceesses = activityNode.SelectNodes(@"Process"); if (xmlnodelistProceesses != null && xmlnodelistProceesses.Count > 0) { Activity activityWithSubProcess = new Activity(); activityWithSubProcess.ErrorExpected = bool.Parse(XmlHelper.GetAttributeValue(activityNode, "exceptionExpected", "False")); activityWithSubProcess.Name = "SubProcess"; activityWithSubProcess.SubProcesses = new List<Process>(); ConstructProcessesFromXMLNodeList(xmlnodelistProceesses, activityWithSubProcess.SubProcesses, xmlRootDir, parentFilename); foreach (Process proc in activityWithSubProcess.SubProcesses) { proc.RootProcess = newProcess; } activities.Add(activityWithSubProcess); } else { List<Activity> newactivities = ConstructActivityFromXMLNode(activityNode, xmlRootDir, parentFilename); activities.AddRange(newactivities); } } else { //could be DataFields, PreMethodCall etc. } } return activities; }
private void whatdoesthisdo(SourceCode.Workflow.Client.Worklist wl, Process p, Activity a, out bool breakFromLoop, out bool actioned) { breakFromLoop = false; actioned = false; //Incase there are multiple worklist items, lets find the activity we want to action. foreach (SourceCode.Workflow.Client.WorklistItem item in wl) { if (string.Equals(item.ActivityInstanceDestination.Name, a.Name, StringComparison.OrdinalIgnoreCase)) { string currentuser = "******" + System.Security.Principal.WindowsIdentity.GetCurrent().Name; //We have found the activity that we should update //first set process instance data fields. Dictionary<string, object> input = new Dictionary<string, object>(); bool breakFromInnerLoop = false; processDataFields(item, a, out breakFromInnerLoop); if (breakFromInnerLoop) { breakFromLoop = true; return; } //We need to see if the allocated person is the curent person , otehrwise reopen the worklst item. if (string.Equals(item.AllocatedUser, currentuser, StringComparison.OrdinalIgnoreCase)) { //Pre Method RunMethod(p, a, MethodType.PreMethod); actioned = ActionActivity(p, a, item, currentuser, false); if (!actioned) { breakFromLoop = true; return; } RunMethod(p, a, MethodType.PostMethod); } else { //open this item as the allocated user. SourceCode.Workflow.Client.WorklistItem newitem = null; try { newitem = k2helper.WorkflowClient().GetWorkListItem(item.SerialNumber, item.AllocatedUser); } catch (Exception ex) { if (ex.IsFatal()) { throw; } string err = "Error getting worklist " + ex.Message; FailTest(err, TestResultStage.ActivityExecutionError, p, a); breakFromLoop = true; return; } actioned = ActionActivity(p, a, newitem, currentuser, false); if (!actioned) { breakFromLoop = true; return; } }//open as allocated user breakFromLoop = true; return; }//activity name matches if (!this.ProceedWithTest) { breakFromLoop = true; return; } }//foreach workitem }
private void retryLoopIteration(int i, Process p, Activity a, out bool breakFromLoop, out bool actioned) { actioned = false; breakFromLoop = false; a.CountOfRetries = i; SendResult(new TestResultArgs(p, a, TestResultStage.DebugMessage, string.Format("a.Retry was {0}", a.Retry))); bool IPCeventFound = false; ///////////////////////////////////////////////////////////////////////////////////////////////////// if (a.Action.StartsWith("[") && a.Action.EndsWith("]")) { CheckActivityStatus(p, a, out breakFromLoop); if (!this.ProceedWithTest) { breakFromLoop = true; } if (breakFromLoop) { return; } }//search for an activity in a certain state else //try to execute the action { //Get all items by the folio so it should be unique SourceCode.Workflow.Management.Criteria.WorklistCriteriaFilter fil = new SourceCode.Workflow.Management.Criteria.WorklistCriteriaFilter(); fil.AddRegularFilter(SourceCode.Workflow.Management.WorklistFields.Folio, SourceCode.Workflow.Management.Criteria.Comparison.Equals, p.Folio); fil.AddRegularFilter(SourceCode.Workflow.Management.WorklistFields.ProcessFullName, SourceCode.Workflow.Management.Criteria.Comparison.Equals, string.IsNullOrEmpty(a.ProcessName) ? p.ProcessName : a.ProcessName); WorklistItems items = k2helper.WorkflowServer().GetWorklistItems(fil); ActionWorklistItemsIfFound(items, p, a, out breakFromLoop, out actioned, out IPCeventFound); if (!actioned) { //Open a conection to the server SourceCode.Workflow.Client.Worklist wl = k2helper.WorkflowClient().GetAllWorkListItem((a.ProcessInstanceID == 0) ? p.ProcessInstanceID : a.ProcessInstanceID); whatdoesthisdo(wl,p,a, out breakFromLoop, out actioned); if (breakFromLoop) { return; } }//workitem exists }// else try to execute action if (a.Retry) { string msg = string.Format("Activity Name: '{0}' was not available, retrying in {1} seconds. Retries left : {2}", a.Name , a.RetryInSeconds , (a.MaxRetryCount - a.CountOfRetries)); SendResult(new TestResultArgs(p, a, TestResultStage.ActivityNotFoundRetrying, msg)); } ErrorLog newError = GetLastError(); if (!AreIDsEqual(newError, lastError)) { //Basically if there is a new error if (a.ErrorExpected) {//Are We expecting an error? SendResult(new TestResultArgs(p, null, TestResultStage.ActivityActioned, string.Format("Error Expected and occured: {0} '{2}' ---> '{1}'" , newError.ID, newError.Description, newError.ProcessName))); a.Retry = false; breakFromLoop = true; } else { SendResult(new TestResultArgs(p, null, TestResultStage.FatalError, string.Format("Error: {0} '{2}' ---> '{1}'" , newError.ID, newError.Description, newError.ProcessName))); a.Retry = false; a.TestStatus = "More errors"; p.ProcessHasUnexpectedErrors = true; } } else {//no new error if (a.ErrorExpected) { SendResult(new TestResultArgs(p, null, TestResultStage.ActivityNotFoundRetrying, string.Format("'{0}' ({1})- '{2}': Error Expected but did not occur!.. yet!: {3}", p.ProcessName, a.ProcessName, a.Name, a.TestStatus))); //Thread.Sleep(1000); actioned = false; a.Retry = true; a.TestStatus = string.Empty; } else { SendResult(new TestResultArgs(p, a, TestResultStage.DebugMessage, "No Error expected and no error!")); } } lastError = newError; if (!this.ProceedWithTest) { breakFromLoop = true; return; } if (a.Retry) { SendResult(new TestResultArgs(p, a, TestResultStage.DebugMessage, "told to retry, sleeping")); System.Threading.Thread.Sleep(a.RetryInSeconds * 1000); } }