WIP 263 다운로드: DNS 서버에 닿지 않아 실패하면 줄에 이유 · [시스템 DNS 로 바꾸기] (검토 대기)
- 관리자 지시 (42 가 찾던 것): DNS 강제 프록시를 거친 yt-dlp · aria2 는 "Tunnel connection failed" 만 남겨 줄에는 "연결할 수 없습니다" 뿐이라 이유 · 할 일을 몰랐다. - 실패한 그때 DNS 강제 + "닿지 않음" 상태이고 오류가 연결 · 프록시 실패이면 줄에 "DNS 서버 (…) 에 닿지 않아 받지 못했습니다". 404 · 로그인 같은 다른 실패는 DNS 탓으로 보지 않는다. - 아직 DNS 강제면 [시스템 DNS 로 바꾸기] (설정에 저장, 버튼은 사라짐). 다시 받기는 [다시 시도] 로 사용자가 (내 판단: 몰래 다시 받지 않음). - 번역 en · ja · zh-Hans, 시험 download_error_test (판단 · 줄 · 버튼). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>묶음6
parent
f596ef7253
commit
a2cb535f1e
|
|
@ -158,6 +158,7 @@
|
||||||
"DNS 서버 ({0}) 에 닿지 않습니다: {1}": "Cannot reach the DNS server ({0}): {1}",
|
"DNS 서버 ({0}) 에 닿지 않습니다: {1}": "Cannot reach the DNS server ({0}): {1}",
|
||||||
"DNS 서버 ({0}) 에 닿지 않아 {1}": "Couldn't reach the DNS server ({0}), so {1}",
|
"DNS 서버 ({0}) 에 닿지 않아 {1}": "Couldn't reach the DNS server ({0}), so {1}",
|
||||||
"DNS 서버 ({0}) 에 닿지 않아 {1} 을(를) 열 수 없습니다.": "Can't open {1} because the DNS server ({0}) can't be reached.",
|
"DNS 서버 ({0}) 에 닿지 않아 {1} 을(를) 열 수 없습니다.": "Can't open {1} because the DNS server ({0}) can't be reached.",
|
||||||
|
"DNS 서버 ({0}) 에 닿지 않아 받지 못했습니다": "Couldn't reach the DNS server ({0}), so the download failed",
|
||||||
"DNS 서버 ({0}) 에 닿지 않아 주소를 찾지 못했습니다. 인터넷 연결이나 환경 설정 > 네트워크 · DNS 의 서버 주소를 확인하세요.": "Couldn't reach the DNS server ({0}), so the address couldn't be found. Check the internet connection or the server address in Settings > Network · DNS.",
|
"DNS 서버 ({0}) 에 닿지 않아 주소를 찾지 못했습니다. 인터넷 연결이나 환경 설정 > 네트워크 · DNS 의 서버 주소를 확인하세요.": "Couldn't reach the DNS server ({0}), so the address couldn't be found. Check the internet connection or the server address in Settings > Network · DNS.",
|
||||||
"DNS 서버 ({0}) 에 닿지 않아 주소를 찾지 못했습니다: {1}": "Could not look up the address because the DNS server ({0}) is unreachable: {1}",
|
"DNS 서버 ({0}) 에 닿지 않아 주소를 찾지 못했습니다: {1}": "Could not look up the address because the DNS server ({0}) is unreachable: {1}",
|
||||||
"DNS 서버 ({0}) 에 닿지 않음 ({1}부터)": "Can't reach DNS server ({0}) (since {1})",
|
"DNS 서버 ({0}) 에 닿지 않음 ({1}부터)": "Can't reach DNS server ({0}) (since {1})",
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,7 @@
|
||||||
"DNS 서버 ({0}) 에 닿지 않습니다: {1}": "DNS サーバー ({0}) に届きません: {1}",
|
"DNS 서버 ({0}) 에 닿지 않습니다: {1}": "DNS サーバー ({0}) に届きません: {1}",
|
||||||
"DNS 서버 ({0}) 에 닿지 않아 {1}": "DNS サーバー ({0}) に接続できないため、{1}",
|
"DNS 서버 ({0}) 에 닿지 않아 {1}": "DNS サーバー ({0}) に接続できないため、{1}",
|
||||||
"DNS 서버 ({0}) 에 닿지 않아 {1} 을(를) 열 수 없습니다.": "DNS サーバー ({0}) に接続できないため、{1} を開けません。",
|
"DNS 서버 ({0}) 에 닿지 않아 {1} 을(를) 열 수 없습니다.": "DNS サーバー ({0}) に接続できないため、{1} を開けません。",
|
||||||
|
"DNS 서버 ({0}) 에 닿지 않아 받지 못했습니다": "DNS サーバー ({0}) に接続できず、ダウンロードできませんでした",
|
||||||
"DNS 서버 ({0}) 에 닿지 않아 주소를 찾지 못했습니다. 인터넷 연결이나 환경 설정 > 네트워크 · DNS 의 서버 주소를 확인하세요.": "DNS サーバー ({0}) に届かず、アドレスを見つけられませんでした。インターネット接続か、環境設定 > ネットワーク · DNS のサーバーアドレスを確認してください。",
|
"DNS 서버 ({0}) 에 닿지 않아 주소를 찾지 못했습니다. 인터넷 연결이나 환경 설정 > 네트워크 · DNS 의 서버 주소를 확인하세요.": "DNS サーバー ({0}) に届かず、アドレスを見つけられませんでした。インターネット接続か、環境設定 > ネットワーク · DNS のサーバーアドレスを確認してください。",
|
||||||
"DNS 서버 ({0}) 에 닿지 않아 주소를 찾지 못했습니다: {1}": "DNS サーバー ({0}) に届かず、アドレスを解決できませんでした: {1}",
|
"DNS 서버 ({0}) 에 닿지 않아 주소를 찾지 못했습니다: {1}": "DNS サーバー ({0}) に届かず、アドレスを解決できませんでした: {1}",
|
||||||
"DNS 서버 ({0}) 에 닿지 않음 ({1}부터)": "DNS サーバー ({0}) に接続できません ({1} から)",
|
"DNS 서버 ({0}) 에 닿지 않음 ({1}부터)": "DNS サーバー ({0}) に接続できません ({1} から)",
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,7 @@
|
||||||
"DNS 서버 ({0}) 에 닿지 않습니다: {1}": "无法连接 DNS 服务器 ({0}): {1}",
|
"DNS 서버 ({0}) 에 닿지 않습니다: {1}": "无法连接 DNS 服务器 ({0}): {1}",
|
||||||
"DNS 서버 ({0}) 에 닿지 않아 {1}": "无法连接 DNS 服务器 ({0}),因此{1}",
|
"DNS 서버 ({0}) 에 닿지 않아 {1}": "无法连接 DNS 服务器 ({0}),因此{1}",
|
||||||
"DNS 서버 ({0}) 에 닿지 않아 {1} 을(를) 열 수 없습니다.": "无法连接 DNS 服务器 ({0}),因此无法打开 {1}。",
|
"DNS 서버 ({0}) 에 닿지 않아 {1} 을(를) 열 수 없습니다.": "无法连接 DNS 服务器 ({0}),因此无法打开 {1}。",
|
||||||
|
"DNS 서버 ({0}) 에 닿지 않아 받지 못했습니다": "无法连接 DNS 服务器 ({0}),下载失败",
|
||||||
"DNS 서버 ({0}) 에 닿지 않아 주소를 찾지 못했습니다. 인터넷 연결이나 환경 설정 > 네트워크 · DNS 의 서버 주소를 확인하세요.": "无法连接 DNS 服务器({0}),未能找到地址。请检查网络连接,或检查 设置 > 网络 · DNS 中的服务器地址。",
|
"DNS 서버 ({0}) 에 닿지 않아 주소를 찾지 못했습니다. 인터넷 연결이나 환경 설정 > 네트워크 · DNS 의 서버 주소를 확인하세요.": "无法连接 DNS 服务器({0}),未能找到地址。请检查网络连接,或检查 设置 > 网络 · DNS 中的服务器地址。",
|
||||||
"DNS 서버 ({0}) 에 닿지 않아 주소를 찾지 못했습니다: {1}": "无法连接 DNS 服务器 ({0}),未能解析地址: {1}",
|
"DNS 서버 ({0}) 에 닿지 않아 주소를 찾지 못했습니다: {1}": "无法连接 DNS 服务器 ({0}),未能解析地址: {1}",
|
||||||
"DNS 서버 ({0}) 에 닿지 않음 ({1}부터)": "无法连接 DNS 服务器 ({0}) (自 {1} 起)",
|
"DNS 서버 ({0}) 에 닿지 않음 ({1}부터)": "无法连接 DNS 服务器 ({0}) (自 {1} 起)",
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import 'package:path/path.dart' as p;
|
||||||
import '../core/download_detect.dart';
|
import '../core/download_detect.dart';
|
||||||
import '../core/playlist.dart';
|
import '../core/playlist.dart';
|
||||||
import '../services/downloader.dart';
|
import '../services/downloader.dart';
|
||||||
|
import 'net_guard.dart';
|
||||||
import 'settings.dart';
|
import 'settings.dart';
|
||||||
import '../l10n/tr.dart';
|
import '../l10n/tr.dart';
|
||||||
|
|
||||||
|
|
@ -41,6 +42,21 @@ class DownloadManager extends ChangeNotifier {
|
||||||
/// "완료 시 자동 동영상 추가" 를 켜고 끄는 함수 (설정에 저장). 앱이 연결해 준다.
|
/// "완료 시 자동 동영상 추가" 를 켜고 끄는 함수 (설정에 저장). 앱이 연결해 준다.
|
||||||
Future<void> Function(bool on)? setAutoAdd;
|
Future<void> Function(bool on)? setAutoAdd;
|
||||||
|
|
||||||
|
/// 263: DNS 를 시스템 DNS 로 바꾸는 함수 (설정에 저장). 앱이 연결해 준다 - 실패한 줄의 [시스템 DNS 로 바꾸기]
|
||||||
|
Future<void> Function()? useSystemDns;
|
||||||
|
|
||||||
|
/// 263: 이 실패가 정한 DNS 서버에 닿지 않아서인지 - 그 서버 (아니면 null).
|
||||||
|
/// DNS 강제가 켜져 있고 "닿지 않음" 상태이며, 오류가 연결 · 프록시 실패일 때만 (404 · 로그인 같은 다른 실패는 DNS 탓으로 보지 않는다)
|
||||||
|
static String? dnsCause(DownloadTask t, {NetGuard? guard}) {
|
||||||
|
final g = guard ?? NetGuard.instance;
|
||||||
|
final f = g.failure.value;
|
||||||
|
if (f == null || !g.config().forced) return null;
|
||||||
|
final err = t.error ?? '';
|
||||||
|
final e = err.toLowerCase();
|
||||||
|
if (!e.contains('proxy') && !e.contains('tunnel') && friendlyDownloadError(err) != tr('연결할 수 없습니다')) return null;
|
||||||
|
return f.server;
|
||||||
|
}
|
||||||
|
|
||||||
/// 설정이 바뀌었을 때 화면 갱신
|
/// 설정이 바뀌었을 때 화면 갱신
|
||||||
void refresh() => notifyListeners();
|
void refresh() => notifyListeners();
|
||||||
|
|
||||||
|
|
@ -235,6 +251,13 @@ class DownloadManager extends ChangeNotifier {
|
||||||
case DownloadState.done:
|
case DownloadState.done:
|
||||||
log?.call(trf('다운로드 완료: {0}{1}', [name, t.totalBytes == null ? '' : ' (${formatBytes(t.totalBytes!)})']));
|
log?.call(trf('다운로드 완료: {0}{1}', [name, t.totalBytes == null ? '' : ' (${formatBytes(t.totalBytes!)})']));
|
||||||
case DownloadState.failed:
|
case DownloadState.failed:
|
||||||
|
// 263: 실패한 그때 DNS 서버에 닿지 않았으면 줄에 이유와 [시스템 DNS 로 바꾸기] (나중에 서버가 다시 답해도 이 줄의 이유는 남는다)
|
||||||
|
final dns = dnsCause(t);
|
||||||
|
if (dns == null) {
|
||||||
|
t.extra.remove('dnsServer');
|
||||||
|
} else {
|
||||||
|
t.extra['dnsServer'] = dns;
|
||||||
|
}
|
||||||
log?.call(trf('다운로드 실패: {0} - {1}', [name, t.error ?? '']));
|
log?.call(trf('다운로드 실패: {0} - {1}', [name, t.error ?? '']));
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
@ -294,6 +317,7 @@ class DownloadManager extends ChangeNotifier {
|
||||||
for (final t in list.toList()) {
|
for (final t in list.toList()) {
|
||||||
if (t.state == DownloadState.paused || t.state == DownloadState.failed) {
|
if (t.state == DownloadState.paused || t.state == DownloadState.failed) {
|
||||||
if (t.state == DownloadState.failed) t.extra.remove('gid'); // 실패한 aria2 작업은 새로 추가
|
if (t.state == DownloadState.failed) t.extra.remove('gid'); // 실패한 aria2 작업은 새로 추가
|
||||||
|
t.extra.remove('dnsServer');
|
||||||
t
|
t
|
||||||
..state = DownloadState.queued
|
..state = DownloadState.queued
|
||||||
..error = null;
|
..error = null;
|
||||||
|
|
|
||||||
|
|
@ -304,6 +304,10 @@ Future<void> main(List<String> args) async {
|
||||||
await controller.updateSettings((x) => x.addFinishedDownloads = on);
|
await controller.updateSettings((x) => x.addFinishedDownloads = on);
|
||||||
downloads.refresh();
|
downloads.refresh();
|
||||||
};
|
};
|
||||||
|
downloads.useSystemDns = () async {
|
||||||
|
await controller.updateSettings((x) => x.dnsMode = 'system');
|
||||||
|
downloads.refresh();
|
||||||
|
};
|
||||||
// COM 준비 상태 지킴이: 풀려 있으면 (앱 안 브라우저가 "CoInitialize 가 호출되지 않았습니다" 로 안 뜨는 원인)
|
// COM 준비 상태 지킴이: 풀려 있으면 (앱 안 브라우저가 "CoInitialize 가 호출되지 않았습니다" 로 안 뜨는 원인)
|
||||||
// 다시 준비하고 작업 기록에 남긴다. 바로 앞의 기록을 보면 무엇을 한 뒤에 풀렸는지 알 수 있다.
|
// 다시 준비하고 작업 기록에 남긴다. 바로 앞의 기록을 보면 무엇을 한 뒤에 풀렸는지 알 수 있다.
|
||||||
Timer.periodic(const Duration(seconds: 3), (_) {
|
Timer.periodic(const Duration(seconds: 3), (_) {
|
||||||
|
|
@ -619,6 +623,10 @@ Future<void> runAndroid(String dataDir) async {
|
||||||
await controller.updateSettings((x) => x.addFinishedDownloads = on);
|
await controller.updateSettings((x) => x.addFinishedDownloads = on);
|
||||||
downloads.refresh();
|
downloads.refresh();
|
||||||
};
|
};
|
||||||
|
downloads.useSystemDns = () async {
|
||||||
|
await controller.updateSettings((x) => x.dnsMode = 'system');
|
||||||
|
downloads.refresh();
|
||||||
|
};
|
||||||
downloads.addToEditList = controller.addDownloaded;
|
downloads.addToEditList = controller.addDownloaded;
|
||||||
downloads.log = controller.note;
|
downloads.log = controller.note;
|
||||||
// 진행 중인 일이 있으면 화면에서 내려가도 계속 (알림에 진행 상황)
|
// 진행 중인 일이 있으면 화면에서 내려가도 계속 (알림에 진행 상황)
|
||||||
|
|
|
||||||
|
|
@ -258,12 +258,18 @@ class _DownloadRow extends StatelessWidget {
|
||||||
static final _small = TextButton.styleFrom(
|
static final _small = TextButton.styleFrom(
|
||||||
visualDensity: VisualDensity.compact, padding: const EdgeInsets.symmetric(horizontal: 6), minimumSize: const Size(0, 28));
|
visualDensity: VisualDensity.compact, padding: const EdgeInsets.symmetric(horizontal: 6), minimumSize: const Size(0, 28));
|
||||||
|
|
||||||
|
/// 실패 이유 (사람 말). 263: DNS 서버에 닿지 않아 실패했으면 그 이유 (yt-dlp 원문은 "프록시 터널 실패" 뿐이라 알 수 없었다)
|
||||||
|
static String _why(DownloadTask t) => switch (t.extra['dnsServer']) {
|
||||||
|
final String s => trf('DNS 서버 ({0}) 에 닿지 않아 받지 못했습니다', [s]),
|
||||||
|
_ => friendlyDownloadError(t.error ?? ''),
|
||||||
|
};
|
||||||
|
|
||||||
/// 136: 실패 원문 (yt-dlp · aria2 가 남긴 그대로 - 복사해 물어볼 수 있게)
|
/// 136: 실패 원문 (yt-dlp · aria2 가 남긴 그대로 - 복사해 물어볼 수 있게)
|
||||||
static Future<void> _showDetail(BuildContext context, DownloadTask t) => showDialog<void>(
|
static Future<void> _showDetail(BuildContext context, DownloadTask t) => showDialog<void>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
scrollable: true,
|
scrollable: true,
|
||||||
title: Text(friendlyDownloadError(t.error ?? '')),
|
title: Text(_why(t)),
|
||||||
content: SizedBox(
|
content: SizedBox(
|
||||||
width: 560,
|
width: 560,
|
||||||
child: Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [
|
child: Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||||
|
|
@ -311,14 +317,16 @@ class _DownloadRow extends StatelessWidget {
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
if (t.state == DownloadState.failed)
|
if (t.state == DownloadState.failed)
|
||||||
Wrap(crossAxisAlignment: WrapCrossAlignment.center, spacing: 4, children: [
|
Wrap(crossAxisAlignment: WrapCrossAlignment.center, spacing: 4, children: [
|
||||||
Text('$label · ${friendlyDownloadError(t.error ?? '')}',
|
Text('$label · ${_why(t)}', style: const TextStyle(fontSize: 12, color: JjColors.danger)),
|
||||||
style: const TextStyle(fontSize: 12, color: JjColors.danger)),
|
|
||||||
if ((t.error ?? '').trim().isNotEmpty)
|
if ((t.error ?? '').trim().isNotEmpty)
|
||||||
TextButton(
|
TextButton(
|
||||||
style: _small,
|
style: _small,
|
||||||
onPressed: () => _showDetail(context, t),
|
onPressed: () => _showDetail(context, t),
|
||||||
child: Text(tr('자세히')),
|
child: Text(tr('자세히')),
|
||||||
),
|
),
|
||||||
|
// 263: 아직 DNS 강제면 [시스템 DNS 로 바꾸기] (바꾼 뒤 [다시 시도] 는 사용자가 - 몰래 다시 받지 않음)
|
||||||
|
if (t.extra['dnsServer'] != null && d.useSystemDns != null && d.settings().dnsMode != 'system')
|
||||||
|
TextButton(style: _small, onPressed: () => d.useSystemDns!(), child: Text(tr('시스템 DNS 로 바꾸기'))),
|
||||||
TextButton(style: _small, onPressed: () => d.resume([t]), child: Text(tr('다시 시도'))),
|
TextButton(style: _small, onPressed: () => d.resume([t]), child: Text(tr('다시 시도'))),
|
||||||
])
|
])
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,18 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:jj_mkvmaker/app/download_manager.dart';
|
import 'package:jj_mkvmaker/app/download_manager.dart';
|
||||||
import 'package:jj_mkvmaker/app/i18n_controller.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/app/settings.dart';
|
||||||
|
import 'package:jj_mkvmaker/core/dns_resolver.dart';
|
||||||
import 'package:jj_mkvmaker/core/download_detect.dart';
|
import 'package:jj_mkvmaker/core/download_detect.dart';
|
||||||
import 'package:jj_mkvmaker/services/downloader.dart';
|
import 'package:jj_mkvmaker/services/downloader.dart';
|
||||||
import 'package:jj_mkvmaker/ui/downloads_page.dart';
|
import 'package:jj_mkvmaker/ui/downloads_page.dart';
|
||||||
|
|
||||||
class _Backend implements DownloadBackend {
|
class _Backend implements DownloadBackend {
|
||||||
var starts = 0;
|
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
|
@override
|
||||||
DownloadKind get kind => DownloadKind.video;
|
DownloadKind get kind => DownloadKind.video;
|
||||||
@override
|
@override
|
||||||
|
|
@ -18,8 +23,7 @@ class _Backend implements DownloadBackend {
|
||||||
starts++;
|
starts++;
|
||||||
t
|
t
|
||||||
..state = DownloadState.failed
|
..state = DownloadState.failed
|
||||||
..error = 'ERROR: [generic] Unable to download webpage: HTTP Error 404: Not Found '
|
..error = error;
|
||||||
'(https://cdn.example.com/v.mp4?sig=SECRET123&expire=1)';
|
|
||||||
changed();
|
changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,4 +97,68 @@ void main() {
|
||||||
expect(b.starts, 2, reason: '[다시 시도] 로 다시 받기 시작 (이 가짜 백엔드는 또 실패)');
|
expect(b.starts, 2, reason: '[다시 시도] 로 다시 받기 시작 (이 가짜 백엔드는 또 실패)');
|
||||||
d.dispose();
|
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.dnsMode = '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.dnsMode, '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();
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue