public void FormulaErrorUndefined() { var engine = new RecalcEngine(); // formula fails since 'B' is undefined. Assert.Throws <InvalidOperationException>(() => engine.SetFormula("A", "B*2", OnUpdate)); }
public void CheckBindError() { var engine = new RecalcEngine(); var result = engine.Check("3+foo+2"); // foo is undefined Assert.False(result.IsSuccess); Assert.Single(result.Errors); Assert.StartsWith("Error 2-5: Name isn't valid. This identifier isn't recognized", result.Errors[0].ToString()); }
public void CheckParseError() { var engine = new RecalcEngine(); var result = engine.Check("3*1+"); Assert.False(result.IsSuccess); Assert.Single(result.Errors); Assert.StartsWith("Error 4-4: Expected an operand", result.Errors[0].ToString()); }
public void FormulaCantRedefine() { var engine = new RecalcEngine(); engine.SetFormula("A", "2", OnUpdate); // Can't redefine an existing formula. Assert.Throws <InvalidOperationException>(() => engine.SetFormula("A", "3", OnUpdate)); }
public void BasicEval() { var engine = new RecalcEngine(); engine.UpdateVariable("M", 10.0); engine.UpdateVariable("M2", -4); var result = engine.Eval("M + Abs(M2)"); Assert.Equal(14.0, ((NumberValue)result).Value); }
public void PropagateNull() { var engine = new RecalcEngine(); engine.SetFormula("A", expr: "Blank()", OnUpdate); engine.SetFormula("B", "A", OnUpdate); var b = engine.GetValue("B"); Assert.True(b is BlankValue); }
public void CantChangeType() { var engine = new RecalcEngine(); engine.UpdateVariable("a", FormulaValue.New(12)); // not supported: Can't change a variable's type. Assert.Throws <NotSupportedException>(() => engine.UpdateVariable("a", FormulaValue.New("str")) ); }
public void EvalWithGlobals() { var engine = new RecalcEngine(); var context = FormulaValue.NewRecord(new { x = 15 }); var result = engine.Eval("With({y:2}, x+y)", context); Assert.Equal(17.0, ((NumberValue)result).Value); }
public void CheckBindEnum() { var engine = new RecalcEngine(); var result = engine.Check("TimeUnit.Hours"); Assert.True(result.IsSuccess); // The resultant type will be the underlying type of the enum provided to // check. In the case of TimeUnit, this is StringType Assert.True(result.ReturnType is StringType); Assert.Empty(result.TopLevelIdentifiers); }
public void CheckSuccess() { var engine = new RecalcEngine(); var result = engine.Check("3*2+x", new RecordType().Add( new NamedFormulaType("x", FormulaType.Number))); Assert.True(result.IsSuccess); Assert.True(result.ReturnType is NumberType); Assert.Single(result.TopLevelIdentifiers); Assert.Equal("x", result.TopLevelIdentifiers.First()); }
public void BasicRecalc() { var engine = new RecalcEngine(); engine.UpdateVariable("A", 15); engine.SetFormula("B", "A*2", OnUpdate); AssertUpdate("B-->30;"); engine.UpdateVariable("A", 20); AssertUpdate("B-->40;"); // Ensure we can update to null. engine.UpdateVariable("A", FormulaValue.NewBlank(FormulaType.Number)); AssertUpdate("B-->0;"); }
public void CustomFunction() { var engine = new RecalcEngine(); engine.AddFunction(new TestCustomFunction()); // Shows up in enuemeration var func = engine.GetAllFunctionNames().First(name => name == "TestCustom"); Assert.NotNull(func); // Can be invoked. var result = engine.Eval("TestCustom(2,3)"); Assert.Equal(6.0, result.ToObject()); }
public void ChangeRecord() { var engine = new RecalcEngine(); engine.UpdateVariable("R", FormulaValue.RecordFromFields( new NamedValue("F1", FormulaValue.NewBlank(FormulaType.Number)), new NamedValue("F2", FormulaValue.New(6)))); engine.SetFormula("A", "R.F2 + 3 + R.F1", OnUpdate); AssertUpdate("A-->9;"); engine.UpdateVariable("R", FormulaValue.RecordFromFields( new NamedValue("F1", FormulaValue.New(2)), new NamedValue("F2", FormulaValue.New(7)))); AssertUpdate("A-->12;"); }
public void RecalcNoExtraCallbacks() { var engine = new RecalcEngine(); engine.UpdateVariable("A1", 1); engine.UpdateVariable("A2", 5); engine.SetFormula("B", "A1+A2", OnUpdate); AssertUpdate("B-->6;"); engine.SetFormula("C", "A2*10", OnUpdate); AssertUpdate("C-->50;"); engine.UpdateVariable("A1", 2); AssertUpdate("B-->7;"); // Don't fire C, not touched engine.UpdateVariable("A2", 7); AssertUpdate("B-->9;C-->70;"); }
public void Recalc2() { var engine = new RecalcEngine(); engine.UpdateVariable("A", 1); engine.SetFormula("B", "A*10", OnUpdate); AssertUpdate("B-->10;"); engine.SetFormula("C", "B+5", OnUpdate); AssertUpdate("C-->15;"); // depend on grand child directly engine.SetFormula("D", "B+A", OnUpdate); AssertUpdate("D-->11;"); // Updating A will recalc both D and B. // But D also depends on B, so verify D pulls new value of B. engine.UpdateVariable("A", 2); // Batched up (we don't double fire) AssertUpdate("B-->20;C-->25;D-->22;"); }
[InlineData("With({B:15}, B> A)", "A")] // B is shadowed public void T1(string expr, string dependsOn) { // var expected = new HashSet<string>(dependsOn.Split(',')); var engine = new RecalcEngine(); var accountType = new TableType() .Add(new NamedFormulaType("Age", FormulaType.Number)); var type = new RecordType() .Add(new NamedFormulaType("A", FormulaType.Number)) .Add(new NamedFormulaType("B", FormulaType.Number)) .Add(new NamedFormulaType("Accounts", accountType)); var result = engine.Check(expr, type); Assert.True(result.IsSuccess); // sets should be equal var sorted = result.TopLevelIdentifiers.OrderBy(x => x).ToArray(); var actualStr = string.Join(',', sorted); Assert.Equal(dependsOn, actualStr); }