fix: initialize selected runtime for live playout

This commit is contained in:
2026-07-28 20:44:20 +09:00
parent 18d40e892f
commit 818acad646
24 changed files with 906 additions and 391 deletions

View File

@@ -117,7 +117,7 @@ public sealed class LegacyDevelopmentLiveStartupContractTests
}
[Fact]
public void AppliedDevelopmentLiveUsesSelectedRuntimePathsAndProtectedDatabaseInputs()
public void AppliedDevelopmentLiveUsesSelectedRuntimePathsAndSelectedResDatabaseAuthority()
{
var window = File.ReadAllText(Path.Combine(ParityRoot, "MainWindow.xaml.cs"));
var playout = File.ReadAllText(Path.Combine(
@@ -142,14 +142,15 @@ public sealed class LegacyDevelopmentLiveStartupContractTests
window,
StringComparison.Ordinal);
Assert.Contains(
"_isDevelopmentLiveLaunch ||\n" +
"var selectedLegacyDatabaseIni =\n" +
" selectedResourceDirectory is null\n" +
" ? null",
window.ReplaceLineEndings("\n"),
StringComparison.Ordinal);
Assert.Contains(
"DatabaseRuntime.CreateDevelopmentLiveLocalOverride()",
window,
"? DatabaseRuntime.CreateDevelopmentLiveLocalOverride(\n" +
" selectedResourceDirectory)",
window.ReplaceLineEndings("\n"),
StringComparison.Ordinal);
Assert.Contains(
"? PlayoutOptionsLoader.LoadDevelopmentLive(",
@@ -168,7 +169,7 @@ public sealed class LegacyDevelopmentLiveStartupContractTests
}
[Fact]
public void DatabaseFailureDoesNotDowngradeOrBlockValidatedLiveEngine()
public void InvalidDevelopmentLiveDatabaseConfigurationBlocksEngineAndAutomaticConnect()
{
var window = File.ReadAllText(Path.Combine(ParityRoot, "MainWindow.xaml.cs"))
.ReplaceLineEndings("\n");
@@ -177,44 +178,50 @@ public sealed class LegacyDevelopmentLiveStartupContractTests
"MainWindow.Playout.cs"))
.ReplaceLineEndings("\n");
var runtimeReset = window.IndexOf(
"_databaseRuntime = null;",
var databaseFailureCondition = window.IndexOf(
"_isDevelopmentLiveLaunch &&\n" +
" exception is DatabaseConfigurationException;",
StringComparison.Ordinal);
var pagePlanReset = window.IndexOf(
"fixedPagePlanProvider = null;",
runtimeReset,
var startupBlock = window.IndexOf(
"_developmentLiveStartupBlocked = true;",
databaseFailureCondition,
StringComparison.Ordinal);
var databaseFailureEnd = window.IndexOf(
"_industryWorkflow = new LegacyIndustrySelectionWorkflow(",
pagePlanReset,
var safeMessage = window.IndexOf(
"\"Development Live 데이터베이스 설정을 확인할 수 없습니다.\"",
startupBlock,
StringComparison.Ordinal);
var playoutInitialization = window.IndexOf(
"InitializePlayoutRuntime();",
safeMessage,
StringComparison.Ordinal);
var automaticConnectGuard = window.IndexOf(
"if (!_developmentLiveStartupBlocked)",
playoutInitialization,
StringComparison.Ordinal);
var automaticConnect = window.IndexOf(
"await ConnectPlayoutAsync(_lifetimeCancellation.Token);",
automaticConnectGuard,
StringComparison.Ordinal);
var earlyBlock = playout.IndexOf(
"if (_developmentLiveStartupBlocked)",
StringComparison.Ordinal);
var earlyReturn = playout.IndexOf(
"return;",
earlyBlock,
StringComparison.Ordinal);
var createEngine = playout.IndexOf(
"_playoutEngine = PlayoutEngineFactory.Create(",
StringComparison.Ordinal);
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(
"_isDevelopmentLiveLaunch\n" +
" ? PlayoutOptionsLoader.LoadDevelopmentLive(",
playout,
StringComparison.Ordinal);
Assert.Contains(
"if (_playoutOptions.Mode != PlayoutMode.Live)",
playout,
StringComparison.Ordinal);
Assert.Contains(
"_playoutEngine = PlayoutEngineFactory.Create(",
playout,
StringComparison.Ordinal);
Assert.True(databaseFailureCondition >= 0);
Assert.True(startupBlock > databaseFailureCondition);
Assert.True(safeMessage > startupBlock);
Assert.True(playoutInitialization > safeMessage);
Assert.True(automaticConnectGuard > playoutInitialization);
Assert.True(automaticConnect > automaticConnectGuard);
Assert.True(earlyBlock >= 0);
Assert.True(earlyReturn > earlyBlock);
Assert.True(createEngine > earlyReturn);
}
[Fact]

View File

@@ -70,7 +70,7 @@ public sealed class LegacyFirstRunSetupNativeContractTests
}
[Fact]
public void BothFolderSelectionsValidateOnlyCanonicalPaths()
public void BothFolderSelectionsValidateCanonicalPathsAndTheSelectedDatabaseIni()
{
var setup = File.ReadAllText(Path.Combine(
AppRoot,
@@ -84,8 +84,11 @@ public sealed class LegacyFirstRunSetupNativeContractTests
"LegacyOperatorSettingsFolderKind.Resource",
setup,
StringComparison.Ordinal);
Assert.DoesNotContain("MmoneyCoder.ini", setup, StringComparison.Ordinal);
Assert.DoesNotContain("LegacyIniDatabaseOptionsLoader", setup,
Assert.Contains(
"Path.Combine(resource, \"MmoneyCoder.ini\")",
setup,
StringComparison.Ordinal);
Assert.Contains("LegacyIniDatabaseOptionsLoader", setup,
StringComparison.Ordinal);
var clearCuts = setup.IndexOf(
"_cutsDirectory = null;",
@@ -116,29 +119,78 @@ public sealed class LegacyFirstRunSetupNativeContractTests
}
[Fact]
public void SetupPersistsOnlyTheSelectedPathsWithoutStartingAChildProcess()
public void SetupRunsTheVerifiedInitializerAndVerifiesEveryDurableOutput()
{
var setup = File.ReadAllText(Path.Combine(
AppRoot,
"FirstRunSetupWindow.xaml.cs"));
var saveSettings = RequiredIndex(
setup,
"var saved = store.Save(current with");
var saveBinding = RequiredIndex(
setup,
"SaveRuntimeBinding(repositoryRoot, runtimeRoot);");
Assert.True(saveSettings < saveBinding);
Assert.Contains(
"var store = new LegacyOperatorSettingsStore();",
"await RunVerifiedSetupAsync(",
setup,
StringComparison.Ordinal);
Assert.Contains(
"SceneDirectory = cutsDirectory",
"new ProcessStartInfo",
setup,
StringComparison.Ordinal);
Assert.Contains(
"ResourceDirectory = resourceDirectory",
"UseShellExecute = false",
setup,
StringComparison.Ordinal);
Assert.Contains(
"CreateNoWindow = true",
setup,
StringComparison.Ordinal);
Assert.Contains(
"RedirectStandardOutput = true",
setup,
StringComparison.Ordinal);
Assert.Contains(
"RedirectStandardError = true",
setup,
StringComparison.Ordinal);
Assert.Contains(
"startInfo.ArgumentList.Add(value)",
setup,
StringComparison.Ordinal);
Assert.Contains(
"Initialize-ExistingDevelopmentPc.ps1",
setup,
StringComparison.Ordinal);
Assert.Contains(
"AddArgument(\"-SkipDatabaseProfile\")",
setup,
StringComparison.Ordinal);
Assert.Contains(
"AddArgument(\"-ConfigureDevelopmentLive\")",
setup,
StringComparison.Ordinal);
Assert.Contains(
"AddArgument(\"127.0.0.1\")",
setup,
StringComparison.Ordinal);
Assert.Contains(
"AddArgument(\"30001\")",
setup,
StringComparison.Ordinal);
Assert.Contains(
"AddArgument(\"-PinRegisteredK3D\")",
setup,
StringComparison.Ordinal);
Assert.Contains(
"DrainOutputAsync(process.StandardOutput)",
setup,
StringComparison.Ordinal);
Assert.Contains(
"DrainOutputAsync(process.StandardError)",
setup,
StringComparison.Ordinal);
Assert.Contains(
"loaded.Settings.SceneDirectory",
setup,
StringComparison.Ordinal);
Assert.Contains(
"loaded.Settings.ResourceDirectory",
setup,
StringComparison.Ordinal);
Assert.Contains(
@@ -146,18 +198,21 @@ public sealed class LegacyFirstRunSetupNativeContractTests
setup,
StringComparison.Ordinal);
Assert.Contains(
"File.Move(temporary, destination, overwrite: true);",
"\"k3d-pins.local.json\"",
setup,
StringComparison.Ordinal);
Assert.DoesNotContain("ProcessStartInfo", setup, StringComparison.Ordinal);
Assert.DoesNotContain("Process.Start", setup, StringComparison.Ordinal);
Assert.DoesNotContain("RunSetupAsync", setup, StringComparison.Ordinal);
Assert.DoesNotContain("powershell.exe", setup, StringComparison.OrdinalIgnoreCase);
Assert.DoesNotContain("dotnet", setup, StringComparison.OrdinalIgnoreCase);
Assert.DoesNotContain("ConfigureDevelopmentLive", setup,
Assert.Contains(
"\"playout.local.json\"",
setup,
StringComparison.Ordinal);
Assert.Contains(
"\"playout.development-live.local.json\"",
setup,
StringComparison.Ordinal);
Assert.DoesNotContain(
"Environment.SetEnvironmentVariable",
setup,
StringComparison.Ordinal);
Assert.DoesNotContain("PinRegisteredK3D", setup, StringComparison.Ordinal);
Assert.DoesNotContain("DatabaseIniPath", setup, StringComparison.Ordinal);
}
[Fact]
@@ -180,6 +235,19 @@ public sealed class LegacyFirstRunSetupNativeContractTests
StringComparison.Ordinal);
}
[Fact]
public void SolutionDeclaresTheVisualStudioWinUiWorkload()
{
var configuration = File.ReadAllText(Path.Combine(
RepositoryRoot,
".vsconfig"));
Assert.Contains(
"\"Microsoft.VisualStudio.Workload.Universal\"",
configuration,
StringComparison.Ordinal);
}
[Fact]
public void AutomaticK3dPinningIsPersistentAndCannotApproveAChangedDll()
{
@@ -277,11 +345,4 @@ public sealed class LegacyFirstRunSetupNativeContractTests
throw new DirectoryNotFoundException("Repository root was not found.");
}
private static int RequiredIndex(string value, string marker)
{
var index = value.IndexOf(marker, StringComparison.Ordinal);
Assert.True(index >= 0, $"Required marker was not found: {marker}");
return index;
}
}

View File

@@ -97,6 +97,11 @@ public sealed class LegacyOperatorSettingsNativeContractTests
Assert.Contains("_appliedOperatorSettings = operatorSettingsLoad.Settings", Window,
StringComparison.Ordinal);
Assert.Contains("ResolveFromResourceDirectory(", Window, StringComparison.Ordinal);
Assert.Contains(
"DatabaseRuntime.CreateDevelopmentLiveLocalOverride(\n" +
" selectedResourceDirectory)",
Window.ReplaceLineEndings("\n"),
StringComparison.Ordinal);
Assert.Contains("preferLegacyOverride: selectedLegacyDatabaseIni is not null", Window,
StringComparison.Ordinal);
Assert.Contains(
@@ -128,13 +133,18 @@ public sealed class LegacyOperatorSettingsNativeContractTests
databaseComposition,
"DatabaseRuntime.CreateDefault()",
gateA);
var selectedDevelopmentLiveIni = RequiredIndex(
databaseComposition,
"DatabaseRuntime.CreateDevelopmentLiveLocalOverride(",
pinnedJson);
var legacyIni = RequiredIndex(
databaseComposition,
"DatabaseRuntime.CreateLegacyExecutableDefault(",
pinnedJson);
selectedDevelopmentLiveIni);
Assert.True(gateA < pinnedJson);
Assert.True(pinnedJson < legacyIni);
Assert.True(pinnedJson < selectedDevelopmentLiveIni);
Assert.True(selectedDevelopmentLiveIni < legacyIni);
}
[Fact]

View File

@@ -373,7 +373,11 @@ public sealed class LegacyRuntimeAssetDeploymentContractTests
"External legacy runtime paths are enabled",
StringComparison.Ordinal));
Assert.Contains(
"Cuts files remain in the selected folder",
@"Cuts and the credential-bearing Res\MmoneyCoder.ini remain",
fullRuntimeMessage.Attribute("Text")?.Value,
StringComparison.Ordinal);
Assert.Contains(
"in the selected external folders",
fullRuntimeMessage.Attribute("Text")?.Value,
StringComparison.Ordinal);
@@ -654,6 +658,10 @@ public sealed class LegacyRuntimeAssetDeploymentContractTests
RepositoryRoot,
"scripts",
"Initialize-ExistingDevelopmentPc.ps1"));
var registrationInspector = File.ReadAllText(Path.Combine(
RepositoryRoot,
"scripts",
"Inspect-K3DRegistration.ps1"));
Assert.Contains("[string] $LegacyRuntimeSourceRoot", initializer,
StringComparison.Ordinal);
@@ -784,6 +792,34 @@ public sealed class LegacyRuntimeAssetDeploymentContractTests
"[string] $classReport.ThreadingModel -cne 'Apartment'",
initializer,
StringComparison.Ordinal);
Assert.Contains(
"$standardK3dRoot = 'C:\\K3DAsyncEngine'",
initializer,
StringComparison.Ordinal);
Assert.Contains(
"DLL\\x64\\Release\\K3DAsyncEngine.dll",
initializer,
StringComparison.Ordinal);
Assert.Contains(
"Bin\\x64\\C#\\Interop.K3DAsyncEngineLib.dll",
initializer,
StringComparison.Ordinal);
Assert.Contains(
"-Right $standardK3dNativePath",
initializer,
StringComparison.Ordinal);
Assert.Contains(
"$expectedVendorRoot = 'C:\\K3DAsyncEngine'",
registrationInspector,
StringComparison.Ordinal);
Assert.Contains(
"-Expected $expectedNativePath",
registrationInspector,
StringComparison.Ordinal);
Assert.Contains(
"Assert-Amd64File -Path $interopPath",
registrationInspector,
StringComparison.Ordinal);
Assert.DoesNotContain("Activator.CreateInstance", initializer,
StringComparison.Ordinal);
Assert.DoesNotContain("Connect(", initializer, StringComparison.Ordinal);