Skip to content

Commit e604ecc

Browse files
committed
Adding tests
1 parent cf42473 commit e604ecc

File tree

7 files changed

+344
-2
lines changed

7 files changed

+344
-2
lines changed
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
using Microsoft.CSharp.Activities;
2+
using Microsoft.VisualBasic.Activities;
3+
using Shouldly;
4+
using System;
5+
using System.Activities;
6+
using System.Activities.Statements;
7+
using System.Activities.Validation;
8+
using System.Collections.Generic;
9+
using System.Linq;
10+
using TestCases.Workflows.WF4Samples;
11+
using Xunit;
12+
13+
namespace TestCases.Workflows;
14+
15+
/// <summary>
16+
/// Tests for improved out argument assignability validation and compilation (STUD-76892)
17+
/// </summary>
18+
public class OutArgumentAssignabilityTests
19+
{
20+
private readonly ValidationSettings _useValidator = new() { ForceExpressionCache = false };
21+
22+
#region Core Out Argument Type Compatibility Tests
23+
24+
[Fact]
25+
public void CS_OutArgument_SameType_ShouldSucceed()
26+
{
27+
var sequence = new Sequence();
28+
sequence.Variables.Add(new Variable<List<string>>("result"));
29+
30+
var activity = new ImproveAssignabilityOutArgumentActivity();
31+
activity.Result = new OutArgument<List<string>>(new CSharpReference<List<string>>("result"));
32+
33+
sequence.Activities.Add(activity);
34+
35+
var validationResults = ActivityValidationServices.Validate(sequence, _useValidator);
36+
validationResults.Errors.ShouldBeEmpty($"Expected no validation errors, but got: {string.Join("; ", validationResults.Errors.Select(e => e.Message))}");
37+
}
38+
39+
[Fact]
40+
public void CS_OutArgument_IncompatibleType_ShouldFail()
41+
{
42+
var sequence = new Sequence();
43+
sequence.Variables.Add(new Variable<string>("result")); // Wrong type - should be List<string>
44+
45+
var activity = new ImproveAssignabilityOutArgumentActivity();
46+
activity.Result = new OutArgument<List<string>>(new CSharpReference<List<string>>("result"));
47+
48+
sequence.Activities.Add(activity);
49+
50+
var validationResults = ActivityValidationServices.Validate(sequence, _useValidator);
51+
validationResults.Errors.Count.ShouldBe(1);
52+
validationResults.Errors[0].Message.ShouldContain("Cannot implicitly convert type");
53+
}
54+
55+
#endregion
56+
57+
#region Nullable Type Assignability Tests
58+
59+
[Fact]
60+
public void CS_OutArgument_DateTime_ToNullableDateTime_ShouldSucceed()
61+
{
62+
var sequence = new Sequence();
63+
sequence.Variables.Add(new Variable<DateTime?>("nullableResult"));
64+
65+
var activity = new ImproveAssignabilityOutArgumentActivity();
66+
activity.DateTimeOutArgument = new OutArgument<DateTime>(new CSharpReference<DateTime>("nullableResult"));
67+
68+
sequence.Activities.Add(activity);
69+
70+
var validationResults = ActivityValidationServices.Validate(sequence, _useValidator);
71+
validationResults.Errors.ShouldBeEmpty($"Expected no validation errors for DateTime to DateTime? assignment, but got: {string.Join("; ", validationResults.Errors.Select(e => e.Message))}");
72+
}
73+
74+
[Fact]
75+
public void CS_OutArgument_NullableDateTime_ToDateTime_ShouldFail()
76+
{
77+
var sequence = new Sequence();
78+
sequence.Variables.Add(new Variable<DateTime>("nonNullableResult"));
79+
80+
var activity = new ImproveAssignabilityOutArgumentActivity();
81+
activity.NullableDateTimeOutArgument = new OutArgument<DateTime?>(new CSharpReference<DateTime?>("nonNullableResult")); // Should fail
82+
83+
sequence.Activities.Add(activity);
84+
85+
var validationResults = ActivityValidationServices.Validate(sequence, _useValidator);
86+
validationResults.Errors.Count.ShouldBe(1);
87+
validationResults.Errors[0].Message.ShouldContain("Cannot implicitly convert type");
88+
}
89+
90+
#endregion
91+
92+
#region Generic Type Assignability Tests
93+
94+
[Fact]
95+
public void CS_OutArgument_GenericList_CorrectType_ShouldSucceed()
96+
{
97+
var sequence = new Sequence();
98+
sequence.Variables.Add(new Variable<List<string>>("stringList"));
99+
100+
var activity = new ImproveAssignabilityOutArgumentActivity();
101+
activity.Result = new OutArgument<List<string>>(new CSharpReference<List<string>>("stringList"));
102+
103+
sequence.Activities.Add(activity);
104+
105+
var validationResults = ActivityValidationServices.Validate(sequence, _useValidator);
106+
validationResults.Errors.ShouldBeEmpty();
107+
}
108+
109+
[Fact]
110+
public void CS_OutArgument_GenericList_WrongGenericType_ShouldFail()
111+
{
112+
var sequence = new Sequence();
113+
sequence.Variables.Add(new Variable<List<int>>("intList")); // Wrong generic type
114+
115+
var activity = new ImproveAssignabilityOutArgumentActivity();
116+
activity.Result = new OutArgument<List<string>>(new CSharpReference<List<string>>("intList"));
117+
118+
sequence.Activities.Add(activity);
119+
120+
var validationResults = ActivityValidationServices.Validate(sequence, _useValidator);
121+
validationResults.Errors.Count.ShouldBe(1);
122+
validationResults.Errors[0].Message.ShouldContain("Cannot implicitly convert type");
123+
}
124+
125+
#endregion
126+
127+
#region Safe Cast Validation Tests
128+
129+
[Fact]
130+
public void CS_OutArgument_SafeCast_BaseToInterface_ShouldSucceed()
131+
{
132+
var sequence = new Sequence();
133+
sequence.Variables.Add(new Variable<IList<string>>("interfaceList"));
134+
135+
var activity = new ImproveAssignabilityOutArgumentActivity();
136+
activity.Result = new OutArgument<List<string>>(new CSharpReference<List<string>>("interfaceList"));
137+
138+
sequence.Activities.Add(activity);
139+
140+
var validationResults = ActivityValidationServices.Validate(sequence, _useValidator);
141+
validationResults.Errors.ShouldBeEmpty("List<string> should be assignable to IList<string>");
142+
}
143+
144+
[Fact]
145+
public void CS_OutArgument_UnsafeCast_InterfaceToBase_ShouldFail()
146+
{
147+
var sequence = new Sequence();
148+
sequence.Variables.Add(new Variable<List<string>>("concreteList"));
149+
150+
// Try to assign IList<string> to List<string> - should fail without explicit cast
151+
var assign = new Assign<IList<string>>
152+
{
153+
To = new OutArgument<IList<string>>(new CSharpReference<IList<string>>("concreteList")),
154+
Value = new InArgument<IList<string>>(new CSharpValue<IList<string>>("new List<string>()"))
155+
};
156+
157+
sequence.Activities.Add(assign);
158+
159+
var validationResults = ActivityValidationServices.Validate(sequence, _useValidator);
160+
validationResults.Errors.Count.ShouldBe(1);
161+
validationResults.Errors[0].Message.ShouldContain("Cannot implicitly convert type");
162+
}
163+
164+
#endregion
165+
166+
#region Multiple Out Arguments Test
167+
168+
[Fact]
169+
public void ImproveAssignabilityOutArgumentActivity_AllArgumentTypes_ShouldValidate()
170+
{
171+
var sequence = new Sequence();
172+
sequence.Variables.Add(new Variable<List<string>>("stringListResult"));
173+
sequence.Variables.Add(new Variable<DateTime>("dateTimeResult"));
174+
sequence.Variables.Add(new Variable<DateTime?>("nullableDateTimeResult"));
175+
176+
var activity = new ImproveAssignabilityOutArgumentActivity
177+
{
178+
Result = new OutArgument<List<string>>(new CSharpReference<List<string>>("stringListResult")),
179+
DateTimeOutArgument = new OutArgument<DateTime>(new CSharpReference<DateTime>("dateTimeResult")),
180+
NullableDateTimeOutArgument = new OutArgument<DateTime?>(new CSharpReference<DateTime?>("nullableDateTimeResult"))
181+
};
182+
183+
sequence.Activities.Add(activity);
184+
185+
var validationResults = ActivityValidationServices.Validate(sequence, _useValidator);
186+
validationResults.Errors.ShouldBeEmpty($"All out arguments should validate correctly. Errors: {string.Join("; ", validationResults.Errors.Select(e => e.Message))}");
187+
}
188+
189+
#endregion
190+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Activity x:Class="TestWorkflow" mc:Ignorable="sap sap2010"
3+
xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
4+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
5+
xmlns:sap="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation"
6+
xmlns:sap2010="http://schemas.microsoft.com/netfx/2010/xaml/activities/presentation"
7+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
8+
xmlns:local="clr-namespace:TestCases.Workflows.WF4Samples;assembly=TestCases.Workflows"
9+
xmlns:scg="clr-namespace:System.Collections.Generic;assembly=System.Private.CoreLib"
10+
xmlns:sco="clr-namespace:System.Collections.ObjectModel;assembly=System.Private.CoreLib"
11+
xmlns:s="clr-namespace:System;assembly=System.Private.CoreLib">
12+
<x:Members>
13+
<x:Property Name="myEnumerable" Type="OutArgument(scg:IEnumerable(x:String))" />
14+
<x:Property Name="dateTimeOutArgument" Type="OutArgument(s:DateTime)" />
15+
<x:Property Name="nullableDateTimeOutArgument" Type="OutArgument(s:Nullable(s:DateTime))" />
16+
</x:Members>
17+
<sap2010:ExpressionActivityEditor.ExpressionActivityEditor>C#</sap2010:ExpressionActivityEditor.ExpressionActivityEditor>
18+
<Sequence DisplayName="RootSequence">
19+
<Sequence.Variables>
20+
</Sequence.Variables>
21+
22+
<!-- Use your custom activity -->
23+
<local:ImproveAssignabilityOutArgumentActivity>
24+
25+
<local:ImproveAssignabilityOutArgumentActivity.Result>
26+
<OutArgument x:TypeArguments="scg:List(x:String)">
27+
<CSharpReference x:TypeArguments="scg:List(x:String)">
28+
myEnumerable
29+
</CSharpReference>
30+
</OutArgument>
31+
</local:ImproveAssignabilityOutArgumentActivity.Result>
32+
33+
<local:ImproveAssignabilityOutArgumentActivity.DateTimeOutArgument>
34+
<OutArgument x:TypeArguments="s:DateTime">
35+
<CSharpReference x:TypeArguments="s:DateTime">
36+
dateTimeOutArgument
37+
</CSharpReference>
38+
</OutArgument>
39+
</local:ImproveAssignabilityOutArgumentActivity.DateTimeOutArgument>
40+
41+
<local:ImproveAssignabilityOutArgumentActivity.NullableDateTimeOutArgument>
42+
<OutArgument x:TypeArguments="s:Nullable(s:DateTime)">
43+
<CSharpReference x:TypeArguments="s:Nullable(s:DateTime)">
44+
nullableDateTimeOutArgument
45+
</CSharpReference>
46+
</OutArgument>
47+
</local:ImproveAssignabilityOutArgumentActivity.NullableDateTimeOutArgument>
48+
49+
</local:ImproveAssignabilityOutArgumentActivity>
50+
51+
<!-- Consume the variable -->
52+
<WriteLine Text="" />
53+
</Sequence>
54+
</Activity>

src/Test/TestCases.Workflows/TestXamls/TestHelper.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,6 @@ public enum TestXamls
4646
SpecialCharacters,
4747
ValueSpecialCharacterCSharp,
4848
ValueSpecialCharacterVb,
49+
ImproveAssignabilityOutArgumentActivity
4950
}
5051
}

src/Test/TestCases.Workflows/WF4Samples/Expressions.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ namespace TestCases.Workflows.WF4Samples
2121
{
2222
using StringDictionary = Dictionary<string, object>;
2323

24-
public abstract class ExpressionsBase
24+
public abstract class ExpressionsBaseCommon
2525
{
2626
protected abstract bool CompileExpressions { get; }
2727
protected Activity GetActivityFromXamlResource(TestXamls xamlName) => TestHelper.GetActivityFromXamlResource(xamlName, CompileExpressions);
@@ -31,6 +31,10 @@ protected Activity Compile(TestXamls xamlName)
3131
Compiler.Run(activity);
3232
return activity;
3333
}
34+
}
35+
36+
public abstract class ExpressionsBase : ExpressionsBaseCommon
37+
{
3438
protected const string CorrectOutput = @"John Doe earns $55000.00
3539
Frank Kimono earns $89000.00
3640
Salary statistics: minimum salary is $55000.00, maximum salary is $89000.00, average salary is $72000.00
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using System;
2+
using System.Activities;
3+
using System.Collections.Generic;
4+
5+
namespace TestCases.Workflows.WF4Samples;
6+
7+
public class ImproveAssignabilityOutArgumentActivity : NativeActivity
8+
{
9+
// OutArgument of type List<string>
10+
public OutArgument<List<string>> Result { get; set; }
11+
12+
// Additional OutArguments
13+
public OutArgument<DateTime> DateTimeOutArgument { get; set; }
14+
public OutArgument<DateTime?> NullableDateTimeOutArgument { get; set; }
15+
16+
public ImproveAssignabilityOutArgumentActivity() : base()
17+
{
18+
}
19+
20+
protected override void CacheMetadata(NativeActivityMetadata metadata)
21+
{
22+
// Result
23+
var resultArg = new RuntimeArgument(
24+
"Result",
25+
typeof(List<string>),
26+
ArgumentDirection.Out);
27+
metadata.Bind(this.Result, resultArg);
28+
metadata.AddArgument(resultArg);
29+
30+
// DateTimeOutArgument
31+
var dateTimeArg = new RuntimeArgument(
32+
"DateTimeOutArgument",
33+
typeof(DateTime),
34+
ArgumentDirection.Out);
35+
metadata.Bind(this.DateTimeOutArgument, dateTimeArg);
36+
metadata.AddArgument(dateTimeArg);
37+
38+
// NullableDateTimeOutArgument
39+
var nullableDateTimeArg = new RuntimeArgument(
40+
"NullableDateTimeOutArgument",
41+
typeof(DateTime?),
42+
ArgumentDirection.Out);
43+
metadata.Bind(this.NullableDateTimeOutArgument, nullableDateTimeArg);
44+
metadata.AddArgument(nullableDateTimeArg);
45+
}
46+
47+
protected override void Execute(NativeActivityContext context)
48+
{
49+
var result = new List<string>
50+
{
51+
"aaa",
52+
"bbb",
53+
"ccc"
54+
};
55+
56+
Result.Set(context, result);
57+
DateTimeOutArgument.Set(context, DateTime.Now);
58+
NullableDateTimeOutArgument.Set(context, DateTime.Now);
59+
}
60+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using Shouldly;
2+
using System;
3+
using System.Activities;
4+
using System.Activities.XamlIntegration;
5+
using System.Collections.Generic;
6+
using Xunit;
7+
8+
namespace TestCases.Workflows.WF4Samples;
9+
10+
public class ImproveAssignabilityOutArgumentTests : ExpressionsBaseCommon
11+
{
12+
protected override bool CompileExpressions => true;
13+
14+
[Fact]
15+
public void CompileImproveAssignabilityOutArgumentActivity()
16+
{
17+
Activity activity = null;
18+
19+
// Assert that ActivityXamlServices.Load does not throw
20+
Should.NotThrow(() =>
21+
{
22+
activity = ActivityXamlServices.Load(TestHelper.GetXamlStream(TestXamls.ImproveAssignabilityOutArgumentActivity),
23+
new ActivityXamlServicesSettings
24+
{
25+
CompileExpressions = true
26+
});
27+
});
28+
29+
var invoker = new WorkflowInvoker(activity);
30+
var result = invoker.Invoke();
31+
result["myEnumerable"].ShouldBe(new List<string> { "aaa", "bbb", "ccc" });
32+
}
33+
}

src/Test/TestCases.Workflows/WF4Samples/ValueSpecialCharacterTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace TestCases.Workflows.WF4Samples
1919
//4. Assign: output = Dict.First.Key. => we expect the key is "KeyName"
2020
//Results: the output has the unexpected value of "ValueName", instead of "KeyName", but if we enable parameter rename,
2121
//everything works as expected
22-
public class ValueSpecialCharacterTests : ExpressionsBase
22+
public class ValueSpecialCharacterTests : ExpressionsBaseCommon
2323
{
2424
protected override bool CompileExpressions => true;
2525

0 commit comments

Comments
 (0)