修复测试程序集编译兼容性

This commit is contained in:
JSD\13999
2026-05-18 14:56:10 +08:00
parent 686dec326e
commit 210715d0cd
7 changed files with 109 additions and 23 deletions

View File

@@ -45,35 +45,35 @@ namespace FlowScope.Tests.EditMode.Config
} }
[Test] [Test]
public void LoadAllAsync_DuplicateId_ThrowsWithId() public async Task LoadAllAsync_DuplicateId_ThrowsWithId()
{ {
var provider = new JsonConfigProvider(new[] var provider = new JsonConfigProvider(new[]
{ {
JsonConfigProvider.Mapping<WeaponConfig>("weapons.json", _ => Task.FromResult("[{\"id\":101,\"name\":\"Axe\"},{\"id\":101,\"name\":\"Sword\"}]")) JsonConfigProvider.Mapping<WeaponConfig>("weapons.json", _ => Task.FromResult("[{\"id\":101,\"name\":\"Axe\"},{\"id\":101,\"name\":\"Sword\"}]"))
}); });
var exception = Assert.ThrowsAsync<InvalidOperationException>( var exception = await ThrowsAsync<InvalidOperationException>(
async () => await provider.LoadAllAsync(CancellationToken.None)); async () => await provider.LoadAllAsync(CancellationToken.None));
Assert.That(exception.Message, Does.Contain("101")); Assert.That(exception.Message, Does.Contain("101"));
} }
[Test] [Test]
public void LoadAllAsync_MalformedJson_ThrowsWithFileName() public async Task LoadAllAsync_MalformedJson_ThrowsWithFileName()
{ {
var provider = new JsonConfigProvider(new[] var provider = new JsonConfigProvider(new[]
{ {
JsonConfigProvider.Mapping<WeaponConfig>("weapons.json", _ => Task.FromResult("[{\"id\":101")) JsonConfigProvider.Mapping<WeaponConfig>("weapons.json", _ => Task.FromResult("[{\"id\":101"))
}); });
var exception = Assert.ThrowsAsync<InvalidOperationException>( var exception = await ThrowsAsync<InvalidOperationException>(
async () => await provider.LoadAllAsync(CancellationToken.None)); async () => await provider.LoadAllAsync(CancellationToken.None));
Assert.That(exception.Message, Does.Contain("weapons.json")); Assert.That(exception.Message, Does.Contain("weapons.json"));
} }
[Test] [Test]
public void LoadAllAsync_CanceledToken_ThrowsOperationCanceledException() public async Task LoadAllAsync_CanceledToken_ThrowsOperationCanceledException()
{ {
using var cts = new CancellationTokenSource(); using var cts = new CancellationTokenSource();
cts.Cancel(); cts.Cancel();
@@ -82,10 +82,26 @@ namespace FlowScope.Tests.EditMode.Config
JsonConfigProvider.Mapping<WeaponConfig>("weapons.json", _ => Task.FromResult("[]")) JsonConfigProvider.Mapping<WeaponConfig>("weapons.json", _ => Task.FromResult("[]"))
}); });
Assert.ThrowsAsync<OperationCanceledException>( await ThrowsAsync<OperationCanceledException>(
async () => await provider.LoadAllAsync(cts.Token)); async () => await provider.LoadAllAsync(cts.Token));
} }
private static async Task<TException> ThrowsAsync<TException>(Func<Task> action)
where TException : Exception
{
try
{
await action();
}
catch (TException exception)
{
return exception;
}
Assert.Fail($"Expected {typeof(TException).Name}.");
return null;
}
private sealed class WeaponConfig : IConfigRow private sealed class WeaponConfig : IConfigRow
{ {
public int id; public int id;

View File

@@ -59,13 +59,13 @@ namespace FlowScope.Tests.EditMode.Flow
} }
[Test] [Test]
public void StartupAsync_WhenFeatureLoadFails_CleansCreatedFeatureAndReturnsIdle() public async Task StartupAsync_WhenFeatureLoadFails_CleansCreatedFeatureAndReturnsIdle()
{ {
var services = new TestServices(); var services = new TestServices();
var root = services.CreateRoot(); var root = services.CreateRoot();
var flow = new GameFlow(); var flow = new GameFlow();
Assert.ThrowsAsync<InvalidOperationException>( await ThrowsAsync<InvalidOperationException>(
async () => await flow.StartupAsync<FailingLoadFeature>(root, CancellationToken.None)); async () => await flow.StartupAsync<FailingLoadFeature>(root, CancellationToken.None));
Assert.That(flow.State, Is.EqualTo(GameFlowState.Idle)); Assert.That(flow.State, Is.EqualTo(GameFlowState.Idle));
@@ -81,7 +81,7 @@ namespace FlowScope.Tests.EditMode.Flow
var flow = new GameFlow(); var flow = new GameFlow();
await flow.StartupAsync<FirstFeature>(root, CancellationToken.None); await flow.StartupAsync<FirstFeature>(root, CancellationToken.None);
Assert.ThrowsAsync<InvalidOperationException>( await ThrowsAsync<InvalidOperationException>(
async () => await flow.SwitchToAsync<FailingEnterFeature>(CancellationToken.None)); async () => await flow.SwitchToAsync<FailingEnterFeature>(CancellationToken.None));
Assert.That(flow.State, Is.EqualTo(GameFlowState.NoActiveFeature)); Assert.That(flow.State, Is.EqualTo(GameFlowState.NoActiveFeature));
@@ -91,11 +91,11 @@ namespace FlowScope.Tests.EditMode.Flow
} }
[Test] [Test]
public void SwitchToAsync_WhenIdle_ThrowsWithOperationAndCurrentState() public async Task SwitchToAsync_WhenIdle_ThrowsWithOperationAndCurrentState()
{ {
var flow = new GameFlow(); var flow = new GameFlow();
var exception = Assert.ThrowsAsync<InvalidOperationException>( var exception = await ThrowsAsync<InvalidOperationException>(
async () => await flow.SwitchToAsync<FirstFeature>(CancellationToken.None)); async () => await flow.SwitchToAsync<FirstFeature>(CancellationToken.None));
Assert.That(exception.Message, Does.Contain(nameof(GameFlow.SwitchToAsync))); Assert.That(exception.Message, Does.Contain(nameof(GameFlow.SwitchToAsync)));
@@ -126,7 +126,7 @@ namespace FlowScope.Tests.EditMode.Flow
using var cts = new CancellationTokenSource(); using var cts = new CancellationTokenSource();
cts.Cancel(); cts.Cancel();
Assert.ThrowsAsync<OperationCanceledException>( await ThrowsAsync<OperationCanceledException>(
async () => await flow.ShutdownAsync(cts.Token)); async () => await flow.ShutdownAsync(cts.Token));
Assert.That(flow.State, Is.EqualTo(GameFlowState.Disposed)); Assert.That(flow.State, Is.EqualTo(GameFlowState.Disposed));
@@ -134,6 +134,22 @@ namespace FlowScope.Tests.EditMode.Flow
Assert.That(services.Resource.Groups[0].IsDisposed, Is.True); Assert.That(services.Resource.Groups[0].IsDisposed, Is.True);
} }
private static async Task<TException> ThrowsAsync<TException>(Func<Task> action)
where TException : Exception
{
try
{
await action();
}
catch (TException exception)
{
return exception;
}
Assert.Fail($"Expected {typeof(TException).Name}.");
return null;
}
private sealed class TestServices private sealed class TestServices
{ {
public TestConfigProvider Config { get; } = new(); public TestConfigProvider Config { get; } = new();

View File

@@ -10,8 +10,11 @@
], ],
"excludePlatforms": [], "excludePlatforms": [],
"allowUnsafeCode": false, "allowUnsafeCode": false,
"overrideReferences": false, "overrideReferences": true,
"precompiledReferences": [], "precompiledReferences": [
"R3.dll",
"nunit.framework.dll"
],
"autoReferenced": false, "autoReferenced": false,
"defineConstraints": [], "defineConstraints": [],
"versionDefines": [], "versionDefines": [],

