Browse Source

Revive BAML decompiler tests as two projects and run them in CI

The BAML tests were orphaned: in no solution, no CI, and missing their
ICSharpCode.BamlDecompiler reference. Revive them split by platform,
mirroring ILSpy.Tests / ILSpy.Tests.Windows: ILSpy.BamlDecompiler.Tests
(net11.0) holds platform-agnostic tests - including a regression test that
decompiles with the WPF assemblies hidden, covering the missing-assembly
fix - and ILSpy.BamlDecompiler.Tests.Windows (net11.0-windows) holds the
WPF round-trip cases that compile XAML into BAML. Single-TFM projects keep
normal lock files; the Windows one declares all RIDs so its lock is
generatable and verifiable on any host.

Wire the cross-platform project into ILSpy.sln, ILSpy.XPlat.slnf and
ILSpy.Desktop.slnf so it builds and runs on the Linux/macOS legs; the
Windows project stays in ILSpy.sln (its tests execute in the Windows leg).
The Windows job also archives that assembly, whose embedded BAML lets the
decompiler be exercised against real BAML on a host without WPF.

Assisted-by: Claude:claude-opus-4-8:Claude Code
pull/3881/head
Siegfried Pammer 2 months ago committed by Siegfried Pammer
parent
commit
5343d849d4
  1. 17
      .github/workflows/build-ilspy.yml
  2. 3
      ICSharpCode.BamlDecompiler/Properties/AssemblyInfo.cs
  3. 0
      ILSpy.BamlDecompiler.Tests.Windows/BamlSecurityTests.cs
  4. 0
      ILSpy.BamlDecompiler.Tests.Windows/BamlTestRunner.cs
  5. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/AttachedEvent.xaml
  6. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/AttachedEvent.xaml.cs
  7. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/AvalonDockBrushes.xaml
  8. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/AvalonDockCommon.xaml
  9. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/CustomControl.cs
  10. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/Dictionary1.xaml
  11. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/EscapeSequence.xaml
  12. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue1435.xaml
  13. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue1546.xaml
  14. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue1547.xaml
  15. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue1547.xaml.cs
  16. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue2052.xaml
  17. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue2097.xaml
  18. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue2097.xaml.cs
  19. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue2116.xaml
  20. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue2116.xaml.cs
  21. 2
      ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue3318.xaml
  22. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue3318.xaml.cs
  23. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue445.xaml
  24. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue775.xaml
  25. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/MarkupExtension.xaml
  26. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/MyControl.xaml
  27. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/MyControl.xaml.cs
  28. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/NamespacePrefix.xaml
  29. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/ReadonlyProperty.xaml
  30. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/ReadonlyProperty.xaml.cs
  31. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/Resources.xaml
  32. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/Resources.xaml.cs
  33. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/Simple.xaml
  34. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/Simple.xaml.cs
  35. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/SimpleDictionary.xaml
  36. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/SimpleNames.xaml
  37. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/SimpleNames.xaml.cs
  38. 0
      ILSpy.BamlDecompiler.Tests.Windows/Cases/SimplePropertyElement.xaml
  39. 137
      ILSpy.BamlDecompiler.Tests.Windows/ILSpy.BamlDecompiler.Tests.Windows.csproj
  40. 0
      ILSpy.BamlDecompiler.Tests.Windows/Mocks/AvalonDock.cs
  41. 4948
      ILSpy.BamlDecompiler.Tests.Windows/packages.lock.json
  42. 98
      ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj
  43. 137
      ILSpy.BamlDecompiler.Tests/MissingReferencesTests.cs
  44. 1218
      ILSpy.BamlDecompiler.Tests/packages.lock.json
  45. 1
      ILSpy.Desktop.slnf
  46. 3
      ILSpy.XPlat.slnf
  47. 28
      ILSpy.sln

17
.github/workflows/build-ilspy.yml

