From 468a7ce7fde908e07f7a299716d09708ff2345b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Herceg?= Date: Sat, 26 Oct 2024 13:23:34 +0200 Subject: [PATCH 1/6] Repro for postback handler binding translation issue --- .../PostBackHandlerBindingViewModel.cs | 20 +++++++++++ .../PostBack/PostBackHandlerBinding.dothtml | 35 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 src/Samples/Common/ViewModels/FeatureSamples/PostBack/PostBackHandlerBindingViewModel.cs create mode 100644 src/Samples/Common/Views/FeatureSamples/PostBack/PostBackHandlerBinding.dothtml diff --git a/src/Samples/Common/ViewModels/FeatureSamples/PostBack/PostBackHandlerBindingViewModel.cs b/src/Samples/Common/ViewModels/FeatureSamples/PostBack/PostBackHandlerBindingViewModel.cs new file mode 100644 index 0000000000..d0ad214be0 --- /dev/null +++ b/src/Samples/Common/ViewModels/FeatureSamples/PostBack/PostBackHandlerBindingViewModel.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DotVVM.Framework.ViewModel; +using DotVVM.Framework.Hosting; + +namespace DotVVM.Samples.Common.ViewModels.FeatureSamples.PostBack +{ + public class PostBackHandlerBindingViewModel : DotvvmViewModelBase + { + public bool Enabled { get; set; } = false; + + public int Counter { get; set; } = 0; + + public string[] Items { get; set; } = new string[] { "Item 1", "Item 2", "Item 3" }; + } +} + diff --git a/src/Samples/Common/Views/FeatureSamples/PostBack/PostBackHandlerBinding.dothtml b/src/Samples/Common/Views/FeatureSamples/PostBack/PostBackHandlerBinding.dothtml new file mode 100644 index 0000000000..8c8e279559 --- /dev/null +++ b/src/Samples/Common/Views/FeatureSamples/PostBack/PostBackHandlerBinding.dothtml @@ -0,0 +1,35 @@ +@viewModel DotVVM.Samples.Common.ViewModels.FeatureSamples.PostBack.PostBackHandlerBindingViewModel, DotVVM.Samples.Common + + + + + + + + + + +

+ +

+

Counter: {{value: Counter}}

+ +

Click on grid rows to increment the counter.

