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
This commit is contained in:
Yun Chan 2026-09-03 22:13:54 +09:00
parent dbc50abb71
commit f1949024fc
81 changed files with 8611 additions and 0 deletions

View file

@ -0,0 +1,142 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Paca.Mobile.Core;
using Xunit;
namespace Paca.Tests.Mobile;
/// <summary>
/// 모바일 프로덕션 7대 핵심 기능 게이트 (Gate 87 ~ Gate 92)
/// </summary>
public class MobileCoreFeaturesGateTests
{
[Fact]
public void Gate87_MobileDiscoveryEngine_UrlParsing_And_PlatformDetection_Audit()
{
// YouTube, Vimeo, Twitter, 일반 미디어 감지
var yt = MobileDiscoveryEngine.DetectPlatform("https://www.youtube.com/watch?v=dQw4w9WgXcQ");
Assert.Equal("YouTube", yt.PlatformName);
Assert.True(yt.IsSupported);
var vm = MobileDiscoveryEngine.DetectPlatform("https://vimeo.com/12345678");
Assert.Equal("Vimeo", vm.PlatformName);
Assert.True(vm.IsSupported);
var general = MobileDiscoveryEngine.DetectPlatform("https://example.com/video.m3u8");
Assert.Equal("HLS Stream", general.PlatformName);
Assert.True(general.IsSupported);
var unknown = MobileDiscoveryEngine.DetectPlatform("https://not-a-video.org/page.html");
Assert.Equal("Web Page", unknown.PlatformName);
}
[Fact]
public void Gate88_MobileQueueManager_Control_And_Retry_Audit()
{
var qm = new MobileQueueManager();
var item1 = qm.Enqueue("https://example.com/v1.mp4", "Video 1");
var item2 = qm.Enqueue("https://example.com/v2.mp4", "Video 2");
Assert.Equal(2, qm.Items.Count);
// 일시정지 & 재개
qm.Pause(item1.Id);
Assert.Equal(MobileQueueStatus.Paused, item1.Status);
qm.Resume(item1.Id);
Assert.Equal(MobileQueueStatus.Downloading, item1.Status);
// 상단 고정 (Pin)
qm.Pin(item2.Id);
Assert.True(item2.IsPinned);
// 실패 상태 설정 후 일괄 재시도 검증
item1.Status = MobileQueueStatus.Failed;
var retriedCount = qm.RetryFailedItems();
Assert.Equal(1, retriedCount);
Assert.Equal(MobileQueueStatus.Queued, item1.Status);
// 완료 항목 정리
item2.Status = MobileQueueStatus.Completed;
qm.ClearCompleted();
Assert.Single(qm.Items);
}
[Fact]
public void Gate89_MobileFavoritesManager_Stash_And_Tagging_Audit()
{
var fm = new MobileFavoritesManager();
var fav = fm.AddFavorite("https://example.com/movie.mp4", "Great Movie", "영화");
Assert.Single(fm.Favorites);
Assert.Equal("영화", fav.Tag);
Assert.False(fav.IsStashedForDownload);
// Stash(나중에 다운로드) 토글
fm.ToggleStash(fav.Id);
Assert.True(fav.IsStashedForDownload);
// 태그 검색 필터
var filtered = fm.GetByTag("영화");
Assert.Single(filtered);
}
[Fact]
public void Gate90_MobileTabSyncEngine_RemoteTabSync_Audit()
{
var sync = new MobileTabSyncEngine();
Assert.Empty(sync.RemoteTabs);
// PC 브라우저 탭 목록 동기화
var remoteTabs = new List<RemoteTabInfo>
{
new("tab-1", "GitHub Paca Repo", "https://github.com/test/paca"),
new("tab-2", "YouTube Video Stream", "https://youtube.com/watch?v=123")
};
sync.UpdateRemoteTabs(remoteTabs);
Assert.Equal(2, sync.RemoteTabs.Count);
Assert.Equal("GitHub Paca Repo", sync.RemoteTabs[0].Title);
// 모바일에서 원격 다운로드 요청 이벤트 트리거
string? requestedUrl = null;
sync.OnRemoteDownloadRequested += url => requestedUrl = url;
sync.RequestRemoteDownload("tab-2");
Assert.Equal("https://youtube.com/watch?v=123", requestedUrl);
}
[Fact]
public void Gate91_MobileSettingsManager_Preferences_Audit()
{
var settings = new MobileSettingsManager();
Assert.True(settings.AutoRetryOnReconnect);
Assert.False(settings.WifiOnlyDownload);
Assert.Equal(0, settings.SpeedLimitKbps);
Assert.Equal(MobileThemeMode.Dark, settings.ThemeMode);
settings.WifiOnlyDownload = true;
settings.SpeedLimitKbps = 2048;
settings.ThemeMode = MobileThemeMode.Light;
Assert.True(settings.WifiOnlyDownload);
Assert.Equal(2048, settings.SpeedLimitKbps);
Assert.Equal(MobileThemeMode.Light, settings.ThemeMode);
}
[Fact]
public void Gate92_MobileNetworkGuard_AutoResume_Audit()
{
var qm = new MobileQueueManager();
var guard = new MobileNetworkGuard(qm);
var item = qm.Enqueue("https://example.com/video.mp4", "Test Video");
item.Status = MobileQueueStatus.PausedNetwork;
// 네트워크 복구 이벤트 발생
guard.OnNetworkRestored(isWifi: true);
// 네트워크 단절로 멈췄던 항목이 자동으로 재개되어야 함
Assert.Equal(MobileQueueStatus.Downloading, item.Status);
}
}