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,135 @@
using System;
using System.Collections.Generic;
using Paca.Mobile.Core;
using Xunit;
namespace Paca.Tests.Mobile;
/// <summary>
/// 모바일 네이티브 컨트롤, 햅틱, 타이포 스케일러, 볼트, 오리엔테이션 게이트 (Gate 115 ~ Gate 122)
/// </summary>
public class MobileAdvancedNativeGateTests
{
[Fact]
public void Gate115_MobileTypographyScaler_Audit()
{
var scaler = new MobileTypographyScaler();
// 기본 1.0x 스케일
Assert.Equal(14.0, scaler.GetScaledFontSize(baseFontSize: 14.0, textScaleFactor: 1.0));
// 1.5x 접근성 큰 글꼴
Assert.Equal(21.0, scaler.GetScaledFontSize(baseFontSize: 14.0, textScaleFactor: 1.5));
// 최대 상한선(2.0x 초과 방지 클램핑)
Assert.Equal(28.0, scaler.GetScaledFontSize(baseFontSize: 14.0, textScaleFactor: 2.5));
}
[Fact]
public void Gate116_MobileOrientationSensor_Landscape_Audit()
{
var sensor = new MobileOrientationSensor();
Assert.Equal(MobileScreenOrientation.Portrait, sensor.CurrentOrientation);
Assert.False(sensor.IsFullscreenVideo);
// 가로 회전 감지
sensor.OnOrientationChanged(MobileScreenOrientation.Landscape);
Assert.Equal(MobileScreenOrientation.Landscape, sensor.CurrentOrientation);
Assert.True(sensor.IsFullscreenVideo);
// 컨트롤 자동 숨김 타이머 (3초)
Assert.Equal(3000, sensor.ControlsAutoHideTimeoutMs);
}
[Fact]
public void Gate117_MobileHapticFeedback_Audit()
{
var haptic = new MobileHapticFeedback();
HapticFeedbackType? triggered = null;
haptic.OnHapticTriggered += type => triggered = type;
haptic.NotifySuccess();
Assert.Equal(HapticFeedbackType.Success, triggered);
haptic.NotifyError();
Assert.Equal(HapticFeedbackType.Error, triggered);
haptic.NotifySelectionChange();
Assert.Equal(HapticFeedbackType.LightClick, triggered);
}
[Fact]
public void Gate118_MobileQueueReorderEngine_Audit()
{
var qm = new MobileQueueManager();
var item1 = qm.Enqueue("https://example.com/1.mp4", "Video 1");
var item2 = qm.Enqueue("https://example.com/2.mp4", "Video 2");
var item3 = qm.Enqueue("https://example.com/3.mp4", "Video 3");
var reorder = new MobileQueueReorderEngine(qm);
// item3을 맨 앞으로 이동 (index 2 -> 0)
reorder.MoveItem(fromIndex: 2, toIndex: 0);
Assert.Equal(item3.Id, qm.Items[0].Id);
Assert.Equal(item1.Id, qm.Items[1].Id);
Assert.Equal(item2.Id, qm.Items[2].Id);
}
[Fact]
public void Gate119_MobileBackgroundWorker_Notification_Audit()
{
var worker = new MobileBackgroundWorker();
Assert.False(worker.IsForegroundServiceRunning);
worker.StartService("Paca 다운로드 진행 중", totalActiveDownloads: 2);
Assert.True(worker.IsForegroundServiceRunning);
Assert.Equal("Paca 다운로드 진행 중 (2개 활성)", worker.LastNotificationTitle);
worker.StopService();
Assert.False(worker.IsForegroundServiceRunning);
}
[Fact]
public void Gate120_MobileMediaVault_Encryption_Audit()
{
var vault = new MobileMediaVault();
var plainBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
var password = "SafePassword123!";
// 암호화
var encrypted = vault.EncryptMedia(plainBytes, password);
Assert.NotEqual(plainBytes, encrypted);
// 복호화
var decrypted = vault.DecryptMedia(encrypted, password);
Assert.Equal(plainBytes, decrypted);
}
[Fact]
public void Gate121_MobileAdBlockEngine_EasyList_Audit()
{
var adBlock = new MobileAdBlockEngine();
Assert.True(adBlock.IsAdUrl("https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"));
Assert.True(adBlock.IsAdUrl("https://adservice.google.com/adsid/google/ui"));
Assert.True(adBlock.IsAdUrl("https://securepubads.g.doubleclick.net/gampad/ads"));
// 정상 미디어 URL은 허용
Assert.False(adBlock.IsAdUrl("https://www.youtube.com/watch?v=dQw4w9WgXcQ"));
Assert.False(adBlock.IsAdUrl("https://vimeo.com/12345678"));
}
[Fact]
public void Gate122_MobileResolutionPicker_Estimation_Audit()
{
var picker = new MobileResolutionPicker();
// 1080p 60fps 비디오 10분(600초) 예상 용량 계산
var estimate1080p = picker.EstimateSizeAndDownloadTime(
durationSec: 600,
resolution: "1080p",
currentNetworkSpeedKbps: 10240 // 10MB/s (80Mbps)
);
Assert.True(estimate1080p.EstimatedSizeBytes > 0);
Assert.True(estimate1080p.EstimatedDurationSec > 0);
Assert.Equal("1080p Full HD", estimate1080p.ResolutionLabel);
}
}