Skip to content

Commit

Permalink
updating process test comments + minor refactor with common utils
Browse files Browse the repository at this point in the history
  • Loading branch information
estenori committed Nov 27, 2024
1 parent 3a4a9df commit 046e44d
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ public sealed class EmitterStep : KernelProcessStep<StepState>
public const string PublicEventFunction = "SomePublicFunctionName";
public const string DualInputPublicEventFunction = "SomeDualInputPublicEventFunctionName";

private readonly int _sleepDurationMs = 100;
private readonly int _sleepDurationMs = 150;

private StepState? _state;

Expand Down
161 changes: 113 additions & 48 deletions dotnet/src/Experimental/Process.IntegrationTests.Shared/ProcessTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,7 @@ public async Task LinearProcessAsync()
var processInfo = await processHandle.GetStateAsync();

// Assert
var repeatStepState = processInfo.Steps.Where(s => s.State.Name == nameof(RepeatStep)).FirstOrDefault()?.State as KernelProcessStepState<StepState>;
Assert.NotNull(repeatStepState?.State);
Assert.Equal(string.Join(" ", Enumerable.Repeat(testInput, 2)), repeatStepState.State.LastMessage);
this.AssertStepStateLastMessage(processInfo, nameof(RepeatStep), expectedLastMessage: string.Join(" ", Enumerable.Repeat(testInput, 2)));
}

/// <summary>
Expand Down Expand Up @@ -101,9 +99,7 @@ public async Task NestedProcessOuterToInnerWorksAsync()
// Assert
var innerProcess = processInfo.Steps.Where(s => s.State.Name == "Inner").Single() as KernelProcess;
Assert.NotNull(innerProcess);
var repeatStepState = innerProcess.Steps.Where(s => s.State.Name == nameof(RepeatStep)).Single().State as KernelProcessStepState<StepState>;
Assert.NotNull(repeatStepState?.State);
Assert.Equal(string.Join(" ", Enumerable.Repeat(testInput, 4)), repeatStepState.State.LastMessage);
this.AssertStepStateLastMessage(innerProcess, nameof(RepeatStep), expectedLastMessage: string.Join(" ", Enumerable.Repeat(testInput, 4)));
}

/// <summary>
Expand Down Expand Up @@ -145,9 +141,7 @@ public async Task NestedProcessInnerToOuterWorksWithPublicEventAsync()
var processInfo = await processHandle.GetStateAsync();

// Assert
var repeatStepState = processInfo.Steps.Where(s => s.State.Name == nameof(RepeatStep)).FirstOrDefault()?.State as KernelProcessStepState<StepState>;
Assert.NotNull(repeatStepState?.State);
Assert.Equal(string.Join(" ", Enumerable.Repeat(testInput, 4)), repeatStepState.State.LastMessage);
this.AssertStepStateLastMessage(processInfo, nameof(RepeatStep), expectedLastMessage: string.Join(" ", Enumerable.Repeat(testInput, 4)));
}

/// <summary>
Expand Down Expand Up @@ -189,9 +183,7 @@ public async Task NestedProcessInnerToOuterDoesNotWorkWithInternalEventAsync()
var processInfo = await processHandle.GetStateAsync();

// Assert
var repeatStepState = processInfo.Steps.Where(s => s.State.Name == nameof(RepeatStep)).FirstOrDefault()?.State as KernelProcessStepState<StepState>;
Assert.NotNull(repeatStepState);
Assert.Null(repeatStepState.State?.LastMessage);
this.AssertStepStateLastMessage(processInfo, nameof(RepeatStep), expectedLastMessage: null);
}

/// <summary>
Expand All @@ -212,9 +204,7 @@ public async Task FanInProcessAsync()
var processInfo = await processHandle.GetStateAsync();

// Assert
var outputStep = processInfo.Steps.Where(s => s.State.Name == nameof(FanInStep)).FirstOrDefault()?.State as KernelProcessStepState<StepState>;
Assert.NotNull(outputStep?.State);
Assert.Equal($"{testInput}-{testInput} {testInput}", outputStep.State.LastMessage);
this.AssertStepStateLastMessage(processInfo, nameof(FanInStep), expectedLastMessage: $"{testInput}-{testInput} {testInput}");
}

/// <summary>
Expand All @@ -234,13 +224,8 @@ public async Task ProcessWithErrorEmitsErrorEventAsync()
var processInfo = await processHandle.GetStateAsync();

// Assert
var reportStep = processInfo.Steps.Where(s => s.State.Name == nameof(ReportStep)).FirstOrDefault()?.State as KernelProcessStepState<StepState>;
Assert.NotNull(reportStep?.State);
Assert.Equal(1, reportStep.State.InvocationCount);