@ -112,6 +112,16 @@ jobs:
path: ICSharpCode.Decompiler.Tests/TestCases path: ICSharpCode.Decompiler.Tests/TestCases
compression-level: 9 compression-level: 9
# The Windows build compiles the WPF XAML fixtures into this assembly as BAML resources.
# Archiving it lets the BAML decompiler be exercised against real BAML on non-Windows hosts.
- name: Upload BamlDecompiler test binaries
uses: actions/upload-artifact@v7
if: failure() && steps.unit-tests.outcome == 'failure'
with:
name: bamldecompiler-tests-${{ matrix.configuration }}
path: ILSpy.BamlDecompiler.Tests.Windows\bin\${{ matrix.configuration }}\net11.0-windows\**
if-no-files-found: warn
- name: Create Test Report - name: Create Test Report
uses: test-summary/action@v2 uses: test-summary/action@v2
if: always() if: always()
@ -340,6 +350,13 @@ jobs:
--no-build --report-trx --no-build --report-trx
--results-directory test-results --results-directory test-results
- name: Execute BAML decompiler tests
run: >
dotnet test --project ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj
--configuration Release
--no-build --report-trx
--results-directory test-results
- name: Upload Test Logs - name: Upload Test Logs
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
if: success() || failure() if: success() || failure()

3
ICSharpCode.BamlDecompiler/Properties/AssemblyInfo.cs

@ -20,6 +20,7 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#endregion #endregion
@ -27,6 +28,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: InternalsVisibleTo("ILSpy.BamlDecompiler.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001004dcf3979c4e902efa4dd2163a039701ed5822e6f1134d77737296abbb97bf0803083cfb2117b4f5446a217782f5c7c634f9fe1fc60b4c11d62c5b3d33545036706296d31903ddcf750875db38a8ac379512f51620bb948c94d0831125fbc5fe63707cbb93f48c1459c4d1749eb7ac5e681a2f0d6d7c60fa527a3c0b8f92b02bf")]
// This sets the default COM visibility of types in the assembly to invisible. // This sets the default COM visibility of types in the assembly to invisible.
// If you need to expose a type to COM, use [ComVisible(true)] on that type. // If you need to expose a type to COM, use [ComVisible(true)] on that type.
[assembly: ComVisible(false)] [assembly: ComVisible(false)]

0
ILSpy.BamlDecompiler.Tests/BamlSecurityTests.cs → ILSpy.BamlDecompiler.Tests.Windows/BamlSecurityTests.cs

0
ILSpy.BamlDecompiler.Tests/BamlTestRunner.cs → ILSpy.BamlDecompiler.Tests.Windows/BamlTestRunner.cs

0
ILSpy.BamlDecompiler.Tests/Cases/AttachedEvent.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/AttachedEvent.xaml

0
ILSpy.BamlDecompiler.Tests/Cases/AttachedEvent.xaml.cs → ILSpy.BamlDecompiler.Tests.Windows/Cases/AttachedEvent.xaml.cs

0
ILSpy.BamlDecompiler.Tests/Cases/AvalonDockBrushes.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/AvalonDockBrushes.xaml

0
ILSpy.BamlDecompiler.Tests/Cases/AvalonDockCommon.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/AvalonDockCommon.xaml

0
ILSpy.BamlDecompiler.Tests/Cases/CustomControl.cs → ILSpy.BamlDecompiler.Tests.Windows/Cases/CustomControl.cs

0
ILSpy.BamlDecompiler.Tests/Cases/Dictionary1.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/Dictionary1.xaml

0
ILSpy.BamlDecompiler.Tests/Cases/EscapeSequence.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/EscapeSequence.xaml

0
ILSpy.BamlDecompiler.Tests/Cases/Issue1435.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue1435.xaml

0
ILSpy.BamlDecompiler.Tests/Cases/Issue1546.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue1546.xaml

0
ILSpy.BamlDecompiler.Tests/Cases/Issue1547.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue1547.xaml

0
ILSpy.BamlDecompiler.Tests/Cases/Issue1547.xaml.cs → ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue1547.xaml.cs

0
ILSpy.BamlDecompiler.Tests/Cases/Issue2052.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue2052.xaml

0
ILSpy.BamlDecompiler.Tests/Cases/Issue2097.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue2097.xaml

0
ILSpy.BamlDecompiler.Tests/Cases/Issue2097.xaml.cs → ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue2097.xaml.cs

0
ILSpy.BamlDecompiler.Tests/Cases/Issue2116.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue2116.xaml

