Skip to content

Commit

Permalink
Parse expressions from activity implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
andrei-ungureanu-uipath committed Apr 24, 2024
1 parent 7d795e5 commit acfad17
Show file tree
Hide file tree
Showing 6 changed files with 1,762 additions and 18 deletions.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

95 changes: 95 additions & 0 deletions src/Test/TestCases.Workflows/ExpressionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Activities.Expressions;
using System.Activities.Statements;
using System.Activities.Validation;
using System.Activities.XamlIntegration;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
Expand Down Expand Up @@ -566,4 +567,98 @@ public void VB_IsCaseInsensitive()
var valid = ActivityValidationServices.Validate(seq, _useValidator);
valid.Errors.ShouldBeEmpty();
}

[Fact]
public void VB_CompileAndInvokeActivityWithValueInImplementation()
{
foreach (var activity in GetActivitiesWithVBValues())
{
var root = new DynamicActivity { Implementation = () => activity };
ActivityXamlServices.Compile(root, new() { CompileExpressions = true });
WorkflowInvoker.Invoke(root);
}
}

[Fact]
public void CS_CompileAndInvokeActivityWithValueInImplementation()
{
foreach (var activity in GetActivitiesWithCSValues())
{
var root = new DynamicActivity { Implementation = () => activity };
ActivityXamlServices.Compile(root, new() { CompileExpressions = true });
WorkflowInvoker.Invoke(root);
}
}

private static IEnumerable<Activity> GetActivitiesWithVBValues()
{
yield return new ActivityWithVBValue("(1 + 2).ToString");
yield return new ActivityWithMultipleNestedVBValues();
}

private static IEnumerable<Activity> GetActivitiesWithCSValues()
{
yield return new ActivityWithCSValue("(1 + 2).ToString()");
yield return new ActivityWithMultipleNestedCSValues();
}

private class ActivityWithVBValue : Activity
{
public ActivityWithVBValue(string expressionText)
{
Implementation = () => new WriteLine() { Text = new InArgument<string>(new VisualBasicValue<string>(expressionText)) };
}
}

private class ActivityWithCSValue : Activity
{
public ActivityWithCSValue(string expressionText)
{
Implementation = () => new WriteLine() { Text = new InArgument<string>(new CSharpValue<string>(expressionText)) };
}
}

private class ActivityWithMultipleNestedVBValues : Activity
{
public ActivityWithMultipleNestedVBValues()
{
Implementation = () => new Sequence()
{
Activities =
{
new ActivityWithVBValue("99.ToString"),
new Sequence()
{
Activities =
{
new ActivityWithVBValue("(1 + 2).ToString"),
}
},
new ActivityWithVBValue("{1, 2, 3}.ToString")
}
};
}
}

private class ActivityWithMultipleNestedCSValues : Activity
{
public ActivityWithMultipleNestedCSValues()
{
Implementation = () => new Sequence()
{
Activities =
{
new ActivityWithCSValue("99.ToString()"),
new Sequence()
{
Activities =
{
new ActivityWithCSValue("(1 + 2).ToString()"),
}
},
new ActivityWithCSValue("(new int[] {1, 2, 3}).ToString()")
}
};
}
}
}
103 changes: 103 additions & 0 deletions src/Test/TestCases.Workflows/TextExpressionCompilerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using Xunit;
using System;
using System.IO;
using System.Activities;
using Microsoft.CSharp.Activities;
using System.Activities.Statements;
using Microsoft.VisualBasic.Activities;
using System.Activities.XamlIntegration;