var repeatStep = processInfo.Steps.Where(s => s.State.Name == nameof(RepeatStep)).FirstOrDefault()?.State as KernelProcessStepState<StepState>;
Assert.NotNull(repeatStep?.State);
Assert.Null(repeatStep.State.LastMessage);
this.AssertStepStateLastMessage(processInfo, nameof(ReportStep), expectedLastMessage: null, expectedInvocationCount: 1);
this.AssertStepStateLastMessage(processInfo, nameof(RepeatStep), expectedLastMessage: null);
}

/// <summary>
Expand All @@ -267,56 +252,65 @@ public async Task StepAndFanInProcessAsync()
var processInfo = await processHandle.GetStateAsync();

// Assert
var outputStep = (processInfo.Steps.Where(s => s.State.Name == fanInStepName).FirstOrDefault() as KernelProcess)?.Steps.Where(s => s.State.Name == nameof(FanInStep)).FirstOrDefault()?.State as KernelProcessStepState<StepState>;
Assert.NotNull(outputStep?.State);
Assert.Equal($"{testInput}-{testInput} {testInput}", outputStep.State.LastMessage);
var subprocessStepInfo = processInfo.Steps.Where(s => s.State.Name == fanInStepName)?.FirstOrDefault() as KernelProcess;
Assert.NotNull(subprocessStepInfo);
this.AssertStepStateLastMessage(subprocessStepInfo, nameof(FanInStep), expectedLastMessage: $"{testInput}-{testInput} {testInput}");
}

/// <summary>
/// Process with multiple "long" nested sequential subprocesses and with multiple single step
/// output fan out only steps
/// <code>
/// ┌───────────────────────────────────────────────┐
/// │ ▼
/// ┌───────┐ │ ┌──────────────┐ ┌──────────────┐ ┌──────┐
/// │ 1st ├──┼──►│ 2nd-nested ├──┬─►│ 3rd-nested ├───►│ last │
/// └───────┘ │ └──────────────┘ │ └──────────────┘ └──────┘
/// ▼ ▼
/// ┌───────┐ ┌───────┐
/// │ 4th │ │ 5th │
/// └───────┘ └───────┘
/// │ 1st ├──┼──►│ 2nd-nested ├──┬─►│ 3rd-nested ├─┬─►│ last │
/// └───────┘ │ └──────────────┘ │ └──────────────┘ │ └──────┘
/// ▼ ▼ ▼
/// ┌─────────┐ ┌─────────┐ ┌─────────┐
/// │ output1 │ │ output2 │ │ output3 │
/// └─────────┘ └─────────┘ └─────────┘
/// </code>
/// </summary>
/// <returns>A <see cref="Task"/></returns>
/// <returns><see cref="Task"/></returns>
[Fact]
public async Task ProcessWith2NestedSubprocessSequentiallyAndMultipleOutputStepsAsync()
{
// Arrange
Kernel kernel = this._kernelBuilder.Build();
string lastStepName = "lastEmitterStep";
string outputStepName1 = "outputStep1";
string outputStepName2 = "outputStep2";
string outputStepName3 = "outputStep3";
ProcessBuilder processBuilder = new(nameof(ProcessWith2NestedSubprocessSequentiallyAndMultipleOutputStepsAsync));

ProcessStepBuilder firstStep = processBuilder.AddStepFromType<EmitterStep>("firstEmitterStep");
ProcessBuilder secondStep = processBuilder.AddStepFromProcess(this.CreateLongSequentialProcessWithFanInAsOutputStep("subprocess1"));
ProcessBuilder thirdStep = processBuilder.AddStepFromProcess(this.CreateLongSequentialProcessWithFanInAsOutputStep("subprocess2"));
ProcessStepBuilder fourthStep = processBuilder.AddStepFromType<EmitterStep>("fourthStep");
ProcessStepBuilder fifthStep = processBuilder.AddStepFromType<EmitterStep>("fifthStep");
ProcessStepBuilder outputStep1 = processBuilder.AddStepFromType<EmitterStep>(outputStepName1);
ProcessStepBuilder outputStep2 = processBuilder.AddStepFromType<EmitterStep>(outputStepName2);
ProcessStepBuilder outputStep3 = processBuilder.AddStepFromType<EmitterStep>(outputStepName3);
ProcessStepBuilder lastStep = processBuilder.AddStepFromType<FanInStep>(lastStepName);

processBuilder
.OnInputEvent(EmitterStep.InputEvent)
.SendEventTo(new ProcessFunctionTargetBuilder(firstStep, functionName: EmitterStep.InternalEventFunction));
firstStep
.OnEvent(EmitterStep.EventId)
.SendEventTo(secondStep.WhereInputEventIs(EmitterStep.InputEvent));
.SendEventTo(secondStep.WhereInputEventIs(EmitterStep.InputEvent))
.SendEventTo(new ProcessFunctionTargetBuilder(outputStep1, functionName: EmitterStep.PublicEventFunction));
secondStep
.OnEvent(ProcessTestsEvents.OutputReadyPublic)
.SendEventTo(new ProcessFunctionTargetBuilder(fourthStep, functionName: EmitterStep.PublicEventFunction))
.SendEventTo(thirdStep.WhereInputEventIs(EmitterStep.InputEvent));
.SendEventTo(thirdStep.WhereInputEventIs(EmitterStep.InputEvent))
.SendEventTo(new ProcessFunctionTargetBuilder(outputStep2, functionName: EmitterStep.PublicEventFunction));
thirdStep
.OnEvent(ProcessTestsEvents.OutputReadyPublic)
.SendEventTo(new ProcessFunctionTargetBuilder(lastStep, parameterName: "secondInput"))
.SendEventTo(new ProcessFunctionTargetBuilder(outputStep3, functionName: EmitterStep.PublicEventFunction));

firstStep
.OnEvent(EmitterStep.EventId)
.SendEventTo(new ProcessFunctionTargetBuilder(lastStep, parameterName: "firstInput"));
thirdStep
.OnEvent(ProcessTestsEvents.OutputReadyPublic)
.SendEventTo(new ProcessFunctionTargetBuilder(lastStep, parameterName: "secondInput"))
.SendEventTo(new ProcessFunctionTargetBuilder(fifthStep, functionName: EmitterStep.PublicEventFunction));

KernelProcess process = processBuilder.Build();

Expand All @@ -326,12 +320,27 @@ public async Task ProcessWith2NestedSubprocessSequentiallyAndMultipleOutputSteps
var processInfo = await processHandle.GetStateAsync();

// Assert
var outputStep = (processInfo.Steps.Where(s => s.State.Name == lastStepName).FirstOrDefault() as KernelProcessStepInfo).State as KernelProcessStepState<StepState>;
Assert.NotNull(outputStep?.State);
Assert.NotNull(outputStep?.State.LastMessage);
Assert.Equal($"{testInput}-{testInput}-{testInput}-{testInput}-{testInput}", outputStep.State.LastMessage);
this.AssertStepStateLastMessage(processInfo, outputStepName1, expectedLastMessage: testInput);
this.AssertStepStateLastMessage(processInfo, outputStepName2, expectedLastMessage: $"{testInput}-{testInput}");
this.AssertStepStateLastMessage(processInfo, outputStepName3, expectedLastMessage: $"{testInput}-{testInput}-{testInput}-{testInput}");
this.AssertStepStateLastMessage(processInfo, lastStepName, expectedLastMessage: $"{testInput}-{testInput}-{testInput}-{testInput}-{testInput}");
}