0
ILSpy.BamlDecompiler.Tests/Cases/Issue2116.xaml.cs → ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue2116.xaml.cs

2
ILSpy.BamlDecompiler.Tests/Cases/Issue3318.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue3318.xaml

@ -1,3 +1,3 @@
<UserControl x:Class="ILSpy.BamlDecompiler.Tests.Cases.Issue3318" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:cases="clr-namespace:ILSpy.BamlDecompiler.Tests.Cases;assembly=ILSpy.BamlDecompiler.Tests"> <UserControl x:Class="ILSpy.BamlDecompiler.Tests.Cases.Issue3318" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:cases="clr-namespace:ILSpy.BamlDecompiler.Tests.Cases;assembly=ILSpy.BamlDecompiler.Tests.Windows">
<Grid Name="Root" x:FieldModifier="public" /> <Grid Name="Root" x:FieldModifier="public" />
</UserControl> </UserControl>

0
ILSpy.BamlDecompiler.Tests/Cases/Issue3318.xaml.cs → ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue3318.xaml.cs

0
ILSpy.BamlDecompiler.Tests/Cases/Issue445.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue445.xaml

0
ILSpy.BamlDecompiler.Tests/Cases/Issue775.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/Issue775.xaml

0
ILSpy.BamlDecompiler.Tests/Cases/MarkupExtension.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/MarkupExtension.xaml

0
ILSpy.BamlDecompiler.Tests/Cases/MyControl.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/MyControl.xaml

0
ILSpy.BamlDecompiler.Tests/Cases/MyControl.xaml.cs → ILSpy.BamlDecompiler.Tests.Windows/Cases/MyControl.xaml.cs

0
ILSpy.BamlDecompiler.Tests/Cases/NamespacePrefix.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/NamespacePrefix.xaml

0
ILSpy.BamlDecompiler.Tests/Cases/ReadonlyProperty.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/ReadonlyProperty.xaml

0
ILSpy.BamlDecompiler.Tests/Cases/ReadonlyProperty.xaml.cs → ILSpy.BamlDecompiler.Tests.Windows/Cases/ReadonlyProperty.xaml.cs

0
ILSpy.BamlDecompiler.Tests/Cases/Resources.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/Resources.xaml

0
ILSpy.BamlDecompiler.Tests/Cases/Resources.xaml.cs → ILSpy.BamlDecompiler.Tests.Windows/Cases/Resources.xaml.cs

0
ILSpy.BamlDecompiler.Tests/Cases/Simple.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/Simple.xaml

0
ILSpy.BamlDecompiler.Tests/Cases/Simple.xaml.cs → ILSpy.BamlDecompiler.Tests.Windows/Cases/Simple.xaml.cs

0
ILSpy.BamlDecompiler.Tests/Cases/SimpleDictionary.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/SimpleDictionary.xaml

0
ILSpy.BamlDecompiler.Tests/Cases/SimpleNames.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/SimpleNames.xaml

0
ILSpy.BamlDecompiler.Tests/Cases/SimpleNames.xaml.cs → ILSpy.BamlDecompiler.Tests.Windows/Cases/SimpleNames.xaml.cs

0
ILSpy.BamlDecompiler.Tests/Cases/SimplePropertyElement.xaml → ILSpy.BamlDecompiler.Tests.Windows/Cases/SimplePropertyElement.xaml

