Skip to content

Commit 422a9b9

Browse files
committed
Add Tests
1 parent 7f5a525 commit 422a9b9

File tree

4 files changed

+70
-14
lines changed

4 files changed

+70
-14
lines changed

src/libraries/Microsoft.PowerFx.Core/Functions/UserDefinedFunction.cs

+7-2
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,14 @@ public void CheckTypesOnDeclaration(CheckTypesContext context, DType actualBodyR
191191
Contracts.AssertValue(actualBodyReturnType);
192192
Contracts.AssertValue(binding);
193193

194-
if (!ReturnType.Accepts(actualBodyReturnType, exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules, true))
194+
if (!ReturnType.Accepts(
195+
actualBodyReturnType,
196+
exact: true,
197+
useLegacyDateTimeAccepts: false,
198+
usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules,
199+
restrictiveAggregateTypes: true))
195200
{
196-
if (actualBodyReturnType.CoercesTo(ReturnType, true, false, context.Features, true))
201+
if (actualBodyReturnType.CoercesTo(ReturnType, true, false, context.Features, restrictiveAggregateTypes: true))
197202
{
198203
_binding.SetCoercedType(binding.Top, ReturnType);
199204
}

src/libraries/Microsoft.PowerFx.Core/Types/DType.cs

+6-3
Original file line numberDiff line numberDiff line change
@@ -1851,7 +1851,7 @@ private bool AcceptsEntityType(DType type, bool usePowerFxV1CompatibilityRules)
18511851
/// <param name="useLegacyDateTimeAccepts">Legacy rules for accepting date/time types.</param>
18521852
/// <param name="usePowerFxV1CompatibilityRules">Use PFx v1 compatibility rules if enabled (less
18531853
/// permissive Accepts relationships).</param>
1854-
/// <param name="restrictiveAggregateTypes">restrictiveAggregateTypes.</param>
1854+
/// <param name="restrictiveAggregateTypes">Flag to restrict using aggregate types with more fields than expected.</param>
18551855
/// <returns>
18561856
/// True if <see cref="DType"/> accepts <paramref name="type"/>, false otherwise.
18571857
/// </returns>
@@ -1886,6 +1886,7 @@ public bool Accepts(DType type, bool exact, bool useLegacyDateTimeAccepts, bool
18861886
/// <param name="useLegacyDateTimeAccepts">Legacy rules for accepting date/time types.</param>
18871887
/// <param name="usePowerFxV1CompatibilityRules">Use PFx v1 compatibility rules if enabled (less
18881888
/// permissive Accepts relationships).</param>
1889+
/// <param name="restrictiveAggregateTypes">Flag to restrict using aggregate types with more fields than expected.</param>
18891890
/// <returns>
18901891
/// True if <see cref="DType"/> accepts <paramref name="type"/>, false otherwise.
18911892
/// </returns>
@@ -3208,7 +3209,8 @@ public bool AggregateCoercesTo(DType typeDest, out bool isSafe, out DType coerci
32083209
out schemaDifferenceType,
32093210
aggregateCoercion: true,
32103211
isTopLevelCoercion: false,
3211-
features);
3212+
features,
3213+
restrictiveAggregateTypes);
32123214
}
32133215

32143216
if (Kind != typeDest.Kind)
@@ -3243,7 +3245,8 @@ public bool AggregateCoercesTo(DType typeDest, out bool isSafe, out DType coerci
32433245
out var fieldSchemaDifferenceType,
32443246
aggregateCoercion,
32453247
isTopLevelCoercion: false,
3246-
features);
3248+
features,
3249+
restrictiveAggregateTypes);
32473250

32483251
// This is the attempted coercion type. If we fail, we need to know this for error handling
32493252
coercionType = coercionType.Add(typedName.Name, fieldCoercionType);

src/tests/Microsoft.PowerFx.Core.Tests.Shared/UserDefinedTypeTests.cs

+14
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,20 @@ public void TestRecordOfErrors(string typeDefinition, string expectedMessageKey)
156156
Assert.Contains(errors, e => e.MessageKey.Contains(expectedMessageKey));
157157
}
158158

159+
[Theory]
160+
[InlineData("f():T = {x: 5, y: 5}; T := Type({x: Number});", "ErrUDF_ReturnTypeSchemaDoesNotMatch")]
161+
[InlineData("f(x:T):Number = x.n; T := Type({n: Number}); g(): Number = f({n: 5, m: 5});", "ErrBadSchema_ExpectedType")]
162+
[InlineData("f():T = [{x: 5, y: 5}]; T := Type([{x: Number}]);", "ErrUDF_ReturnTypeSchemaDoesNotMatch")]
163+
[InlineData("f(x:T):T = x; T := Type([{n: Number}]); g(): T = f([{n: 5, m: 5}]);", "ErrBadSchema_ExpectedType")]
164+
public void TestAggregateTypeErrors(string typeDefinition, string expectedMessageKey)
165+
{
166+
var checkResult = new DefinitionsCheckResult()
167+
.SetText(typeDefinition)
168+
.SetBindingInfo(_primitiveTypes);
169+
var errors = checkResult.ApplyErrors();
170+
Assert.Contains(errors, e => e.MessageKey.Contains(expectedMessageKey));
171+
}
172+
159173
[Theory]
160174
[InlineData("T := Type({ x: 5+5, y: -5 });", 2)]
161175
[InlineData("T := Type(Type(Number));", 1)]

