MEDIABROWSER 개명·업데이트 채널 전환·아이콘 tofu 근본 수정 (BACKLOG 13차) — ①개명: installer.iss AppName/출력 MediaBrowser-Setup-*(AppId·exe·데이터 경로 유지로 업그레이드 호환), 워크플로 산출물명(mediabrowser-*.tar.gz, MediaBrowser-<ver>-arm64.apk), 창 타이틀·모바일 표시명·obtainium.json·README. ②UpdateChecker: apiBase 주입(기본=자체 Forgejo /api/v1, GitHub 는 apiBase 로 호환), osx 자산 tar.gz 허용(기존 dmg 만 → mac 영원히 자산 없음 결함 수정), AppConfig 기본 UpdateRepo=Video-Downloader/releases+UpdateApiBase — 테스트 4건. ③아이콘 tofu(사용자 보고 "절대 안고쳐지네") 근본 원인: Shared.xaml 암시적 TextBlock 스타일의 FontFamily=UiFont 강제가 버튼 ContentPresenter 자동 TextBlock 의 IconFont 상속을 덮어 모든 PUA 글리프가 notdef — 폰트 설치/글리프 존재/HW·SW 렌더/RTB/DPI/런타임 8·10/자체포함·공유 전부 배제 후 실창 픽셀 비교로 격리, 콘텐츠 TextBlock 의 실폰트가 Pretendard 체인임을 테스트로 실증. 수정: 암시 스타일 FontFamily 제거(창 루트 상속으로 통일, PairingWindow/PlayerWindow 는 SetResourceReference), PUA+텍스트 혼합 콘텐츠 7곳+코드 1곳 정리, 순수 글리프 2곳 IconFont 명시. 회귀 방어: IconGlyphXamlTests(정적 스캔)·IconContentFontTests(실창 폰트 단정)·IconGlyphRenderTests(픽셀 비교) — 462/462 GREEN, 수정 전후 실창 캡처로 글리프 렌더 확인

This commit is contained in:
Yun Chan 2026-08-18 07:13:18 +09:00
parent c8b061959b
commit 02995f4153
24 changed files with 451 additions and 64 deletions

View file

@ -0,0 +1,116 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using VideoDownloader.Tests.Infrastructure;
namespace VideoDownloader.Tests.E2E;
/// <summary>
/// 아이콘 tofu 실결함(사용자 보고: "아이콘은 절대 안고쳐지네") 재현 시도 —
/// 테마 사전을 앱과 같은 방식으로 로드했을 때 FluentIcon/IconButton 경유 글리프가
/// 명시적 Segoe Fluent Icons 렌더와 픽셀 단위로 같은지 검증한다.
/// 같지 않고 본문 폰트 렌더(tofu)와 같다면 리소스 해석이 깨진 것이다.
/// </summary>
[Collection("E2E")]
public class IconGlyphRenderTests
{
private static byte[] Render(FrameworkElement el)
{
el.Measure(new Size(48, 48));
el.Arrange(new Rect(0, 0, 48, 48));
el.UpdateLayout();
var rtb = new RenderTargetBitmap(48, 48, 96, 96, PixelFormats.Pbgra32);
rtb.Render(el);
var px = new byte[48 * 48 * 4];
rtb.CopyPixels(px, 48 * 4, 0);
return px;
}
private static int Lit(byte[] px)
{
var lit = 0;
for (var i = 3; i < px.Length; i += 4) if (px[i] > 60) lit++;
return lit;
}
[Fact]
public async Task FluentIcon_스타일_글리프는_명시적_아이콘폰트_렌더와_동일하다()
{
using var sta = StaDispatcher.Start();
WpfTestApp.Ensure(sta.Dispatcher);
var (family, litStyled, litExplicit, litBody, sameAsExplicit, sameAsBody) = await sta.Dispatcher.InvokeAsync(() =>
{
TextBlock Make() => new()
{
Text = "", FontSize = 14, Foreground = Brushes.White,
Background = Brushes.Black, Width = 48, Height = 48,
};
var styled = Make();
styled.Style = (Style)Application.Current.FindResource("FluentIcon");
var explicitIcon = Make();
explicitIcon.FontFamily = new FontFamily("Segoe Fluent Icons");
var body = Make();
body.FontFamily = (FontFamily)Application.Current.FindResource("UiFont");
var pStyled = Render(styled);
var pExplicit = Render(explicitIcon);
var pBody = Render(body);
return (styled.FontFamily.Source, Lit(pStyled), Lit(pExplicit), Lit(pBody),
pStyled.SequenceEqual(pExplicit), pStyled.SequenceEqual(pBody));
});
Assert.True(litExplicit > 0, "기준(명시적 Segoe Fluent Icons) 렌더 자체가 비어 있음 — 폰트 미설치?");
Assert.True(sameAsExplicit && !sameAsBody,
$"FluentIcon 경유 글리프가 아이콘 폰트로 렌더되지 않음 — 해석된 폰트: '{family}', " +
$"픽셀(스타일/명시/본문): {litStyled}/{litExplicit}/{litBody}, 본문과 동일: {sameAsBody}");
}
[Fact]
public async Task 앱_시작_시퀀스를_그대로_태워도_FluentIcon_글리프가_렌더된다()
{
// 실제 App.OnStartup 과 동일한 순서: ApplyTheme(절대 pack URI 재로드) → 강조색 → 폰트 → 모션
var dataDir = Path.Combine(Path.GetTempPath(), $"vd_icon_{Guid.NewGuid():N}");
Environment.SetEnvironmentVariable("VD_DATA_DIR", dataDir);
try
{
using var sta = StaDispatcher.Start();
WpfTestApp.Ensure(sta.Dispatcher);
var (family, same, litStyled, litExplicit) = await sta.Dispatcher.InvokeAsync(() =>
{
VideoDownloader.App.App.ApplyTheme("dark");
VideoDownloader.App.App.ApplyAccent(null);
VideoDownloader.App.App.ApplyFontSettings();
VideoDownloader.App.App.ApplyReduceMotion(false);
var styled = new TextBlock
{
Text = "\uE76B", FontSize = 14, Foreground = Brushes.White,
Background = Brushes.Black, Width = 48, Height = 48,
Style = (Style)Application.Current.FindResource("FluentIcon"),
};
var explicitIcon = new TextBlock
{
Text = "\uE76B", FontSize = 14, Foreground = Brushes.White,
Background = Brushes.Black, Width = 48, Height = 48,
FontFamily = new FontFamily("Segoe Fluent Icons"),
};
var pStyled = Render(styled);
var pExplicit = Render(explicitIcon);
return (styled.FontFamily.Source, pStyled.SequenceEqual(pExplicit), Lit(pStyled), Lit(pExplicit));
});
Assert.True(same,
$"앱 시작 시퀀스 후 FluentIcon 글리프 깨짐 — 해석된 폰트: '{family}', 픽셀(스타일/명시): {litStyled}/{litExplicit}");
}
finally
{
Environment.SetEnvironmentVariable("VD_DATA_DIR", null);
try { Directory.Delete(dataDir, true); } catch { }
}
}
}