137
ILSpy.BamlDecompiler.Tests.Windows/ILSpy.BamlDecompiler.Tests.Windows.csproj

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsWindowsX64 Condition="$([MSBuild]::IsOsPlatform('Windows')) And $([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) == X64">true</IsWindowsX64>
<IsWindowsARM64 Condition="$([MSBuild]::IsOsPlatform('Windows')) And $([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) == ARM64">true</IsWindowsARM64>
</PropertyGroup>
<PropertyGroup>
<!-- The BAML round-trip cases compile WPF XAML into this assembly, so the project is
Windows-bound (built and run only in the Windows CI leg via ILSpy.sln). EnableWindowsTargeting
lets the package graph restore on non-Windows hosts, so the lock file can be generated and
verified anywhere even though the tests themselves only execute on Windows. -->
<TargetFramework>net11.0-windows</TargetFramework>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<UseWpf>true</UseWpf>
<!-- RID-complete lock so the committed packages.lock.json matches regardless of the host that
restores it; the concrete RuntimeIdentifier gives BamlTestRunner the bin\...\<rid> layout
it walks up from to find the .xaml sources. -->
<RuntimeIdentifiers>win-x64;win-arm64;linux-x64;osx-arm64</RuntimeIdentifiers>
<RuntimeIdentifier Condition="$(IsWindowsX64) == true">win-x64</RuntimeIdentifier>
<RuntimeIdentifier Condition="$(IsWindowsARM64) == true">win-arm64</RuntimeIdentifier>
<IsPackable>false</IsPackable>
<OutputType>Exe</OutputType>
<EnableDefaultItems>false</EnableDefaultItems>
<ExtrasEnableDefaultPageItems>false</ExtrasEnableDefaultPageItems>
<ExtrasEnableDefaultResourceItems>false</ExtrasEnableDefaultResourceItems>
<AutoGenerateBindingRedirects>True</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>pdbonly</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NUnit" />
<PackageReference Include="NUnit3TestAdapter" />
<PackageReference Include="AwesomeAssertions" />
<PackageReference Include="Microsoft.Testing.Extensions.TrxReport" />
<PackageReference Include="Microsoft.Testing.Extensions.VSTestBridge" />
<PackageReference Include="coverlet.MTP" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ICSharpCode.Decompiler.Tests\ICSharpCode.Decompiler.Tests.csproj" />
<ProjectReference Include="..\ICSharpCode.BamlDecompiler\ICSharpCode.BamlDecompiler.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Include="BamlSecurityTests.cs" />
<Compile Include="BamlTestRunner.cs" />
<Compile Include="Cases\AttachedEvent.xaml.cs">
<DependentUpon>AttachedEvent.xaml</DependentUpon>
</Compile>
<Compile Include="Cases\CustomControl.cs" />
<Compile Include="Cases\Issue1547.xaml.cs" />
<Compile Include="Cases\Issue2097.xaml.cs" />
<Compile Include="Cases\Issue2116.xaml.cs" />
<Compile Include="Cases\Issue3318.xaml.cs" />
<Compile Include="Cases\MyControl.xaml.cs">
<DependentUpon>MyControl.xaml</DependentUpon>
</Compile>
<Compile Include="Cases\ReadonlyProperty.xaml.cs" />
<Compile Include="Cases\Resources.xaml.cs">
<DependentUpon>Resources.xaml</DependentUpon>
</Compile>
<Compile Include="Cases\Simple.xaml.cs">
<DependentUpon>Simple.xaml</DependentUpon>
</Compile>
<Compile Include="Cases\SimpleNames.xaml.cs">
<DependentUpon>SimpleNames.xaml</DependentUpon>
</Compile>
<Compile Include="Mocks\AvalonDock.cs" />
</ItemGroup>
<ItemGroup>
<Page Include="Cases\AttachedEvent.xaml" />
<Page Include="Cases\AvalonDockBrushes.xaml" />
<Page Include="Cases\AvalonDockCommon.xaml" />
<Page Include="Cases\EscapeSequence.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Cases\Issue1435.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Cases\Issue1546.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Cases\Issue1547.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Cases\Issue2052.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Cases\Issue2097.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Cases\Issue2116.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Cases\Issue3318.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Cases\Issue775.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="Cases\MarkupExtension.xaml" />
<Page Include="Cases\MyControl.xaml" />
<Page Include="Cases\NamespacePrefix.xaml" />
<Page Include="Cases\ReadonlyProperty.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Cases\Resources.xaml" />
<Page Include="Cases\Simple.xaml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Page>
<Page Include="Cases\SimpleDictionary.xaml" />
<Page Include="Cases\SimpleNames.xaml" />
<Page Include="Cases\SimplePropertyElement.xaml" />
<Page Include="Cases\Dictionary1.xaml" />
<Page Include="Cases\Issue445.xaml" />
</ItemGroup>
</Project>

0
ILSpy.BamlDecompiler.Tests/Mocks/AvalonDock.cs → ILSpy.BamlDecompiler.Tests.Windows/Mocks/AvalonDock.cs

