private static bool CheckDuplicates(StepDefinition currentStep) { // if we are not a root step, we are done if (!currentStep.IsRootStep) { return(true); } // we are a root step, so get duplicates list var duplicateSteps = currentStep.RootState.Stack.FindDuplicates(p => p.Name); // if we have no duplicates we are done if (!duplicateSteps.Any()) { return(true); } // we found duplicates, so print them out for the user var message = ""; foreach (var name in duplicateSteps) { if (message.Length == 0) { message += "Duplicate name found in state machine!" + Environment.NewLine + Environment.NewLine + "State Machine:" + Environment.NewLine + $"{currentStep.Name}" + Environment.NewLine + Environment.NewLine + "Steps:" + Environment.NewLine; } message += $"{currentStep.RootState.Stack.Count(o => o.Name == name)}x {name}\n"; } throw new Exception(message); }
/// <summary> /// Execute the supplied step recursivly. This will loop over the step and all children until it finds /// the current state step, then execute it. User should always apply the root step to this function, /// and allow the state machine find the correct child to execute. /// </summary> /// <param name="currentStep">The root step of the state machine.</param> /// <returns>Returns if a step was executed.</returns> public static bool ExecuteStep(StepDefinition currentStep) { if (currentStep.SubStepIndex < currentStep.SubSteps.Count) { // execute the current substep if (ExecuteStep(currentStep.SubSteps[currentStep.SubStepIndex])) { // substep is complete, so advance to the next one currentStep.SubStepIndex++; } } else { // set delay for step completion currentStep.RootState.DelayTimeMs = currentStep.DelayTimeMs; // all substeps are complete, execute final function var result = currentStep.Delegate(currentStep); // if the user doesn't want to skip the delay, go ahead and return if (!currentStep.SkipDelayTime) { return(result); } // skip the delay, then return the results currentStep.SkipDelayTime = false; currentStep.RootState.DelayTimeMs = 0; return(result); } return(false); }
/// <summary> /// Recursive part of JumpToStep, do not use this function except from inside JumpToStep! /// </summary> /// <param name="name">The step Name to jump too.</param> /// <param name="currentStep">The root step to loop over.</param> /// <returns></returns> private static int JumpToStepWorker(string name, StepDefinition currentStep) { var foundName = false; currentStep.SubStepIndex = 0; for (var i = 0; i < currentStep.SubSteps.Count; i++) { if (currentStep.SubSteps[i].Name != name && JumpToStepWorker(name, currentStep.SubSteps[i]) < 0) { continue; } currentStep.SubStepIndex = i; foundName = true; break; } if (foundName) { return(currentStep.SubStepIndex); } currentStep.SubStepIndex = currentStep.SubSteps.Count; return(-1); }
/// <summary> /// See if the current step has timed out. /// </summary> /// <param name="timeoutMilliseconds"></param> /// <param name="currentStep"></param> /// <returns></returns> protected bool IsTimedOut(int timeoutMilliseconds, StepDefinition currentStep) { if (timeoutMilliseconds == 0) { return(false); } var stepsBeforeTimeout = (int)Math.Round(timeoutMilliseconds / (double)currentStep.DelayTimeMs, 0); return(currentStep.FailCount >= stepsBeforeTimeout); }
/// <summary> /// Allows the user to specify a step label to jump too. This will not execute any additional steps /// between the current and target steps. /// </summary> /// <param name="name">The Name of the step to jump too.</param> /// <param name="currentStep">The step the function is called from.</param> /// <returns>True = Jump Successfull, False = Jump Failed</returns> public static bool JumpToStep(string name, StepDefinition currentStep) { InitializeStates(currentStep.Root); if (JumpToStepWorker(name, currentStep.Root) < 0) { throw new Exception($"State Machine Error! Unable to jump to step: {name}"); } // pulse the worker currentStep.SkipDelayTime = true; currentStep.RootThread?.WaitHandle?.Set(); return(true); }
/// <summary> /// Initialize all the steps and the step machine state. /// </summary> /// <param name="currentStep">The root step of the machine</param> /// <returns></returns> public static bool InitializeStates(StepDefinition currentStep) { // initialze the tree on the root step if (currentStep.IsRootStep) { currentStep.Root = currentStep; currentStep.RootState.Stack.Clear(); currentStep.RootState.WorkingIndex = 0; } // step up the tree currentStep.RootState.TreeLevel++; // reset the current step currentStep.Reset(); // loop over all substeps foreach (var step in currentStep.SubSteps) { // add step to root tree currentStep.RootState.Stack.Add(new StackInfo { Name = step.Name, TreeLevel = currentStep.RootState.TreeLevel, Parent = currentStep }); // initalize global state information step.StackIndex = currentStep.RootState.WorkingIndex++; step.Parent = currentStep; step.Root = currentStep.IsRootStep ? currentStep : currentStep.Root; step.RootState = currentStep.RootState; // recursive step InitializeStates(step); } // step back down the tree currentStep.RootState.TreeLevel--; // check for name duplicates return(CheckDuplicates(currentStep)); }
/// <summary> /// Default function to call if one is not provided. /// </summary> /// <param name="currentStep"></param> /// <returns></returns> private static bool DefaultFinalFunction(StepDefinition currentStep) { return(StepReturn.ContinueToNext); }
/// <summary> /// Check to see if this is the first time entering this step. Will reset after jumps. /// </summary> /// <param name="currentStep">The step to check.</param> /// <returns>True if first time entering the step.</returns> protected bool IsFirstPass(StepDefinition currentStep) { return(currentStep.FailCount == 0); }
/// <summary> /// Allows the user to skip the current step without completion. /// </summary> /// <param name="currentStep">The step the function is called from.</param> /// <returns>True = Jump Successfull, False = Jump Failed</returns> public static bool JumpToNext(StepDefinition currentStep) { return(JumpToStep(currentStep.RootState.Stack[currentStep.StackIndex + 1].Name, currentStep)); }
/// <summary> /// Allows the user to jump to the first step in the state machine, which should be /// a closing function to end the test. /// </summary> /// <param name="currentStep">The step the function is called from.</param> /// <returns>True = Jump Successfull, False = Jump Failed</returns> public static bool JumpToFirst(StepDefinition currentStep) { return(JumpToStep(currentStep.RootState.Stack.First().Name, currentStep)); }