View File

@@ -61,27 +61,43 @@ namespace FlowScope.Tests.EditMode.Save
} }
[Test] [Test]
public void SaveAsync_WriteFailure_ThrowsWithKey() public async Task SaveAsync_WriteFailure_ThrowsWithKey()
{ {
var service = new SaveService(new FailingWriteStorage(), new JsonSaveSerializer()); var service = new SaveService(new FailingWriteStorage(), new JsonSaveSerializer());
var exception = Assert.ThrowsAsync<InvalidOperationException>( var exception = await ThrowsAsync<InvalidOperationException>(
async () => await service.SaveAsync("player", new PlayerData(), CancellationToken.None)); async () => await service.SaveAsync("player", new PlayerData(), CancellationToken.None));
Assert.That(exception.Message, Does.Contain("player")); Assert.That(exception.Message, Does.Contain("player"));
} }
[Test] [Test]
public void SaveAsync_CanceledToken_ThrowsOperationCanceledException() public async Task SaveAsync_CanceledToken_ThrowsOperationCanceledException()
{ {
using var cts = new CancellationTokenSource(); using var cts = new CancellationTokenSource();
cts.Cancel(); cts.Cancel();
var service = new SaveService(new MemorySaveStorage(), new JsonSaveSerializer()); var service = new SaveService(new MemorySaveStorage(), new JsonSaveSerializer());
Assert.ThrowsAsync<OperationCanceledException>( await ThrowsAsync<OperationCanceledException>(
async () => await service.SaveAsync("player", new PlayerData(), cts.Token)); async () => await service.SaveAsync("player", new PlayerData(), cts.Token));
} }
private static async Task<TException> ThrowsAsync<TException>(Func<Task> action)
where TException : Exception
{
try
{
await action();
}
catch (TException exception)
{
return exception;
}
Assert.Fail($"Expected {typeof(TException).Name}.");
return null;
}
private sealed class PlayerData private sealed class PlayerData
{ {
public ReactiveProperty<int> Gold { get; } = new(0); public ReactiveProperty<int> Gold { get; } = new(0);

View File

@@ -9,8 +9,11 @@
"includePlatforms": [], "includePlatforms": [],
"excludePlatforms": [], "excludePlatforms": [],
"allowUnsafeCode": false, "allowUnsafeCode": false,
"overrideReferences": false, "overrideReferences": true,
"precompiledReferences": [], "precompiledReferences": [
"R3.dll",
"nunit.framework.dll"
],
"autoReferenced": false, "autoReferenced": false,
"defineConstraints": [], "defineConstraints": [],
"versionDefines": [], "versionDefines": [],

View File

@@ -39,19 +39,35 @@ namespace FlowScope.Tests.PlayMode.Resources
} }
[Test] [Test]
public void LoadAsync_WhenBackendFails_IncludesKeyAndBackend() public async Task LoadAsync_WhenBackendFails_IncludesKeyAndBackend()
{ {
var service = new AddressablesResourceService( var service = new AddressablesResourceService(
(_, _, _) => throw new InvalidOperationException("missing"), (_, _, _) => throw new InvalidOperationException("missing"),
(_, _) => { }); (_, _) => { });
var exception = Assert.ThrowsAsync<InvalidOperationException>( var exception = await ThrowsAsync<InvalidOperationException>(
() => service.LoadAsync<TestAsset>("missing-key", CancellationToken.None)); () => service.LoadAsync<TestAsset>("missing-key", CancellationToken.None));
StringAssert.Contains("missing-key", exception.Message); StringAssert.Contains("missing-key", exception.Message);
StringAssert.Contains("Addressables", exception.Message); StringAssert.Contains("Addressables", exception.Message);
} }
private static async Task<TException> ThrowsAsync<TException>(Func<Task> action)
where TException : Exception
{
try
{
await action();
}
catch (TException exception)
{
return exception;
}
Assert.Fail($"Expected {typeof(TException).Name}.");
return null;
}
private sealed class TestAsset private sealed class TestAsset
{ {
} }