#region Predefiend ProcessBuilders for testing

Check warning on line 329 in dotnet/src/Experimental/Process.IntegrationTests.Shared/ProcessTests.cs

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"Predefiend" should be "Predefined".
/// <summary>
/// Sample long sequential process, each step has a delay.<br/>
/// Input Event: <see cref="EmitterStep.InputEvent"/><br/>
/// Output Event: <see cref="ProcessTestsEvents.OutputReadyPublic"/><br/>
/// <code>
/// ┌───────────────────────────────────────────────┐
/// │ ▼
/// ┌───────┐ │ ┌───────┐ ┌───────┐ ┌────────┐ ┌──────┐
/// │ 1st ├──┴──►│ 2nd ├───►│ ... ├───►│ 10th ├───►│ last │
/// └───────┘ └───────┘ └───────┘ └────────┘ └──────┘
/// </code>
/// </summary>
/// <param name="name">name of the process</param>
/// <returns><see cref="ProcessBuilder"/></returns>
private ProcessBuilder CreateLongSequentialProcessWithFanInAsOutputStep(string name)
{
ProcessBuilder processBuilder = new(name);
Expand All @@ -355,14 +364,22 @@ private ProcessBuilder CreateLongSequentialProcessWithFanInAsOutputStep(string n
sixthNestedStep.OnEvent(EmitterStep.EventId).SendEventTo(new ProcessFunctionTargetBuilder(seventhNestedStep, functionName: EmitterStep.InternalEventFunction));
seventhNestedStep.OnEvent(EmitterStep.EventId).SendEventTo(new ProcessFunctionTargetBuilder(eighthNestedStep, functionName: EmitterStep.InternalEventFunction));
eighthNestedStep.OnEvent(EmitterStep.EventId).SendEventTo(new ProcessFunctionTargetBuilder(ninthNestedStep, functionName: EmitterStep.InternalEventFunction));
firstNestedStep.OnEvent(EmitterStep.EventId).SendEventTo(new ProcessFunctionTargetBuilder(tenthNestedStep, functionName: EmitterStep.DualInputPublicEventFunction, parameterName: "firstInput"));
ninthNestedStep.OnEvent(EmitterStep.EventId).SendEventTo(new ProcessFunctionTargetBuilder(tenthNestedStep, functionName: EmitterStep.DualInputPublicEventFunction, parameterName: "secondInput"));

firstNestedStep.OnEvent(EmitterStep.EventId).SendEventTo(new ProcessFunctionTargetBuilder(tenthNestedStep, functionName: EmitterStep.DualInputPublicEventFunction, parameterName: "firstInput"));

return processBuilder;
}

/// <summary>
/// Creates a simple linear process with two steps.
/// Creates a simple linear process with two steps.<br/>
/// Input Event: <see cref="ProcessTestsEvents.StartProcess"/><br/>
/// Output Events: [<see cref="ProcessTestsEvents.OutputReadyInternal"/>, <see cref="ProcessTestsEvents.OutputReadyPublic"/>]<br/>
/// <code>
/// ┌────────┐ ┌────────┐
/// │ echo ├───►│ repeat │
/// └────────┘ └────────┘
/// </code>
/// </summary>
private ProcessBuilder CreateLinearProcess(string name)
{
Expand All @@ -379,13 +396,30 @@ private ProcessBuilder CreateLinearProcess(string name)
return processBuilder;
}

/// <summary>
/// Simple process with fan in functionality.<br/>
/// Input Event: <see cref="ProcessTestsEvents.StartProcess"/><br/>
/// Output Events: <see cref="ProcessTestsEvents.OutputReadyPublic"/><br/>
/// <code>
/// ┌─────────┐
/// │ echoA ├──────┐
/// └─────────┘ ▼
/// ┌────────┐
/// │ fanInC │
/// └────────┘
/// ┌─────────┐ ▲
/// │ repeatB ├──────┘
/// └─────────┘
/// </code>
/// </summary>
/// <param name="name">name of the process</param>
/// <returns><see cref="ProcessBuilder"/></returns>
private ProcessBuilder CreateFanInProcess(string name)
{
var processBuilder = new ProcessBuilder(name);
var echoAStep = processBuilder.AddStepFromType<EchoStep>("EchoStepA");
var repeatBStep = processBuilder.AddStepFromType<RepeatStep>("RepeatStepB");
var fanInCStep = processBuilder.AddStepFromType<FanInStep>();
var echoDStep = processBuilder.AddStepFromType<EchoStep>();

processBuilder.OnInputEvent(ProcessTestsEvents.StartProcess).SendEventTo(new ProcessFunctionTargetBuilder(echoAStep));
processBuilder.OnInputEvent(ProcessTestsEvents.StartProcess).SendEventTo(new ProcessFunctionTargetBuilder(repeatBStep, parameterName: "message"));
Expand All @@ -396,6 +430,22 @@ private ProcessBuilder CreateFanInProcess(string name)
return processBuilder;
}

/// <summary>
/// Creates a simple linear process with that emit error events.<br/>
/// Input Event: <see cref="ProcessTestsEvents.StartProcess"/><br/>
/// Output Events: <see cref="ProcessStepBuilder.OnFunctionError(string?)"/> <br/>
/// <code>
/// ┌────────┐
/// ┌───────►│ repeat │
/// │ └────────┘
/// ┌───┴───┐
/// │ error │
/// └───┬───┘
/// │ ┌────────┐
/// └───────►│ report │
/// └────────┘
/// </code>
/// </summary>
private ProcessBuilder CreateProcessWithError(string name)
{
var processBuilder = new ProcessBuilder(name);
Expand All @@ -409,4 +459,19 @@ private ProcessBuilder CreateProcessWithError(string name)

return processBuilder;
}
#endregion
#region Assert Utils
private void AssertStepStateLastMessage(KernelProcess processInfo, string stepName, string? expectedLastMessage, int? expectedInvocationCount = null)
{
KernelProcessStepInfo? stepInfo = processInfo.Steps.FirstOrDefault(s => s.State.Name == stepName);
Assert.NotNull(stepInfo);
var outputStepResult = stepInfo.State as KernelProcessStepState<StepState>;
Assert.NotNull(outputStepResult?.State);
Assert.Equal(expectedLastMessage, outputStepResult.State.LastMessage);
if (expectedInvocationCount.HasValue)
{
Assert.Equal(expectedInvocationCount.Value, outputStepResult.State.InvocationCount);
}
}
#endregion
}
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,6 @@ private void EnqueueExternalMessages(Queue<ProcessMessage> messageChannel)
private void EnqueueStepMessages(LocalStep step, Queue<ProcessMessage> messageChannel)
{
var allStepEvents = step.GetAllEvents();

foreach (ProcessEvent stepEvent in allStepEvents)
{
// Emit the event out of the process (this one) if it's visibility is public.
Expand Down

0 comments on commit 046e44d

Please sign in to comment.