using System.Collections.Generic; using System.Linq; using AibisDream.Kit; using AibisDream.SaveSystem; using FMOD; using FMOD.Studio; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NUnit.Framework; namespace AibisDream.SystemEditor.Tests { public sealed class AudioSnapshotContractTests { [Test] public void Registry_CapturesFinalValuesOnlyForSuccessfulParameters() { var api = new FakeFmodGlobalParameterApi(); api.Reads["stage"] = new ParameterRead(9f, 2.37f, RESULT.OK); api.SetResults["missing"] = RESULT.ERR_EVENT_NOTFOUND; var registry = new YarnGlobalParameterRegistry(api); registry.SetFromYarn("stage", 9f); registry.SetFromYarn("STAGE", 10f); registry.SetFromYarn("missing", 1f); var snapshot = registry.CaptureFinalValues(_ => { }); Assert.That(registry.TrackedNames, Is.EqualTo(new[] { "stage" })); Assert.That(snapshot["stage"], Is.EqualTo(2.37f)); Assert.That(api.SetCalls.All(call => !call.IgnoreSeekSpeed), Is.True); } [Test] public void Registry_RestoreJumpsImmediatelyThenYarnUsesSeekSpeed() { var api = new FakeFmodGlobalParameterApi(); var registry = new YarnGlobalParameterRegistry(api); registry.RestoreFinalValues( new Dictionary { ["stage"] = 2.37f }, _ => { }); registry.SetFromYarn("stage", 3f); Assert.That(api.SetCalls, Has.Count.EqualTo(2)); Assert.That(api.SetCalls[0].Value, Is.EqualTo(2.37f)); Assert.That(api.SetCalls[0].IgnoreSeekSpeed, Is.True); Assert.That(api.SetCalls[1].Value, Is.EqualTo(3f)); Assert.That(api.SetCalls[1].IgnoreSeekSpeed, Is.False); } [Test] public void AudioSnapshot_RoundTripsWithoutChangingSchema() { var source = new SaveSnapshot(); source.sections[SnapshotProviderIds.Audio] = new AudioSnapshotDto { ambState = "Dream", yarnGlobalParameters = new Dictionary { ["reverb"] = 0.42f, ["hs1LogStage"] = 2.37f } }; var restored = JsonConvert.DeserializeObject( JsonConvert.SerializeObject(source)); var audio = ((JObject)restored.sections[SnapshotProviderIds.Audio]) .ToObject(); Assert.That(restored.schemaVersion, Is.EqualTo(SaveSnapshotSchema.CurrentVersion)); Assert.That(audio.yarnGlobalParameters["reverb"], Is.EqualTo(0.42f)); Assert.That(audio.yarnGlobalParameters["hs1LogStage"], Is.EqualTo(2.37f)); } private readonly struct SetCall { internal SetCall(float value, bool ignoreSeekSpeed) { Value = value; IgnoreSeekSpeed = ignoreSeekSpeed; } internal float Value { get; } internal bool IgnoreSeekSpeed { get; } } private readonly struct ParameterRead { internal ParameterRead(float value, float finalValue, RESULT result) { Value = value; FinalValue = finalValue; Result = result; } internal float Value { get; } internal float FinalValue { get; } internal RESULT Result { get; } } private sealed class FakeFmodGlobalParameterApi : IFmodGlobalParameterApi { internal readonly Dictionary SetResults = new Dictionary(System.StringComparer.OrdinalIgnoreCase); internal readonly Dictionary Reads = new Dictionary(System.StringComparer.OrdinalIgnoreCase); internal readonly List SetCalls = new List(); public RESULT SetParameterByName( string name, float value, bool ignoreSeekSpeed) { SetCalls.Add(new SetCall(value, ignoreSeekSpeed)); return SetResults.TryGetValue(name, out var result) ? result : RESULT.OK; } public RESULT GetParameterByName( string name, out float value, out float finalValue) { if (Reads.TryGetValue(name, out var read)) { value = read.Value; finalValue = read.FinalValue; return read.Result; } value = 0f; finalValue = 0f; return RESULT.ERR_EVENT_NOTFOUND; } public RESULT GetParameterDescriptionByName( string name, out PARAMETER_DESCRIPTION description) { description = default; return RESULT.ERR_EVENT_NOTFOUND; } public RESULT SetParameterById( PARAMETER_ID id, float value, bool ignoreSeekSpeed) { return RESULT.OK; } } } }