releases/tests/Paca.Tests/Mobile/MobileProductionExcellenceGateTests.cs
Yun Chan f1949024fc feat(mobile): advance Paca.Mobile to production excellence with rich design assets and core systems
- Generate high-resolution glassmorphism app icon and splash screen assets
- Implement MobileIconSystem with semantic SF Symbols/Material glyph mappings
- Add OLEDBlack theme and contrast tokens to MobileThemeSystem
- Implement VideoSnifferParser.NegotiateBestQuality for multi-stream quality resolution
- Enhance DiscoverPage, FavoritesPage, and SyncSettingsPage with production controls
- Add Gate 103-114 test suites with 100% test pass rate
2026-09-03 22:13:54 +09:00

199 lines
7.9 KiB
C#

using Paca.Mobile;
using System;
using System.Collections.Generic;
using System.Linq;
using Paca.Mobile.Core;
using Xunit;
namespace Paca.Tests.Mobile;
/// <summary>
/// 모바일 프로덕션 레벨 종합 고도화 게이트 테스트 (Gate 103 ~ Gate 110)
/// - 아이콘 시스템, 테마 시스템, 탭 시스템, 즐겨찾기, 영상 찾기, 재시도 큐 관리, 설정, 탭 동기화
/// </summary>
public class MobileProductionExcellenceGateTests
{
// [Gate 103] 아이콘 시스템: 이모지 배제 및 시맨틱 아이콘 글리프 매핑 무결성
[Theory]
[InlineData(MobileIconKind.Search, "magnifyingglass")]
[InlineData(MobileIconKind.Queue, "list.bullet")]
[InlineData(MobileIconKind.Download, "arrow.down.circle")]
[InlineData(MobileIconKind.Favorites, "star.fill")]
[InlineData(MobileIconKind.Settings, "gearshape.fill")]
[InlineData(MobileIconKind.Retry, "arrow.clockwise")]
public void Gate103_MobileIconSystem_SemanticGlyph_Audit(MobileIconKind kind, string expectedGlyph)
{
var glyph = MobileIconSystem.GetGlyph(kind);
Assert.Equal(expectedGlyph, glyph);
// 유니코드 이모지가 포함되지 않았는지 검증
Assert.DoesNotMatch(@"[\uD83C-\uDBFF\uDC00-\uDFFF]", glyph);
}
// [Gate 104] 테마 시스템: OLED Black을 포함한 4대 테마 토큰 및 명도 대비
[Theory]
[InlineData(MobileThemeMode.Dark, "#0B0C10", "#161822", "#FFFFFF", "#5E56E8")]
[InlineData(MobileThemeMode.Light, "#F8FAFC", "#FFFFFF", "#0F172A", "#4F46E5")]
[InlineData(MobileThemeMode.OLEDBlack, "#000000", "#090A0F", "#FFFFFF", "#00F0FF")]
[InlineData(MobileThemeMode.HighContrast, "#000000", "#000000", "#FFFFFF", "#FFFF00")]
public void Gate104_MobileThemeSystem_Tokens_And_Contrast_Audit(
MobileThemeMode mode, string bg, string card, string text, string accent)
{
var p = MobileThemeSystem.GetPalette(mode);
Assert.Equal(bg, p.Background);
Assert.Equal(card, p.CardBackground);
Assert.Equal(text, p.TextPrimary);
Assert.Equal(accent, p.Accent);
}
// [Gate 105] 탭 시스템: 탭 추가, 전환, 닫기, 시크릿 탭 모드 라이프사이클
[Fact]
public void Gate105_MobileTabManager_MultiTab_Lifecycle_Audit()
{
var tm = new MobileTabManager();
var tab1 = tm.NewTab("https://www.youtube.com", "YouTube");
var tab2 = tm.NewTab("https://vimeo.com", "Vimeo", isIncognito: true);
Assert.Equal(3, tm.Tabs.Count);
Assert.Equal(tab2.Id, tm.ActiveTab?.Id);
Assert.True(tab2.IsIncognito);
// 탭 1로 전환
tm.SwitchTab(tab1.Id);
Assert.Equal(tab1.Id, tm.ActiveTab?.Id);
// 탭 닫기
tm.CloseTab(tab2.Id);
Assert.Equal(2, tm.Tabs.Count);
Assert.Equal(tab1.Id, tm.ActiveTab?.Id);
}
// [Gate 106] 즐겨찾기 & 보관함(Stash): 태그 필터링 및 다운로드 대기 큐 연계
[Fact]
public void Gate106_MobileFavoritesManager_TagFilter_And_Stash_Audit()
{
var fav = new MobileFavoritesManager();
fav.AddFavorite("https://youtube.com/v1", "Rock Festival Live", "음악");
fav.AddFavorite("https://youtube.com/v2", "Dev Conference 2026", "기술");
var f3 = fav.AddFavorite("https://youtube.com/v3", "Jazz Night", "음악");
fav.ToggleStash(f3.Id);
var musicList = fav.GetByTag("음악");
Assert.Equal(2, musicList.Count);
var stashed = fav.GetStashedItems();
Assert.Single(stashed);
Assert.Equal(f3.Id, stashed[0].Id);
}
// [Gate 107] 영상 찾기(스니퍼 탐색): 플랫폼 감지 및 메타데이터 추출
[Fact]
public void Gate107_MobileDiscoveryEngine_PlatformDetection_And_FormatFilter_Audit()
{
var res1 = MobileDiscoveryEngine.DetectPlatform("https://youtu.be/abcdefg");
Assert.True(res1.IsSupported);
Assert.Equal("YouTube", res1.PlatformName);
Assert.Contains("1080p", res1.RecommendedQualities);
var res2 = MobileDiscoveryEngine.DetectPlatform("https://example.com/live.m3u8");
Assert.True(res2.IsSupported);
Assert.Equal("HLS Stream", res2.PlatformName);
}
// [Gate 108] 영상 재시도 및 큐 관리: 우선순위 핀, 실패 재시도, 일괄 비우기
[Fact]
public void Gate108_MobileQueueManager_Priorities_And_FailureRecovery_Audit()
{
var qm = new MobileQueueManager();
var i1 = qm.Enqueue("https://v1.mp4", "Video 1");
var i2 = qm.Enqueue("https://v2.mp4", "Video 2");
var i3 = qm.Enqueue("https://v3.mp4", "Video 3");
i2.Status = MobileQueueStatus.Failed;
i2.ErrorMessage = "Network Timeout";
// 실패 항목만 재시도
var count = qm.RetryFailedItems();
Assert.Equal(1, count);
Assert.Equal(MobileQueueStatus.Queued, i2.Status);
// 중요 항목 고정
qm.Pin(i3.Id);
Assert.Equal(i3.Id, qm.Items[0].Id);
// 완료된 항목 일괄 정리
i1.Status = MobileQueueStatus.Completed;
qm.ClearCompleted();
Assert.DoesNotContain(qm.Items, x => x.Id == i1.Id);
}
// [Gate 109] 설정 관리자: 영속성 및 기본값
[Fact]
public void Gate109_MobileSettingsManager_Defaults_And_Persistence_Audit()
{
var settings = new MobileSettingsManager();
Assert.Equal(MobileThemeMode.Dark, settings.ThemeMode);
Assert.False(settings.WifiOnlyDownload);
Assert.Equal(0, settings.SpeedLimitKbps); // 0 = 무제한
settings.SpeedLimitKbps = 2048;
settings.ThemeMode = MobileThemeMode.OLEDBlack;
Assert.Equal(2048, settings.SpeedLimitKbps);
Assert.Equal(MobileThemeMode.OLEDBlack, settings.ThemeMode);
}
// [Gate 110] 사용자 탭 동기화: PC 열린 탭 원격 수신 및 동기화 요청
[Fact]
public void Gate110_MobileTabSyncEngine_RemoteTabMapping_Audit()
{
var sync = new MobileTabSyncEngine();
sync.UpdateRemoteTabs(new List<RemoteTabInfo>
{
new("t1", "Design System Talk", "https://talk.com/1"),
new("t2", "Tutorial Part 2", "https://tutorial.com/2")
});
Assert.Equal(2, sync.RemoteTabs.Count);
string? requestedUrl = null;
sync.OnRemoteDownloadRequested += u => requestedUrl = u;
sync.RequestRemoteDownload("t2");
Assert.Equal("https://tutorial.com/2", requestedUrl);
}
// [Gate 114] 비디오 스니퍼: 다중 스트림 품질 자동 협상 (Quality Negotiation)
[Fact]
public void Gate114_VideoSniffer_QualityNegotiator_Audit()
{
var streams = new List<VideoSniffItem>
{
new() { StreamUrl = "https://cdn.com/4k.mp4", Resolution = "4K" },
new() { StreamUrl = "https://cdn.com/1080p.mp4", Resolution = "1080p" },
new() { StreamUrl = "https://cdn.com/720p.mp4", Resolution = "720p" },
new() { StreamUrl = "https://cdn.com/audio.m4a", Resolution = "Audio Only" }
};
// 1. 선호 화질 1080p -> 1080p 매칭
var best1080 = VideoSnifferParser.NegotiateBestQuality(streams, "1080p");
Assert.NotNull(best1080);
Assert.Equal("1080p", best1080.Resolution);
// 2. 선호 화질 1440p (목록에 없음) -> 1440p 이하 최고 화질인 1080p 선택
var fallback = VideoSnifferParser.NegotiateBestQuality(streams, "1440p");
Assert.NotNull(fallback);
Assert.Equal("1080p", fallback.Resolution);
// 3. 선호 화질 최고 화질("Max") -> 4K 선택
var max = VideoSnifferParser.NegotiateBestQuality(streams, "Max");
Assert.NotNull(max);
Assert.Equal("4K", max.Resolution);
// 4. 선호 화질 오디오("Audio Only") -> Audio Only 선택
var audio = VideoSnifferParser.NegotiateBestQuality(streams, "Audio Only");
Assert.NotNull(audio);
Assert.Equal("Audio Only", audio.Resolution);
}
}