From e8f34d205ff24209ddbfb2f9602582553fd7793e Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 17 Apr 2026 08:04:08 +0200 Subject: [PATCH 1/6] Make sure TryGetGlyphTypeface does not fail for concurrent access --- src/Avalonia.Base/Media/FontManager.cs | 21 +++---- .../Media/FontManagerTests.cs | 59 +++++++++++++++++++ 2 files changed, 67 insertions(+), 13 deletions(-) diff --git a/src/Avalonia.Base/Media/FontManager.cs b/src/Avalonia.Base/Media/FontManager.cs index 1f15820b9a6..cd753133377 100644 --- a/src/Avalonia.Base/Media/FontManager.cs +++ b/src/Avalonia.Base/Media/FontManager.cs @@ -360,25 +360,20 @@ private bool TryGetFontCollection(Uri source, [NotNullWhen(true)] out IFontColle source = SystemFontsKey; } - if (!_fontCollections.TryGetValue(source, out fontCollection)) + fontCollection = _fontCollections.GetOrAdd(source, static (key, platformImpl) => { - if (source == SystemFontsKey) + if (key == SystemFontsKey) { - fontCollection = new SystemFontCollection(PlatformImpl); - } - else - { - if (source.IsAbsoluteResm() || source.IsAvares()) - { - fontCollection = new EmbeddedFontCollection(source, source); - } + return new SystemFontCollection(platformImpl); } - if (fontCollection != null) + if (key.IsAbsoluteResm() || key.IsAvares()) { - return _fontCollections.TryAdd(fontCollection.Key, fontCollection); + return new EmbeddedFontCollection(key, key); } - } + + return null!; + }, PlatformImpl); return fontCollection != null; } diff --git a/tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs b/tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs index ed95773630f..ab49e0a30d0 100644 --- a/tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs @@ -1,5 +1,10 @@ using System; +using System.Collections.Concurrent; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; using Avalonia.Media; +using Avalonia.Media.Fonts; using Avalonia.UnitTests; using Xunit; @@ -86,5 +91,59 @@ public void Should_Return_First_Installed_Font_Family_Name_When_Default_Family_N Assert.Equal("DejaVu", FontManager.Current.DefaultFontFamily.Name); } } + + [Fact] + public async Task TryGetGlyphTypeface_Should_Be_Thread_Safe_For_Embedded_Fonts() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var fontManager = FontManager.Current; + var fontCollections = (ConcurrentDictionary) + typeof(FontManager) + .GetField("_fontCollections", BindingFlags.NonPublic | BindingFlags.Instance)! + .GetValue(fontManager)!; + + const string fontUri = + "resm:Avalonia.Base.UnitTests.Assets?assembly=Avalonia.Base.UnitTests#Noto Mono"; + var collectionKey = + new Uri("resm:Avalonia.Base.UnitTests.Assets?assembly=Avalonia.Base.UnitTests"); + + // Warm up to validate the font URI is correct. + Assert.True(fontManager.TryGetGlyphTypeface(new Typeface(new FontFamily(fontUri)), out _)); + + const int iterations = 50; + int failures = 0; + + for (int i = 0; i < iterations; i++) + { + // Remove the cached collection so both threads must re-create it. + fontCollections.TryRemove(collectionKey, out _); + + using var barrier = new Barrier(2); + bool r1 = false, r2 = false; + + var t1 = Task.Run(() => + { + barrier.SignalAndWait(); + r1 = fontManager.TryGetGlyphTypeface( + new Typeface(new FontFamily(fontUri)), out _); + }); + + var t2 = Task.Run(() => + { + barrier.SignalAndWait(); + r2 = fontManager.TryGetGlyphTypeface( + new Typeface(new FontFamily(fontUri)), out _); + }); + + await Task.WhenAll(t1, t2); + + if (!r1 || !r2) + Interlocked.Increment(ref failures); + } + + Assert.Equal(0, failures); + } + } } } From 8b21872430676ba7e69e6b861f4228ca5a86850a Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Mon, 27 Apr 2026 11:15:39 +0200 Subject: [PATCH 2/6] Update tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs b/tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs index ab49e0a30d0..4d46feda512 100644 --- a/tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs @@ -117,7 +117,8 @@ public async Task TryGetGlyphTypeface_Should_Be_Thread_Safe_For_Embedded_Fonts() for (int i = 0; i < iterations; i++) { // Remove the cached collection so both threads must re-create it. - fontCollections.TryRemove(collectionKey, out _); + if (fontCollections.TryRemove(collectionKey, out var removedCollection)) + removedCollection.Dispose(); using var barrier = new Barrier(2); bool r1 = false, r2 = false; From a70c6ea8aab54a1cdf83134564ca2113a70ac7e1 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 12 May 2026 12:25:01 +0200 Subject: [PATCH 3/6] Update gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 23b8efffa05..6f6576ea536 100644 --- a/.gitignore +++ b/.gitignore @@ -220,3 +220,6 @@ src/Browser/Avalonia.Browser/wwwroot api/diff src/Browser/Avalonia.Browser/staticwebassets .serena + +# Claude agent worktrees +.claude/worktrees/ From ab6b104ac08d01169c19493b67a1e269f4924806 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 12 May 2026 13:17:18 +0200 Subject: [PATCH 4/6] Make TryGetFontCollection more robust and add unit tests --- src/Avalonia.Base/Media/FontManager.cs | 35 +-- .../Media/FontManagerTests.cs | 22 +- .../FontManagerTryGetFontCollectionTests.cs | 263 ++++++++++++++++++ 3 files changed, 290 insertions(+), 30 deletions(-) create mode 100644 tests/Avalonia.Base.UnitTests/Media/FontManagerTryGetFontCollectionTests.cs diff --git a/src/Avalonia.Base/Media/FontManager.cs b/src/Avalonia.Base/Media/FontManager.cs index cd753133377..f7d425e844b 100644 --- a/src/Avalonia.Base/Media/FontManager.cs +++ b/src/Avalonia.Base/Media/FontManager.cs @@ -351,31 +351,34 @@ internal IReadOnlyList GetFamilyTypefaces(FontFamily fontFamily) return []; } - private bool TryGetFontCollection(Uri source, [NotNullWhen(true)] out IFontCollection? fontCollection) + internal bool TryGetFontCollection(Uri source, [NotNullWhen(true)] out IFontCollection? fontCollection) { Debug.Assert(source.IsAbsoluteUri); - if (source.Scheme == SystemFontScheme) + // Both the systemfont: scheme and SystemFontsKey (fonts:SystemFonts) map to the system + // font collection. SystemFontsKey is checked before the generic IsFontCollection branch + // so that the SystemFontCollection is created on demand regardless of which URI form is used. + if (source.Scheme == SystemFontScheme || source == SystemFontsKey) { - source = SystemFontsKey; + fontCollection = _fontCollections.GetOrAdd(SystemFontsKey, static (_, platformImpl) => new SystemFontCollection(platformImpl), PlatformImpl); + return true; } - fontCollection = _fontCollections.GetOrAdd(source, static (key, platformImpl) => + // Other fonts: URIs are only returned when they have been explicitly registered + // via AddFontCollection — no implicit creation to avoid caching null for unknown keys. + if (source.IsFontCollection()) { - if (key == SystemFontsKey) - { - return new SystemFontCollection(platformImpl); - } - - if (key.IsAbsoluteResm() || key.IsAvares()) - { - return new EmbeddedFontCollection(key, key); - } + return _fontCollections.TryGetValue(source, out fontCollection); + } - return null!; - }, PlatformImpl); + if (source.IsAbsoluteResm() || source.IsAvares()) + { + fontCollection = _fontCollections.GetOrAdd(source, static (key, _) => new EmbeddedFontCollection(key, key), PlatformImpl); + return true; + } - return fontCollection != null; + fontCollection = null; + return false; } private string GetDefaultFontFamilyName(FontManagerOptions? options) diff --git a/tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs b/tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs index ab49e0a30d0..142fbb827b9 100644 --- a/tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs @@ -4,7 +4,6 @@ using System.Threading; using System.Threading.Tasks; using Avalonia.Media; -using Avalonia.Media.Fonts; using Avalonia.UnitTests; using Xunit; @@ -98,11 +97,7 @@ public async Task TryGetGlyphTypeface_Should_Be_Thread_Safe_For_Embedded_Fonts() using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) { var fontManager = FontManager.Current; - var fontCollections = (ConcurrentDictionary) - typeof(FontManager) - .GetField("_fontCollections", BindingFlags.NonPublic | BindingFlags.Instance)! - .GetValue(fontManager)!; - + const string fontUri = "resm:Avalonia.Base.UnitTests.Assets?assembly=Avalonia.Base.UnitTests#Noto Mono"; var collectionKey = @@ -116,8 +111,7 @@ public async Task TryGetGlyphTypeface_Should_Be_Thread_Safe_For_Embedded_Fonts() for (int i = 0; i < iterations; i++) { - // Remove the cached collection so both threads must re-create it. - fontCollections.TryRemove(collectionKey, out _); + fontManager.RemoveFontCollection(collectionKey); using var barrier = new Barrier(2); bool r1 = false, r2 = false; @@ -125,21 +119,21 @@ public async Task TryGetGlyphTypeface_Should_Be_Thread_Safe_For_Embedded_Fonts() var t1 = Task.Run(() => { barrier.SignalAndWait(); - r1 = fontManager.TryGetGlyphTypeface( - new Typeface(new FontFamily(fontUri)), out _); - }); + r1 = fontManager.TryGetGlyphTypeface(new Typeface(new FontFamily(fontUri)), out _); + }, TestContext.Current.CancellationToken); var t2 = Task.Run(() => { barrier.SignalAndWait(); - r2 = fontManager.TryGetGlyphTypeface( - new Typeface(new FontFamily(fontUri)), out _); - }); + r2 = fontManager.TryGetGlyphTypeface(new Typeface(new FontFamily(fontUri)), out _); + }, TestContext.Current.CancellationToken); await Task.WhenAll(t1, t2); if (!r1 || !r2) + { Interlocked.Increment(ref failures); + } } Assert.Equal(0, failures); diff --git a/tests/Avalonia.Base.UnitTests/Media/FontManagerTryGetFontCollectionTests.cs b/tests/Avalonia.Base.UnitTests/Media/FontManagerTryGetFontCollectionTests.cs new file mode 100644 index 00000000000..329a4c72ded --- /dev/null +++ b/tests/Avalonia.Base.UnitTests/Media/FontManagerTryGetFontCollectionTests.cs @@ -0,0 +1,263 @@ +using System; +using Avalonia.Media; +using Avalonia.Media.Fonts; +using Avalonia.UnitTests; +using Xunit; + +namespace Avalonia.Base.UnitTests.Media +{ + public class FontManagerTryGetFontCollectionTests + { + [Fact] + public void TryGetFontCollection_SystemFontScheme_ReturnsTrue() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var source = new Uri($"{FontManager.SystemFontScheme}:Arial", UriKind.Absolute); + + Assert.True(FontManager.Current.TryGetFontCollection(source, out var collection)); + Assert.NotNull(collection); + } + } + + [Fact] + public void TryGetFontCollection_SystemFontScheme_YieldsSystemFontCollection() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var source = new Uri($"{FontManager.SystemFontScheme}:Arial", UriKind.Absolute); + + FontManager.Current.TryGetFontCollection(source, out var collection); + + Assert.IsType(collection); + } + } + + [Fact] + public void TryGetFontCollection_SystemFontScheme_ReturnsSameInstanceOnSubsequentCalls() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var source = new Uri($"{FontManager.SystemFontScheme}:Arial", UriKind.Absolute); + var fm = FontManager.Current; + + fm.TryGetFontCollection(source, out var first); + fm.TryGetFontCollection(source, out var second); + + Assert.Same(first, second); + } + } + + [Fact] + public void TryGetFontCollection_SystemFontsKey_ReturnsTrue() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + Assert.True(FontManager.Current.TryGetFontCollection(FontManager.SystemFontsKey, out var collection)); + Assert.NotNull(collection); + } + } + + [Fact] + public void TryGetFontCollection_SystemFontsKey_YieldsSystemFontCollection() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + FontManager.Current.TryGetFontCollection(FontManager.SystemFontsKey, out var collection); + + Assert.IsType(collection); + } + } + + [Fact] + public void TryGetFontCollection_SystemFontSchemeAndSystemFontsKey_ReturnSameInstance() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var fm = FontManager.Current; + var schemeSource = new Uri($"{FontManager.SystemFontScheme}:Arial", UriKind.Absolute); + + fm.TryGetFontCollection(schemeSource, out var fromScheme); + fm.TryGetFontCollection(FontManager.SystemFontsKey, out var fromKey); + + Assert.Same(fromScheme, fromKey); + } + } + + [Fact] + public void TryGetFontCollection_RegisteredFontsCollection_ReturnsTrue() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var key = new Uri("fonts:MyTest", UriKind.Absolute); + var stub = new StubFontCollection(key); + var fm = FontManager.Current; + fm.AddFontCollection(stub); + + Assert.True(fm.TryGetFontCollection(key, out var collection)); + Assert.Same(stub, collection); + } + } + + [Fact] + public void TryGetFontCollection_UnregisteredFontsCollection_ReturnsFalse() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var key = new Uri("fonts:DoesNotExist", UriKind.Absolute); + + Assert.False(FontManager.Current.TryGetFontCollection(key, out var collection)); + Assert.Null(collection); + } + } + + [Fact] + public void TryGetFontCollection_UnregisteredFontsCollection_DoesNotCacheNull() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var key = new Uri("fonts:DoesNotExist2", UriKind.Absolute); + var fm = FontManager.Current; + + // First call returns false + Assert.False(fm.TryGetFontCollection(key, out _)); + + // Register after the first failed lookup + var stub = new StubFontCollection(key); + fm.AddFontCollection(stub); + + // Now it should be found if null had been cached this would still fail + Assert.True(fm.TryGetFontCollection(key, out var collection)); + Assert.Same(stub, collection); + } + } + + [Fact] + public void TryGetFontCollection_AbsoluteResm_ReturnsTrue() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var source = new Uri("resm:Avalonia.Base.UnitTests.Assets?assembly=Avalonia.Base.UnitTests", UriKind.Absolute); + + Assert.True(FontManager.Current.TryGetFontCollection(source, out var collection)); + Assert.NotNull(collection); + } + } + + [Fact] + public void TryGetFontCollection_AbsoluteResm_YieldsEmbeddedFontCollection() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var source = new Uri("resm:Avalonia.Base.UnitTests.Assets?assembly=Avalonia.Base.UnitTests", UriKind.Absolute); + + FontManager.Current.TryGetFontCollection(source, out var collection); + + Assert.IsType(collection); + } + } + + [Fact] + public void TryGetFontCollection_AbsoluteResm_ReturnsSameInstanceOnSubsequentCalls() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var source = new Uri("resm:Avalonia.Base.UnitTests.Assets?assembly=Avalonia.Base.UnitTests", UriKind.Absolute); + var fm = FontManager.Current; + + fm.TryGetFontCollection(source, out var first); + fm.TryGetFontCollection(source, out var second); + + Assert.Same(first, second); + } + } + + [Fact] + public void TryGetFontCollection_Avares_ReturnsTrue() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var source = new Uri("avares://Avalonia.Base.UnitTests/Assets", UriKind.Absolute); + + Assert.True(FontManager.Current.TryGetFontCollection(source, out var collection)); + Assert.NotNull(collection); + } + } + + [Fact] + public void TryGetFontCollection_Avares_YieldsEmbeddedFontCollection() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var source = new Uri("avares://Avalonia.Base.UnitTests/Assets", UriKind.Absolute); + + FontManager.Current.TryGetFontCollection(source, out var collection); + + Assert.IsType(collection); + } + } + + [Fact] + public void TryGetFontCollection_Avares_ReturnsSameInstanceOnSubsequentCalls() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var source = new Uri("avares://Avalonia.Base.UnitTests/Assets", UriKind.Absolute); + var fm = FontManager.Current; + + fm.TryGetFontCollection(source, out var first); + fm.TryGetFontCollection(source, out var second); + + Assert.Same(first, second); + } + } + + [Fact] + public void TryGetFontCollection_UnknownScheme_ReturnsFalse() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var source = new Uri("https://example.com/fonts", UriKind.Absolute); + + Assert.False(FontManager.Current.TryGetFontCollection(source, out var collection)); + Assert.Null(collection); + } + } + + [Fact] + public void TryGetFontCollection_UnknownScheme_DoesNotCacheNull() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + // If null were stored, a second call with a different unknown URI sharing + // the same key would incorrectly find a null entry. Simply verify that + // repeated lookups with an unknown scheme consistently return false/null. + var source = new Uri("file:///some/path/fonts", UriKind.Absolute); + var fm = FontManager.Current; + + Assert.False(fm.TryGetFontCollection(source, out var first)); + Assert.Null(first); + + Assert.False(fm.TryGetFontCollection(source, out var second)); + Assert.Null(second); + } + } + + private sealed class StubFontCollection : IFontCollection + { + public StubFontCollection(Uri key) => Key = key; + + public Uri Key { get; } + public int Count => 0; + public FontFamily this[int index] => throw new NotSupportedException(); + public bool TryGetGlyphTypeface(string familyName, FontStyle style, FontWeight weight, FontStretch stretch, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out GlyphTypeface? glyphTypeface) { glyphTypeface = null; return false; } + public bool TryMatchCharacter(int codepoint, FontStyle style, FontWeight weight, FontStretch stretch, string? familyName, System.Globalization.CultureInfo? culture, out Typeface typeface) { typeface = default; return false; } + public bool TryGetFamilyTypefaces(string familyName, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IReadOnlyList? familyTypefaces) { familyTypefaces = null; return false; } + public bool TryCreateSyntheticGlyphTypeface(GlyphTypeface glyphTypeface, FontStyle style, FontWeight weight, FontStretch stretch, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out GlyphTypeface? syntheticGlyphTypeface) { syntheticGlyphTypeface = null; return false; } + public bool TryGetNearestMatch(string familyName, FontStyle style, FontWeight weight, FontStretch stretch, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out GlyphTypeface? glyphTypeface) { glyphTypeface = null; return false; } + public System.Collections.Generic.IEnumerator GetEnumerator() => System.Linq.Enumerable.Empty().GetEnumerator(); + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); + public void Dispose() { } + } + } +} From 67ceb0fdeb2e1315c5dfb1f25315edb1aad61fb3 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 12 May 2026 14:26:04 +0200 Subject: [PATCH 5/6] Introdcue GetOrCreateFontCollection helper for safe disposal of losing instances --- src/Avalonia.Base/Media/FontManager.cs | 28 +++++++++++++++++-- .../Media/FontManagerTests.cs | 2 -- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Base/Media/FontManager.cs b/src/Avalonia.Base/Media/FontManager.cs index f7d425e844b..60200dea851 100644 --- a/src/Avalonia.Base/Media/FontManager.cs +++ b/src/Avalonia.Base/Media/FontManager.cs @@ -360,7 +360,8 @@ internal bool TryGetFontCollection(Uri source, [NotNullWhen(true)] out IFontColl // so that the SystemFontCollection is created on demand regardless of which URI form is used. if (source.Scheme == SystemFontScheme || source == SystemFontsKey) { - fontCollection = _fontCollections.GetOrAdd(SystemFontsKey, static (_, platformImpl) => new SystemFontCollection(platformImpl), PlatformImpl); + fontCollection = GetOrCreateFontCollection(SystemFontsKey, PlatformImpl, + static (_, impl) => new SystemFontCollection(impl)); return true; } @@ -373,7 +374,8 @@ internal bool TryGetFontCollection(Uri source, [NotNullWhen(true)] out IFontColl if (source.IsAbsoluteResm() || source.IsAvares()) { - fontCollection = _fontCollections.GetOrAdd(source, static (key, _) => new EmbeddedFontCollection(key, key), PlatformImpl); + fontCollection = GetOrCreateFontCollection(source, 0, + static (key, _) => new EmbeddedFontCollection(key, key)); return true; } @@ -381,6 +383,28 @@ internal bool TryGetFontCollection(Uri source, [NotNullWhen(true)] out IFontColl return false; } + /// + /// Thread-safe get-or-create that disposes any candidate that loses the insertion race, + /// preventing resource leaks that + /// can cause when the factory is invoked concurrently by multiple threads. + /// + private IFontCollection GetOrCreateFontCollection(Uri key, TState state, Func factory) + { + if (_fontCollections.TryGetValue(key, out var existing)) + return existing; + + var candidate = factory(key, state); + + // GetOrAdd(key, value) atomically inserts or returns the existing value; + // it never invokes a factory, so only one IFontCollection instance survives. + var winner = _fontCollections.GetOrAdd(key, candidate); + + if (!ReferenceEquals(winner, candidate)) + candidate.Dispose(); // Our candidate lost the race – dispose it to avoid the leak. + + return winner; + } + private string GetDefaultFontFamilyName(FontManagerOptions? options) { var defaultFontFamilyName = options?.DefaultFamilyName diff --git a/tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs b/tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs index 142fbb827b9..d63f238dc16 100644 --- a/tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Concurrent; -using System.Reflection; using System.Threading; using System.Threading.Tasks; using Avalonia.Media; From 3652f1f1e0d9256469bebbb43e7838191ad85777 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 12 May 2026 14:43:08 +0200 Subject: [PATCH 6/6] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../Media/FontManagerTryGetFontCollectionTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Avalonia.Base.UnitTests/Media/FontManagerTryGetFontCollectionTests.cs b/tests/Avalonia.Base.UnitTests/Media/FontManagerTryGetFontCollectionTests.cs index 329a4c72ded..2f85f0b63fc 100644 --- a/tests/Avalonia.Base.UnitTests/Media/FontManagerTryGetFontCollectionTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/FontManagerTryGetFontCollectionTests.cs @@ -229,9 +229,9 @@ public void TryGetFontCollection_UnknownScheme_DoesNotCacheNull() { using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) { - // If null were stored, a second call with a different unknown URI sharing - // the same key would incorrectly find a null entry. Simply verify that - // repeated lookups with an unknown scheme consistently return false/null. + // Verify that repeated lookups for the same unknown-scheme URI + // consistently return false/null rather than succeeding due to an + // accidentally cached null or invalid entry. var source = new Uri("file:///some/path/fonts", UriKind.Absolute); var fm = FontManager.Current;