JJ_MKVMaker/test/download_error_test.dart

165 lines
8.2 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:jj_mkvmaker/app/download_manager.dart';
import 'package:jj_mkvmaker/app/i18n_controller.dart';
import 'package:jj_mkvmaker/app/net_guard.dart';
import 'package:jj_mkvmaker/app/settings.dart';
import 'package:jj_mkvmaker/core/dns_resolver.dart';
import 'package:jj_mkvmaker/core/download_detect.dart';
import 'package:jj_mkvmaker/services/downloader.dart';
import 'package:jj_mkvmaker/ui/downloads_page.dart';
class _Backend implements DownloadBackend {
var starts = 0;
final String error;
_Backend([this.error = 'ERROR: [generic] Unable to download webpage: HTTP Error 404: Not Found '
'(https://cdn.example.com/v.mp4?sig=SECRET123&expire=1)']);
@override
DownloadKind get kind => DownloadKind.video;
@override
Future<(String, List<PlaylistEntry>)?> expandPlaylist(String url) async => null;
@override
Future<void> start(DownloadTask t, void Function() changed) async {
starts++;
t
..state = DownloadState.failed
..error = error;
changed();
}
@override
Future<void> pause(DownloadTask t) async {}
@override
Future<void> cancel(DownloadTask t) async {}
@override
Future<void> shutdown() async {}
}
/// 136: 실패한 다운로드 줄 - 진행 막대 대신 실패 · 사람 말 이유 · [자세히] (원문) · [다시 시도]
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
test('yt-dlp · aria2 원문 → 사람 말 이유', () {
expect(friendlyDownloadError('ERROR: [generic] x: HTTP Error 404: Not Found'), '주소에 영상이 없습니다 (404)');
expect(friendlyDownloadError('ERROR: [vimeo] 1: HTTP Error 403: Forbidden'), '로그인이 필요합니다 (403)');
expect(friendlyDownloadError('ERROR: Unsupported URL: https://example.com/'), '지원하지 않는 사이트입니다');
expect(friendlyDownloadError('ERROR: [generic] Unable to download webpage: <urlopen error [Errno 11001] getaddrinfo failed>'),
'연결할 수 없습니다');
expect(friendlyDownloadError('Network problem has occurred. cause:Connection refused (errorCode=6)'), '연결할 수 없습니다');
expect(friendlyDownloadError('Resource not found (errorCode=3)'), '주소에 영상이 없습니다 (404)');
expect(friendlyDownloadError('There is not enough disk space available (errorCode=9)'), '저장 공간이 모자랍니다');
expect(friendlyDownloadError('ERROR: [youtube] x: Sign in to confirm you’re not a bot'), contains('로봇 확인'));
expect(friendlyDownloadError('무언가 이상함'), '받지 못했습니다');
});
test('화면 언어를 따른다 (59 와 같은 방식)', () async {
await i18n.apply('ja', save: false);
addTearDown(() => i18n.apply('ko', save: false));
expect(friendlyDownloadError('HTTP Error 404: Not Found'), 'このアドレスに動画がありません (404)');
});
test('서명 · 키가 든 주소 부분은 가린다', () {
expect(redactUrlSecrets('x https://cdn.example.com/v.mp4?sig=AAA&expire=1 y'), 'x https://cdn.example.com/v.mp4?… y');
expect(redactUrlSecrets('(https://user:pw@nas.local/dav/a.mkv#t=1)'), '(https://nas.local/dav/a.mkv?…)');
expect(redactUrlSecrets('https://www.youtube.com/watch'), 'https://www.youtube.com/watch');
});
testWidgets('실패한 줄: 진행 막대 ("준비 중…") 없이 이유 · [자세히] 는 원문 (주소의 서명은 가림) · [다시 시도]', (tester) async {
tester.view.physicalSize = const Size(1400, 900);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.reset);
final b = _Backend();
final d = DownloadManager(backends: [b], settings: () => AppSettings(), readClipboard: () async => null);
final t = d.addPage('https://cdn.example.com/page')!..title = '영상';
await tester.pumpWidget(MaterialApp(home: DownloadsPage(d: d)));
for (var i = 0; i < 20 && t.state != DownloadState.failed; i++) {
await tester.pump(const Duration(milliseconds: 50));
}
expect(t.state, DownloadState.failed);
await tester.pump();
expect(find.text('준비 중…'), findsNothing);
expect(find.byType(DownloadProgressBar), findsNothing);
expect(find.textContaining('주소에 영상이 없습니다 (404)'), findsOneWidget);
expect(find.textContaining('HTTP Error'), findsNothing, reason: '원문은 [자세히] 에서만');
await tester.tap(find.text('자세히'));
await tester.pumpAndSettle();
expect(find.textContaining('HTTP Error 404'), findsOneWidget);
expect(find.textContaining('SECRET123'), findsNothing, reason: '서명된 주소를 보이지 않음');
await tester.tap(find.text('닫기'));
await tester.pumpAndSettle();
expect(b.starts, 1);
await tester.tap(find.text('다시 시도'));
for (var i = 0; i < 20 && b.starts < 2; i++) {
await tester.pump(const Duration(milliseconds: 50));
}
expect(b.starts, 2, reason: '[다시 시도] 로 다시 받기 시작 (이 가짜 백엔드는 또 실패)');
d.dispose();
});
// 263: DNS 강제 프록시를 거친 yt-dlp 는 "Tunnel connection failed" 만 남겨, 이유를 몰랐다 (42 · a4)
group('263 DNS 서버에 닿지 않아 실패한 다운로드', () {
final g = NetGuard.instance;
late DnsConfig Function() before;
setUp(() {
before = g.config;
g.config = () => const DnsConfig(mode: 'custom', primary: '192.0.2.1');
});
tearDown(() {
g.config = before;
g.failure.value = null;
});
DownloadTask failed(String error) => DownloadTask(id: '1', kind: DownloadKind.video, source: 'https://x.example/v', dir: '.')
..state = DownloadState.failed
..error = error;
const tunnel = "ERROR: [generic] Unable to download webpage: ('Unable to connect to proxy', OSError('Tunnel connection failed: 502 Bad Gateway'))";
test('연결 · 프록시 실패이고 지금 닿지 않음일 때만 DNS 탓', () {
expect(DownloadManager.dnsCause(failed(tunnel)), isNull, reason: '닿지 않음 상태가 아니면 아님');
g.failure.value = DnsFailure('192.0.2.1', 'x', DateTime.now());
expect(DownloadManager.dnsCause(failed(tunnel)), '192.0.2.1');
expect(DownloadManager.dnsCause(failed('Network problem has occurred. cause:Connection refused (errorCode=6)')), '192.0.2.1');
expect(DownloadManager.dnsCause(failed('ERROR: [generic] x: HTTP Error 404: Not Found')), isNull, reason: '404 는 DNS 탓이 아님');
g.config = () => const DnsConfig.system();
expect(DownloadManager.dnsCause(failed(tunnel)), isNull, reason: '시스템 DNS 면 강제 프록시가 없다');
});
testWidgets('줄에 이유 · [시스템 DNS 로 바꾸기] - 누르면 설정이 시스템 DNS 가 되고 버튼은 사라진다 (다시 받기는 사용자가)', (tester) async {
tester.view.physicalSize = const Size(1400, 900);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.reset);
g.failure.value = DnsFailure('192.0.2.1', 'x', DateTime.now());
final s = AppSettings();
final b = _Backend(tunnel);
final d = DownloadManager(backends: [b], settings: () => s, readClipboard: () async => null);
d.useSystemDns = () async {
s.network.mode = 'system';
d.refresh();
};
final t = d.addPage('https://cdn.example.com/page')!..title = '영상';
await tester.pumpWidget(MaterialApp(home: DownloadsPage(d: d)));
for (var i = 0; i < 20 && t.state != DownloadState.failed; i++) {
await tester.pump(const Duration(milliseconds: 50));
}
await tester.pump();
expect(find.textContaining('DNS 서버 (192.0.2.1) 에 닿지 않아 받지 못했습니다'), findsOneWidget);
expect(find.text('시스템 DNS 로 바꾸기'), findsOneWidget);
await tester.tap(find.text('시스템 DNS 로 바꾸기'));
await tester.pump();
expect(s.network.mode, 'system');
expect(find.text('시스템 DNS 로 바꾸기'), findsNothing);
expect(b.starts, 1, reason: '몰래 다시 받지 않는다');
await tester.tap(find.text('다시 시도'));
for (var i = 0; i < 20 && b.starts < 2; i++) {
await tester.pump(const Duration(milliseconds: 50));
}
expect(b.starts, 2);
d.dispose();
});
});
}