Browse Source

Add TestFixtures.Resources project for manual resource-view tests

Single .NET classlib that embeds one resource of every type the Avalonia port
recognises — .xml / .xsd / .xslt / .xaml / .png / .bmp / .jpg / .gif / .ico /
.cur / .resources / .bin (unknown-extension fall-through). The image fixtures
render a 64×64 cream square with a blue circle and red diagonal stripe so the
inline preview is obviously decoded rather than blank; the .ico is multi-frame
(16/32/48) so future per-frame work has something to chew on.

Assisted-by: Claude:claude-opus-4-7:Claude Code
pull/3755/head
Siegfried Pammer 2 months ago
parent
commit
52595b56be
  1. 130
      TestFixtures.Resources/Generate.cs
  2. 18
      TestFixtures.Resources/Generate.csproj
  3. 34
      TestFixtures.Resources/TestFixtures.Resources.csproj
  4. BIN
      TestFixtures.Resources/fixtures/Strings.resources
  5. 20
      TestFixtures.Resources/fixtures/Window.xaml
  6. 1
      TestFixtures.Resources/fixtures/blob.bin
  7. BIN
      TestFixtures.Resources/fixtures/favicon.ico
  8. BIN
      TestFixtures.Resources/fixtures/logo.bmp
  9. BIN
      TestFixtures.Resources/fixtures/logo.gif
  10. BIN
      TestFixtures.Resources/fixtures/logo.jpg
  11. BIN
      TestFixtures.Resources/fixtures/logo.png
  12. BIN
      TestFixtures.Resources/fixtures/pointer.cur
  13. 15
      TestFixtures.Resources/fixtures/sample.xml
  14. 26
      TestFixtures.Resources/fixtures/schema.xsd
  15. 25
      TestFixtures.Resources/fixtures/transform.xslt

130
TestFixtures.Resources/Generate.cs

