using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Paca.Mobile.Core;
using Xunit;
namespace Paca.Tests.Mobile;
///
/// 데스크톱 Paca와 1:1 대응하는 모바일 다운로드 관리자, 필터링, 정렬, 정리 게이트 (Gate 153 ~ Gate 160)
///
public class MobileDesktopParityDownloadGateTests
{
private static readonly string MobileProjectDir = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", "..", "src", "Paca.Mobile"));
[Fact]
public void Gate153_StatusAndMediaFilter_Parity_Audit()
{
var engine = new MobileDesktopParityFilterEngine();
var items = new List
{
new("1", "Video Active", "https://ex.com/1.mp4", MobileParityStatus.Active, MobileParityMediaKind.Video, "MP4", 1024 * 1024 * 10, 0.5),
new("2", "Video Done", "https://ex.com/2.mp4", MobileParityStatus.Done, MobileParityMediaKind.Video, "MP4", 1024 * 1024 * 20, 1.0),
new("3", "Audio Done", "https://ex.com/3.mp3", MobileParityStatus.Done, MobileParityMediaKind.Audio, "MP3", 1024 * 1024 * 3, 1.0),
new("4", "Audio Failed", "https://ex.com/4.m4a", MobileParityStatus.Failed, MobileParityMediaKind.Audio, "M4A", 1024 * 500, 0.2)
};
// 1. 상태 필터: Done만
var doneOnly = engine.Filter(items, status: MobileParityStatusFilter.Done, mediaKind: MobileParityMediaFilter.All, format: "모든 포맷", hideSmall: false, query: "");
Assert.Equal(2, doneOnly.Count);
// 2. 미디어 필터: Audio만
var audioOnly = engine.Filter(items, status: MobileParityStatusFilter.All, mediaKind: MobileParityMediaFilter.Audio, format: "모든 포맷", hideSmall: false, query: "");
Assert.Equal(2, audioOnly.Count);
}
[Fact]
public void Gate154_FormatFilter_Parity_Audit()
{
var engine = new MobileDesktopParityFilterEngine();
var items = new List
{
new("1", "HLS Live", "https://ex.com/live.m3u8", MobileParityStatus.Active, MobileParityMediaKind.Video, "HLS", 1024 * 1024 * 5, 0.3),
new("2", "DASH VoD", "https://ex.com/manifest.mpd", MobileParityStatus.Done, MobileParityMediaKind.Video, "DASH", 1024 * 1024 * 50, 1.0),
new("3", "WebM Clip", "https://ex.com/clip.webm", MobileParityStatus.Done, MobileParityMediaKind.Video, "WEBM", 1024 * 1024 * 15, 1.0)
};
var hlsItems = engine.Filter(items, MobileParityStatusFilter.All, MobileParityMediaFilter.All, format: "HLS", hideSmall: false, query: "");
Assert.Single(hlsItems);
Assert.Equal("HLS Live", hlsItems[0].Title);
var webmItems = engine.Filter(items, MobileParityStatusFilter.All, MobileParityMediaFilter.All, format: "WEBM", hideSmall: false, query: "");
Assert.Single(webmItems);
Assert.Equal("WebM Clip", webmItems[0].Title);
}
[Fact]
public void Gate155_SortEngine_Parity_Audit()
{
var engine = new MobileDesktopParitySortEngine();
var items = new List
{
new("1", "Bravo", "url1", MobileParityStatus.Done, MobileParityMediaKind.Video, "MP4", 100, 0.2, DateTime.UtcNow.AddMinutes(-10)),
new("2", "Alpha", "url2", MobileParityStatus.Done, MobileParityMediaKind.Video, "MP4", 500, 0.9, DateTime.UtcNow.AddMinutes(-5)),
new("3", "Charlie", "url3", MobileParityStatus.Done, MobileParityMediaKind.Video, "MP4", 300, 0.5, DateTime.UtcNow)
};
// 최신순 (Newest)
var byNewest = engine.Sort(items, MobileParitySortCriteria.Newest);
Assert.Equal("Charlie", byNewest[0].Title);
// 크기순 (Largest)
var byLargest = engine.Sort(items, MobileParitySortCriteria.Largest);
Assert.Equal("Alpha", byLargest[0].Title); // 500
// 진행률순 (Progress)
var byProgress = engine.Sort(items, MobileParitySortCriteria.Progress);
Assert.Equal("Alpha", byProgress[0].Title); // 0.9
// 제목순 (Title)
var byTitle = engine.Sort(items, MobileParitySortCriteria.Title);
Assert.Equal("Alpha", byTitle[0].Title);
Assert.Equal("Charlie", byTitle[2].Title);
}
[Fact]
public void Gate156_SmallMediaFilter_Parity_Audit()
{
var engine = new MobileDesktopParityFilterEngine();
var items = new List
{
new("1", "Small Preview", "url1", MobileParityStatus.Done, MobileParityMediaKind.Video, "MP4", 500 * 1024, 1.0), // 500KB
new("2", "Full Movie", "url2", MobileParityStatus.Done, MobileParityMediaKind.Video, "MP4", 10 * 1024 * 1024, 1.0) // 10MB
};
// 1MB 미만 숨김 활성화 시 10MB만 남아야 함
var filtered = engine.Filter(items, MobileParityStatusFilter.All, MobileParityMediaFilter.All, format: "모든 포맷", hideSmall: true, query: "");
Assert.Single(filtered);
Assert.Equal("Full Movie", filtered[0].Title);
}
[Fact]
public void Gate157_BatchActionManager_Parity_Audit()
{
var manager = new MobileParityBatchManager();
var items = new List
{
new("1", "Active Item", "url1", MobileParityStatus.Active, MobileParityMediaKind.Video, "MP4", 100, 0.5),
new("2", "Done Item 1", "url2", MobileParityStatus.Done, MobileParityMediaKind.Video, "MP4", 100, 1.0),
new("3", "Done Item 2", "url3", MobileParityStatus.Done, MobileParityMediaKind.Video, "MP4", 100, 1.0),
new("4", "Failed Item", "url4", MobileParityStatus.Failed, MobileParityMediaKind.Video, "MP4", 100, 0.1)
};
// 1. 완료 정리 -> Done 항목만 제거
var afterClearDone = manager.ClearFinishedDownloads(items);
Assert.Equal(2, afterClearDone.Count);
Assert.DoesNotContain(afterClearDone, i => i.Status == MobileParityStatus.Done);
// 2. 전체 비우기 -> Active 제외하고 모두 제거
var afterClearAll = manager.ClearAllDownloads(items);
Assert.Single(afterClearAll);
Assert.Equal("Active Item", afterClearAll[0].Title);
}
[Fact]
public void Gate158_MobileDownloadCardActions_Parity_Audit()
{
var card = new MobileParityDownloadItem("1", "Test Video", "https://ex.com/video.mp4", MobileParityStatus.Done, MobileParityMediaKind.Video, "MP4", 1024, 1.0);
Assert.False(card.IsPinned);
// 상단 고정 토글
card.IsPinned = true;
Assert.True(card.IsPinned);
// 우선순위 조절
Assert.Equal(0, card.Priority);
card.PriorityUp();
Assert.Equal(1, card.Priority);
card.PriorityDown();
Assert.Equal(0, card.Priority);
}
[Fact]
public void Gate159_DownloadsPage_DesktopParity_UiElements_Audit()
{
var downloadsPageCs = File.ReadAllText(Path.Combine(MobileProjectDir, "DownloadsPage.cs"));
// 데스크톱 1:1 대응 UI 요소 검증
Assert.Contains("DownloadSearchBox", downloadsPageCs);
Assert.Contains("FilterAll", downloadsPageCs);
Assert.Contains("FilterActive", downloadsPageCs);
Assert.Contains("FilterDone", downloadsPageCs);
Assert.Contains("FilterFailed", downloadsPageCs);
Assert.Contains("FilterMediaAll", downloadsPageCs);
Assert.Contains("FilterMediaVideo", downloadsPageCs);
Assert.Contains("FilterMediaAudio", downloadsPageCs);
Assert.Contains("FormatFilterPicker", downloadsPageCs);
Assert.Contains("DownloadSortPicker", downloadsPageCs);
Assert.Contains("FilterHideSmallMedia", downloadsPageCs);
Assert.Contains("ClearFinishedDownloadsBtn", downloadsPageCs);
Assert.Contains("ClearAllDownloadsBtn", downloadsPageCs);
}
[Fact]
public void Gate160_EndToEndDesktopParityJourney_Audit()
{
var filter = new MobileDesktopParityFilterEngine();
var sorter = new MobileDesktopParitySortEngine();
var batch = new MobileParityBatchManager();
var list = new List
{
new("1", "Nature 4K", "https://ex.com/n.mp4", MobileParityStatus.Done, MobileParityMediaKind.Video, "MP4", 50 * 1024 * 1024, 1.0, DateTime.UtcNow.AddHours(-1)),
new("2", "Nature 720p", "https://ex.com/n2.mp4", MobileParityStatus.Active, MobileParityMediaKind.Video, "MP4", 10 * 1024 * 1024, 0.4, DateTime.UtcNow),
new("3", "Background Music", "https://ex.com/bgm.mp3", MobileParityStatus.Done, MobileParityMediaKind.Audio, "MP3", 3 * 1024 * 1024, 1.0, DateTime.UtcNow.AddHours(-2)),
new("4", "Tiny Icon", "https://ex.com/icon.png", MobileParityStatus.Failed, MobileParityMediaKind.Other, "PNG", 50 * 1024, 0.1, DateTime.UtcNow)
};
// 1. 검색어로 필터링 ("Nature")
var searched = filter.Filter(list, MobileParityStatusFilter.All, MobileParityMediaFilter.All, "모든 포맷", false, "Nature");
Assert.Equal(2, searched.Count);
// 2. 크기순 정렬
var sorted = sorter.Sort(searched, MobileParitySortCriteria.Largest);
Assert.Equal("Nature 4K", sorted[0].Title);
// 3. 완료 정리 수행
var remaining = batch.ClearFinishedDownloads(list);
Assert.Equal(2, remaining.Count);
Assert.Contains(remaining, i => i.Title == "Nature 720p");
Assert.Contains(remaining, i => i.Title == "Tiny Icon");
}
}