View File

@@ -154,7 +154,7 @@ namespace FlowScope.Tests.PlayMode.UI
var manager = new UIManager(resources); var manager = new UIManager(resources);
manager.RegisterLayer("popup", canvas, 0); manager.RegisterLayer("popup", canvas, 0);
Assert.ThrowsAsync<InvalidOperationException>( await ThrowsAsync<InvalidOperationException>(
() => manager.OpenAsync<ThrowingBindPanel, TestViewModel>(new TestViewModel("boom"), CancellationToken.None)); () => manager.OpenAsync<ThrowingBindPanel, TestViewModel>(new TestViewModel("boom"), CancellationToken.None));
Assert.That(resources.Handles[0].IsDisposed, Is.True); Assert.That(resources.Handles[0].IsDisposed, Is.True);
@@ -169,6 +169,22 @@ namespace FlowScope.Tests.PlayMode.UI
return gameObject.AddComponent<Canvas>(); return gameObject.AddComponent<Canvas>();
} }
private static async Task<TException> ThrowsAsync<TException>(Func<Task> action)
where TException : Exception
{
try
{
await action();
}
catch (TException exception)
{
return exception;
}
Assert.Fail($"Expected {typeof(TException).Name}.");
return null;
}
private GameObject CreatePrefab<TPanel>(string name) private GameObject CreatePrefab<TPanel>(string name)
where TPanel : Component where TPanel : Component
{ {