@ -0,0 +1,130 @@ @@ -0,0 +1,130 @@
// Tiny helper that produces all the binary fixture files (PNG / BMP / JPG / GIF / ICO / CUR /
// .resources / .bin) into the fixtures/ directory. Text fixtures (XML / XSD / XSLT / XAML) are
// checked in as-is. Run with `dotnet run --project Generate.csproj` from this directory.
//
// Each image format renders an obvious recognizable shape (coloured square with a circle and a
// diagonal stripe) at 64×64 so manual ILSpy testing can eyeball that the rendering pipeline
// is decoding the bytes — not just rendering a blank tile.
using System;
using System.Collections.Generic;
using System.IO;
using System.Resources;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
var dir = Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "fixtures");
dir = Path.GetFullPath(dir);
Directory.CreateDirectory(dir);
WriteImage(Path.Combine(dir, "logo.png"), new PngEncoder());
WriteImage(Path.Combine(dir, "logo.bmp"), new BmpEncoder());
WriteImage(Path.Combine(dir, "logo.jpg"), new JpegEncoder { Quality = 92 });
WriteImage(Path.Combine(dir, "logo.gif"), new GifEncoder());
// Multi-frame .ico: 16x16, 32x32, 48x48, each PNG-encoded inside the ICO container so Avalonia
// can decode the largest frame for preview while a savvy viewer can pick any.
File.WriteAllBytes(Path.Combine(dir, "favicon.ico"), BuildIcoOrCur(isCursor: false, sizes: new[] { 16, 32, 48 }));
File.WriteAllBytes(Path.Combine(dir, "pointer.cur"), BuildIcoOrCur(isCursor: true, sizes: new[] { 32 }));
// .resources file with a mix of strings and other-typed entries — the .resources DataGrid view
// splits these into two tables.
var ms = new MemoryStream();
using (var rw = new ResourceWriter(ms))
{
rw.AddResource("greeting", "Hello, world");
rw.AddResource("city", "Linz");
rw.AddResource("year", 2026);
rw.AddResource("ratio", 1.61803);
rw.AddResource("flag", true);
}
File.WriteAllBytes(Path.Combine(dir, "Strings.resources"), ms.ToArray());
// Unknown extension — should fall through to the generic ResourceTreeNode.
File.WriteAllBytes(Path.Combine(dir, "blob.bin"), new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE });
Console.WriteLine($"Wrote fixtures into {dir}");
// --- helpers ---
static void WriteImage(string path, IImageEncoder encoder, int size = 64)
{
using var img = RenderShape(size);
using var fs = File.Create(path);
img.Save(fs, encoder);
}
static Image<Rgba32> RenderShape(int size)
{
var img = new Image<Rgba32>(size, size, new Rgba32(0xFF, 0xF6, 0xE5));
img.Mutate(ctx => {
// Filled blue circle in the centre.
ctx.Fill(new Rgba32(0x33, 0x99, 0xCC),
new SixLabors.ImageSharp.Drawing.EllipsePolygon(size / 2f, size / 2f, size * 0.35f));
// Red diagonal stripe.
ctx.DrawLine(new Rgba32(0xCC, 0x33, 0x33),
Math.Max(2, size / 16f),
new PointF(0, size),
new PointF(size, 0));
// 1px dark border so the bounds are obvious against any background.
ctx.Draw(new Rgba32(0x33, 0x33, 0x33), 1,
new RectangleF(0, 0, size - 1, size - 1));
});
return img;
}
// Builds an ICO/CUR file containing one PNG-encoded entry per requested size. The on-disk
// layout: ICONDIR + ICONDIRENTRY[*] + concatenated frame bytes; only byte 2 (image type)
// distinguishes ICO (1) from CUR (2). Hotspot for cursors is centred at (size/2, size/2).
static byte[] BuildIcoOrCur(bool isCursor, int[] sizes)
{
var frames = new List<byte[]>(sizes.Length);
var pngEncoder = new PngEncoder();
foreach (var sz in sizes)
{
using var img = RenderShape(sz);
using var ms = new MemoryStream();
img.Save(ms, pngEncoder);
frames.Add(ms.ToArray());
}
var fileMs = new MemoryStream();
var w = new BinaryWriter(fileMs);
w.Write((ushort)0); // reserved
w.Write((ushort)(isCursor ? 2 : 1)); // type: 1=ICO, 2=CUR
w.Write((ushort)sizes.Length); // image count
int dirSize = 6 + 16 * sizes.Length;
int offset = dirSize;
for (int i = 0; i < sizes.Length; i++)
{
var sz = sizes[i];
var dim = (byte)(sz >= 256 ? 0 : sz);
w.Write(dim); // width
w.Write(dim); // height
w.Write((byte)0); // palette count
w.Write((byte)0); // reserved
if (isCursor)
{
w.Write((ushort)(sz / 2)); // hotspot X
w.Write((ushort)(sz / 2)); // hotspot Y
}
else
{
w.Write((ushort)1); // colour planes
w.Write((ushort)32); // bits per pixel
}
w.Write((uint)frames[i].Length); // bytes in frame
w.Write((uint)offset); // offset to frame
offset += frames[i].Length;
}
foreach (var frame in frames)
w.Write(frame);
return fileMs.ToArray();
}

18
TestFixtures.Resources/Generate.csproj

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<RootNamespace>TestFixtures.Generate</RootNamespace>
<Nullable>enable</Nullable>
<!-- This generator runs once to (re)build the binary fixtures and isn't part of the
solution's CPM scope; pin SkiaSharp inline so the parent Directory.Packages.props
doesn't need to learn about a one-shot tool. -->
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<!-- ImageSharp covers every encoder we need (PNG/JPG/BMP/GIF) cross-platform; SkiaSharp's
encoder list doesn't include BMP/GIF. -->
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.4" />
</ItemGroup>
</Project>

