acdream/tests/AcDream.App.Tests/Rendering/LinuxPlatformBoundaryTests.cs
Erik 897e262eda refactor(vtank): slice 7 fix round B item 13 — glob mosstank*.xml everywhere it's named
Reordered ahead of items 5-12 on purpose: item 5 adds a fourth popup
(mosstank-metaeditor.xml) and would otherwise need the same three-file
manual edit this item retires. Every place a mosstank-*.xml file used to be
named one at a time now globs mosstank*.xml instead, so adding a new popup
panel needs exactly one new file, not four matching edits:

- AcDream.Plugins.MossTank.csproj: <None Update="mosstank*.xml"> replaces
  four separate <None Update> entries.
- AcDream.Plugins.MossTank.Tests.csproj: one globbed <None Include> with
  Link="%(Filename)%(Extension)" replaces four separate Include/Link pairs.
- AcDream.App.csproj: a new _MossTankPluginMarkup item
  (Include="…/mosstank*.xml") replaces the literal four-file semicolon list
  in both CopyMossTankPluginToBuildOutput and …ToPublishOutput's Copy
  SourceFiles.
- MossTankMarkupContractTests: AuthoredControlsInTheSameContainerNeverOverlapASibling
  (item 2's new pin) and NoButtonAnywhereUsesTheUnrenderableArrowGlyphs now
  iterate Directory.GetFiles(AppContext.BaseDirectory, "mosstank*.xml")
  instead of a hardcoded 4-file array/Theory. Left
  SecondaryPopupPanelsFitTheirOwnBoundsAndEveryBindingResolves alone — it
  pairs each file with its OWN expected w/h, which a glob can't supply.
- LinuxPlatformBoundaryTests.ShippedPluginCopiesUseResolvedTargetPathsForBuildAndPublish
  now asserts on the glob pattern instead of the literal mosstank.xml
  substring the old Copy SourceFiles list contained.

A missed file in any of these four places used to silently drop a panel at
mount (fix round A's own #missing-popup-files near-miss) instead of failing
the build; the glob makes that failure mode structurally impossible.

tests/AcDream.Plugins.MossTank.Tests: 662/662 (665 -> 662: the 4-case
Theory collapsed into 1 Fact with an internal loop — same coverage, 3 fewer
reported xunit tests).
tests/AcDream.App.Tests --filter Markup|Plugin|UiMenu|Slider: 276/3 skipped/279 (unchanged).
Verified the glob actually copies all four files: `ls
src/AcDream.App/bin/Release/net10.0/plugins/AcDream.Plugins.MossTank/*.xml`
lists all four post-build.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 11:53:01 +02:00

154 lines
5.4 KiB
C#

namespace AcDream.App.Tests.Rendering;
public sealed class LinuxPlatformBoundaryTests
{
[Fact]
public void FramePacingSelectsPlatformWaiterOnlyAtFactoryBoundary()
{
string app = AppSourceRoot();
string controller = File.ReadAllText(Path.Combine(
app,
"Rendering",
"FramePacingController.cs"));
Assert.Contains(
"PlatformFramePacingWaiterFactory.ForCurrentProcess()",
controller,
StringComparison.Ordinal);
Assert.DoesNotContain(
"WindowsHighResolutionFramePacingWaiter.Create()",
controller,
StringComparison.Ordinal);
string[] windowsImports = Directory
.EnumerateFiles(app, "*.cs", SearchOption.AllDirectories)
.Where(path => File.ReadAllText(path).Contains(
"LibraryImport(\"kernel32.dll\"",
StringComparison.Ordinal))
.Select(path => Path.GetFileName(path)!)
.ToArray();
Assert.Equal(
["WindowsHighResolutionFramePacingWaiter.cs"],
windowsImports);
}
[Fact]
public void GraphicalFeatureCodeDoesNotResolveWindowsLocalAppData()
{
string root = RepositoryRoot();
string[] productionFiles =
[
.. Directory.EnumerateFiles(
Path.Combine(root, "src", "AcDream.App"),
"*.cs",
SearchOption.AllDirectories),
.. Directory.EnumerateFiles(
Path.Combine(root, "src", "AcDream.UI.Abstractions"),
"*.cs",
SearchOption.AllDirectories),
];
string[] offenders = productionFiles
.Where(path => File.ReadAllText(path).Contains(
"LocalApplicationData",
StringComparison.Ordinal))
.Select(path => Path.GetRelativePath(root, path))
.ToArray();
Assert.Empty(offenders);
}
[Fact]
public void OperatingSystemChecksRemainInsidePlatformOwners()
{
string app = AppSourceRoot();
string[] offenders = Directory
.EnumerateFiles(app, "*.cs", SearchOption.AllDirectories)
.Where(path => File.ReadAllText(path).Contains(
"OperatingSystem.Is",
StringComparison.Ordinal))
.Where(path =>
{
string relative = Path.GetRelativePath(app, path)
.Replace('\\', '/');
return !relative.StartsWith(
"Platform/",
StringComparison.Ordinal)
&& relative is not
"Rendering/FramePacingWaiterFactory.cs"
&& relative is not
"Rendering/LinuxMonotonicFramePacingWaiter.cs"
&& relative is not
"Rendering/WindowsHighResolutionFramePacingWaiter.cs";
})
.Select(path => Path.GetRelativePath(app, path))
.ToArray();
Assert.Empty(offenders);
}
[Fact]
public void RuntimePlatformGuardHasOneDefinitionAndOneApprovedConsumer()
{
string app = AppSourceRoot();
string[] files = Directory
.EnumerateFiles(app, "*.cs", SearchOption.AllDirectories)
.Where(path => File.ReadAllText(path).Contains(
"RuntimePlatformGuard",
StringComparison.Ordinal))
.Select(path => Path.GetRelativePath(app, path).Replace('\\', '/'))
.OrderBy(static path => path, StringComparer.Ordinal)
.ToArray();
Assert.Equal(
[
"Credentials/AppCredentialResolver.cs",
"Platform/GraphicalHostPlatformServices.cs",
],
files);
}
[Fact]
public void ShippedPluginCopiesUseResolvedTargetPathsForBuildAndPublish()
{
string project = File.ReadAllText(Path.Combine(
AppSourceRoot(),
"AcDream.App.csproj"));
Assert.Contains("$(TargetFramework)", project, StringComparison.Ordinal);
Assert.Contains("$(RuntimeIdentifier)", project, StringComparison.Ordinal);
Assert.Contains("$(OutputPath)plugins/", project, StringComparison.Ordinal);
Assert.Contains("$(PublishDir)plugins/", project, StringComparison.Ordinal);
Assert.Equal(
2,
project.Split("Targets=\"GetTargetPath\"", StringSplitOptions.None)
.Length - 1);
// Fix round B item 13: globbed (mosstank*.xml) rather than named
// one file at a time — see AcDream.App.csproj's own comment.
Assert.Contains(
"../AcDream.Plugins.MossTank/mosstank*.xml",
project,
StringComparison.Ordinal);
Assert.DoesNotContain(
"/bin/$(Configuration)",
project,
StringComparison.Ordinal);
}
private static string AppSourceRoot() =>
Path.Combine(RepositoryRoot(), "src", "AcDream.App");
private static string RepositoryRoot()
{
DirectoryInfo? directory = new(AppContext.BaseDirectory);
while (directory is not null)
{
if (File.Exists(Path.Combine(directory.FullName, "AcDream.slnx")))
return directory.FullName;
directory = directory.Parent;
}
throw new DirectoryNotFoundException(
"Could not locate repository root from test output.");
}
}