+ + + + + + + + + + + + + + + + + + From 60b4226ec1838aed06e4a013bb0706447da15109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Standa=20Luke=C5=A1?= Date: Sat, 26 Oct 2024 22:55:36 +0200 Subject: [PATCH 2/6] postbacki handlers: Fix knockout context access in value bindings Fixes accesses to parent (or higher) knockout contexts or view models. It is necessary to call the AssignParameters function recursively on default assignments of the parent VM/context parameters. --- .../Javascript/ParametrizedCode.cs | 21 +++++-- .../Framework/Controls/KnockoutHelper.cs | 4 +- src/Tests/Binding/BindingCompilationTests.cs | 2 +- .../Binding/JavascriptCompilationTests.cs | 34 +++++++++++ .../ControlTests/PostbackHandlerTests.cs | 61 +++++++++++++++++++ .../PostbackHandlerTests.ButtonHandlers.html | 21 +++++++ ...ServerSideStyleTests.PostbackHandlers.html | 2 +- 7 files changed, 137 insertions(+), 8 deletions(-) create mode 100644 src/Tests/ControlTests/PostbackHandlerTests.cs create mode 100644 src/Tests/ControlTests/testoutputs/PostbackHandlerTests.ButtonHandlers.html diff --git a/src/Framework/Framework/Compilation/Javascript/ParametrizedCode.cs b/src/Framework/Framework/Compilation/Javascript/ParametrizedCode.cs index 95a0941035..fec592e561 100644 --- a/src/Framework/Framework/Compilation/Javascript/ParametrizedCode.cs +++ b/src/Framework/Framework/Compilation/Javascript/ParametrizedCode.cs @@ -141,20 +141,33 @@ public ParametrizedCode AssignParameters(Func - o == JavascriptTranslator.KnockoutContextParameter ? new ParametrizedCode("c") : - o == JavascriptTranslator.KnockoutViewModelParameter ? new ParametrizedCode("d") : + o == JavascriptTranslator.KnockoutContextParameter ? CodeParameterAssignment.FromIdentifier("c") : + o == JavascriptTranslator.KnockoutViewModelParameter ? CodeParameterAssignment.FromIdentifier("d") : default(CodeParameterAssignment) ); return new JsSymbolicParameter(new CodeSymbolicParameter("tmp symbol", defaultAssignment: adjustedCode)); diff --git a/src/Tests/Binding/BindingCompilationTests.cs b/src/Tests/Binding/BindingCompilationTests.cs index fdd0d6d360..1c7f2c6c8c 100755 --- a/src/Tests/Binding/BindingCompilationTests.cs +++ b/src/Tests/Binding/BindingCompilationTests.cs @@ -1493,7 +1493,7 @@ public override string ToString() return SomeString + ": " + MyProperty; } } - class TestViewModel3 : DotvvmViewModelBase + public class TestViewModel3 : DotvvmViewModelBase { public string SomeString { get; set; } } diff --git a/src/Tests/Binding/JavascriptCompilationTests.cs b/src/Tests/Binding/JavascriptCompilationTests.cs index d387558513..9575077737 100644 --- a/src/Tests/Binding/JavascriptCompilationTests.cs +++ b/src/Tests/Binding/JavascriptCompilationTests.cs @@ -1395,6 +1395,40 @@ public void JavascriptCompilation_CustomPrimitiveParse() Assert.AreEqual("VehicleNumber()==\"123\"", result); } + [TestMethod] + public void JavascriptCompilation_ParametrizedCode_ParentContexts() + { + // root: TestViewModel + // parent: TestViewModel2 + _index + _collection + // this: TestViewModel3 + _index + var context0 = DataContextStack.Create(typeof(TestViewModel)); + var context1 = DataContextStack.Create(typeof(TestViewModel2), parent: context0, extensionParameters: [ new CurrentCollectionIndexExtensionParameter(), new BindingCollectionInfoExtensionParameter("_collection") ]); + var context2 = DataContextStack.Create(typeof(TestViewModel3), parent: context1, extensionParameters: [ new CurrentCollectionIndexExtensionParameter() ]); + + var result = bindingHelper.ValueBindingToParametrizedCode("_root.IntProp + _parent._index + _parent._collection.Index + _parent.MyProperty + _index + SomeString.Length", context2); + + Assert.AreEqual("$parents[1].IntProp() + $parentContext.$index() + $parentContext.$index() + $parent.MyProperty() + $index() + SomeString().length", result.ToDefaultString()); + + // assign `context` and `vm` variables + var formatted2 = result.ToString(o => + o == JavascriptTranslator.KnockoutContextParameter ? new ParametrizedCode("context", OperatorPrecedence.Max) : + o == JavascriptTranslator.KnockoutViewModelParameter ? new ParametrizedCode("vm", OperatorPrecedence.Max) : + default(CodeParameterAssignment) + ); + Console.WriteLine(formatted2); + + var symbolicParameters = result.Parameters.ToArray(); + Assert.AreEqual(6, symbolicParameters.Length); + Assert.AreEqual(2, XAssert.IsAssignableFrom(symbolicParameters[0].Parameter).ParentIndex); + Assert.AreEqual(JavascriptTranslator.KnockoutContextParameter, symbolicParameters[1].Parameter); // JavascriptTranslator.ParentKnockoutContextParameter would also work + Assert.AreEqual(JavascriptTranslator.KnockoutContextParameter, symbolicParameters[2].Parameter); // JavascriptTranslator.ParentKnockoutContextParameter would also work + Assert.AreEqual(JavascriptTranslator.ParentKnockoutViewModelParameter, symbolicParameters[3].Parameter); + Assert.AreEqual(JavascriptTranslator.KnockoutContextParameter, symbolicParameters[4].Parameter); + Assert.AreEqual(JavascriptTranslator.KnockoutViewModelParameter, symbolicParameters[5].Parameter); + + Assert.AreEqual("context.$parents[1].IntProp() + context.$parentContext.$index() + context.$parentContext.$index() + context.$parent.MyProperty() + context.$index() + vm.SomeString().length", formatted2); + } + public class TestMarkupControl: DotvvmMarkupControl { public string SomeProperty diff --git a/src/Tests/ControlTests/PostbackHandlerTests.cs b/src/Tests/ControlTests/PostbackHandlerTests.cs new file mode 100644 index 0000000000..478587a7eb --- /dev/null +++ b/src/Tests/ControlTests/PostbackHandlerTests.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Globalization; +using System.Threading.Tasks; +using CheckTestOutput; +using DotVVM.Framework.Compilation; +using DotVVM.Framework.Controls; +using DotVVM.Framework.Tests.Binding; +using DotVVM.Framework.ViewModel; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using DotVVM.Framework.Testing; +using System.Security.Claims; +using System.Collections; +using DotVVM.Framework.Binding; +using DotVVM.Framework.Compilation.ControlTree; +using DotVVM.Framework.Hosting; + +namespace DotVVM.Framework.Tests.ControlTests +{ + [TestClass] + public class PostbackHandlerTests + { + static readonly ControlTestHelper cth = new ControlTestHelper(config: config => { + }); + readonly OutputChecker check = new OutputChecker("testoutputs"); + + + [TestMethod] + public async Task ButtonHandlers() + { + var r = await cth.RunPage(typeof(BasicTestViewModel), """ + + + + 100 || SomeString.Length < 5} /> + + + + + + + + + + """ + ); + + check.CheckString(r.FormattedHtml, fileExtension: "html"); + } + + public class BasicTestViewModel: DotvvmViewModelBase + { + public int Integer { get; set; } = 123; + public bool Boolean { get; set; } = false; + public string String { get; set; } = "some-string"; + + public TestViewModel3 Nested { get; set; } = new TestViewModel3 { SomeString = "a" }; + } + } +} diff --git a/src/Tests/ControlTests/testoutputs/PostbackHandlerTests.ButtonHandlers.html b/src/Tests/ControlTests/testoutputs/PostbackHandlerTests.ButtonHandlers.html new file mode 100644 index 0000000000..08ca8d6fd0 --- /dev/null +++ b/src/Tests/ControlTests/testoutputs/PostbackHandlerTests.ButtonHandlers.html @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + diff --git a/src/Tests/ControlTests/testoutputs/ServerSideStyleTests.PostbackHandlers.html b/src/Tests/ControlTests/testoutputs/ServerSideStyleTests.PostbackHandlers.html index d2f57d3194..bc0ae66127 100644 --- a/src/Tests/ControlTests/testoutputs/ServerSideStyleTests.PostbackHandlers.html +++ b/src/Tests/ControlTests/testoutputs/ServerSideStyleTests.PostbackHandlers.html @@ -6,7 +6,7 @@ Two handlers Two handlers, value binding message - + Two handlers, resource binding message Two handlers, default message From cd912913c82ef8145784d6201f0455f5605c2ee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Herceg=20=28RIGANTI=20s=2Er=2Eo=2E=29?= Date: Tue, 29 Oct 2024 21:50:02 +0100 Subject: [PATCH 3/6] Added UI test --- .../PostBack/PostBackHandlerBinding.dothtml | 2 +- .../Abstractions/SamplesRouteUrls.designer.cs | 1 + .../Tests/Tests/Feature/PostBackTests.cs | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Samples/Common/Views/FeatureSamples/PostBack/PostBackHandlerBinding.dothtml b/src/Samples/Common/Views/FeatureSamples/PostBack/PostBackHandlerBinding.dothtml index 8c8e279559..a381148d91 100644 --- a/src/Samples/Common/Views/FeatureSamples/PostBack/PostBackHandlerBinding.dothtml +++ b/src/Samples/Common/Views/FeatureSamples/PostBack/PostBackHandlerBinding.dothtml @@ -12,7 +12,7 @@

-

Counter: {{value: Counter}}

+

Counter: {{value: Counter}}

Click on grid rows to increment the counter.

diff --git a/src/Samples/Tests/Abstractions/SamplesRouteUrls.designer.cs b/src/Samples/Tests/Abstractions/SamplesRouteUrls.designer.cs index 22217bbc28..c24d80059f 100644 --- a/src/Samples/Tests/Abstractions/SamplesRouteUrls.designer.cs +++ b/src/Samples/Tests/Abstractions/SamplesRouteUrls.designer.cs @@ -309,6 +309,7 @@ public partial class SamplesRouteUrls public const string FeatureSamples_ParameterBinding_OptionalParameterBinding = "FeatureSamples/ParameterBinding/OptionalParameterBinding"; public const string FeatureSamples_ParameterBinding_ParameterBinding = "FeatureSamples/ParameterBinding/ParameterBinding"; public const string FeatureSamples_PostBack_ConfirmPostBackHandler = "FeatureSamples/PostBack/ConfirmPostBackHandler"; + public const string FeatureSamples_PostBack_PostBackHandlerBinding = "FeatureSamples/PostBack/PostBackHandlerBinding"; public const string FeatureSamples_PostBack_PostBackHandlerCommandTypes = "FeatureSamples/PostBack/PostBackHandlerCommandTypes"; public const string FeatureSamples_PostBack_PostbackUpdate = "FeatureSamples/PostBack/PostbackUpdate"; public const string FeatureSamples_PostBack_PostbackUpdateRepeater = "FeatureSamples/PostBack/PostbackUpdateRepeater"; diff --git a/src/Samples/Tests/Tests/Feature/PostBackTests.cs b/src/Samples/Tests/Tests/Feature/PostBackTests.cs index 86ac1bc458..87e2304874 100644 --- a/src/Samples/Tests/Tests/Feature/PostBackTests.cs +++ b/src/Samples/Tests/Tests/Feature/PostBackTests.cs @@ -87,6 +87,24 @@ public void Feature_PostBack_PostBackHandlers_Localization() }); } + [Fact] + public void Feature_PostBack_PostBackHandlerBinding() + { + RunInAllBrowsers(browser => { + browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_PostBack_PostBackHandlerBinding); + + var counter = browser.Single(".result"); + AssertUI.TextEquals(counter, "0"); + + browser.ElementAt("td", 0).Click(); + AssertUI.TextEquals(counter, "1"); + + browser.Single("input[type=checkbox]").Click(); + browser.ElementAt("td", 0).Click(); + AssertUI.TextEquals(counter, "1"); + }); + } + private void ValidatePostbackHandlersComplexSection(string sectionSelector, IBrowserWrapper browser) { IElementWrapper section = null; From 9fd853e398877920d1dbc94942f53637a61a3c25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Standa=20Luke=C5=A1?= Date: Tue, 29 Oct 2024 22:43:08 +0100 Subject: [PATCH 4/6] ParametrizedCode: fix recursive assignment in parameter default values --- .../Javascript/ParametrizedCode.cs | 81 +++++++++++++------ .../ControlTests/PostbackHandlerTests.cs | 3 +- .../PostbackHandlerTests.ButtonHandlers.html | 2 +- 3 files changed, 59 insertions(+), 27 deletions(-) diff --git a/src/Framework/Framework/Compilation/Javascript/ParametrizedCode.cs b/src/Framework/Framework/Compilation/Javascript/ParametrizedCode.cs index fec592e561..4bfa3f88fb 100644 --- a/src/Framework/Framework/Compilation/Javascript/ParametrizedCode.cs +++ b/src/Framework/Framework/Compilation/Javascript/ParametrizedCode.cs @@ -130,9 +130,25 @@ public ParametrizedCode AssignParameters(Func parameterAssigner, out bool allIsDefault) { - var pp = FindAssignment(parameterAssigner, optional: false, allIsDefault: out allIsDefault); + allIsDefault = true; var codes = new(CodeParameterAssignment parameter, string code)[parameters!.Length]; for (int i = 0; i < parameters.Length; i++) { - codes[i] = (pp[i], pp[i].Code!.ToString(parameterAssigner, out bool allIsDefault_local)); + var assignment = parameterAssigner(parameters[i].Parameter); + if (assignment.Code == null) + { + assignment = parameters[i].DefaultAssignment; + if (assignment.Code == null) + throw new InvalidOperationException($"Assignment of parameter '{parameters[i].Parameter}' was not found."); + } + else + allIsDefault = false; + + codes[i] = (assignment, assignment.Code!.ToString(parameterAssigner, out bool allIsDefault_local)); allIsDefault &= allIsDefault_local; } return codes; } - private CodeParameterAssignment[] FindAssignment(Func parameterAssigner, bool optional, out bool allIsDefault) + private (CodeParameterAssignment[]? assigned, ParametrizedCode?[]? newDefaults) FindAssignment(Func parameterAssigner) { - allIsDefault = true; - var pp = new CodeParameterAssignment[parameters!.Length]; + if (parameters is null) + return (null, null); + + // these are different variables, as we have to preserve the tree-like structure of the ParametrizedCodes, + // when we assign parameters in the default values. + // newDefaults -> we will change the code in the parameters[i].DefaultAssignment + // assigned -> this parameter will be removed and its assignment inlined into stringParts[i] and parameters[i] + CodeParameterAssignment[]? assigned = null; // when null, all are default + ParametrizedCode[]? newDefaults = null; // when null, no defaults were changed for (int i = 0; i < parameters.Length; i++) { - if ((pp[i] = parameterAssigner(parameters[i].Parameter)).Code == null) + var p = parameterAssigner(parameters[i].Parameter); + if (p.Code is not null) + { + assigned ??= new CodeParameterAssignment[parameters.Length]; + assigned[i] = p; + } + else if (parameters[i].DefaultAssignment is { Code: { HasParameters: true } } defaultAssignment) { - if (!optional) + // check if the default assignment contains any of the assigned parameters, and adjust the default if necessary + var newDefault = defaultAssignment.Code.AssignParameters(parameterAssigner); + if (newDefault != defaultAssignment.Code) { - pp[i] = parameters[i].DefaultAssignment; - if (pp[i].Code == null) - throw new InvalidOperationException($"Assignment of parameter '{parameters[i].Parameter}' was not found."); + newDefaults ??= new ParametrizedCode[parameters.Length]; + newDefaults[i] = newDefault; } } - else - allIsDefault = false; } - return pp; + return (assigned, newDefaults); } public IEnumerable EnumerateAllParameters() diff --git a/src/Tests/ControlTests/PostbackHandlerTests.cs b/src/Tests/ControlTests/PostbackHandlerTests.cs index 478587a7eb..aa4a5ca76b 100644 --- a/src/Tests/ControlTests/PostbackHandlerTests.cs +++ b/src/Tests/ControlTests/PostbackHandlerTests.cs @@ -33,7 +33,8 @@ public async Task ButtonHandlers() - 100 || SomeString.Length < 5} /> + 100} /> + diff --git a/src/Tests/ControlTests/testoutputs/PostbackHandlerTests.ButtonHandlers.html b/src/Tests/ControlTests/testoutputs/PostbackHandlerTests.ButtonHandlers.html index 08ca8d6fd0..b2a163c90e 100644 --- a/src/Tests/ControlTests/testoutputs/PostbackHandlerTests.ButtonHandlers.html +++ b/src/Tests/ControlTests/testoutputs/PostbackHandlerTests.ButtonHandlers.html @@ -6,7 +6,7 @@ +},this,[["suppress",(c,d)=>({suppress:c.$parent.Integer() > 100})],["suppress",(c,d)=>({suppress:d.SomeString()?.length < 5})]]).catch(dotvvm.log.logPostBackScriptError);event.stopPropagation();return false;" type="button" value="Test supress"> From a15a0e4f3e08f55db736a97c3c1fe9ff3ab9d7be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Standa=20Luke=C5=A1?= Date: Wed, 30 Oct 2024 23:04:32 +0100 Subject: [PATCH 5/6] Add some more tests for ParametrizedCodes with default values in symbols --- .../Javascript/ParametrizedCode.cs | 2 +- .../JavascriptCompilation/JsFormatterTests.cs | 13 -- .../JsParametrizedCodeTests.cs | 125 ++++++++++++++++++ 3 files changed, 126 insertions(+), 14 deletions(-) create mode 100644 src/Tests/Runtime/JavascriptCompilation/JsParametrizedCodeTests.cs diff --git a/src/Framework/Framework/Compilation/Javascript/ParametrizedCode.cs b/src/Framework/Framework/Compilation/Javascript/ParametrizedCode.cs index 4bfa3f88fb..e743efdd9f 100644 --- a/src/Framework/Framework/Compilation/Javascript/ParametrizedCode.cs +++ b/src/Framework/Framework/Compilation/Javascript/ParametrizedCode.cs @@ -173,7 +173,7 @@ public ParametrizedCode AssignParameters(Func o == symbol ? CodeParameterAssignment.FromExpression(new JsIdentifierExpression("global"), isGlobalContext: true) : - throw new Exception())); - } - [TestMethod] public void JsFormatter_FunctionExpression() { diff --git a/src/Tests/Runtime/JavascriptCompilation/JsParametrizedCodeTests.cs b/src/Tests/Runtime/JavascriptCompilation/JsParametrizedCodeTests.cs new file mode 100644 index 0000000000..0b9dc9ac31 --- /dev/null +++ b/src/Tests/Runtime/JavascriptCompilation/JsParametrizedCodeTests.cs @@ -0,0 +1,125 @@ +using DotVVM.Framework.Compilation.Javascript.Ast; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using DotVVM.Framework.Compilation.Javascript; + +namespace DotVVM.Framework.Tests.Runtime.JavascriptCompilation +{ + [TestClass] + public class JsParametrizedCodeTests + { + static CodeSymbolicParameter symbolA = new CodeSymbolicParameter("A"); + static CodeSymbolicParameter symbolB = new CodeSymbolicParameter("B"); + static CodeSymbolicParameter symbolC = new CodeSymbolicParameter("C", defaultAssignment: new JsIdentifier("defaultValue").FormatParametrizedScript()); + static CodeSymbolicParameter symbolD = new CodeSymbolicParameter("D", defaultAssignment: new JsBinaryExpression(new JsSymbolicParameter(symbolA), BinaryOperatorType.Times, new JsSymbolicParameter(symbolC)).FormatParametrizedScript()); + static CodeSymbolicParameter symbolE = new CodeSymbolicParameter("E", defaultAssignment: new JsBinaryExpression(new JsSymbolicParameter(symbolA), BinaryOperatorType.Plus, new JsSymbolicParameter(symbolD)).FormatParametrizedScript()); + + [TestMethod] + public void SymbolicParameters_Global() + { + // full would be "global.a+global + var pcode = new JsBinaryExpression(new JsMemberAccessExpression(new JsSymbolicParameter(symbolA), "a"), BinaryOperatorType.Plus, + new JsSymbolicParameter(symbolA)) + .FormatParametrizedScript(); + Assert.AreEqual("a+global", + pcode.ToString(o => o == symbolA ? CodeParameterAssignment.FromExpression(new JsIdentifierExpression("global"), isGlobalContext: true) : + throw new Exception())); + Assert.AreEqual("a+global", + pcode.AssignParameters(o => o == symbolA ? CodeParameterAssignment.FromExpression(new JsIdentifierExpression("global"), isGlobalContext: true) : + throw new Exception()) + .ToString(o => default)); + } + + [TestMethod] + public void SymbolicParameters_Global_ThroughDefault() + { + var globalDefaultSymbol = new CodeSymbolicParameter("global", defaultAssignment: symbolA.ToParametrizedCode()); + // may be "global.a+global" or "a+global" - doesn't matter if the optimization works in this case, but it shouldn't be broken + var pcode = new JsBinaryExpression(new JsMemberAccessExpression(new JsSymbolicParameter(globalDefaultSymbol), "a"), BinaryOperatorType.Plus, + new JsSymbolicParameter(globalDefaultSymbol)) + .FormatParametrizedScript(); + Assert.AreEqual("global.a+global", + pcode.ToString(o => o == symbolA ? CodeParameterAssignment.FromExpression(new JsIdentifierExpression("global"), isGlobalContext: true) : + default)); + Assert.AreEqual("global.a+global", + pcode.AssignParameters(o => o == symbolA ? CodeParameterAssignment.FromExpression(new JsIdentifierExpression("global"), isGlobalContext: true) : + default) + .ToString(o => default)); + } + + [TestMethod] + public void SymbolicParameters_Throws_Unassigned() + { + var pcode = new JsBinaryExpression(new JsMemberAccessExpression(new JsSymbolicParameter(symbolA), "a"), BinaryOperatorType.Plus, new JsSymbolicParameter(symbolA)).FormatParametrizedScript(); + + var pcode2 = pcode.AssignParameters(o => default); // fine, not all have to be assigned + var pcodeResolved = pcode.AssignParameters(o => o == symbolA ? CodeParameterAssignment.FromIdentifier("x") : default); + + Assert.AreEqual("y.a+y", pcode.ToString(o => o == symbolA ? CodeParameterAssignment.FromIdentifier("y") : default)); + Assert.AreEqual("z.a+z", pcode2.ToString(o => o == symbolA ? CodeParameterAssignment.FromIdentifier("z") : default)); + Assert.AreEqual("x.a+x", pcodeResolved.ToString(o => default)); + Assert.AreEqual("x.a+x", pcodeResolved.ToString(o => o == symbolA ? CodeParameterAssignment.FromIdentifier("AREADY_ASSIGNED_BEFORE") : default)); + + var ex = Assert.ThrowsException(() => pcode.ToString(o => default)); + XAssert.Contains("Assignment of parameter '{A|", ex.Message); + + ex = Assert.ThrowsException(() => pcode2.ToString(o => default)); + XAssert.Contains("Assignment of parameter '{A|", ex.Message); + } + + [TestMethod] + public void SymbolicParameters_Partial_DefaultChain() + { + var expr = new JsBinaryExpression(symbolA.ToExpression(), BinaryOperatorType.Plus, symbolE.ToExpression()); + var pcode = expr.FormatParametrizedScript(); + + Assert.AreEqual("x+(x+x*defaultValue)", pcode.ToString(o => o == symbolA ? CodeParameterAssignment.FromIdentifier("x") : default)); + Assert.AreEqual("y+(y+y*defaultValue)", pcode.ToString(o => o == symbolA ? CodeParameterAssignment.FromIdentifier("y") : default)); + + // substitute symbolD for identifier, order doesn't matter + Assert.AreEqual("x+(x+D)", + pcode.AssignParameters(o => o == symbolA ? CodeParameterAssignment.FromIdentifier("x") : default) + .ToString(o => o == symbolD ? CodeParameterAssignment.FromExpression(new JsIdentifierExpression("D")) : default)); + Assert.AreEqual("x+(x+D)", + pcode.AssignParameters(o => o == symbolD ? CodeParameterAssignment.FromExpression(new JsIdentifierExpression("D")) : default) + .ToString(o => o == symbolA ? CodeParameterAssignment.FromIdentifier("x") : default)); + + // substitute symbolD for symbolA, order now matters + Assert.AreEqual("x+(x+x)", + pcode.AssignParameters(o => o == symbolD ? CodeParameterAssignment.FromExpression(new JsSymbolicParameter(symbolA)) : default) + .ToString(o => o == symbolA ? CodeParameterAssignment.FromIdentifier("x") : default)); + Assert.AreEqual("x+(x+y)", + pcode.AssignParameters(o => o == symbolA ? CodeParameterAssignment.FromIdentifier("x") : default) + .AssignParameters(o => o == symbolD ? CodeParameterAssignment.FromExpression(new JsSymbolicParameter(symbolA)) : default) + .ToString(o => o == symbolA ? CodeParameterAssignment.FromIdentifier("y") : default)); + } + + [TestMethod] + public void SymbolicParameters_AddDefault() + { + var expr = new JsBinaryExpression(symbolA.ToExpression(), BinaryOperatorType.Plus, symbolE.ToExpression()); + var pcode = expr.FormatParametrizedScript(); + + // add default for symbolA + var assigned = pcode.AssignParameters(o => o == symbolA ? symbolA.ToExpression(CodeParameterAssignment.FromIdentifier("a")).FormatParametrizedScript() : default); + + Assert.AreEqual("a+(a+a*defaultValue)", assigned.ToString(o => default)); + Assert.AreEqual("a+(a+a*defaultValue)", assigned.ToDefaultString()); + + Assert.AreEqual("x+(x+x*defaultValue)", assigned.ToString(o => o == symbolA ? CodeParameterAssignment.FromIdentifier("x") : default)); + Assert.AreEqual("a+x", assigned.ToString(o => o == symbolE ? CodeParameterAssignment.FromIdentifier("x") : default)); + } + + [TestMethod] + public void SymbolicParameters_NoAssignmentNoAllocation() + { + var expr = new JsBinaryExpression(symbolA.ToExpression(), BinaryOperatorType.Plus, symbolE.ToExpression()); + var pcode = expr.FormatParametrizedScript(); + Func noAssignment = o => default; + + var b = GC.GetAllocatedBytesForCurrentThread(); + pcode.AssignParameters(noAssignment); + Assert.AreEqual(0, GC.GetAllocatedBytesForCurrentThread() - b); + } + } +} From 6a75b421f4b8643003d24cd53d8db4e5f835e318 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Standa=20Luke=C5=A1?= Date: Thu, 31 Oct 2024 21:13:02 +0100 Subject: [PATCH 6/6] Fix unit test build on .NET Framework --- .../Runtime/JavascriptCompilation/JsParametrizedCodeTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Tests/Runtime/JavascriptCompilation/JsParametrizedCodeTests.cs b/src/Tests/Runtime/JavascriptCompilation/JsParametrizedCodeTests.cs index 0b9dc9ac31..89df492ea9 100644 --- a/src/Tests/Runtime/JavascriptCompilation/JsParametrizedCodeTests.cs +++ b/src/Tests/Runtime/JavascriptCompilation/JsParametrizedCodeTests.cs @@ -110,6 +110,7 @@ public void SymbolicParameters_AddDefault() Assert.AreEqual("a+x", assigned.ToString(o => o == symbolE ? CodeParameterAssignment.FromIdentifier("x") : default)); } +#if DotNetCore [TestMethod] public void SymbolicParameters_NoAssignmentNoAllocation() { @@ -121,5 +122,6 @@ public void SymbolicParameters_NoAssignmentNoAllocation() pcode.AssignParameters(noAssignment); Assert.AreEqual(0, GC.GetAllocatedBytesForCurrentThread() - b); } +#endif } }