namespace TestCases.Workflows
{
public class TextExpressionCompilerTests
{
[Theory]
[InlineData("VB")]
[InlineData("C#")]
public void EnsureCompilationParity(string expressionLanguage)
{
var actualSourceWriter = new StringWriter();
var currentAsm = typeof(TextExpressionCompilerTests).Assembly;

TextExpressionCompilerSettings settings = new()
{
RootNamespace = null,
ForImplementation = true,
ActivityName = "TestClass",
AlwaysGenerateSource = true,
Language = expressionLanguage,
GenerateAsPartialClass = false,
ActivityNamespace = "TestNamespace",
Activity = new DynamicActivity { Implementation = () => GetActivity(expressionLanguage) },
};

new TextExpressionCompiler(settings).GenerateSource(actualSourceWriter);
var expectedStream = currentAsm.GetManifestResourceStream($"{currentAsm.GetName().Name}.ExpressionCompilerResults.{GetExpectedResourceName(expressionLanguage)}");

var actualSource = actualSourceWriter.ToString();
var expectedSource = new StreamReader(expectedStream).ReadToEnd();
Assert.Equal(expectedSource, actualSource);
}

private static string GetExpectedResourceName(string expressionLanguage)
=> expressionLanguage switch
{
"C#" => "CS_ExpressionCompilerResult",
"VB" => "VB_ExpressionCompilerResult",
_ => throw new NotImplementedException()
};

private static Activity GetActivity(string expressionLanguage)
=> expressionLanguage switch
{
"C#" => GetActivity(v1 => new CSharpValue<bool>(v1), v2 => new CSharpValue<string>(v2), v3 => new CSharpReference<string>(v3)),
"VB" => GetActivity(v1 => new VisualBasicValue<bool>(v1), v2 => new VisualBasicValue<string>(v2), v3 => new VisualBasicReference<string>(v3)),
_ => throw new NotImplementedException()
};

private static Activity GetActivity<T1, T2, T3>(Func<string, T1> boolValueFactory, Func<string, T2> strValueFactory, Func<string, T3> referenceFactory)
where T1 : TextExpressionBase<bool>
where T2 : TextExpressionBase<string>
where T3 : TextExpressionBase<Location<string>>
=> new Sequence()
{
Variables =
{
new Variable<string>("var1")
},
Activities =
{
new WriteLine() { Text = new InArgument<string>(strValueFactory("var1")) },
new Assign() { To = new OutArgument<string>(referenceFactory("var1")), Value = new InArgument<string>("test1") },
new If() {
Condition = new InArgument<bool>(boolValueFactory("var1 != \"test\"")),
Then = new Sequence()
{
Variables =
{
new Variable<string>("var2")
},
Activities =
{
new Assign() { To = new OutArgument<string>(referenceFactory("var2")), Value = new InArgument<string>("test2") },
new WriteLine() { Text = new InArgument<string>(strValueFactory("var2")) },
}
},
Else = new Sequence()
{
Activities =
{
new Sequence()
{
Activities =
{
new WriteLine() { Text = new InArgument<string>(strValueFactory("test3")) },
}
}
}
}
}
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -169,16 +169,14 @@ private bool TryGetCurrentCompiledExpressionRoot(ActivityContext activityContext
{
ActivityInstance current = activityContext.CurrentInstance;

while (current != null && current.Activity != _metadataRoot)
while (current != null)
{

if (TryGetCompiledExpressionRoot(current.Activity, true, out ICompiledExpressionRoot currentCompiledExpressionRoot))
if (current.Activity != _metadataRoot
&& TryGetCompiledExpressionRoot(current.Activity, true, out ICompiledExpressionRoot currentCompiledExpressionRoot)
&& CanExecuteExpression(currentCompiledExpressionRoot, out expressionId))
{
if (CanExecuteExpression(currentCompiledExpressionRoot, out expressionId))
{
compiledExpressionRoot = currentCompiledExpressionRoot;
return true;
}
compiledExpressionRoot = currentCompiledExpressionRoot;
return true;
}
current = current.Parent;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ protected virtual void Visit(Activity activity, out bool exit)
{
return;
}

VisitImplementationChildren(activity, out exit);
if (exit)
{
return;
}
}

protected virtual void VisitRoot(Activity activity, out bool exit)
Expand Down Expand Up @@ -205,6 +211,22 @@ protected virtual void VisitImportedChildren(Activity activity, out bool exit)
exit = false;
}

protected virtual void VisitImplementationChildren(Activity activity, out bool exit)
{
if (activity.ImplementationChildren is not null)
{
for (int i = 0; i < activity.ImplementationChildren.Count; i++)
{
VisitCore(activity.ImplementationChildren[i], out exit);
if (exit)
{
return;
}
}
}
exit = false;
}

protected virtual void VisitDelegates(Activity activity, out bool exit)
{
if (activity.Delegates != null)
Expand Down Expand Up @@ -370,18 +392,11 @@ private void VisitRootImplementation(Activity activity, out bool exit)
return;
}

if (activity.ImplementationChildren != null)
VisitImplementationChildren(activity, out exit);
if (exit)
{
for (int i = 0; i < activity.ImplementationChildren.Count; i++)
{
VisitCore(activity.ImplementationChildren[i], out exit);
if (exit)
{
return;
}
}
return;
}
exit = false;
}

private void VisitPublicActivities(Activity activity, out bool exit)
Expand Down

0 comments on commit acfad17

Please sign in to comment.