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>
154 lines
5.4 KiB
C#
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.");
|
|
}
|
|
}
|