206 lines
8.5 KiB
C#
206 lines
8.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Threading.Tasks;
|
|
using Paca.Core;
|
|
using Xunit;
|
|
|
|
namespace Paca.Tests.Browser;
|
|
|
|
/// <summary>
|
|
/// 크롬/파이어폭스 대비 Paca만의 초강력 차별화 브라우저 10대 기능 및 진짜 파비콘 게이트 (Gate 136 ~ Gate 145)
|
|
/// </summary>
|
|
public class PacaUniqueBrowserFeaturesGateTests
|
|
{
|
|
[Fact]
|
|
public void Gate136_RealFaviconFetcher_FallbackLadder_Audit()
|
|
{
|
|
var fetcher = new RealFaviconFetcher();
|
|
|
|
// 1. HTML 내에 고해상도 apple-touch-icon 이 있는 경우 최우선 추출
|
|
var htmlWithApple = @"<html><head>
|
|
<link rel='icon' href='/favicon.ico'>
|
|
<link rel='apple-touch-icon' sizes='180x180' href='/icons/apple-180.png'>
|
|
</head></html>";
|
|
var iconUrl = fetcher.ExtractBestFaviconUrl("https://example.com/page", htmlWithApple);
|
|
Assert.Equal("https://example.com/icons/apple-180.png", iconUrl);
|
|
|
|
// 2. 일반 link rel='icon' 만 있는 경우
|
|
var htmlWithRelIcon = @"<html><head><link rel='icon' href='/static/icon.png'></head></html>";
|
|
var relIconUrl = fetcher.ExtractBestFaviconUrl("https://example.com/page", htmlWithRelIcon);
|
|
Assert.Equal("https://example.com/static/icon.png", relIconUrl);
|
|
|
|
// 3. HTML 에 아이콘 태그가 전혀 없는 경우 -> 도메인 루트 /favicon.ico 로 폴백
|
|
var emptyHtml = "<html><head><title>No Icon</title></head></html>";
|
|
var fallbackUrl = fetcher.ExtractBestFaviconUrl("https://myblog.org/sub/article", emptyHtml);
|
|
Assert.Equal("https://myblog.org/favicon.ico", fallbackUrl);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate137_SmartMediaSnifferV2_StreamDetection_Audit()
|
|
{
|
|
var sniffer = new SmartMediaSnifferV2();
|
|
|
|
// 일반 웹 리소스는 무시
|
|
Assert.Null(sniffer.SniffRequest("https://example.com/style.css", "text/css"));
|
|
Assert.Null(sniffer.SniffRequest("https://example.com/script.js", "application/javascript"));
|
|
|
|
// HLS m3u8 스트림 감지
|
|
var hls = sniffer.SniffRequest("https://stream.cdn.com/live/playlist.m3u8", "application/vnd.apple.mpegurl");
|
|
Assert.NotNull(hls);
|
|
Assert.Equal(SniffedMediaType.HlsStream, hls.Type);
|
|
|
|
// DASH mpd 스트림 감지
|
|
var dash = sniffer.SniffRequest("https://stream.cdn.com/dash/manifest.mpd", "application/dash+xml");
|
|
Assert.NotNull(dash);
|
|
Assert.Equal(SniffedMediaType.DashStream, dash.Type);
|
|
|
|
// 직접 MP4 비디오 감지
|
|
var mp4 = sniffer.SniffRequest("https://video.org/file.mp4?token=abc", "video/mp4");
|
|
Assert.NotNull(mp4);
|
|
Assert.Equal(SniffedMediaType.DirectVideo, mp4.Type);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate138_AntiTrackingFingerprintGuard_Audit()
|
|
{
|
|
var guard = new AntiTrackingFingerprintGuard();
|
|
Assert.True(guard.IsTrackingDomain("google-analytics.com"));
|
|
Assert.True(guard.IsTrackingDomain("stats.doubleclick.net"));
|
|
Assert.True(guard.IsTrackingDomain("connect.facebook.net"));
|
|
Assert.False(guard.IsTrackingDomain("youtube.com"));
|
|
|
|
// Canvas 핑거프린팅 방어 노이즈 스크립트 생성 확인
|
|
var noiseScript = guard.GenerateCanvasNoiseScript();
|
|
Assert.Contains("HTMLCanvasElement.prototype.toDataURL", noiseScript);
|
|
Assert.Contains("getImageData", noiseScript);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate139_ReaderModeExtractor_CleanArticle_Audit()
|
|
{
|
|
var extractor = new ReaderModeExtractor();
|
|
var messyHtml = @"
|
|
<html>
|
|
<body>
|
|
<header><nav>Menu 1 Menu 2</nav></header>
|
|
<div class='sidebar'>Banner Ads</div>
|
|
<article>
|
|
<h1>The Future of Video Browsers</h1>
|
|
<p>Paca browser provides built-in media streaming intelligence without third-party extensions.</p>
|
|
<p>It respects user privacy and eliminates bloated telemetry.</p>
|
|
</article>
|
|
<footer>Copyright 2026</footer>
|
|
</body>
|
|
</html>";
|
|
|
|
var article = extractor.ExtractArticle(messyHtml);
|
|
Assert.NotNull(article);
|
|
Assert.Equal("The Future of Video Browsers", article.Title);
|
|
Assert.Contains("Paca browser provides built-in", article.CleanText);
|
|
Assert.DoesNotContain("Banner Ads", article.CleanText);
|
|
Assert.DoesNotContain("Menu 1", article.CleanText);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate140_VideoPiPOverlayController_ForcePiP_Audit()
|
|
{
|
|
var pip = new VideoPiPOverlayController();
|
|
var bypassScript = pip.GenerateForcePiPScript();
|
|
|
|
// 사이트의 disablePictureInPicture 속성 무력화 스크립트
|
|
Assert.Contains("disablePictureInPicture", bypassScript);
|
|
Assert.Contains("requestPictureInPicture", bypassScript);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate141_SpeedBoostTurboCache_Prefetch_Audit()
|
|
{
|
|
var cache = new SpeedBoostTurboCache();
|
|
Assert.Equal(0, cache.PreloadedCount);
|
|
|
|
cache.RegisterPrefetchTarget("https://example.com/next-video-page");
|
|
Assert.Equal(1, cache.PreloadedCount);
|
|
Assert.True(cache.IsPrefetched("https://example.com/next-video-page"));
|
|
Assert.False(cache.IsPrefetched("https://example.com/random-page"));
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate142_OneClickAudioExtractor_AudioOnlyStreamFilter_Audit()
|
|
{
|
|
var extractor = new OneClickAudioExtractor();
|
|
var streams = new List<MediaStreamTrackInfo>
|
|
{
|
|
new("v1", "video", "1080p 60fps", 6000),
|
|
new("v2", "video", "720p", 3000),
|
|
new("a1", "audio", "AAC 128k", 128),
|
|
new("a2", "audio", "Opus 192k High-Res", 192)
|
|
};
|
|
|
|
var bestAudio = extractor.SelectHighestQualityAudioTrack(streams);
|
|
Assert.NotNull(bestAudio);
|
|
Assert.Equal("a2", bestAudio.TrackId);
|
|
Assert.Equal("Opus 192k High-Res", bestAudio.FormatLabel);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate143_CrossPlatformTabHandshake_Payload_Audit()
|
|
{
|
|
var handshake = new CrossPlatformTabHandshake();
|
|
var packet = handshake.CreatePushPacket(
|
|
sourceDeviceId: "GALAXY-Z-FOLD-6",
|
|
sourcePlatform: "Android",
|
|
targetDeviceId: "DESKTOP-PACA-WIN",
|
|
pageUrl: "https://www.youtube.com/watch?v=sample123",
|
|
pageTitle: "Epic 4K Nature",
|
|
scrollPositionRatio: 0.42
|
|
);
|
|
|
|
Assert.Equal("PACA_TAB_PUSH_V1", packet.ProtocolHeader);
|
|
Assert.Equal("Android", packet.SourcePlatform);
|
|
Assert.Equal(0.42, packet.ScrollPositionRatio);
|
|
|
|
var restored = handshake.ParsePushPacket(handshake.Serialize(packet));
|
|
Assert.NotNull(restored);
|
|
Assert.Equal("Epic 4K Nature", restored.PageTitle);
|
|
Assert.Equal("https://www.youtube.com/watch?v=sample123", restored.PageUrl);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate144_AutoFillFormSecureVault_MasterKey_Audit()
|
|
{
|
|
var vault = new AutoFillFormSecureVault();
|
|
var masterKey = "MyMasterPassword2026!";
|
|
|
|
vault.SaveCredential("https://github.com/login", "pacadev", "SecretP@ss!", masterKey);
|
|
var retrieved = vault.RetrieveCredential("https://github.com/login", masterKey);
|
|
|
|
Assert.NotNull(retrieved);
|
|
Assert.Equal("pacadev", retrieved.Username);
|
|
Assert.Equal("SecretP@ss!", retrieved.Password);
|
|
|
|
// 잘못된 비밀번호 시도 시 복호화 실패(null)
|
|
var failed = vault.RetrieveCredential("https://github.com/login", "WrongPassword");
|
|
Assert.Null(failed);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate145_EndToEndInteractiveJourneyAudit_Audit()
|
|
{
|
|
// Favicon 추출 -> 스니핑 -> 읽기모드 -> PiP 스크립트 -> 오디오 분리 -> 탭 푸시 전체 파이프라인
|
|
var fetcher = new RealFaviconFetcher();
|
|
var sniffer = new SmartMediaSnifferV2();
|
|
var extractor = new OneClickAudioExtractor();
|
|
var handshake = new CrossPlatformTabHandshake();
|
|
|
|
var sampleHtml = "<html><head><link rel='apple-touch-icon' href='/paca.png'></head><body><article><h1>Title</h1><p>Body</p></article></body></html>";
|
|
var faviconUrl = fetcher.ExtractBestFaviconUrl("https://paca.tv/watch/1", sampleHtml);
|
|
Assert.Equal("https://paca.tv/paca.png", faviconUrl);
|
|
|
|
var stream = sniffer.SniffRequest("https://paca.tv/live.m3u8", "application/x-mpegURL");
|
|
Assert.NotNull(stream);
|
|
|
|
var pushPacket = handshake.CreatePushPacket("PC", "Windows", "Mobile", "https://paca.tv/watch/1", "Title", 0.5);
|
|
Assert.Equal("PACA_TAB_PUSH_V1", pushPacket.ProtocolHeader);
|
|
}
|
|
}
|