Skip to content

Commit 28050c0

Browse files
MikeStallMike Stall
and
Mike Stall
authored
Fix #378 (#379)
* Fix #378 If(true, OptionSet.Option1) OptionSetValue should derive from ValidFormulaValue, not FormulaValue * Pr feedback Co-authored-by: Mike Stall <[email protected]>
1 parent 4860013 commit 28050c0

File tree

7 files changed

+75
-19
lines changed

7 files changed

+75
-19
lines changed

src/libraries/Microsoft.PowerFx.Core/Public/Values/OptionSetValue.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ namespace Microsoft.PowerFx.Types
1010
/// <summary>
1111
/// A value within an option set.
1212
/// </summary>
13-
[DebuggerDisplay("{ToString})")]
14-
public class OptionSetValue : FormulaValue
13+
[DebuggerDisplay("{ToString()})")]
14+
public class OptionSetValue : ValidFormulaValue
1515
{
1616
/// <summary>
1717
/// Logical name for this option set value.

src/libraries/Microsoft.PowerFx.Interpreter/EvalVisitor.cs

+1-8
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,7 @@ internal async ValueTask<DValue<T>> EvalArgAsync<T>(FormulaValue arg, SymbolCont
4545
{
4646
if (arg is LambdaFormulaValue lambda)
4747
{
48-
var val = await lambda.EvalAsync(this, context);
49-
return val switch
50-
{
51-
T t => DValue<T>.Of(t),
52-
BlankValue b => DValue<T>.Of(b),
53-
ErrorValue e => DValue<T>.Of(e),
54-
_ => DValue<T>.Of(CommonErrors.RuntimeTypeMismatch(irContext))
55-
};
48+
arg = await lambda.EvalAsync(this, context);
5649
}
5750

5851
return arg switch

src/tests/Microsoft.PowerFx.Core.Tests/ExpressionTestCases/OptionSet.txt

+11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
#SETUP: OptionSetTestSetup
22

3+
// Both logical and display names bind
4+
>> OptionSet.option_1
5+
OptionSet.option_1
6+
7+
>> OptionSet.Option1
8+
OptionSet.option_1
9+
10+
// Can use in If
11+
>> If(true, OptionSet.Option1)
12+
OptionSet.option_1
13+
314
>> OptionSet.Option1 <> OptionSet.Option2
415
true
516

src/tests/Microsoft.PowerFx.Core.Tests/ExpressionTestHelpers/TestRunner.cs

+4
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,10 @@ internal static void TestToString(FormulaValue result, StringBuilder sb)
374374
var dateTime = dt.Value;
375375
sb.Append($"DateTime({dateTime.Year},{dateTime.Month},{dateTime.Day},{dateTime.Hour},{dateTime.Minute},{dateTime.Second},{dateTime.Millisecond})");
376376
}
377+
else if (result is OptionSetValue opt)
378+
{
379+
sb.Append($"{opt.Type.OptionSetName}.{opt.Option}");
380+
}
377381
else if (result is ErrorValue)
378382
{
379383
sb.Append(result);

src/tests/Microsoft.PowerFx.Interpreter.Tests/DisplayNameOptionSetTests.cs

+20
Original file line numberDiff line numberDiff line change
@@ -115,5 +115,25 @@ public void PowerFxConfigCollisionsThrow()
115115
Assert.True(config.TryGetSymbol(new DName("SomeDisplayName"), out _, out displayName));
116116
Assert.Equal("SomeDisplayName", displayName.Value);
117117
}
118+
119+
[Fact]
120+
public void Sample()
121+
{
122+
var config = new PowerFxConfig();
123+
var displayNames = DisplayNameUtility.MakeUnique(new Dictionary<string, string>
124+
{
125+
{ "option_1", "Option1" },
126+
{ "option_2", "Option2" }
127+
});
128+
129+
var option = new OptionSet("OptionSet", displayNames);
130+
131+
config.AddOptionSet(option);
132+
133+
var engine = new RecalcEngine(config);
134+
135+
var expression = "If(true, OptionSet.Option1)";
136+
var value = engine.Eval(expression);
137+
}
118138
}
119139
}

src/tests/Microsoft.PowerFx.Interpreter.Tests/FileExpressionEvaluationTests.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public void InterpreterTestCase(ExpressionTestCase testCase)
4646
Skip.If(true, prefix + msg);
4747
break;
4848
}
49-
}
49+
}
5050

5151
// Since test discovery runs in a separate process, run a dedicated
5252
// parse pass as a single unit test to verify all the .txt will parse.

