Step 6 of multi-instancing migration. HeadlessEngineEnv.EnsureInitialized is split into EnsureProcessGlobals (idempotent, process-once) + SeedCharaIdsOnCurrentAmbient (per-test). New TestBattleScope IDisposable sets up a fresh BattleAmbientContext per test. NonParallelizable removed from converted classes; assembly-level Parallelizable(Fixtures) enabled. SVSim.BattleEngine.Tests fully green under parallel test execution. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
51 lines
2.1 KiB
C#
51 lines
2.1 KiB
C#
#nullable enable
|
|
using System;
|
|
using System.Runtime.Serialization;
|
|
using SVSim.BattleEngine.Ambient;
|
|
|
|
namespace SVSim.BattleEngine.Tests;
|
|
|
|
/// <summary>Per-test ambient scope. Each test that touches engine statics wraps its body
|
|
/// in `using var scope = new TestBattleScope();` (or with an explicit Mgr/ViewerId).
|
|
///
|
|
/// The constructor enters a fresh <see cref="BattleAmbientContext"/> (carrying a brand-new
|
|
/// <see cref="GameMgr"/> so per-test mgr/DataMgr writes never bleed across tests), then
|
|
/// runs the per-ambient seeders that <see cref="HeadlessEngineEnv.EnsureProcessGlobals"/>
|
|
/// no longer does (chara ids on DataMgr, NetworkUserInfoData). Process-globals
|
|
/// (card master, LoadDetail, Crossover, Certification.udid) come from
|
|
/// <see cref="HeadlessEngineEnv.EnsureProcessGlobals"/> which runs once per process.
|
|
///
|
|
/// Public surface (vs. internal) so SVSim.UnitTests can reuse it via the same project
|
|
/// reference in Task 7.</summary>
|
|
public sealed class TestBattleScope : IDisposable
|
|
{
|
|
private readonly BattleAmbient.Scope _scope;
|
|
public BattleAmbientContext Ctx { get; }
|
|
|
|
public TestBattleScope(BattleManagerBase? mgr = null, int viewerId = 1001)
|
|
{
|
|
// Make sure process-globals are seeded before we enter; idempotent + cheap after first call.
|
|
HeadlessEngineEnv.EnsureProcessGlobals();
|
|
|
|
Ctx = new BattleAmbientContext
|
|
{
|
|
Mgr = mgr,
|
|
GameMgr = new GameMgr(),
|
|
ViewerId = viewerId,
|
|
IsForecast = true,
|
|
IsRandomDraw = true,
|
|
RecoveryInfo = (Wizard.BattleRecoveryInfo)FormatterServices
|
|
.GetUninitializedObject(typeof(Wizard.BattleRecoveryInfo)),
|
|
};
|
|
_scope = BattleAmbient.Enter(Ctx);
|
|
|
|
// Per-ambient seeders MUST run AFTER scope entry so GameMgr.GetIns() resolves to this
|
|
// scope's GameMgr (not a stray one). EnsureProcessGlobals used to do these writes against
|
|
// the global GameMgr; now they're scoped.
|
|
HeadlessEngineEnv.SeedCharaIdsOnCurrentAmbient();
|
|
HeadlessEngineEnv.SeedNetUserOnCurrentAmbient();
|
|
}
|
|
|
|
public void Dispose() => _scope.Dispose();
|
|
}
|