4948
ILSpy.BamlDecompiler.Tests.Windows/packages.lock.json

File diff suppressed because it is too large Load Diff

98
ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj

@ -2,26 +2,20 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<IsWindowsX64 Condition="$([MSBuild]::IsOsPlatform('Windows')) And $([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) == X64">true</IsWindowsX64> <!-- Platform-agnostic BAML decompiler tests: they need no WPF, so they build and run on every
<IsWindowsARM64 Condition="$([MSBuild]::IsOsPlatform('Windows')) And $([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) == ARM64">true</IsWindowsARM64> CI leg (including Linux/macOS). The WPF round-trip cases live in the Windows-only companion
</PropertyGroup> project ILSpy.BamlDecompiler.Tests.Windows. -->
<TargetFramework>net11.0</TargetFramework>
<PropertyGroup>
<TargetFramework>net11.0-windows</TargetFramework>
<RuntimeIdentifier Condition="$(IsWindowsX64) == true">win-x64</RuntimeIdentifier>
<RuntimeIdentifier Condition="$(IsWindowsARM64) == true">win-arm64</RuntimeIdentifier>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<EnableDefaultItems>false</EnableDefaultItems> <EnableDefaultItems>false</EnableDefaultItems>
<UseWpf>true</UseWpf>
<ExtrasEnableDefaultPageItems>false</ExtrasEnableDefaultPageItems>
<ExtrasEnableDefaultResourceItems>false</ExtrasEnableDefaultResourceItems>
<AutoGenerateBindingRedirects>True</AutoGenerateBindingRedirects> <!-- Strong-named with the shared snk so ICSharpCode.BamlDecompiler can grant InternalsVisibleTo
<EnableWindowsTargeting>true</EnableWindowsTargeting> for the resilience tests, mirroring ICSharpCode.Decompiler.Tests. -->
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>..\ICSharpCode.Decompiler\ICSharpCode.Decompiler.snk</AssemblyOriginatorKeyFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'"> <PropertyGroup Condition="'$(Configuration)' == 'Debug'">
@ -45,83 +39,11 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\ICSharpCode.Decompiler.Tests\ICSharpCode.Decompiler.Tests.csproj" /> <ProjectReference Include="..\ICSharpCode.BamlDecompiler\ICSharpCode.BamlDecompiler.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Include="BamlSecurityTests.cs" />
<Compile Include="BamlTestRunner.cs" />
<Compile Include="Cases\AttachedEvent.xaml.cs">
<DependentUpon>AttachedEvent.xaml</DependentUpon>
</Compile>
<Compile Include="Cases\CustomControl.cs" />
<Compile Include="Cases\Issue1547.xaml.cs" />
<Compile Include="Cases\Issue2097.xaml.cs" />
<Compile Include="Cases\Issue2116.xaml.cs" />
<Compile Include="Cases\Issue3318.xaml.cs" />
<Compile Include="Cases\MyControl.xaml.cs">
<DependentUpon>MyControl.xaml</DependentUpon>
</Compile>
<Compile Include="Cases\ReadonlyProperty.xaml.cs" />
<Compile Include="Cases\Resources.xaml.cs">
<DependentUpon>Resources.xaml</DependentUpon>
</Compile>
<Compile Include="Cases\Simple.xaml.cs">
<DependentUpon>Simple.xaml</DependentUpon>
</Compile>
<Compile Include="Cases\SimpleNames.xaml.cs">
<DependentUpon>SimpleNames.xaml</DependentUpon>
</Compile>
<Compile Include="Mocks\AvalonDock.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Page Include="Cases\AttachedEvent.xaml" /> <Compile Include="MissingReferencesTests.cs" />
<Page Include="Cases\AvalonDockBrushes.xaml" />
<Page Include="Cases\AvalonDockCommon.xaml" />
<Page Include="Cases\EscapeSequence.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Cases\Issue1435.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Cases\Issue1546.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Cases\Issue1547.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Cases\Issue2052.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Cases\Issue2097.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Cases\Issue2116.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Cases\Issue3318.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Cases\Issue775.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="Cases\MarkupExtension.xaml" />
<Page Include="Cases\MyControl.xaml" />
<Page Include="Cases\NamespacePrefix.xaml" />
<Page Include="Cases\ReadonlyProperty.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Cases\Resources.xaml" />
<Page Include="Cases\Simple.xaml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Page>
<Page Include="Cases\SimpleDictionary.xaml" />
<Page Include="Cases\SimpleNames.xaml" />
<Page Include="Cases\SimplePropertyElement.xaml" />
<Page Include="Cases\Dictionary1.xaml" />
<Page Include="Cases\Issue445.xaml" />
</ItemGroup> </ItemGroup>
</Project> </Project>