src/tests/Microsoft.PowerFx.Interpreter.Tests/ValueTests.cs

+36-8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Collections;
6+
using System.Collections.Generic;
67
using System.Linq;
78
using Microsoft.PowerFx.Types;
89
using Xunit;
@@ -53,11 +54,11 @@ public void Number(double val, string expectedStr)
5354
double? val2 = val;
5455
var formulaValue2 = FormulaValue.New((double?)val); // nullable overload
5556
Assert.Equal(expectedStr, formulaValue2.Dump());
56-
57+
5758
var formulaValue3 = FormulaValue.New((double?)null);
5859
Assert.IsType<NumberType>(formulaValue3.Type);
5960
Assert.IsType<BlankValue>(formulaValue3);
60-
}
61+
}
6162

6263
[Theory]
6364
[InlineData("abc", "\"abc\"")]
@@ -168,7 +169,7 @@ private class TestRow
168169
[Fact]
169170
public void Table()
170171
{
171-
TableValue val = _cache.NewTable(
172+
TableValue val = _cache.NewTable(
172173
new TestRow { a = 10, str = "alpha" },
173174
new TestRow { a = 15, str = "beta" });
174175

@@ -183,7 +184,7 @@ public void Table()
183184

184185
Assert.Equal("Table({a:10,str:\"alpha\"},{a:15,str:\"beta\"})", resultStr);
185186
}
186-
187+
187188
[Fact]
188189
public void TableFromRecords()
189190
{
@@ -193,7 +194,7 @@ public void TableFromRecords()
193194

194195
var result1 = ((RecordValue)val.Index(2).Value).GetField("a").ToObject();
195196
Assert.Equal(15.0, result1);
196-
197+
197198
dynamic d = val.ToObject();
198199
Assert.Equal(10.0, d[0].a);
199200

@@ -211,7 +212,7 @@ public void TableFromMixedRecords()
211212
{
212213
var cache = new TypeMarshallerCache();
213214
RecordValue r1 = _cache.NewRecord(new { a = 10, b = 20, c = 30 });
214-
RecordValue r2 = _cache.NewRecord(new { a = 11, c = 31 });
215+
RecordValue r2 = _cache.NewRecord(new { a = 11, c = 31 });
215216
TableValue val = FormulaValue.NewTable(r1.Type, r1, r2);
216217

217218
// Users first type
@@ -221,7 +222,7 @@ public void TableFromMixedRecords()
221222

222223
var result2 = ((RecordValue)val.Index(2).Value).GetField("b");
223224
Assert.IsType<BlankValue>(result2);
224-
Assert.IsType<NumberType>(result2.Type);
225+
Assert.IsType<NumberType>(result2.Type);
225226
}
226227

227228
[Fact]
@@ -259,7 +260,7 @@ public void TableFromPrimitive()
259260
Assert.Equal("[10,20]", resultStr);
260261

261262
// Must use NewSingleColumnTable to create a single column table.
262-
Assert.Throws<InvalidOperationException>(() => NewTableT(r1, r2));
263+
Assert.Throws<InvalidOperationException>(() => NewTableT(r1, r2));
263264
}
264265

265266
[Fact]
@@ -309,6 +310,33 @@ public void Blanks()
309310
Assert.True(r.GetField("missing") is BlankValue);
310311
Assert.Equal(15.1, r.GetField("number").ToObject());
311312
}
313+
314+
[Fact]
315+
public void DeriveFromValidFormulaValue()
316+
{
317+
// Only Blank and Error can derive from FormulaValue directly.
318+
// All else should derive from ValidFormulaValue.
319+
// See ValidFormulaValue for explanation.
320+
var set = new HashSet<Type>
321+
{
322+
typeof(BlankValue),
323+
typeof(ErrorValue),
324+
typeof(ValidFormulaValue),
325+
typeof(LambdaFormulaValue), // Special, can eval to any FormulaValue.
326+
};
327+
328+
var asmInterpreter = typeof(RecalcEngine).Assembly;
329+
var asmCore = typeof(Engine).Assembly;
330+
var allTypes = asmInterpreter.GetTypes().Concat(asmCore.GetTypes());
331+
332+
foreach (var type in allTypes)
333+
{
334+
if (type.BaseType == typeof(FormulaValue))
335+
{
336+
Assert.True(set.Contains(type), $"Type {type.FullName} should derive from {typeof(ValidFormulaValue).FullName}, not FormulaValue.");
337+
}
338+
}
339+
}
312340
}
313341

314342
public static class FormulaValueExtensions

0 commit comments

Comments
 (0)