src/tests/Microsoft.PowerFx.Interpreter.Tests.Shared/RecalcEngineTests.cs

+43-9
Original file line numberDiff line numberDiff line change
@@ -1871,13 +1871,6 @@ protected override bool TryGetField(FormulaType fieldType, string fieldName, out
18711871
"getAge({Name: \"Bob\", Age: 21})",
18721872
true,
18731873
21.0)]
1874-
[InlineData(
1875-
@"Employee := Type({Name: Text, Age: Number, Title: Text}); Employees := Type([Employee]); EmployeeNames := Type([{Name: Text}]);
1876-
getNames(e: Employees):EmployeeNames = ShowColumns(e, Name);
1877-
getNamesCount(e: EmployeeNames):Number = CountRows(getNames(e));",
1878-
"getNamesCount([{Name: \"Jim\", Age:25}, {Name: \"Tony\", Age:42}])",
1879-
true,
1880-
2.0)]
18811874
[InlineData(
18821875
@"Employee := Type({Name: Text, Age: Number, Title: Text});
18831876
getAge(e: Employee): Number = e.Age;
@@ -1946,14 +1939,55 @@ protected override bool TryGetField(FormulaType fieldType, string fieldName, out
19461939
true,
19471940
1.0)]
19481941

1949-
// Aggregate types with more than expected fields are not allowed in UDF
1942+
// Aggregate types with more than expected fields are not allowed in UDF args and return types
1943+
// Records
19501944
[InlineData(
19511945
"f():T = {x: 5, y: 5}; T := Type({x: Number});",
1952-
"f().x",
1946+
"",
1947+
false)]
1948+
[InlineData(
1949+
"f():T = {x: 5, y: 5}; T1 := Type([{x: Number}]); T2 := Type(RecordOf(T1));",
1950+
"",
1951+
false)]
1952+
[InlineData(
1953+
"g(x:T):Number = x.n; T := Type({n: Number});",
1954+
"g({x: 5, y: 5})",
1955+
false)]
1956+
1957+
// Nested Records
1958+
[InlineData(
1959+
"f():T = {a: 5, b: {c: {d: 5, e:42}}}; T := Type({a: Number, b: {c: {d: Number}}});",
1960+
"",
1961+
false)]
1962+
[InlineData(
1963+
"g(x:T):Number = x.b.c.d; T := Type({a: Number, b: {c: {d: Number}}});",
1964+
"g({a: 5, b: {c: {d: 5, e:42}}})",
1965+
false)]
1966+
1967+
// Tables
1968+
[InlineData(
1969+
"f():T = [{x: 5, y: 5}]; T := Type([{x: Number}]);",
1970+
"",
19531971
false)]
19541972
[InlineData(
19551973
"People := Type([{Name: Text, Age: Number}]); countMinors(p: People): Number = CountRows(Filter(p, Age < 18));",
19561974
"countMinors([{Name: \"Bob\", Age: 21, Title: \"Engineer\"}, {Name: \"Alice\", Age: 25, Title: \"Manager\"}])",
1975+
false)]
1976+
[InlineData(
1977+
@"Employee := Type({Name: Text, Age: Number, Title: Text}); Employees := Type([Employee]); EmployeeNames := Type([{Name: Text}]);
1978+
getNames(e: Employees):EmployeeNames = ShowColumns(e, Name);
1979+
getNamesCount(e: EmployeeNames):Number = CountRows(getNames(e));",
1980+
"getNamesCount([{Name: \"Jim\", Age:25}, {Name: \"Tony\", Age:42}])",
1981+
false)]
1982+
1983+
// Nested Tables
1984+
[InlineData(
1985+
"f():T = {a: 5, b: [{c: {d: 5, e:42}}]}; T := Type([{a: Number, b: [{c: {d: Number}}]}]);",
1986+
"",
1987+
false)]
1988+
[InlineData(
1989+
"g(x:T):Number = First(First(x).b).c.d; T := Type([{a: Number, b: [{c: {d: Number}}]}])",
1990+
"g({a: 5, b: [{c: {d: 5, e:42}}]})",
19571991
false)]
19581992
public void UserDefinedTypeTest(string userDefinitions, string evalExpression, bool isValid, double expectedResult = 0)
19591993
{

0 commit comments

Comments
 (0)