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>
This commit is contained in:
Erik 2026-09-07 11:53:01 +02:00
parent 7f95c771db
commit 897e262eda
5 changed files with 47 additions and 48 deletions

View file

@ -101,18 +101,23 @@
LogicalName="AcDream.App.Rendering.Icons.acdream-client-256.png" /> LogicalName="AcDream.App.Rendering.Icons.acdream-client-256.png" />
</ItemGroup> </ItemGroup>
<!-- MossTank ships as a copy-only plugin, plus its panel markup: <!-- MossTank ships as a copy-only plugin, plus its panel markup:
MossTankPlugin resolves mosstank.xml (and, since fix round A moved MossTankPlugin resolves mosstank.xml and every mosstank-*.xml popup
the Advanced Options / Loot Editor popups to their own panels, panel relative to its own assembly, so all of them must land in the
mosstank-advanced.xml / mosstank-loot-editor.xml too, plus Campaign same plugin directory. Fix round B item 13: globbed (mosstank*.xml)
VT S7.4's mosstank-buffpicker.xml popup) relative to its own instead of named one by one — a new popup used to need a matching
assembly, so all five files must land in the same plugin manual edit in both Copy targets below, the plugin csproj's own
directory. --> CopyToOutputDirectory list, and the test project's Link list; a
missed one silently dropped a panel at mount instead of failing the
build. -->
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\AcDream.Plugins.MossTank\AcDream.Plugins.MossTank.csproj"> <ProjectReference Include="..\AcDream.Plugins.MossTank\AcDream.Plugins.MossTank.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly> <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties> <SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<_MossTankPluginMarkup Include="$(MSBuildProjectDirectory)/../AcDream.Plugins.MossTank/mosstank*.xml" />
</ItemGroup>
<Target <Target
Name="CopyMossTankPluginToBuildOutput" Name="CopyMossTankPluginToBuildOutput"
AfterTargets="Build" AfterTargets="Build"
@ -128,7 +133,7 @@
</MSBuild> </MSBuild>
<MakeDir Directories="$(_MossTankDestDir)" /> <MakeDir Directories="$(_MossTankDestDir)" />
<Copy <Copy
SourceFiles="@(_MossTankPluginBuildTarget);$(MSBuildProjectDirectory)/../AcDream.Plugins.MossTank/mosstank.xml;$(MSBuildProjectDirectory)/../AcDream.Plugins.MossTank/mosstank-advanced.xml;$(MSBuildProjectDirectory)/../AcDream.Plugins.MossTank/mosstank-loot-editor.xml;$(MSBuildProjectDirectory)/../AcDream.Plugins.MossTank/mosstank-buffpicker.xml" SourceFiles="@(_MossTankPluginBuildTarget);@(_MossTankPluginMarkup)"
DestinationFolder="$(_MossTankDestDir)" DestinationFolder="$(_MossTankDestDir)"
SkipUnchangedFiles="true" /> SkipUnchangedFiles="true" />
<WriteLinesToFile <WriteLinesToFile
@ -152,7 +157,7 @@
</MSBuild> </MSBuild>
<MakeDir Directories="$(_MossTankPublishDestDir)" /> <MakeDir Directories="$(_MossTankPublishDestDir)" />
<Copy <Copy
SourceFiles="@(_MossTankPluginPublishTarget);$(MSBuildProjectDirectory)/../AcDream.Plugins.MossTank/mosstank.xml;$(MSBuildProjectDirectory)/../AcDream.Plugins.MossTank/mosstank-advanced.xml;$(MSBuildProjectDirectory)/../AcDream.Plugins.MossTank/mosstank-loot-editor.xml;$(MSBuildProjectDirectory)/../AcDream.Plugins.MossTank/mosstank-buffpicker.xml" SourceFiles="@(_MossTankPluginPublishTarget);@(_MossTankPluginMarkup)"
DestinationFolder="$(_MossTankPublishDestDir)" DestinationFolder="$(_MossTankPublishDestDir)"
SkipUnchangedFiles="true" /> SkipUnchangedFiles="true" />
<WriteLinesToFile <WriteLinesToFile

View file

@ -19,16 +19,13 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="mosstank.xml"> <!-- Fix round B item 13: glob every mosstank*.xml plugin-panel file
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> instead of naming each one — a new popup (mosstank-metaeditor.xml,
</None> item 5) used to need a matching manual edit here, in
<None Update="mosstank-advanced.xml"> AcDream.App.csproj's two Copy targets, and in the test project's
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> Link list; missing one silently dropped a panel at mount instead
</None> of failing the build. -->
<None Update="mosstank-loot-editor.xml"> <None Update="mosstank*.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="mosstank-buffpicker.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<EmbeddedResource Include="VtankCraftRecipes.tsv" /> <EmbeddedResource Include="VtankCraftRecipes.tsv" />

View file

@ -123,8 +123,10 @@ public sealed class LinuxPlatformBoundaryTests
2, 2,
project.Split("Targets=\"GetTargetPath\"", StringSplitOptions.None) project.Split("Targets=\"GetTargetPath\"", StringSplitOptions.None)
.Length - 1); .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( Assert.Contains(
"../AcDream.Plugins.MossTank/mosstank.xml", "../AcDream.Plugins.MossTank/mosstank*.xml",
project, project,
StringComparison.Ordinal); StringComparison.Ordinal);
Assert.DoesNotContain( Assert.DoesNotContain(

View file

@ -20,17 +20,13 @@
<ProjectReference Include="..\..\src\AcDream.Plugins.MossTank\AcDream.Plugins.MossTank.csproj" /> <ProjectReference Include="..\..\src\AcDream.Plugins.MossTank\AcDream.Plugins.MossTank.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\..\src\AcDream.Plugins.MossTank\mosstank.xml" <!-- Fix round B item 13: glob every mosstank*.xml plugin-panel file
Link="mosstank.xml" (see AcDream.Plugins.MossTank.csproj's own comment) — %(Filename)
CopyToOutputDirectory="PreserveNewest" /> %(Extension) batching keeps each file's Link its own bare name
<None Include="..\..\src\AcDream.Plugins.MossTank\mosstank-advanced.xml" (mosstank-metaeditor.xml, etc.) instead of the source-relative path
Link="mosstank-advanced.xml" MSBuild would otherwise use. -->
CopyToOutputDirectory="PreserveNewest" /> <None Include="..\..\src\AcDream.Plugins.MossTank\mosstank*.xml"
<None Include="..\..\src\AcDream.Plugins.MossTank\mosstank-loot-editor.xml" Link="%(Filename)%(Extension)"
Link="mosstank-loot-editor.xml"
CopyToOutputDirectory="PreserveNewest" />
<None Include="..\..\src\AcDream.Plugins.MossTank\mosstank-buffpicker.xml"
Link="mosstank-buffpicker.xml"
CopyToOutputDirectory="PreserveNewest" /> CopyToOutputDirectory="PreserveNewest" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -323,19 +323,19 @@ public sealed class MossTankMarkupContractTests
/// <c>&lt;column&gt;</c> (Campaign VT slice 1's <c>&lt;list&gt;&lt;column&gt;</c> /// <c>&lt;column&gt;</c> (Campaign VT slice 1's <c>&lt;list&gt;&lt;column&gt;</c>
/// children have no <c>x</c>/<c>y</c> grammar at all — see /// children have no <c>x</c>/<c>y</c> grammar at all — see
/// docs/plugin-ui-markup.md's "Columns" — so every column reads as /// docs/plugin-ui-markup.md's "Columns" — so every column reads as
/// (0,0) and would trivially "overlap" every other column). /// (0,0) and would trivially "overlap" every other column). Fix round B
/// item 13: the file list is discovered (Directory.GetFiles) rather than
/// named one by one, so a new mosstank-*.xml popup is covered for free.
/// </summary> /// </summary>
[Theory] [Fact]
[InlineData("mosstank.xml")] public void AuthoredControlsInTheSameContainerNeverOverlapASibling()
[InlineData("mosstank-advanced.xml")]
[InlineData("mosstank-loot-editor.xml")]
[InlineData("mosstank-buffpicker.xml")]
public void AuthoredControlsInTheSameContainerNeverOverlapASibling(string fileName)
{ {
XDocument document = XDocument.Load( foreach (string path in Directory.GetFiles(AppContext.BaseDirectory, "mosstank*.xml"))
Path.Combine(AppContext.BaseDirectory, fileName)); {
XElement root = Assert.IsType<XElement>(document.Root); XDocument document = XDocument.Load(path);
AssertNoSiblingOverlap(root); XElement root = Assert.IsType<XElement>(document.Root);
AssertNoSiblingOverlap(root);
}
} }
/// <summary>Direct pin on the overlap PREDICATE itself (independent of /// <summary>Direct pin on the overlap PREDICATE itself (independent of
@ -464,14 +464,13 @@ public sealed class MossTankMarkupContractTests
[Fact] [Fact]
public void NoButtonAnywhereUsesTheUnrenderableArrowGlyphs() public void NoButtonAnywhereUsesTheUnrenderableArrowGlyphs()
{ {
foreach (string fileName in new[] // Fix round B item 13: discovered file list (Directory.GetFiles)
{ // instead of a named-one-by-one array, so a new mosstank-*.xml
"mosstank.xml", "mosstank-advanced.xml", // popup is covered for free.
"mosstank-loot-editor.xml", "mosstank-buffpicker.xml", foreach (string path in Directory.GetFiles(AppContext.BaseDirectory, "mosstank*.xml"))
})
{ {
XDocument document = XDocument.Load( string fileName = Path.GetFileName(path);
Path.Combine(AppContext.BaseDirectory, fileName)); XDocument document = XDocument.Load(path);
XElement root = Assert.IsType<XElement>(document.Root); XElement root = Assert.IsType<XElement>(document.Root);
foreach (XElement button in root.Descendants("button")) foreach (XElement button in root.Descendants("button"))
{ {