Files
mod_dialog_base/tests/NumericAssignmentTests.cs

108 lines
6.9 KiB
C#

namespace Cthangover.Mods.Dialog.Tests
{
using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using Cthangover.Core.Factories.Impls;
using Cthangover.Core.Mods.Testing;
using Cthangover.Mods.Dialog.Scenarios;
using Godot;
/// <summary>Old string scripts, calculated assignments and action arguments share the existing DSL strategy pipeline.</summary>
public class NumericAssignmentTests
{
[ModTest("numeric_assignment/literals_case_and_assignment_syntax")]
static void Assignments(ModTestContext ctx)
{
var runtime=Run("set greeting Hello\nset literal 1+2\nset Value 7\ncset total \"1 + 2 + ${Value}\"\nset Money=rnd(3,3)\ncset quoted=\"${total} * 2\"\nend\n");
ModAssert.Equal("Hello",runtime.GetVariable("greeting"),"old strings stay strings");
ModAssert.Equal("1+2",runtime.GetVariable("literal"),"set does not guess expressions");
ModAssert.Equal("10",runtime.GetVariable("total"),"earlier numeric string is read at runtime");
ModAssert.Equal("3",runtime.GetVariable("Money"),"assigned variable keeps its case");
ModAssert.Equal("20",runtime.GetVariable("quoted"),"quoted assignment expression retains spaces");
ModAssert.Null(runtime.LastExpressionError,"valid assignments have no errors");
}
[ModTest("numeric_assignment/errors_abort_dependents_without_overwriting")]
static void Errors(ModTestContext ctx)
{
foreach(var expression in new[]{"1/0","${missing}+1","rnd(5,3)","max()"})
{
var runtime=Run("set amount 9\ncset amount \""+expression+"\"\nset after wrong\nend\n");
ModAssert.Equal("9",runtime.GetVariable("amount"),"bad assignment preserves the previous value for inspection");
ModAssert.Null(runtime.GetVariable("after"),"dependent actions never run after a bad calculation");
ModAssert.True(runtime.LastExpressionError?.Contains(":2:")==true,"error reports the source line");
}
var text=Run("set value abc\ncset result ${value}+1\nset after wrong\nend\n");
ModAssert.True(text.LastExpressionError?.Contains("not numeric")==true,"non-numeric variables are diagnosed");
}
[ModTest("numeric_assignment/invariant_format_and_json_tokens")]
static void Formatting(ModTestContext ctx)
{
var culture=CultureInfo.CurrentCulture;
try
{
// A hand-built comma culture: ru-RU/es-ES cannot be created where ICU is missing
// (invariant globalization), and the rule must hold for any culture anyway.
var comma=(CultureInfo)CultureInfo.InvariantCulture.Clone();
comma.NumberFormat.NumberDecimalSeparator=",";
comma.NumberFormat.NumberGroupSeparator=" ";
CultureInfo.CurrentCulture=comma;
ModAssert.Equal("2.5",Run("cset amount 5/2\nend\n").GetVariable("amount"),"stored numeric syntax does not depend on UI culture");
}
finally{CultureInfo.CurrentCulture=culture;}
var positional=new List<string>();var named=new Dictionary<string,string>();
ScenarioParser.SplitLine("light_set [{\"x\":0.2,\"color\":\"#fff\"}]",positional,named,out _);
ModAssert.Equal("[{\"x\":0.2,\"color\":\"#fff\"}]",positional[0],"JSON quotes are not stripped by assignment quoting");
ModAssert.Equal(0,named.Count,"JSON never becomes a named argument");
}
[ModTest("numeric_assignment/command_owned_validation")]
static void Validation(ModTestContext ctx)
{
IScenarioCommandSpec spec=new CsetCommandStrategy();
ModAssert.NotNull(spec.ValidateArguments(new(){"amount"},new(),""),"missing operand diagnosed");
ModAssert.NotNull(spec.ValidateArguments(new(){"amount","rnd(1)"},new(),""),"function arity checked without rolling");
ModAssert.Null(spec.ValidateArguments(new(),new(){["Money"]="rnd(3,5)"},""),"assignment form recognized by strategy contract");
var positional=new List<string>();var named=new Dictionary<string,string>{["Money"]="rnd(3,5)"};
spec.NormalizeArguments(positional,named);
ModAssert.Equal("Money",positional[0],"schema normalization preserves case");
ModAssert.Equal("rnd(3,5)",positional[1],"schema receives the unevaluated expression");
ModAssert.Equal(0,named.Count,"normalized arguments are canonical");
}
[ModTest("numeric_assignment/localized_text_and_action_share_one_value",TimeoutSeconds=10)]
static async Task TextAndAction(ModTestContext ctx)
{
var stage=await DialogStage.Create();var capture=new NumericCaptureAction();
ScenarioActionFactory.Instance.Register(capture);
try
{
var dialog=ScenarioParser.Parse("cset money=rnd(3,3)\naction dsl_test.capture id=money/coin count=${money}\nptext \"Fallback\" key=mod_dialog_base_name\nend\n",new NumericTextLocale());
stage.Box.SetDialogQueueAndRun(dialog,null,0,null);
ModAssert.Equal("3",capture.Amount,"action receives a number, never the placeholder");
ModAssert.Equal("money/coin",capture.Item,"ordinary string parameters still work");
ModAssert.Equal("Received 3 coins.",stage.Box.GetNode<Label>("DialogBody/TextField").Text,"localized ptext displays the same stored value");
stage.Box.NextAction();
ModAssert.Equal("3",stage.Box.Runtime.GetVariable("money"),"continuing the text does not reroll");
}
finally{ScenarioActionFactory.Instance.UnregisterAssembly(typeof(NumericCaptureAction).Assembly);stage.Free();}
}
[ModTest("numeric_assignment/undefined_action_parameter_stops_action")]
static void MissingParameter(ModTestContext ctx)
{
var capture=new NumericCaptureAction();ScenarioActionFactory.Instance.Register(capture);
try
{
var runtime=Run("set marker kept\naction dsl_test.capture count=${missing}\nset marker wrong\nend\n");
ModAssert.Null(capture.Amount,"invalid parameter is never delivered to the action");
ModAssert.Equal("kept",runtime.GetVariable("marker"),"later state changes are skipped");
ModAssert.True(runtime.LastExpressionError?.Contains("missing")==true,"undefined parameter is reported");
}
finally{ScenarioActionFactory.Instance.UnregisterAssembly(typeof(NumericCaptureAction).Assembly);}
}
private static DialogRuntime Run(string script)
{
var dialog=ScenarioParser.Parse(script);var runtime=new DialogRuntime();
runtime.SetDialogQueueAndRun(null,dialog.Queue,null);runtime.Run();return runtime;
}
}
}