From 568586c541376c15fe6a1decf5a14f90a095c9d0 Mon Sep 17 00:00:00 2001 From: gramps Date: Fri, 30 Oct 2026 10:29:22 -0700 Subject: [PATCH] =?UTF-8?q?fix(xaml):=20the=20app=20refused=20to=20launch?= =?UTF-8?q?=20=E2=80=94=20RecordPillToggle=20was=20defined=20before=20Segm?= =?UTF-8?q?entToggle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `BasedOn="{StaticResource SegmentToggle}"` resolves while the dictionary is being built, so a forward reference fails at load with "Cannot find resource named 'SegmentToggle'". The style now sits after SegmentToggle and SegmentLabel, where its base already exists. Guard: ThemeResourceTests gains the ordering rule — within one dictionary, no StaticResource may point at a key defined later in the same file. The existence check alone passed this, which is exactly why the app died on launch while the tests were green. Root cause of the unreliable test results this session: a stale `testhost` held ytLive.dll locked, so builds silently kept the previous assembly (MSB3027 on copy). Killed it; the suite now rebuilds and the two new facts actually run. --- Themes/Controls.xaml | 51 +++++++++++++++--------------- ytLive.Tests/ThemeResourceTests.cs | 30 ++++++++++++++++++ 2 files changed, 56 insertions(+), 25 deletions(-) diff --git a/Themes/Controls.xaml b/Themes/Controls.xaml index 211e84c..75dc73f 100644 --- a/Themes/Controls.xaml +++ b/Themes/Controls.xaml @@ -654,31 +654,6 @@ - - - + + + + diff --git a/ytLive.Tests/ThemeResourceTests.cs b/ytLive.Tests/ThemeResourceTests.cs index dbd5bde..3ef9af1 100644 --- a/ytLive.Tests/ThemeResourceTests.cs +++ b/ytLive.Tests/ThemeResourceTests.cs @@ -46,6 +46,36 @@ public class ThemeResourceTests + string.Join("\n ", missing.Distinct())); } + [Fact] + public void A_dictionary_never_references_a_key_defined_later_in_itself() + { + // StaticResource resolves while the dictionary is being built, so a forward reference + // fails at load with "Cannot find resource named …" — exactly how the app refused to + // launch on 2026-10-30 (a BasedOn pointing at a style defined further down the file). + var root = System.IO.Path.GetDirectoryName(SourceAssert.RepoPath("App.xaml"))!; + var files = new[] { System.IO.Path.Combine(root, "App.xaml") } + .Concat(Directory.GetFiles(System.IO.Path.Combine(root, "Themes"), "*.xaml")); + + var forward = new List(); + foreach (var file in files) + { + var text = File.ReadAllText(file); + var definitions = Regex.Matches(text, @"x:Key=""([^""]+)""") + .ToDictionary(m => m.Groups[1].Value, m => m.Index, StringComparer.Ordinal); + + foreach (Match use in Regex.Matches(text, @"\{StaticResource ([^}]+)\}")) + { + var key = use.Groups[1].Value.Trim(); + if (!definitions.TryGetValue(key, out var defined)) continue; + if (defined > use.Index) + forward.Add($"{System.IO.Path.GetFileName(file)}: {key} used before it is defined"); + } + } + + Assert.True(forward.Count == 0, + "Forward StaticResource references fail at load:\n " + string.Join("\n ", forward.Distinct())); + } + private static HashSet KeysIn(string text) => Regex.Matches(text, @"x:Key=""([^""]+)""").Select(m => m.Groups[1].Value) .ToHashSet(StringComparer.Ordinal);