137
ILSpy.BamlDecompiler.Tests/MissingReferencesTests.cs

@ -0,0 +1,137 @@
// Copyright (c) 2026 Siegfried Pammer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection.PortableExecutable;
using System.Threading.Tasks;
using ICSharpCode.BamlDecompiler;
using ICSharpCode.BamlDecompiler.Baml;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using NUnit.Framework;
namespace ILSpy.BamlDecompiler.Tests
{
/// <summary>
/// Verifies that the BAML decompiler survives when the well-known WPF assemblies cannot be
/// resolved - the situation on any machine without WPF installed (Linux/macOS). Previously
/// <see cref="KnownThings"/> threw because it could not find PresentationFramework et al.;
/// now those assemblies are substituted by synthetic stand-ins.
/// </summary>
[TestFixture]
public class MissingReferencesTests
{
const string PresentationXmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";
/// <summary>
/// An assembly resolver that hides the WPF assemblies, reproducing the "no WPF available"
/// environment deterministically on every platform (on Windows the real assemblies would
/// otherwise be found in the runtime pack).
/// </summary>
sealed class WpfHidingResolver : IAssemblyResolver
{
static readonly HashSet<string> hidden = new(StringComparer.OrdinalIgnoreCase) {
"WindowsBase", "PresentationCore", "PresentationFramework", "PresentationUI", "System.Xaml"
};
readonly IAssemblyResolver inner;
public WpfHidingResolver(IAssemblyResolver inner) => this.inner = inner;
public MetadataFile Resolve(IAssemblyReference reference)
=> hidden.Contains(reference.Name) ? null : inner.Resolve(reference);
public MetadataFile ResolveModule(MetadataFile mainModule, string moduleName)
=> inner.ResolveModule(mainModule, moduleName);
public Task<MetadataFile> ResolveAsync(IAssemblyReference reference)
=> hidden.Contains(reference.Name) ? Task.FromResult<MetadataFile>(null) : inner.ResolveAsync(reference);
public Task<MetadataFile> ResolveModuleAsync(MetadataFile mainModule, string moduleName)
=> inner.ResolveModuleAsync(mainModule, moduleName);
}
static BamlDecompilerTypeSystem CreateTypeSystemWithoutWpf()
{
// This test assembly is a managed module that does not itself reference WPF; combined
// with the WPF-hiding resolver, none of the well-known WPF assemblies resolve.
var location = typeof(MissingReferencesTests).Assembly.Location;
// PrefetchEntireImage reads the whole image into memory, so the file stream is only
// needed while the PEFile is being constructed and can be closed right afterwards.
using var stream = new FileStream(location, FileMode.Open, FileAccess.Read);
var file = new PEFile(location, stream, streamOptions: PEStreamOptions.PrefetchEntireImage);
var resolver = new WpfHidingResolver(new UniversalAssemblyResolver(location, throwOnError: false,
file.DetectTargetFrameworkId(), file.DetectRuntimePack()));
return new BamlDecompilerTypeSystem(file, resolver);
}
[Test]
public void KnownThings_DoesNotThrow_WhenWpfAssembliesAreUnavailable()
{
var typeSystem = CreateTypeSystemWithoutWpf();
Assert.DoesNotThrow(() => new KnownThings(typeSystem));
}
[Test]
public void KnownType_ResolvesToSyntheticDefinition_WithCorrectIdentity()
{
var knownThings = new KnownThings(CreateTypeSystemWithoutWpf());
var button = knownThings.Types(KnownTypes.Button);
Assert.Multiple(() => {
Assert.That(button.Namespace, Is.EqualTo("System.Windows.Controls"));
Assert.That(button.Name, Is.EqualTo("Button"));
Assert.That(button.ParentModule.AssemblyName, Is.EqualTo("PresentationFramework"));
});
}
[Test]
public void SyntheticModule_IsBounded_ReturnsNullForUnseededType()
{
var knownThings = new KnownThings(CreateTypeSystemWithoutWpf());
var module = knownThings.Types(KnownTypes.Button).ParentModule;
// A type the decompiler never seeded must stay unresolved, so that references to
// non-well-known WPF types keep degrading to UnknownType exactly as before.
var unseeded = module.GetTypeDefinition(
new TopLevelTypeName("System.Windows.Controls", "ThisTypeDoesNotExist"));
Assert.That(unseeded, Is.Null);
}
[Test]
public void SyntheticModule_ExposesPresentationXmlnsDefinition()
{
var knownThings = new KnownThings(CreateTypeSystemWithoutWpf());
var module = knownThings.Types(KnownTypes.Button).ParentModule;
var controlsMapping = module.GetAssemblyAttributes()
.Where(a => a.AttributeType.FullName == "System.Windows.Markup.XmlnsDefinitionAttribute")
.FirstOrDefault(a => (string)a.FixedArguments[1].Value == "System.Windows.Controls");
Assert.That(controlsMapping, Is.Not.Null, "expected an XmlnsDefinition for System.Windows.Controls");
Assert.That((string)controlsMapping.FixedArguments[0].Value, Is.EqualTo(PresentationXmlns));
}
}
}