34
TestFixtures.Resources/TestFixtures.Resources.csproj

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<AssemblyName>TestFixtures.Resources</AssemblyName>
<RootNamespace>TestFixtures.Resources</RootNamespace>
<Nullable>enable</Nullable>
<!-- Standalone fixture for manual ILSpy testing — keep build artifacts entirely
under this directory so the parent solution doesn't try to compile it. -->
<BaseOutputPath>$(MSBuildThisFileDirectory)bin\</BaseOutputPath>
</PropertyGroup>
<ItemGroup>
<!-- Each resource keeps its file name as the manifest name so it ends up routed to the
right specialised resource node by extension. -->
<EmbeddedResource Include="fixtures\sample.xml" LogicalName="sample.xml" />
<EmbeddedResource Include="fixtures\schema.xsd" LogicalName="schema.xsd" />
<EmbeddedResource Include="fixtures\transform.xslt" LogicalName="transform.xslt" />
<EmbeddedResource Include="fixtures\Window.xaml" LogicalName="Window.xaml" />
<EmbeddedResource Include="fixtures\logo.png" LogicalName="logo.png" />
<EmbeddedResource Include="fixtures\logo.bmp" LogicalName="logo.bmp" />
<EmbeddedResource Include="fixtures\logo.jpg" LogicalName="logo.jpg" />
<EmbeddedResource Include="fixtures\logo.gif" LogicalName="logo.gif" />
<EmbeddedResource Include="fixtures\favicon.ico" LogicalName="favicon.ico" />
<EmbeddedResource Include="fixtures\pointer.cur" LogicalName="pointer.cur" />
<EmbeddedResource Include="fixtures\Strings.resources" LogicalName="Strings.resources" />
<EmbeddedResource Include="fixtures\blob.bin" LogicalName="blob.bin" />
</ItemGroup>
<!-- Generate.cs is for the Generate.csproj only; exclude from compilation here. -->
<ItemGroup>
<Compile Remove="Generate.cs" />
<None Remove="Generate.cs" />
</ItemGroup>
</Project>

BIN
TestFixtures.Resources/fixtures/Strings.resources

Binary file not shown.

20
TestFixtures.Resources/fixtures/Window.xaml

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Demo Window"
Width="640"
Height="480">
<DockPanel>
<TextBlock DockPanel.Dock="Top"
Text="Hello, world"
FontSize="20"
Margin="12" />
<Border BorderBrush="Gray"
BorderThickness="1"
Margin="12">
<StackPanel>
<Button Content="Click me" />
<TextBox Text="Some text" />
</StackPanel>
</Border>
</DockPanel>
</Window>

1
TestFixtures.Resources/fixtures/blob.bin

@ -0,0 +1 @@ @@ -0,0 +1 @@
゙ュセ<EFBFBD><EFBFBD>コセ

BIN
TestFixtures.Resources/fixtures/favicon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
TestFixtures.Resources/fixtures/logo.bmp

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
TestFixtures.Resources/fixtures/logo.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
TestFixtures.Resources/fixtures/logo.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
TestFixtures.Resources/fixtures/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
TestFixtures.Resources/fixtures/pointer.cur

Binary file not shown.

After

Width:  |  Height:  |  Size: 796 B

15
TestFixtures.Resources/fixtures/sample.xml

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="environment" value="staging" />
<add key="timeoutSeconds" value="30" />
</appSettings>
<connectionStrings>
<add name="default"
connectionString="Data Source=.;Initial Catalog=Demo;Integrated Security=True"
providerName="System.Data.SqlClient" />
</connectionStrings>
<runtime>
<gcServer enabled="true" />
</runtime>
</configuration>

26
TestFixtures.Resources/fixtures/schema.xsd

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.com/order"
xmlns="http://example.com/order"
elementFormDefault="qualified">
<xs:element name="order">
<xs:complexType>
<xs:sequence>
<xs:element name="customer" type="xs:string" />
<xs:element name="items">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="sku" type="xs:string" use="required" />
<xs:attribute name="qty" type="xs:positiveInteger" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="id" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:schema>

25
TestFixtures.Resources/fixtures/transform.xslt

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes" />
<xsl:template match="/orders">
<html>
<body>
<h1>Orders</h1>
<table border="1">
<tr>
<th>ID</th>
<th>Customer</th>
</tr>
<xsl:for-each select="order">
<tr>
<td><xsl:value-of select="@id" /></td>
<td><xsl:value-of select="customer" /></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Loading…
Cancel
Save