214 lines
6.9 KiB
C#
214 lines
6.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using FlowScope.Audio;
|
|
using FlowScope.Resources;
|
|
using NUnit.Framework;
|
|
using UnityEngine;
|
|
using UnityEngine.TestTools;
|
|
using Object = UnityEngine.Object;
|
|
|
|
namespace FlowScope.Tests.PlayMode.Audio
|
|
{
|
|
public sealed class AudioServiceTests
|
|
{
|
|
private FakeResourceService resources;
|
|
private AudioClip bgmA;
|
|
private AudioClip bgmB;
|
|
private AudioClip sfx;
|
|
|
|
[SetUp]
|
|
public void SetUp()
|
|
{
|
|
resources = new FakeResourceService();
|
|
bgmA = CreateClip("bgm-a", 0.25f);
|
|
bgmB = CreateClip("bgm-b", 0.25f);
|
|
sfx = CreateClip("sfx", 0.1f);
|
|
|
|
resources.Add("bgm-a", bgmA);
|
|
resources.Add("bgm-b", bgmB);
|
|
resources.Add("sfx", sfx);
|
|
}
|
|
|
|
[TearDown]
|
|
public void TearDown()
|
|
{
|
|
foreach (var service in Object.FindObjectsOfType<AudioService>())
|
|
{
|
|
Object.Destroy(service.gameObject);
|
|
}
|
|
}
|
|
|
|
[UnityTest]
|
|
public System.Collections.IEnumerator PlayBgm_ReturnsHandleAndStopBgmStopsIt()
|
|
{
|
|
var service = CreateService();
|
|
|
|
var handle = service.PlayBgm("bgm-a");
|
|
|
|
yield return null;
|
|
Assert.That(handle.IsPlaying, Is.True);
|
|
|
|
service.StopBgm();
|
|
|
|
yield return null;
|
|
Assert.That(handle.IsPlaying, Is.False);
|
|
}
|
|
|
|
[UnityTest]
|
|
public System.Collections.IEnumerator PlayBgm_WhenNewBgmStarts_StopsPreviousBgm()
|
|
{
|
|
var service = CreateService();
|
|
var first = service.PlayBgm("bgm-a");
|
|
|
|
yield return null;
|
|
var second = service.PlayBgm("bgm-b");
|
|
|
|
yield return null;
|
|
Assert.That(first.IsPlaying, Is.False);
|
|
Assert.That(second.IsPlaying, Is.True);
|
|
}
|
|
|
|
[UnityTest]
|
|
public System.Collections.IEnumerator PlaySfx_UsesConfiguredPoolLimitAndSkipsWhenExhausted()
|
|
{
|
|
var service = CreateService(new AudioServiceConfig
|
|
{
|
|
SfxPoolSize = 2,
|
|
ReuseOldestWhenExhausted = false
|
|
});
|
|
|
|
var first = service.PlaySfx("sfx");
|
|
var second = service.PlaySfx("sfx");
|
|
|
|
LogAssert.Expect(LogType.Warning, "SFX pool exhausted, skipped key: sfx");
|
|
var skipped = service.PlaySfx("sfx");
|
|
|
|
yield return null;
|
|
Assert.That(first.IsPlaying, Is.True);
|
|
Assert.That(second.IsPlaying, Is.True);
|
|
Assert.That(skipped.IsPlaying, Is.False);
|
|
Assert.That(Object.FindObjectsOfType<AudioSource>().Length, Is.EqualTo(2));
|
|
}
|
|
|
|
[UnityTest]
|
|
public System.Collections.IEnumerator SetVolumes_ClampsAndMuteUpdatesExistingSources()
|
|
{
|
|
var service = CreateService();
|
|
var bgm = service.PlayBgm("bgm-a");
|
|
var sfxHandle = service.PlaySfx("sfx");
|
|
|
|
yield return null;
|
|
service.SetBgmVolume(2f);
|
|
service.SetSfxVolume(-1f);
|
|
|
|
var sources = Object.FindObjectsOfType<AudioSource>();
|
|
Assert.That(Array.Find(sources, source => source.clip == bgmA).volume, Is.EqualTo(1f));
|
|
Assert.That(Array.Find(sources, source => source.clip == sfx).volume, Is.EqualTo(0f));
|
|
|
|
service.SetMute(true);
|
|
|
|
Assert.That(Array.Find(sources, source => source.clip == bgmA).volume, Is.EqualTo(0f));
|
|
Assert.That(Array.Find(sources, source => source.clip == sfx).volume, Is.EqualTo(0f));
|
|
Assert.That(bgm.IsPlaying, Is.True);
|
|
Assert.That(sfxHandle.IsPlaying, Is.True);
|
|
}
|
|
|
|
[UnityTest]
|
|
public System.Collections.IEnumerator PlayBgm_FadeInAndFadeOutUseLinearProgress()
|
|
{
|
|
var service = CreateService();
|
|
|
|
var handle = service.PlayBgm("bgm-a", fadeIn: 0.1f);
|
|
var bgmSource = Array.Find(Object.FindObjectsOfType<AudioSource>(), source => source.clip == bgmA);
|
|
Assert.That(bgmSource.volume, Is.LessThan(1f));
|
|
|
|
yield return new WaitForSeconds(0.15f);
|
|
Assert.That(bgmSource.volume, Is.EqualTo(1f).Within(0.01f));
|
|
|
|
handle.Stop(0.1f);
|
|
|
|
yield return new WaitForSeconds(0.15f);
|
|
Assert.That(handle.IsPlaying, Is.False);
|
|
Assert.That(bgmSource.isPlaying, Is.False);
|
|
Assert.That(bgmSource.volume, Is.EqualTo(0f).Within(0.01f));
|
|
}
|
|
|
|
[Test]
|
|
public void PlayBgm_WhenResourceMissing_ThrowsWithKey()
|
|
{
|
|
var service = CreateService();
|
|
|
|
var exception = Assert.Throws<InvalidOperationException>(() => service.PlayBgm("missing-bgm"));
|
|
|
|
Assert.That(exception.Message, Does.Contain("missing-bgm"));
|
|
}
|
|
|
|
private AudioService CreateService(AudioServiceConfig config = null)
|
|
{
|
|
var gameObject = new GameObject("AudioServiceTest");
|
|
return gameObject.AddComponent<AudioService>().Initialize(resources, config);
|
|
}
|
|
|
|
private static AudioClip CreateClip(string name, float lengthSeconds)
|
|
{
|
|
var sampleCount = Mathf.CeilToInt(44100 * lengthSeconds);
|
|
var clip = AudioClip.Create(name, sampleCount, 1, 44100, false);
|
|
var data = new float[sampleCount];
|
|
for (var i = 0; i < data.Length; i++)
|
|
{
|
|
data[i] = 0.1f;
|
|
}
|
|
|
|
clip.SetData(data, 0);
|
|
return clip;
|
|
}
|
|
|
|
private sealed class FakeResourceService : IResourceService
|
|
{
|
|
private readonly Dictionary<string, AudioClip> clips = new();
|
|
|
|
public void Add(string key, AudioClip clip)
|
|
{
|
|
clips[key] = clip;
|
|
}
|
|
|
|
public Task<IResourceHandle<T>> LoadAsync<T>(string key, CancellationToken cancellationToken)
|
|
where T : class
|
|
{
|
|
if (typeof(T) != typeof(AudioClip) || !clips.TryGetValue(key, out var clip))
|
|
{
|
|
throw new InvalidOperationException($"missing resource: {key}");
|
|
}
|
|
|
|
return Task.FromResult<IResourceHandle<T>>(new FakeResourceHandle<T>(key, clip as T));
|
|
}
|
|
|
|
public IResourceGroup CreateGroup()
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
}
|
|
|
|
private sealed class FakeResourceHandle<T> : IResourceHandle<T> where T : class
|
|
{
|
|
public FakeResourceHandle(string key, T asset)
|
|
{
|
|
Key = key;
|
|
Asset = asset;
|
|
}
|
|
|
|
public string Key { get; }
|
|
public T Asset { get; private set; }
|
|
public bool IsDisposed { get; private set; }
|
|
|
|
public void Dispose()
|
|
{
|
|
IsDisposed = true;
|
|
Asset = null;
|
|
}
|
|
}
|
|
}
|
|
}
|