1218
ILSpy.BamlDecompiler.Tests/packages.lock.json

File diff suppressed because it is too large Load Diff

1
ILSpy.Desktop.slnf

@ -7,6 +7,7 @@
"ICSharpCode.Decompiler.Tests\\ICSharpCode.Decompiler.Tests.csproj", "ICSharpCode.Decompiler.Tests\\ICSharpCode.Decompiler.Tests.csproj",
"ICSharpCode.Decompiler\\ICSharpCode.Decompiler.csproj", "ICSharpCode.Decompiler\\ICSharpCode.Decompiler.csproj",
"ICSharpCode.ILSpyX\\ICSharpCode.ILSpyX.csproj", "ICSharpCode.ILSpyX\\ICSharpCode.ILSpyX.csproj",
"ILSpy.BamlDecompiler.Tests\\ILSpy.BamlDecompiler.Tests.csproj",
"ILSpy.ReadyToRun\\ILSpy.ReadyToRun.csproj", "ILSpy.ReadyToRun\\ILSpy.ReadyToRun.csproj",
"ILSpy.Tests.Windows\\ILSpy.Tests.Windows.csproj", "ILSpy.Tests.Windows\\ILSpy.Tests.Windows.csproj",
"ILSpy.Tests\\ILSpy.Tests.csproj", "ILSpy.Tests\\ILSpy.Tests.csproj",

3
ILSpy.XPlat.slnf

@ -7,7 +7,8 @@
"ICSharpCode.Decompiler.TestRunner\\ICSharpCode.Decompiler.TestRunner.csproj", "ICSharpCode.Decompiler.TestRunner\\ICSharpCode.Decompiler.TestRunner.csproj",
"ICSharpCode.Decompiler.Tests\\ICSharpCode.Decompiler.Tests.csproj", "ICSharpCode.Decompiler.Tests\\ICSharpCode.Decompiler.Tests.csproj",
"ICSharpCode.Decompiler\\ICSharpCode.Decompiler.csproj", "ICSharpCode.Decompiler\\ICSharpCode.Decompiler.csproj",
"ICSharpCode.ILSpyX\\ICSharpCode.ILSpyX.csproj" "ICSharpCode.ILSpyX\\ICSharpCode.ILSpyX.csproj",
"ILSpy.BamlDecompiler.Tests\\ILSpy.BamlDecompiler.Tests.csproj"
] ]
} }
} }

28
ILSpy.sln

