feat: make LegacyParityApp playout live-only

This commit is contained in:
2026-07-28 14:52:59 +09:00
parent 11d3849933
commit ea96a08ad5
23 changed files with 456 additions and 270 deletions

View File

@@ -17,29 +17,25 @@ public sealed class LegacyDevelopmentLiveStartupContractTests
"MBN_STOCK_WEBVIEW.LegacyParityApp");
[Fact]
public void VisualStudioProfilesPutDevelopmentLiveFirstAndKeepExplicitDryRun()
public void VisualStudioExposesOneLivePackageProfileWithoutDryRun()
{
using var document = JsonDocument.Parse(File.ReadAllText(Path.Combine(
ParityRoot,
"Properties",
"launchSettings.json")));
var profiles = document.RootElement.GetProperty("profiles")
.EnumerateObject()
.ToArray();
var profile = Assert.Single(document.RootElement.GetProperty("profiles")
.EnumerateObject());
Assert.Equal(2, profiles.Length);
Assert.Equal(
"MBN_STOCK_WEBVIEW.LegacyParityApp - Development Live (Package)",
profiles[0].Name);
Assert.Equal("MsixPackage", profiles[0].Value.GetProperty("commandName").GetString());
Assert.Equal(ExactArgument,
profiles[0].Value.GetProperty("commandLineArgs").GetString());
Assert.Equal(
"MBN_STOCK_WEBVIEW.LegacyParityApp - Explicit DryRun (Package)",
profiles[1].Name);
Assert.Equal("MsixPackage", profiles[1].Value.GetProperty("commandName").GetString());
Assert.Equal(string.Empty,
profiles[1].Value.GetProperty("commandLineArgs").GetString());
Assert.Contains("Live", profile.Name, StringComparison.Ordinal);
Assert.DoesNotContain("DryRun", profile.Name, StringComparison.Ordinal);
Assert.Equal("MsixPackage", profile.Value.GetProperty("commandName").GetString());
var arguments = profile.Value.TryGetProperty("commandLineArgs", out var value)
? value.GetString()
: null;
Assert.True(
string.IsNullOrEmpty(arguments) ||
string.Equals(arguments, ExactArgument, StringComparison.Ordinal),
"The sole Live package profile may use only the normal launch or exact Live argument.");
}
[Fact]
@@ -75,7 +71,7 @@ public sealed class LegacyDevelopmentLiveStartupContractTests
}
[Fact]
public void AppAppliesCompileTimeBuildGateBeforeConstructingMainWindow()
public void AppAppliesRuntimeGateBeforeConstructingAnyRuntimeWindow()
{
var app = File.ReadAllText(Path.Combine(ParityRoot, "App.xaml.cs"));
var bootstrap = app.IndexOf(
@@ -87,35 +83,37 @@ public sealed class LegacyDevelopmentLiveStartupContractTests
var constructMainWindow = app.IndexOf(
"new MainWindow();",
StringComparison.Ordinal);
var sourceOnlyDirective = app.IndexOf(
"#if SOURCE_ONLY_RUNTIME",
StringComparison.Ordinal);
var fullRuntimeDirective = app.IndexOf(
"#else",
constructSetupWindow,
StringComparison.Ordinal);
var runtimeEndDirective = app.IndexOf(
"#endif",
constructMainWindow,
StringComparison.Ordinal);
Assert.True(bootstrap >= 0);
Assert.True(constructSetupWindow > bootstrap);
Assert.True(constructMainWindow > constructSetupWindow);
Assert.Contains("#if DEBUG", app, StringComparison.Ordinal);
Assert.Contains("const bool isDebugBuild = true;", app, StringComparison.Ordinal);
Assert.Contains("const bool isDebugBuild = false;", app, StringComparison.Ordinal);
Assert.Contains("#if SOURCE_ONLY_RUNTIME", app, StringComparison.Ordinal);
Assert.Contains("const bool isSourceOnlyBuild = true;", app,
StringComparison.Ordinal);
Assert.Contains("const bool isSourceOnlyBuild = false;", app,
StringComparison.Ordinal);
Assert.Contains("isSourceOnlyBuild", app, StringComparison.Ordinal);
Assert.Contains("? null", app, StringComparison.Ordinal);
Assert.True(sourceOnlyDirective >= 0);
Assert.True(constructSetupWindow > sourceOnlyDirective);
Assert.True(fullRuntimeDirective > constructSetupWindow);
Assert.True(bootstrap > fullRuntimeDirective);
Assert.True(constructMainWindow > bootstrap);
Assert.True(runtimeEndDirective > constructMainWindow);
Assert.Contains("args.Arguments", app, StringComparison.Ordinal);
Assert.Contains("Environment.GetCommandLineArgs()", app, StringComparison.Ordinal);
Assert.Contains("ResolveLaunchArguments(", app, StringComparison.Ordinal);
Assert.Contains(
"IsDevelopmentLiveLaunchRequested = string.Equals(",
"IsDevelopmentLiveLaunchRequested = developmentLive.IsRequested;",
app,
StringComparison.Ordinal);
Assert.Contains(
"IsDevelopmentLiveLaunch = developmentLive.IsApplied;",
app,
StringComparison.Ordinal);
Assert.Contains(
"_window = showFirstRunSetup",
app,
StringComparison.Ordinal);
Assert.DoesNotContain("#if DEBUG", app, StringComparison.Ordinal);
}
[Fact]
@@ -162,14 +160,15 @@ public sealed class LegacyDevelopmentLiveStartupContractTests
" _appliedOperatorSettings.SceneDirectory",
playout.ReplaceLineEndings("\n"),
StringComparison.Ordinal);
Assert.DoesNotContain("forceSafeDryRun", playout, StringComparison.Ordinal);
Assert.Contains(
"IsSourceOnlyBuild ||\n _isDevelopmentLiveLaunchRequested",
playout.ReplaceLineEndings("\n"),
"if (_playoutOptions.Mode != PlayoutMode.Live)",
playout,
StringComparison.Ordinal);
}
[Fact]
public void DevelopmentLiveDatabaseFailureForcesDryRunAndSkipsAutomaticConnect()
public void DatabaseFailureDoesNotDowngradeOrBlockValidatedLiveEngine()
{
var window = File.ReadAllText(Path.Combine(ParityRoot, "MainWindow.xaml.cs"))
.ReplaceLineEndings("\n");
@@ -178,49 +177,87 @@ public sealed class LegacyDevelopmentLiveStartupContractTests
"MainWindow.Playout.cs"))
.ReplaceLineEndings("\n");
var blockAssignment = window.IndexOf(
"_developmentLiveStartupBlocked = true;",
StringComparison.Ordinal);
var successAssignment = window.IndexOf(
"databaseInitializationSucceeded = true;",
StringComparison.Ordinal);
var runtimeReset = window.IndexOf(
"_databaseRuntime = null;",
successAssignment,
StringComparison.Ordinal);
var pagePlanReset = window.IndexOf(
"fixedPagePlanProvider = null;",
successAssignment,
runtimeReset,
StringComparison.Ordinal);
var databaseFailureEnd = window.IndexOf(
"_industryWorkflow = new LegacyIndustrySelectionWorkflow(",
pagePlanReset,
StringComparison.Ordinal);
var playoutInitialization = window.IndexOf(
"InitializePlayoutRuntime();",
StringComparison.Ordinal);
Assert.True(successAssignment >= 0);
Assert.True(runtimeReset > successAssignment);
Assert.True(pagePlanReset > successAssignment);
Assert.True(blockAssignment > runtimeReset);
Assert.True(playoutInitialization > blockAssignment);
Assert.Contains(
"var databaseInitializationSucceeded = false;",
window,
Assert.True(runtimeReset >= 0);
Assert.True(pagePlanReset > runtimeReset);
Assert.True(databaseFailureEnd > pagePlanReset);
Assert.True(playoutInitialization > databaseFailureEnd);
Assert.DoesNotContain(
"_developmentLiveStartupBlocked = true;",
window[runtimeReset..databaseFailureEnd],
StringComparison.Ordinal);
Assert.DoesNotContain("databaseInitializationSucceeded", window,
StringComparison.Ordinal);
Assert.Contains(
"if (_isDevelopmentLiveLaunch && !databaseInitializationSucceeded)\n" +
" {\n" +
" // Development Live is one fail-closed startup transaction.",
window,
StringComparison.Ordinal);
Assert.Contains(
"_isDevelopmentLiveLaunch &&\n" +
" !_developmentLiveStartupBlocked\n" +
"_isDevelopmentLiveLaunch\n" +
" ? PlayoutOptionsLoader.LoadDevelopmentLive(",
playout,
StringComparison.Ordinal);
Assert.Contains(
"_isDevelopmentLiveLaunchRequested ||\n" +
" _developmentLiveStartupBlocked",
"if (_playoutOptions.Mode != PlayoutMode.Live)",
playout,
StringComparison.Ordinal);
Assert.Contains(
"_playoutEngine = PlayoutEngineFactory.Create(",
playout,
StringComparison.Ordinal);
}
[Fact]
public void BootstrapOrPlayoutInvariantFailureBlocksEngineAndAutomaticConnect()
{
var window = File.ReadAllText(Path.Combine(ParityRoot, "MainWindow.xaml.cs"))
.ReplaceLineEndings("\n");
var playout = File.ReadAllText(Path.Combine(
ParityRoot,
"MainWindow.Playout.cs"))
.ReplaceLineEndings("\n");
var initialBlock = window.IndexOf(
"_developmentLiveStartupBlocked =\n" +
" _isDevelopmentLiveLaunchRequested &&\n" +
" !_isDevelopmentLiveLaunch;",
StringComparison.Ordinal);
var earlyBlock = playout.IndexOf(
"if (_developmentLiveStartupBlocked)",
StringComparison.Ordinal);
var earlyReturn = playout.IndexOf("return;", earlyBlock, StringComparison.Ordinal);
var loadLive = playout.IndexOf(
"PlayoutOptionsLoader.LoadDevelopmentLive(",
StringComparison.Ordinal);
var requireLive = playout.IndexOf(
"if (_playoutOptions.Mode != PlayoutMode.Live)",
StringComparison.Ordinal);
var createEngine = playout.IndexOf(
"_playoutEngine = PlayoutEngineFactory.Create(",
StringComparison.Ordinal);
var blockOnInitializationFailure = playout.IndexOf(
"_developmentLiveStartupBlocked = true;",
createEngine,
StringComparison.Ordinal);
Assert.True(initialBlock >= 0);
Assert.True(earlyBlock >= 0);
Assert.True(earlyReturn > earlyBlock);
Assert.True(loadLive > earlyReturn);
Assert.True(requireLive > loadLive);
Assert.True(createEngine > requireLive);
Assert.True(blockOnInitializationFailure > createEngine);
Assert.DoesNotContain("forceSafeDryRun", playout, StringComparison.Ordinal);
Assert.Contains(
"if (!_developmentLiveStartupBlocked)\n" +
" {\n" +