Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

robo-4382 executor handling delay activity in lrwf #347

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from
120 changes: 120 additions & 0 deletions src/Test/TestCases.Runtime/StatementsBehaviorExtensionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
using System;
using System.Activities;
using System.Activities.Statements;
using System.Threading;
using Shouldly;
using Test.Common.TestObjects.CustomActivities;
using UiPath.Workflow.Runtime.Statements;
using WorkflowApplicationTestExtensions.Persistence;
using Xunit;

namespace TestCases.Runtime
{
public class StatementsBehaviorExtensionTests
{
/// <summary>
/// Tests that using StatementsBehaviorExtension extension with BlockingDelay = true, the Delay activity doesn't trigger PersistableIdle
/// </summary>
[Fact]
public static void TestBlockingDelayShouldNotPersist()
{
var testSequence = new Sequence()
{
Activities =
{
new Delay()
{
Duration = TimeSpan.FromMilliseconds(100)
},
}
};
bool workflowIdleAndPersistable = false;
var completed = new ManualResetEvent(false);
WorkflowApplication workflowApplication = new WorkflowApplication(testSequence);
workflowApplication.InstanceStore = new MemoryInstanceStore();
workflowApplication.Extensions.Add(new StatementsBehaviorExtension { BlockingDelay = true });
workflowApplication.PersistableIdle = (_) => { workflowIdleAndPersistable = true; return PersistableIdleAction.None; };
workflowApplication.Completed = (_) => completed.Set();
workflowApplication.Run();
completed.WaitOne();
workflowIdleAndPersistable.ShouldBeFalse(); //there is no persistable idle
}

/// <summary>
/// Tests that without using StatementsBehaviorExtension extension, the Delay activity triggers PersistableIdle
/// </summary>
[Fact]
public static void TestNonBlockingDelayShouldPersist()
{
var testSequence = new Sequence()
{
Activities =
{
new Delay()
{
Duration = TimeSpan.FromMilliseconds(100)
},
}
};
bool workflowIdleAndPersistable = false;
var completed = new ManualResetEvent(false);
WorkflowApplication workflowApplication = new WorkflowApplication(testSequence);
workflowApplication.InstanceStore = new MemoryInstanceStore();
workflowApplication.PersistableIdle = (_) => { workflowIdleAndPersistable = true; return PersistableIdleAction.None; };
workflowApplication.Completed = (_) => completed.Set();
workflowApplication.Run();
completed.WaitOne();
workflowIdleAndPersistable.ShouldBeTrue();
}

/// <summary>
/// Tests that having StatementsBehaviorExtension extension with BlockingDelay=true, the Delay activity blocks the
/// PersistableIdle event from any other activity in the workflow, including parallel activities.
/// </summary>
[Fact]
public static void TestParallelBlockingActivityShouldTriggerPersistAfterDelayFinishes()
{
var delayDuration = TimeSpan.FromMilliseconds(100);

var testSequence = new Sequence()
{
Activities =
{
new Parallel()
{
Branches =
{
new Delay() { Duration = delayDuration },
new BlockingActivity("B")
}
},
}
};
bool workflowIdleAndPersistable = false;
var completed = new ManualResetEvent(false);
var persistableIdleTriggered = new ManualResetEvent(false);

var sw = new System.Diagnostics.Stopwatch();

WorkflowApplication workflowApplication = new WorkflowApplication(testSequence);
workflowApplication.InstanceStore = new MemoryInstanceStore();
workflowApplication.Extensions.Add(new StatementsBehaviorExtension { BlockingDelay = true });


workflowApplication.PersistableIdle = (_) => {
//check if more than 100ms passed
sw.ElapsedMilliseconds.ShouldBeGreaterThan(delayDuration.Milliseconds);
workflowIdleAndPersistable = true;
persistableIdleTriggered.Set();
return PersistableIdleAction.None;
};
workflowApplication.Completed = (_) => completed.Set();
sw.Start();
workflowApplication.Run();
persistableIdleTriggered.WaitOne(); // Wait for PersistableIdle to trigger
workflowApplication.ResumeBookmark("B", null); //Resume the bookmark from the blocking activity
completed.WaitOne();
workflowIdleAndPersistable.ShouldBeTrue();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ public static void TestResumeWithDelay()
{
new TestDelay()
{
Duration = TimeSpan.FromMilliseconds(100)
Duration = TimeSpan.FromMilliseconds(200)
},
}
};
Expand All @@ -393,7 +393,7 @@ public static void TestResumeWithDelay()
[Fact]
public static void TestNoPersistSerialization()
{
TestSequence testSequence = new() { Activities = { new TestNoPersist() }};
TestSequence testSequence = new() { Activities = { new TestNoPersist() } };
WorkflowApplicationTestExtensions.Persistence.FileInstanceStore jsonStore = new WorkflowApplicationTestExtensions.Persistence.FileInstanceStore(".\\~");
TestWorkflowRuntime workflowRuntime = TestRuntime.CreateTestWorkflowRuntime(testSequence, null, jsonStore, PersistableIdleAction.Unload);
workflowRuntime.ExecuteWorkflow();
Expand Down
14 changes: 14 additions & 0 deletions src/UiPath.Workflow.Runtime/Statements/Delay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Activities.Runtime;
using System.Collections.ObjectModel;
using System.Windows.Markup;
using UiPath.Workflow.Runtime.Statements;

namespace System.Activities.Statements;

Expand All @@ -12,6 +13,8 @@ public sealed class Delay : NativeActivity
{
private static readonly Func<TimerExtension> getDefaultTimerExtension = new Func<TimerExtension>(GetDefaultTimerExtension);
private readonly Variable<Bookmark> _timerBookmark;
private readonly Variable<NoPersistHandle> _noPersistHandle = new Variable<NoPersistHandle>();


public Delay()
: base()
Expand All @@ -23,6 +26,7 @@ public Delay()
[DefaultValue(null)]
public InArgument<TimeSpan> Duration { get; set; }


protected override bool CanInduceIdle => true;

protected override void CacheMetadata(NativeActivityMetadata metadata)
Expand All @@ -31,6 +35,7 @@ protected override void CacheMetadata(NativeActivityMetadata metadata)
metadata.Bind(Duration, durationArgument);
metadata.SetArgumentsCollection(new Collection<RuntimeArgument> { durationArgument });
metadata.AddImplementationVariable(_timerBookmark);
metadata.AddImplementationVariable(_noPersistHandle);
metadata.AddDefaultExtensionProvider(getDefaultTimerExtension);
}

Expand All @@ -50,6 +55,10 @@ protected override void Execute(NativeActivityContext context)
}

TimerExtension timerExtension = GetTimerExtension(context);

if (HasBlockingDelay(context))
_noPersistHandle.Get(context).Enter(context);

Bookmark bookmark = context.CreateBookmark();
timerExtension.RegisterTimer(duration, bookmark);
_timerBookmark.Set(context, bookmark);
Expand Down Expand Up @@ -82,4 +91,9 @@ private TimerExtension GetTimerExtension(ActivityContext context)
Fx.Assert(timerExtension != null, "TimerExtension must exist.");
return timerExtension;
}

private bool HasBlockingDelay(NativeActivityContext context)
{
return context.GetExtension<StatementsBehaviorExtension>()?.BlockingDelay == true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UiPath.Workflow.Runtime.Statements
{
/// <summary>
/// An extension that configures/changes the behavior of some statements like Delay.
/// </summary>
public class StatementsBehaviorExtension
{
/// <summary>
/// When true, the delay activity will block the persistance idle event until the delay is over,
/// preventing the workflow from being persisted.
/// </summary>
public bool BlockingDelay { get; set; } = false;
}
}