fix: apply direct runtime paths and respect taskbar

This commit is contained in:
2026-07-28 00:35:47 +09:00
parent e3b174eec7
commit 347900701b
16 changed files with 522 additions and 401 deletions

View File

@@ -119,7 +119,7 @@ public sealed class LegacyDevelopmentLiveStartupContractTests
}
[Fact]
public void AppliedDevelopmentLiveUsesOnlyProtectedStartupInputs()
public void AppliedDevelopmentLiveUsesSelectedRuntimePathsAndProtectedDatabaseInputs()
{
var window = File.ReadAllText(Path.Combine(ParityRoot, "MainWindow.xaml.cs"));
var playout = File.ReadAllText(Path.Combine(
@@ -135,11 +135,18 @@ public sealed class LegacyDevelopmentLiveStartupContractTests
window,
StringComparison.Ordinal);
Assert.Contains(
"? LegacyRuntimeStockCutMenuLoader.ResolveFromBaseDirectory(",
"var selectedResourceDirectory =\n" +
" _appliedOperatorSettings.ResourceDirectory;",
window.ReplaceLineEndings("\n"),
StringComparison.Ordinal);
Assert.Contains(
"LegacyRuntimeStockCutMenuLoader.ResolveFromResourceDirectory(",
window,
StringComparison.Ordinal);
Assert.Contains(
"? null\n : _appliedOperatorSettings.ResourceDirectory;",
"_isDevelopmentLiveLaunch ||\n" +
" selectedResourceDirectory is null\n" +
" ? null",
window.ReplaceLineEndings("\n"),
StringComparison.Ordinal);
Assert.Contains(
@@ -150,6 +157,11 @@ public sealed class LegacyDevelopmentLiveStartupContractTests
"? PlayoutOptionsLoader.LoadDevelopmentLive(",
playout,
StringComparison.Ordinal);
Assert.Contains(
"operatorSceneDirectory:\n" +
" _appliedOperatorSettings.SceneDirectory",
playout.ReplaceLineEndings("\n"),
StringComparison.Ordinal);
Assert.Contains(
"IsSourceOnlyBuild ||\n _isDevelopmentLiveLaunchRequested",
playout.ReplaceLineEndings("\n"),

View File

@@ -241,11 +241,13 @@ public sealed class LegacyFirstRunSetupNativeContractTests
var pinPreflight = initializer.LastIndexOf(
"$k3dPinPreflight = Get-K3DPinPreflight",
StringComparison.Ordinal);
var pinWrite = initializer.LastIndexOf(
var pinWrite = initializer.IndexOf(
"Write-ProtectedUtf8TextAtomically",
pinPreflight,
StringComparison.Ordinal);
var livePreflight = initializer.LastIndexOf(
var livePreflight = initializer.IndexOf(
"$livePreflight = Get-DevelopmentLivePreflight",
pinWrite,
StringComparison.Ordinal);
Assert.True(pinPreflight >= 0);
Assert.True(pinWrite > pinPreflight);

View File

@@ -12,13 +12,29 @@ public sealed class LegacyRuntimeAssetDeploymentContractTests
"MBN_STOCK_WEBVIEW.LegacyParityApp.csproj");
[Fact]
public void LegacyParityProjectLinksCutsAndResIntoOutputPublishAndMsix()
public void LegacyParityProjectUsesCutsInPlaceAndLinksOnlyAllowlistedRes()
{
var project = XDocument.Load(ProjectPath);
var contents = project.Descendants("Content").ToArray();
AssertLinkedRuntimeTree(contents, "$(LegacyCutsSourceRoot)\\**\\*", "Cuts\\");
Assert.DoesNotContain(
contents,
element => string.Equals(
element.Attribute("Include")?.Value,
"$(LegacyCutsSourceRoot)\\**\\*",
StringComparison.Ordinal));
AssertLinkedRuntimeTree(contents, "@(LegacyPackagedResAsset)", "Res\\");
var cutsScrub = Assert.Single(
project.Descendants("Target"),
target => string.Equals(
target.Attribute("Name")?.Value,
"ScrubCopiedCutsFromBuildOutput",
StringComparison.Ordinal));
Assert.Contains(
"$(TargetDir)Cuts",
Assert.Single(cutsScrub.Elements("RemoveDir"))
.Attribute("Directories")?.Value,
StringComparison.Ordinal);
var packagedResNames = project.Descendants("LegacyPackagedResAsset")
.Select(element => Path.GetFileName(element.Attribute("Include")?.Value))
@@ -354,10 +370,10 @@ public sealed class LegacyRuntimeAssetDeploymentContractTests
validationTarget.Elements("Message"),
message => (message.Attribute("Text")?.Value ?? string.Empty)
.Contains(
"Verified full legacy runtime assets are enabled",
"External legacy runtime paths are enabled",
StringComparison.Ordinal));
Assert.Contains(
"Verified full legacy runtime assets are enabled",
"Cuts files remain in the selected folder",
fullRuntimeMessage.Attribute("Text")?.Value,
StringComparison.Ordinal);
@@ -379,7 +395,6 @@ public sealed class LegacyRuntimeAssetDeploymentContractTests
string[] externalContentIncludes =
[
@"$(LegacyCutsSourceRoot)\**\*",
"@(LegacyPackagedResAsset)",
"@(LegacyFinancialPreviewAsset)",
"@(LegacyBrandAsset)"
@@ -397,6 +412,12 @@ public sealed class LegacyRuntimeAssetDeploymentContractTests
content.Attribute("Condition")?.Value,
StringComparison.Ordinal);
});
Assert.DoesNotContain(
project.Descendants("Content"),
element => string.Equals(
element.Attribute("Include")?.Value,
@"$(LegacyCutsSourceRoot)\**\*",
StringComparison.Ordinal));
Assert.DoesNotContain(
project.Descendants("Target"),
@@ -454,8 +475,6 @@ public sealed class LegacyRuntimeAssetDeploymentContractTests
"$(LegacyRuntimeSourceRoot)",
"$(LegacyCutsSourceRoot)",
"$(LegacyResSourceRoot)",
"$(LegacyCutsSourceRoot)\\%(LegacyRequiredScene.Identity).t2s",
"$(LegacyCutsSourceRoot)\\%(LegacyRequiredBuiltInAsset.Identity)",
"$(LegacyResSourceRoot)\\logo.png",
"$(LegacyResSourceRoot)\\종목.ini",
"$(LegacyResSourceRoot)\\업종_코스피.ini",
@@ -673,42 +692,52 @@ public sealed class LegacyRuntimeAssetDeploymentContractTests
"must be outside and must not overlap this repository",
initializer,
StringComparison.Ordinal);
Assert.Contains("Assert-NoReparsePointInTree", initializer,
StringComparison.Ordinal);
Assert.Contains("'New-LegacyRuntimeBundle.ps1'", initializer,
StringComparison.Ordinal);
Assert.Contains("'Initialize-LegacyRuntimeBundle.ps1'", initializer,
Assert.Contains("Assert-NoReparsePointInExistingAncestry", initializer,
StringComparison.Ordinal);
Assert.Contains(
"$existingRuntime.ManifestSha256.Equals(",
"Legacy runtime source root contains characters reserved by MSBuild.",
initializer,
StringComparison.Ordinal);
Assert.DoesNotContain("$bundleCreatorPath =", initializer,
StringComparison.Ordinal);
Assert.DoesNotContain("$bundleInitializerPath =", initializer,
StringComparison.Ordinal);
Assert.DoesNotContain("& $bundleCreatorPath", initializer,
StringComparison.Ordinal);
Assert.DoesNotContain("& $bundleInitializerPath", initializer,
StringComparison.Ordinal);
Assert.Contains(
"function New-DirectRuntimeBindingFile",
initializer,
StringComparison.Ordinal);
Assert.Contains(
"Assert-InstalledRuntimeMatchesManifest",
"function Get-RuntimeFoldersPreflight",
initializer,
StringComparison.Ordinal);
Assert.Contains(
"Installed runtime bundle contains an unexpected file",
"runtime-folders.local.json",
initializer,
StringComparison.Ordinal);
Assert.Contains("[switch] $ReplaceRuntimeBinding", initializer,
StringComparison.Ordinal);
Assert.Contains(
"The selected Cuts/Res path differs from the existing runtime binding.",
initializer,
StringComparison.Ordinal);
Assert.Contains("Set-RuntimeBindingAtomically", initializer,
StringComparison.Ordinal);
Assert.Contains(
"[IO.File]::Replace($temporary, $destination, $backup)",
initializer,
StringComparison.Ordinal);
var shadowInstall = initializer.IndexOf(
"-RepositoryRoot $shadowRepositoryRoot",
var directBinding = initializer.LastIndexOf(
"New-DirectRuntimeBindingFile",
StringComparison.Ordinal);
var bindingReplace = initializer.IndexOf(
var bindingReplace = initializer.LastIndexOf(
"Set-RuntimeBindingAtomically",
shadowInstall,
StringComparison.Ordinal);
Assert.True(shadowInstall >= 0);
Assert.True(bindingReplace > shadowInstall);
Assert.True(directBinding >= 0);
Assert.True(bindingReplace > directBinding);
Assert.Contains("[string] $DatabaseIniPath", initializer,
StringComparison.Ordinal);
@@ -790,6 +819,7 @@ public sealed class LegacyRuntimeAssetDeploymentContractTests
Assert.Contains("& $DotNetPath build $ProjectPath",
initializer,
StringComparison.Ordinal);
Assert.Contains("'--no-restore'", initializer, StringComparison.Ordinal);
Assert.Contains(
"'-p:LegacyRuntimeAssetsMode=Required'",
initializer,
@@ -810,7 +840,9 @@ public sealed class LegacyRuntimeAssetDeploymentContractTests
"unexpectedly resolved as SourceOnly",
initializer,
StringComparison.Ordinal);
Assert.Contains("Debug x64 payload count mismatch", initializer,
Assert.Contains(
"Debug x64 copied Cuts instead of using the selected external path.",
initializer,
StringComparison.Ordinal);
Assert.Contains(
"Credential-bearing MmoneyCoder.ini entered the Debug build output.",
@@ -820,12 +852,15 @@ public sealed class LegacyRuntimeAssetDeploymentContractTests
var runtimeWrite = initializer.LastIndexOf(
"Set-RuntimeBindingAtomically",
StringComparison.Ordinal);
var pinWrite = initializer.LastIndexOf(
"Write-ProtectedUtf8TextAtomically",
var pinWrite = initializer.IndexOf(
"-Description 'K3D persistent pin'",
StringComparison.Ordinal);
var databaseWrite = initializer.LastIndexOf(
"Copy-DatabaseProfileAtomically",
StringComparison.Ordinal);
var runtimeFoldersWrite = initializer.LastIndexOf(
"-Description 'runtime folder configuration'",
StringComparison.Ordinal);
var build = initializer.LastIndexOf(
"Invoke-DebugBuildAndVerify",
StringComparison.Ordinal);
@@ -841,7 +876,8 @@ public sealed class LegacyRuntimeAssetDeploymentContractTests
Assert.True(pinWrite >= 0);
Assert.True(build > pinWrite);
Assert.True(databaseWrite > build);
Assert.True(finalK3dVerification > databaseWrite);
Assert.True(runtimeFoldersWrite > databaseWrite);
Assert.True(finalK3dVerification > runtimeFoldersWrite);
Assert.True(liveWrite > finalK3dVerification);
Assert.True(runtimeWrite > liveWrite);
Assert.True(cleanup > runtimeWrite);
@@ -910,6 +946,12 @@ public sealed class LegacyRuntimeAssetDeploymentContractTests
var databaseRollback = initializer.LastIndexOf(
"-Snapshot $databaseSnapshot",
StringComparison.Ordinal);
var runtimeFoldersMutation = initializer.LastIndexOf(
"$runtimeFoldersMutationStarted = $true",
StringComparison.Ordinal);
var runtimeFoldersRollback = initializer.LastIndexOf(
"-Snapshot $runtimeFoldersSnapshot",
StringComparison.Ordinal);
var runtimeMutation = initializer.LastIndexOf(
"$runtimeBindingMutationStarted = $true",
StringComparison.Ordinal);
@@ -920,8 +962,10 @@ public sealed class LegacyRuntimeAssetDeploymentContractTests
"-Snapshot $propsSnapshot",
StringComparison.Ordinal);
Assert.True(databaseMutation >= 0 && databaseMutation < databaseWrite);
Assert.True(runtimeFoldersMutation >= 0);
Assert.True(runtimeMutation >= 0 && runtimeMutation < runtimeWrite);
Assert.True(databaseRollback > databaseWrite);
Assert.True(runtimeFoldersRollback > runtimeFoldersMutation);
Assert.True(runtimeRollback > runtimeWrite);
Assert.Contains("Original failure:", initializer, StringComparison.Ordinal);
Assert.Contains("Secondary recovery failure(s):", initializer,
@@ -929,51 +973,16 @@ public sealed class LegacyRuntimeAssetDeploymentContractTests
Assert.Contains("function Assert-ExactObjectProperties", initializer,
StringComparison.Ordinal);
Assert.Contains(
"-Description 'Runtime bundle creation result'",
initializer,
StringComparison.Ordinal);
Assert.Contains(
"-Description 'Runtime bundle initialization result'",
initializer,
StringComparison.Ordinal);
Assert.Contains(
"-Description 'K3D registration inspection result'",
initializer,
StringComparison.Ordinal);
Assert.Contains(
"Runtime bundle creation result paths escaped or changed the exact output contract.",
"function New-DirectRuntimeBindingFile",
initializer,
StringComparison.Ordinal);
Assert.Contains(
"Runtime bundle initialization result paths or hashes violate containment.",
initializer,
StringComparison.Ordinal);
Assert.Contains("function Get-VerifiedRuntimeManifest", initializer,
StringComparison.Ordinal);
Assert.Contains(
"duplicate/extra/case/type/format changes are rejected.",
initializer,
StringComparison.Ordinal);
Assert.Contains(
"function Assert-DebugRuntimePayloadMatchesManifest",
initializer,
StringComparison.Ordinal);
Assert.Contains(
"Debug x64 runtime payload differs from the manifest:",
initializer,
StringComparison.Ordinal);
Assert.Contains(
"Debug x64 runtime output contains an unexpected file:",
initializer,
StringComparison.Ordinal);
Assert.Contains(
"Debug x64 runtime output does not match the exact manifest file set.",
initializer,
StringComparison.Ordinal);
Assert.Contains(
"-Root $targetRoot",
"Runtime folder configuration rollback failed:",
initializer,
StringComparison.Ordinal);
@@ -1008,8 +1017,8 @@ public sealed class LegacyRuntimeAssetDeploymentContractTests
var buildVerification = initializer.LastIndexOf(
"Invoke-DebugBuildAndVerify",
StringComparison.Ordinal);
var pinIssue = initializer.LastIndexOf(
"Write-ProtectedUtf8TextAtomically",
var pinIssue = initializer.IndexOf(
"-Description 'K3D persistent pin'",
StringComparison.Ordinal);
var finalK3dVerification = initializer.LastIndexOf(
"$finalNativeHash = (",

View File

@@ -0,0 +1,111 @@
namespace MBN_STOCK_WEBVIEW.LegacyWeb.Tests;
public sealed class LegacyWindowSizingNativeContractTests
{
private static readonly string Window = File.ReadAllText(Path.Combine(
FindRepositoryRoot(),
"src",
"MBN_STOCK_WEBVIEW.LegacyParityApp",
"MainWindow.xaml.cs"));
[Fact]
public void MainWindowClampsRestoreBoundsAndUsesWorkAreaAwareSystemMaximize()
{
var configureWindow = Slice(
Window,
"private void ConfigureWindow()",
"private async Task InitializeWebViewAsync()");
var displayArea = RequiredIndex(
configureWindow,
"DisplayArea.GetFromWindowId(windowId, DisplayAreaFallback.Primary)");
var workArea = RequiredIndex(
configureWindow,
"var workArea = displayArea.WorkArea;");
var resizeClient = RequiredIndex(
configureWindow,
"appWindow.ResizeClient(new SizeInt32(1905, 1015));");
var readOuterSize = RequiredIndex(
configureWindow,
"var requestedWindowSize = appWindow.Size;");
var moveAndResize = RequiredIndex(
configureWindow,
"appWindow.MoveAndResize(");
var workAreaX = RequiredIndex(configureWindow, "workArea.X,");
var workAreaY = RequiredIndex(configureWindow, "workArea.Y,");
var allowSystemMaximize = RequiredIndex(
configureWindow,
"presenter.IsMaximizable = true;");
var maximize = RequiredIndex(
configureWindow,
"presenter.Maximize();");
Assert.True(displayArea < workArea);
Assert.True(workArea < resizeClient);
Assert.True(resizeClient < readOuterSize);
Assert.True(readOuterSize < moveAndResize);
Assert.True(moveAndResize < workAreaX);
Assert.True(workAreaX < workAreaY);
Assert.True(moveAndResize < allowSystemMaximize);
Assert.True(allowSystemMaximize < maximize);
Assert.DoesNotContain(
"presenter.IsMaximizable = false;",
configureWindow,
StringComparison.Ordinal);
Assert.Contains(
"presenter.IsResizable = false;",
configureWindow,
StringComparison.Ordinal);
Assert.Contains(
"var requestedWindowSize = appWindow.Size;",
configureWindow,
StringComparison.Ordinal);
Assert.Contains(
"Math.Min(requestedWindowSize.Width, workArea.Width)",
configureWindow,
StringComparison.Ordinal);
Assert.Contains(
"Math.Min(requestedWindowSize.Height, workArea.Height)",
configureWindow,
StringComparison.Ordinal);
Assert.DoesNotContain(
"appWindow.Move(new PointInt32(",
configureWindow,
StringComparison.Ordinal);
}
private static int RequiredIndex(string value, string needle)
{
var index = value.IndexOf(needle, StringComparison.Ordinal);
Assert.True(index >= 0, $"Required marker was not found: {needle}");
return index;
}
private static string Slice(string value, string start, string end)
{
var startIndex = value.IndexOf(start, StringComparison.Ordinal);
var endIndex = value.IndexOf(
end,
startIndex + start.Length,
StringComparison.Ordinal);
Assert.True(startIndex >= 0, $"Required marker was not found: {start}");
Assert.True(endIndex >= 0, $"Required marker was not found: {end}");
return value[startIndex..endIndex];
}
private static string FindRepositoryRoot()
{
var directory = new DirectoryInfo(AppContext.BaseDirectory);
while (directory is not null)
{
if (File.Exists(Path.Combine(directory.FullName, "MBN_STOCK_WEBVIEW.sln")))
{
return directory.FullName;
}
directory = directory.Parent;
}
throw new DirectoryNotFoundException("Could not find the repository root.");
}
}