Files
aibis-dream/Assets/Editor/AudioGlobalParameterSnapshotTests.cs
T

261 lines
10 KiB
C#

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 AudioGlobalParameterSnapshotTests
{
[Test]
public void SetFromYarn_TracksOnlySuccessfulUniqueNames()
{
var api = new FakeFmodGlobalParameterApi();
api.SetByNameResults["missing"] = RESULT.ERR_EVENT_NOTFOUND;
var registry = new YarnGlobalParameterRegistry(api);
Assert.That(registry.SetFromYarn("reverb", 0.4f), Is.EqualTo(RESULT.OK));
Assert.That(registry.SetFromYarn("REVERB", 0.7f), Is.EqualTo(RESULT.OK));
Assert.That(
registry.SetFromYarn("missing", 1f),
Is.EqualTo(RESULT.ERR_EVENT_NOTFOUND));
Assert.That(registry.TrackedNames, Has.Count.EqualTo(1));
Assert.That(registry.TrackedNames.Single(), Is.EqualTo("reverb"));
Assert.That(api.SetByNameCalls[0].IgnoreSeekSpeed, Is.False);
Assert.That(api.SetByNameCalls[1].IgnoreSeekSpeed, Is.False);
}
[Test]
public void CaptureFinalValues_ReadsFmodFinalValueAndRetainsFailuresForRetry()
{
var api = new FakeFmodGlobalParameterApi();
api.Reads["stage"] = new ParameterRead(9f, 2.37f, RESULT.OK);
api.Reads["missing"] = new ParameterRead(0f, 0f, RESULT.ERR_EVENT_NOTFOUND);
var registry = new YarnGlobalParameterRegistry(api);
registry.SetFromYarn("stage", 9f);
registry.SetFromYarn("missing", 1f);
var warnings = new List<string>();
var snapshot = registry.CaptureFinalValues(warnings.Add);
Assert.That(snapshot, Has.Count.EqualTo(1));
Assert.That(snapshot["stage"], Is.EqualTo(2.37f));
Assert.That(snapshot["stage"], Is.Not.EqualTo(9f));
Assert.That(registry.TrackedNames, Is.EquivalentTo(new[] { "stage", "missing" }));
Assert.That(warnings, Has.Count.EqualTo(1));
StringAssert.Contains("missing", warnings[0]);
}
[Test]
public void ResetToDefaults_RemovesOnlyParametersThatFmodResetSuccessfully()
{
var goodId = new PARAMETER_ID { data1 = 1, data2 = 2 };
var failedId = new PARAMETER_ID { data1 = 3, data2 = 4 };
var api = new FakeFmodGlobalParameterApi();
api.Descriptions["good"] = new ParameterDescriptionRead(
new PARAMETER_DESCRIPTION { id = goodId, defaultvalue = 0.25f },
RESULT.OK);
api.Descriptions["failed"] = new ParameterDescriptionRead(
new PARAMETER_DESCRIPTION { id = failedId, defaultvalue = 0.75f },
RESULT.OK);
api.SetByIdResults[failedId] = RESULT.ERR_INVALID_PARAM;
var registry = new YarnGlobalParameterRegistry(api);
registry.SetFromYarn("good", 1f);
registry.SetFromYarn("failed", 1f);
var warnings = new List<string>();
registry.ResetToDefaults(warnings.Add);
Assert.That(registry.TrackedNames, Is.EquivalentTo(new[] { "failed" }));
Assert.That(api.SetByIdCalls, Has.Count.EqualTo(2));
var goodReset = api.SetByIdCalls.Single(call => call.Id.Equals(goodId));
Assert.That(goodReset.Value, Is.EqualTo(0.25f));
Assert.That(goodReset.IgnoreSeekSpeed, Is.True);
Assert.That(warnings, Has.Count.EqualTo(1));
StringAssert.Contains("failed", warnings[0]);
}
[Test]
public void RestoreFinalValues_JumpsImmediatelyThenNextYarnTargetUsesSeekSpeed()
{
var api = new FakeFmodGlobalParameterApi();
api.SetByNameResults["missing"] = RESULT.ERR_EVENT_NOTFOUND;
var registry = new YarnGlobalParameterRegistry(api);
var warnings = new List<string>();
registry.RestoreFinalValues(
new Dictionary<string, float>
{
["stage"] = 2.37f,
["missing"] = 1f
},
warnings.Add);
registry.SetFromYarn("stage", 3f);
Assert.That(api.SetByNameCalls, Has.Count.EqualTo(3));
Assert.That(api.SetByNameCalls[0].Value, Is.EqualTo(2.37f));
Assert.That(api.SetByNameCalls[0].IgnoreSeekSpeed, Is.True);
Assert.That(api.SetByNameCalls[1].Name, Is.EqualTo("missing"));
Assert.That(api.SetByNameCalls[1].IgnoreSeekSpeed, Is.True);
Assert.That(api.SetByNameCalls[2].Value, Is.EqualTo(3f));
Assert.That(api.SetByNameCalls[2].IgnoreSeekSpeed, Is.False);
Assert.That(registry.TrackedNames, Is.EquivalentTo(new[] { "stage" }));
Assert.That(warnings, Has.Count.EqualTo(1));
StringAssert.Contains("missing", warnings[0]);
}
[Test]
public void AudioSnapshotDto_RoundTripsFinalValuesWithoutChangingSchema()
{
var source = new SaveSnapshot();
source.sections[SnapshotProviderIds.Audio] = new AudioSnapshotDto
{
ambState = "Dream",
yarnGlobalParameters = new Dictionary<string, float>
{
["reverb"] = 0.42f,
["hs1LogStage"] = 2.37f
}
};
var json = JsonConvert.SerializeObject(source);
var restored = JsonConvert.DeserializeObject<SaveSnapshot>(json);
var audio = ((JObject)restored.sections[SnapshotProviderIds.Audio])
.ToObject<AudioSnapshotDto>();
Assert.That(restored.schemaVersion, Is.EqualTo(2));
Assert.That(SaveSnapshotSchema.CurrentVersion, Is.EqualTo(2));
Assert.That(audio.yarnGlobalParameters["reverb"], Is.EqualTo(0.42f));
Assert.That(audio.yarnGlobalParameters["hs1LogStage"], Is.EqualTo(2.37f));
}
private readonly struct SetByNameCall
{
internal SetByNameCall(string name, float value, bool ignoreSeekSpeed)
{
Name = name;
Value = value;
IgnoreSeekSpeed = ignoreSeekSpeed;
}
internal string Name { get; }
internal float Value { get; }
internal bool IgnoreSeekSpeed { get; }
}
private readonly struct SetByIdCall
{
internal SetByIdCall(PARAMETER_ID id, float value, bool ignoreSeekSpeed)
{
Id = id;
Value = value;
IgnoreSeekSpeed = ignoreSeekSpeed;
}
internal PARAMETER_ID Id { get; }
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 readonly struct ParameterDescriptionRead
{
internal ParameterDescriptionRead(
PARAMETER_DESCRIPTION description,
RESULT result)
{
Description = description;
Result = result;
}
internal PARAMETER_DESCRIPTION Description { get; }
internal RESULT Result { get; }
}
private sealed class FakeFmodGlobalParameterApi : IFmodGlobalParameterApi
{
internal readonly Dictionary<string, RESULT> SetByNameResults =
new(System.StringComparer.OrdinalIgnoreCase);
internal readonly Dictionary<string, ParameterRead> Reads =
new(System.StringComparer.OrdinalIgnoreCase);
internal readonly Dictionary<string, ParameterDescriptionRead> Descriptions =
new(System.StringComparer.OrdinalIgnoreCase);
internal readonly Dictionary<PARAMETER_ID, RESULT> SetByIdResults = new();
internal readonly List<SetByNameCall> SetByNameCalls = new();
internal readonly List<SetByIdCall> SetByIdCalls = new();
public RESULT SetParameterByName(
string name,
float value,
bool ignoreSeekSpeed)
{
SetByNameCalls.Add(new SetByNameCall(name, value, ignoreSeekSpeed));
return SetByNameResults.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.OK;
}
public RESULT GetParameterDescriptionByName(
string name,
out PARAMETER_DESCRIPTION description)
{
if (Descriptions.TryGetValue(name, out var read))
{
description = read.Description;
return read.Result;
}
description = default;
return RESULT.ERR_EVENT_NOTFOUND;
}
public RESULT SetParameterById(
PARAMETER_ID id,
float value,
bool ignoreSeekSpeed)
{
SetByIdCalls.Add(new SetByIdCall(id, value, ignoreSeekSpeed));
return SetByIdResults.TryGetValue(id, out var result)
? result
: RESULT.OK;
}
}
}
}