@ -45,6 +45,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy.ReadyToRun", "ILSpy.R
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestPlugin", "TestPlugin\TestPlugin.csproj", "{62B21FB3-2A55-4F51-8892-9D6B9864BC22}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestPlugin", "TestPlugin\TestPlugin.csproj", "{62B21FB3-2A55-4F51-8892-9D6B9864BC22}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy.BamlDecompiler.Tests", "ILSpy.BamlDecompiler.Tests\ILSpy.BamlDecompiler.Tests.csproj", "{DE2934C2-3FA4-41AA-8A08-502957ED0FE4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy.BamlDecompiler.Tests.Windows", "ILSpy.BamlDecompiler.Tests.Windows\ILSpy.BamlDecompiler.Tests.Windows.csproj", "{7A5C3CEF-0744-4643-BA1E-7AE6610FF269}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -211,6 +215,30 @@ Global
{62B21FB3-2A55-4F51-8892-9D6B9864BC22}.Release|x64.Build.0 = Release|Any CPU {62B21FB3-2A55-4F51-8892-9D6B9864BC22}.Release|x64.Build.0 = Release|Any CPU
{62B21FB3-2A55-4F51-8892-9D6B9864BC22}.Release|x86.ActiveCfg = Release|Any CPU {62B21FB3-2A55-4F51-8892-9D6B9864BC22}.Release|x86.ActiveCfg = Release|Any CPU
{62B21FB3-2A55-4F51-8892-9D6B9864BC22}.Release|x86.Build.0 = Release|Any CPU {62B21FB3-2A55-4F51-8892-9D6B9864BC22}.Release|x86.Build.0 = Release|Any CPU
{DE2934C2-3FA4-41AA-8A08-502957ED0FE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DE2934C2-3FA4-41AA-8A08-502957ED0FE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DE2934C2-3FA4-41AA-8A08-502957ED0FE4}.Debug|x64.ActiveCfg = Debug|Any CPU
{DE2934C2-3FA4-41AA-8A08-502957ED0FE4}.Debug|x64.Build.0 = Debug|Any CPU
{DE2934C2-3FA4-41AA-8A08-502957ED0FE4}.Debug|x86.ActiveCfg = Debug|Any CPU
{DE2934C2-3FA4-41AA-8A08-502957ED0FE4}.Debug|x86.Build.0 = Debug|Any CPU
{DE2934C2-3FA4-41AA-8A08-502957ED0FE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DE2934C2-3FA4-41AA-8A08-502957ED0FE4}.Release|Any CPU.Build.0 = Release|Any CPU
{DE2934C2-3FA4-41AA-8A08-502957ED0FE4}.Release|x64.ActiveCfg = Release|Any CPU
{DE2934C2-3FA4-41AA-8A08-502957ED0FE4}.Release|x64.Build.0 = Release|Any CPU
{DE2934C2-3FA4-41AA-8A08-502957ED0FE4}.Release|x86.ActiveCfg = Release|Any CPU
{DE2934C2-3FA4-41AA-8A08-502957ED0FE4}.Release|x86.Build.0 = Release|Any CPU
{7A5C3CEF-0744-4643-BA1E-7AE6610FF269}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7A5C3CEF-0744-4643-BA1E-7AE6610FF269}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7A5C3CEF-0744-4643-BA1E-7AE6610FF269}.Debug|x64.ActiveCfg = Debug|Any CPU
{7A5C3CEF-0744-4643-BA1E-7AE6610FF269}.Debug|x64.Build.0 = Debug|Any CPU
{7A5C3CEF-0744-4643-BA1E-7AE6610FF269}.Debug|x86.ActiveCfg = Debug|Any CPU
{7A5C3CEF-0744-4643-BA1E-7AE6610FF269}.Debug|x86.Build.0 = Debug|Any CPU
{7A5C3CEF-0744-4643-BA1E-7AE6610FF269}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7A5C3CEF-0744-4643-BA1E-7AE6610FF269}.Release|Any CPU.Build.0 = Release|Any CPU
{7A5C3CEF-0744-4643-BA1E-7AE6610FF269}.Release|x64.ActiveCfg = Release|Any CPU
{7A5C3CEF-0744-4643-BA1E-7AE6610FF269}.Release|x64.Build.0 = Release|Any CPU
{7A5C3CEF-0744-4643-BA1E-7AE6610FF269}.Release|x86.ActiveCfg = Release|Any CPU
{7A5C3CEF-0744-4643-BA1E-7AE6610FF269}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

Loading…
Cancel
Save