rc 회귀 고침: 네트워크 공유 (UNC) 목록 · 기록 파일은 \\ 그대로 · 받는 쪽 UNC 도 \\ · 막히면 모니터링 [▶ 실행] 도 끔 · 표준 입력 (-) 문구 (검토 대기)
관리자 지시 (10/20, 28 이next11f43f067에서 확인 - 지운 것은 없음): 1. 회귀:ed459a0이 목록 · 기록 · 배치 파일의 \\localhost\C$\… 를 //localhost/C$/… 로 바꿔 넘겨 rsync 가 "failed to open exclude file" (exit 11) 로 멈췄다 (985 는 \\ 그대로라 됨). 15 가 앱이 실제로 띄우는 방식 (응답 파일 · MSYS=noglob, rsync 3.5.1) 으로 자리마다 확인 - 28 의 측정과 같음: 원본 = // 만 됨 (\\ 는 exit 23), 받는 쪽 = \\ 만 됨 (// 는 change_dir 실패 exit 3), 목록 · 기록 파일 = \\ 만 됨 (// 는 exit 11 · 기록이 안 생김). - rsyncFileOptionArgs: UNC 는 \\ 로 (//서버 로 적은 것도 \\ 로 바꿈), 드라이브는 /cygdrive/… 그대로. - 받는 쪽: 새 rsyncDestPath - UNC 면 \\서버\… (rsyncArgs 의 대상 · 실시간 동기화 rsyncArgsFor 의 받는 쪽). 원본은 toCygwinPath (//) 그대로.ed459a0전부터 Windows 네트워크 공유가 받는 쪽인 rsync 는 실패했다 (관리자 판단: 데이터 문제는 아니지만 사용자가 겪는 실패라 rc 에). 2. 모니터링 복사 · rsync 카드: 옵션이 막혀 빨간 안내가 보이면 [▶ 실행] 도 끈다 (확인 창과 같게 - 누르면 실패로 끝났다). 3. --files-from=- · --exclude-from=- (표준 입력) 은 따로 된 문구 "표준 입력 (-) 은 쓸 수 없습니다" · 끝 문구 "목록 파일의 전체 경로를 적어 주세요" (예전에는 "적어 주세요: - - C:\ …" 로 하이픈이 겹침). 시험: rsync_msys_args_test (실제 rsync - 관리 공유 \\localhost\C$ 로 원본 · 받는 쪽 UNC 와 목록 파일 \\ · // 를 복사 작업으로, 받는 쪽 UNC 실시간 동기화 쌍 · 바꿔 넘기기 단위 · 표준 입력 문구) · monitor_test (막히면 [실행] 꺼짐 · 고치면 켜짐). 전체 시험 1142 통과 · 1 실패 (explorer_test "여러 개 표시 → 삭제 · 새 폴더 · 이름 변경" - 이번 고침과 상관없음, 혼자 두 번 통과 ·ed459a0의 전체 시험에서도 통과 - 부하 흔들림) · analyze 문제 없음 · l10n 빠진 번역 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
parent
f43f067d4b
commit
9e40b485a9
|
|
@ -1245,6 +1245,7 @@
|
|||
"목록 · 영상 옆 · 영상 위에 나오는 광고 영역을 감춥니다": "Hides ad areas in lists · next to · on top of videos",
|
||||
"목록 보기": "View list",
|
||||
"목록 파일은 전체 경로로 적어 주세요: {0}": "Please write the full path of the list file: {0}",
|
||||
"목록 파일의 전체 경로를 적어 주세요": "write the full path of the list file",
|
||||
"목록에 JJ_MKVMaker 가 없으면 [다른 앱 허용] 으로 JJ_MKVMaker.exe 를 고르고 \"개인\" 에 체크합니다. 바꾼 뒤 공유를 끄고 다시 켜세요.": "If JJ_MKVMaker is not in the list, use [Allow another app] to pick JJ_MKVMaker.exe and check \"Private\". Turn sharing off and on again afterwards.",
|
||||
"목록에서 동영상을 누르면 여기에 자세히 보입니다": "Tap a video in the list to see its details here",
|
||||
"목록에서 빼기": "Remove from list",
|
||||
|
|
@ -2731,6 +2732,7 @@
|
|||
"표시 이름은 바로 고칠 수 있습니다. 동영상 줄을 세 번 누르면 첫 번째 버튼의 폴더로 옮깁니다": "You can edit display names right here. Pressing a video row three times moves it to the first button's folder",
|
||||
"표시 지우기": "Clear marks",
|
||||
"표준": "Standard",
|
||||
"표준 입력 (-) 은 쓸 수 없습니다": "Standard input (-) cannot be used",
|
||||
"푸는 중": "Extracting",
|
||||
"푸는 중: {0}": "Extracting: {0}",
|
||||
"풀 수 없는 이름 {0}개": "{0} invalid names",
|
||||
|
|
|
|||
|
|
@ -1245,6 +1245,7 @@
|
|||
"목록 · 영상 옆 · 영상 위에 나오는 광고 영역을 감춥니다": "リスト・動画の横・動画の上に出る広告エリアを隠します",
|
||||
"목록 보기": "リストを見る",
|
||||
"목록 파일은 전체 경로로 적어 주세요: {0}": "リストファイルはフルパスで書いてください: {0}",
|
||||
"목록 파일의 전체 경로를 적어 주세요": "リストファイルのフルパスを書いてください",
|
||||
"목록에 JJ_MKVMaker 가 없으면 [다른 앱 허용] 으로 JJ_MKVMaker.exe 를 고르고 \"개인\" 에 체크합니다. 바꾼 뒤 공유를 끄고 다시 켜세요.": "一覧に JJ_MKVMaker がなければ [別のアプリの許可] で JJ_MKVMaker.exe を選び「プライベート」にチェックします。変更後は共有をオフにして再びオンにしてください。",
|
||||
"목록에서 동영상을 누르면 여기에 자세히 보입니다": "リストの動画をタップすると、ここに詳細が表示されます",
|
||||
"목록에서 빼기": "リストから外す",
|
||||
|
|
@ -2731,6 +2732,7 @@
|
|||
"표시 이름은 바로 고칠 수 있습니다. 동영상 줄을 세 번 누르면 첫 번째 버튼의 폴더로 옮깁니다": "表示名はその場で変更できます。動画の行を 3回押すと最初のボタンのフォルダーに移動します",
|
||||
"표시 지우기": "マークを解除",
|
||||
"표준": "標準",
|
||||
"표준 입력 (-) 은 쓸 수 없습니다": "標準入力 (-) は使えません",
|
||||
"푸는 중": "展開中",
|
||||
"푸는 중: {0}": "展開中: {0}",
|
||||
"풀 수 없는 이름 {0}개": "展開できない名前 {0} 件",
|
||||
|
|
|
|||
|
|
@ -1245,6 +1245,7 @@
|
|||
"목록 · 영상 옆 · 영상 위에 나오는 광고 영역을 감춥니다": "隐藏列表 · 视频旁 · 视频上方出现的广告区域",
|
||||
"목록 보기": "查看列表",
|
||||
"목록 파일은 전체 경로로 적어 주세요: {0}": "请填写列表文件的完整路径: {0}",
|
||||
"목록 파일의 전체 경로를 적어 주세요": "请填写列表文件的完整路径",
|
||||
"목록에 JJ_MKVMaker 가 없으면 [다른 앱 허용] 으로 JJ_MKVMaker.exe 를 고르고 \"개인\" 에 체크합니다. 바꾼 뒤 공유를 끄고 다시 켜세요.": "如果列表中没有 JJ_MKVMaker,请用 [允许其他应用] 选择 JJ_MKVMaker.exe 并勾选“专用”。更改后请关闭共享再重新开启。",
|
||||
"목록에서 동영상을 누르면 여기에 자세히 보입니다": "点按列表中的视频即可在此查看详情",
|
||||
"목록에서 빼기": "从列表中移除",
|
||||
|
|
@ -2731,6 +2732,7 @@
|
|||
"표시 이름은 바로 고칠 수 있습니다. 동영상 줄을 세 번 누르면 첫 번째 버튼의 폴더로 옮깁니다": "显示名称可直接修改。连续点击视频行三次会移到第一个按钮的文件夹",
|
||||
"표시 지우기": "清除标记",
|
||||
"표준": "标准",
|
||||
"표준 입력 (-) 은 쓸 수 없습니다": "不能使用标准输入 (-)",
|
||||
"푸는 중": "正在解压",
|
||||
"푸는 중: {0}": "正在解压: {0}",
|
||||
"풀 수 없는 이름 {0}개": "{0} 个无法解压的名称",
|
||||
|
|
|
|||
|
|
@ -661,7 +661,7 @@ class LiveSync extends ChangeNotifier {
|
|||
if (x.delete && splitOptions(s.rsyncOptions).contains('--delete-excluded')) '--delete-excluded',
|
||||
...appTempRsyncExcludes, // 78 보충: 이름 바꾸는 중의 임시 이름은 보내지도 지우지도 않는다
|
||||
'${toCygwinPath(x.source, windows: windows)}/',
|
||||
'${toCygwinPath(x.target, windows: windows)}/',
|
||||
'${rsyncDestPath(x.target, windows: windows)}/', // 받는 쪽 UNC 는 \\서버\… (28 · 15 확인 10/20)
|
||||
];
|
||||
|
||||
/// robocopy 인수 (실시간 동기화). 지우기 (/MIR · /PURGE) 는 쌍의 "지우기 포함" 으로만 (/MIR 는 /E 로) - 97
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ const _rsyncLongWithValue = {
|
|||
List<String> unreadableOptions(String options, {bool? windows}) => [for (final x in optionProblems(options, windows: windows)) x.text];
|
||||
|
||||
/// 옵션 칸의 문제 종류 - 안내의 끝 문구 (고칠 방법) 를 고른다 (관리자 지시 10/20, 28 지적)
|
||||
enum OptionProblemKind { unclosed, path, noValue, odd, doubleQuote, relativePath }
|
||||
enum OptionProblemKind { unclosed, path, noValue, odd, doubleQuote, relativePath, stdin }
|
||||
|
||||
/// [unreadableOptions] 의 한 줄과 그 종류
|
||||
class OptionProblem {
|
||||
|
|
@ -230,6 +230,7 @@ String optionProblemHint(Iterable<OptionProblem> problems, {bool? windows}) {
|
|||
if (kinds.contains(OptionProblemKind.doubleQuote)) tr('큰따옴표 (") 를 빼 주세요'),
|
||||
// 관리자 지시 (10/20): Windows 는 드라이브로 시작하는 전체 경로만 ("/" 는 rsync 가 자기 설치 폴더 기준으로 읽음), Android 는 /
|
||||
if (kinds.contains(OptionProblemKind.relativePath)) windows ? tr('C:\\ 처럼 드라이브로 시작하는 전체 경로로') : tr('/ 로 시작하는 전체 경로로'),
|
||||
if (kinds.contains(OptionProblemKind.stdin)) tr('목록 파일의 전체 경로를 적어 주세요'),
|
||||
].join(' · ');
|
||||
}
|
||||
|
||||
|
|
@ -280,7 +281,12 @@ List<OptionProblem> optionProblems(String options, {bool? windows}) {
|
|||
// 관리자 판단 (10/20): 목록 파일의 상대 경로 - rsync 는 자기 작업 폴더 (Windows 는 응답 파일 폴더) 기준으로, 미리 보기는 앱의
|
||||
// 작업 폴더 기준으로 읽어 서로 다른 파일을 볼 수 있다. 전체 경로만 받는다
|
||||
for (final f in listFileValues(parsed.words)) {
|
||||
if (!_absolutePath(f, windows: windows)) bad.add(OptionProblem(OptionProblemKind.relativePath, trf('목록 파일은 전체 경로로 적어 주세요: {0}', [f]), f));
|
||||
// 관리자 지시 (10/20, 28 지적): "-" (표준 입력) 는 따로 - 앱은 rsync 에 표준 입력을 주지 않는다
|
||||
if (f == '-') {
|
||||
bad.add(OptionProblem(OptionProblemKind.stdin, tr('표준 입력 (-) 은 쓸 수 없습니다'), f));
|
||||
} else if (!_absolutePath(f, windows: windows)) {
|
||||
bad.add(OptionProblem(OptionProblemKind.relativePath, trf('목록 파일은 전체 경로로 적어 주세요: {0}', [f]), f));
|
||||
}
|
||||
}
|
||||
// 관리자 지시 (10/20, 28 확인): 기록 · 배치 파일도 - 상대 경로면 응답 파일 임시 폴더에 생겼다가 함께 지워져 찾을 수 없다.
|
||||
// --backup-dir · --partial-dir · --temp-dir · --compare-dest 류는 받는 쪽 기준이라 그대로
|
||||
|
|
@ -347,7 +353,13 @@ List<String> rsyncFileOptionArgs(List<String> words, {required bool windows}) {
|
|||
const opts = {
|
||||
'--exclude-from', '--include-from', '--files-from', '--log-file', '--write-batch', '--read-batch', '--only-write-batch',
|
||||
};
|
||||
String conv(String v) => RegExp(r'^(?:[A-Za-z]:[\\/]|\\\\)').hasMatch(v) ? toCygwinPath(v) : v;
|
||||
// 28 · 15 확인 (10/20, MSYS 3.5.1): 이 파일들은 UNC 를 \\서버\… 로만 연다 (//서버/… 는 "failed to open", exit 11) - UNC 는 \\ 로,
|
||||
// 드라이브는 /cygdrive/… 로
|
||||
String conv(String v) => RegExp(r'^[A-Za-z]:[\\/]').hasMatch(v)
|
||||
? toCygwinPath(v)
|
||||
: RegExp(r'^(?:\\\\|//)(?!cygdrive/)').hasMatch(v)
|
||||
? v.replaceAll('/', '\\')
|
||||
: v;
|
||||
String rule(String r) {
|
||||
final m = RegExp(r'^(\s*(?:\.|merge)(?:,[^\s]*)?\s+)(.+)$').firstMatch(r);
|
||||
return m == null ? r : '${m[1]}${conv(m[2]!)}';
|
||||
|
|
@ -399,6 +411,13 @@ List<String> rsyncFileOptionArgs(List<String> words, {required bool windows}) {
|
|||
|
||||
/// Windows 경로 → MSYS2 rsync 가 읽는 경로 (C:\a\b → /cygdrive/c/a/b, \\server\share\x → //server/share/x).
|
||||
/// MSYS2 rsync 는 "C:" 를 원격 컴퓨터 이름으로 읽으므로 꼭 바꿔야 한다.
|
||||
/// 받는 쪽 (대상) 경로를 rsync 에 넘길 꼴로. 28 · 15 확인 (10/20, MSYS 3.5.1): 받는 쪽의 UNC 는 \\서버\… 만 된다
|
||||
/// (//서버/… 는 change_dir 실패, exit 3) - 원본은 거꾸로 //서버/… 만 되어 [toCygwinPath] 그대로
|
||||
String rsyncDestPath(String path, {bool windows = true}) {
|
||||
if (windows && RegExp(r'^(?:\\\\|//)(?!cygdrive/)').hasMatch(path)) return path.replaceAll('/', '\\');
|
||||
return toCygwinPath(path, windows: windows);
|
||||
}
|
||||
|
||||
String toCygwinPath(String path, {bool windows = true}) {
|
||||
if (!windows) return path;
|
||||
final s = path.replaceAll('\\', '/');
|
||||
|
|
@ -436,7 +455,7 @@ List<String> rsyncArgs({
|
|||
...appTempRsyncExcludes, // 78 보충: 일괄 이름 바꾸기 중의 임시 이름 (~jjren_ · ~jjback_) 은 보내지 않는다
|
||||
// [contents]: 폴더 "안의 것" 을 대상에 (rsync 의 끝 / - 동기화 · lsync 와 같은 모양)
|
||||
for (final s in sources) '${toCygwinPath(_noTrailingSlash(s), windows: windows)}${contents ? '/' : ''}',
|
||||
'${toCygwinPath(_noTrailingSlash(dest), windows: windows)}/',
|
||||
'${rsyncDestPath(_noTrailingSlash(dest), windows: windows)}/',
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -698,7 +698,8 @@ class _CopyCardState extends State<_CopyCard> {
|
|||
TextButton.icon(onPressed: () => center.cancel(t.id), icon: const Icon(Icons.stop, size: 18), label: Text(tr('취소')))
|
||||
else
|
||||
FilledButton.icon(
|
||||
onPressed: () => _runPressed(t),
|
||||
// 관리자 지시 (10/20, 28 지적): 옵션이 막히면 (빨간 안내가 보이면) 확인 창처럼 [실행] 도 끈다 - 누르면 실패로 끝났다
|
||||
onPressed: _method == 'rsync' && optionProblems(_options.text).isNotEmpty ? null : () => _runPressed(t),
|
||||
icon: const Icon(Icons.play_arrow, size: 18),
|
||||
label: Text(t.lastResult == 'cancelled' || t.lastResult == 'failed' ? tr('재개') : tr('실행')),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -329,6 +329,26 @@ void main() {
|
|||
expect(find.text('복사 · rsync 로 이동'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('관리자 지시 (10/20, 28 지적): 옵션이 막히면 (빨간 안내) 카드의 [▶ 실행] 도 꺼진다', (tester) async {
|
||||
final tmp = Directory.systemTemp.createTempSync('jj_monbad_');
|
||||
addTearDown(() => tmp.deleteSync(recursive: true));
|
||||
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
|
||||
c.settings.copyTasks = [CopyTask(id: '1', sources: [tmp.path], dest: p.join(tmp.path, '..'), method: 'rsync', options: '-av delete')];
|
||||
tester.view.physicalSize = const Size(1500, 1000);
|
||||
tester.view.devicePixelRatio = 1;
|
||||
addTearDown(tester.view.reset);
|
||||
await tester.pumpWidget(MaterialApp(home: MonitorPage(c: c)));
|
||||
await tester.pump();
|
||||
FilledButton run() => tester.widget<FilledButton>(find.ancestor(of: find.text('실행'), matching: find.byWidgetPredicate((w) => w is FilledButton)));
|
||||
expect(find.textContaining('옵션을 알아볼 수 없습니다'), findsOneWidget);
|
||||
expect(run().onPressed, isNull, reason: '막히면 꺼짐');
|
||||
// 옵션을 고치면 다시 켜진다
|
||||
await tester.enterText(find.widgetWithText(TextField, '옵션'), '-av');
|
||||
await tester.pump();
|
||||
expect(find.textContaining('옵션을 알아볼 수 없습니다'), findsNothing);
|
||||
expect(run().onPressed, isNotNull);
|
||||
});
|
||||
|
||||
testWidgets('233: 이동 카드의 저장 창은 "이 이동을" · 229 설정은 Rsync 탭에도 있다', (tester) async {
|
||||
final tmp = Directory.systemTemp.createTempSync('jj_mon233_');
|
||||
addTearDown(() => tmp.deleteSync(recursive: true));
|
||||
|
|
|
|||
|
|
@ -86,8 +86,20 @@ void main() {
|
|||
});
|
||||
|
||||
test('관리자 판단 (10/20, 28 확인): 목록 · 기록 · 배치 파일의 C:\\ · C:/ · UNC 는 rsync 가 읽는 꼴로 바꿔 넘긴다 (막지 않음)', () {
|
||||
expect(rsyncFileOptionArgs([r'--exclude-from=C:\l\a.txt', '--include-from', 'C:/l/b.txt', r'--files-from=\\nas\s\f.txt'], windows: true),
|
||||
['--exclude-from=/cygdrive/c/l/a.txt', '--include-from', '/cygdrive/c/l/b.txt', '--files-from=//nas/s/f.txt']);
|
||||
// UNC 는 \\서버\… 로 (28 · 15 확인 10/20: 목록 · 기록 파일은 //서버/… 를 열지 못함, exit 11)
|
||||
expect(
|
||||
rsyncFileOptionArgs(
|
||||
[r'--exclude-from=C:\l\a.txt', '--include-from', 'C:/l/b.txt', r'--files-from=\\nas\s\f.txt', '--exclude-from=//nas/s/g.txt'],
|
||||
windows: true),
|
||||
['--exclude-from=/cygdrive/c/l/a.txt', '--include-from', '/cygdrive/c/l/b.txt', r'--files-from=\\nas\s\f.txt', r'--exclude-from=\\nas\s\g.txt']);
|
||||
// 받는 쪽은 UNC 를 \\ 로 (//서버 는 change_dir 실패, exit 3) · 원본은 // 그대로 (toCygwinPath)
|
||||
expect(rsyncDestPath(r'\\nas\s\d'), r'\\nas\s\d');
|
||||
expect(rsyncDestPath('//nas/s/d'), r'\\nas\s\d');
|
||||
expect(rsyncDestPath(r'C:\d'), '/cygdrive/c/d');
|
||||
expect(rsyncDestPath(r'\\nas\s\d', windows: false), r'\\nas\s\d');
|
||||
expect(toCygwinPath(r'\\nas\s\src'), '//nas/s/src');
|
||||
final a = rsyncArgs(options: '-a', sources: [r'\\nas\s\src'], dest: r'\\nas\s\dst', contents: true);
|
||||
expect(a.sublist(a.length - 2), ['//nas/s/src/', r'\\nas\s\dst/']);
|
||||
expect(rsyncFileOptionArgs(['--filter=merge,- C:/r/m.txt', '--filter', '. C:/r/d.txt', '-f. C:/r/e.txt', '-avf', 'merge C:/r/g.txt'], windows: true),
|
||||
['--filter=merge,- /cygdrive/c/r/m.txt', '--filter', '. /cygdrive/c/r/d.txt', '-f. /cygdrive/c/r/e.txt', '-avf', 'merge /cygdrive/c/r/g.txt']);
|
||||
expect(rsyncFileOptionArgs(['--log-file=C:/logs/r.log', '--write-batch', r'D:\b\batch', '--only-write-batch=C:/b2', '--read-batch=C:/b3'], windows: true),
|
||||
|
|
@ -98,6 +110,14 @@ void main() {
|
|||
expect(rsyncFileOptionArgs([r'--exclude-from=C:\l.txt'], windows: false), [r'--exclude-from=C:\l.txt']);
|
||||
});
|
||||
|
||||
test('관리자 지시 (10/20, 28 지적): 표준 입력 (-) 은 따로 된 문구 (하이픈이 겹치지 않게)', () {
|
||||
for (final o in ['-a --files-from=-', '-a --exclude-from=-', '-a --include-from -']) {
|
||||
final bad = optionProblems(o, windows: true);
|
||||
expect(bad.map((x) => x.text), ['표준 입력 (-) 은 쓸 수 없습니다'], reason: o);
|
||||
expect(optionProblemHint(bad, windows: true), '목록 파일의 전체 경로를 적어 주세요', reason: o);
|
||||
}
|
||||
});
|
||||
|
||||
group('실제 rsync', () {
|
||||
final rsync = sceneRsync;
|
||||
late Directory tmp;
|
||||
|
|
@ -187,6 +207,35 @@ void main() {
|
|||
expect(File(p.join(lists.path, 'r.log')).existsSync(), isTrue);
|
||||
}, skip: Platform.isWindows ? false : 'Windows 만 (MSYS2 rsync)');
|
||||
|
||||
test('28 · 15 확인 (10/20): 네트워크 공유 (UNC, 관리 공유 \\\\localhost\\C\$ 로) - 원본 · 받는 쪽 · 목록 파일 (\\\\ · //) 이 모두 된다', () async {
|
||||
if (rsync == null) return markTestSkipped('앱에 든 rsync 가 없음');
|
||||
String unc(String local) => '\\\\localhost\\C\$${local.substring(2)}';
|
||||
if (!Directory(unc(tmp.path)).existsSync()) return markTestSkipped('관리 공유 \\\\localhost\\C\$ 를 쓸 수 없음');
|
||||
final list = File(p.join(tmp.path, 'ex.txt'))..writeAsStringSync('keep.txt\n');
|
||||
for (final (name, listArg) in [('목록 \\\\', unc(list.path)), ('목록 //', unc(list.path).replaceAll(r'\', '/'))]) {
|
||||
final (s, d) = scene('us_${name.length}', 'ud_${name.length}', ['a.txt'], ['keep.txt', 'old.txt']);
|
||||
// 사용자가 고른 그대로의 UNC 경로 (원본 · 받는 쪽 모두)
|
||||
final job = TransferJob(
|
||||
sources: [unc(s)], dest: unc(d), move: false, method: CopyMethod.rsync, options: '-a --delete --exclude-from=$listArg', rsyncExe: rsync, contents: true);
|
||||
await job.run();
|
||||
expect(job.error, isNull, reason: '$name: ${job.error}');
|
||||
expect(treeOf(d).keys.toSet(), {'keep.txt', 'a.txt'}, reason: name);
|
||||
}
|
||||
// 실시간 동기화 쌍의 받는 쪽도 UNC
|
||||
final (s, d) = scene('ls', 'ld', ['a.txt'], ['old.txt']);
|
||||
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
|
||||
c.settings
|
||||
..rsyncSource = 'custom'
|
||||
..rsyncPath = rsync
|
||||
..rsyncOptions = '-a'
|
||||
..liveSyncPairs = [LiveSyncPair(unc(s), unc(d), method: 'rsync', delete: true, deleteConfirmed: true, confirmedOptions: '-a')];
|
||||
final live = LiveSync(c);
|
||||
addTearDown(live.dispose);
|
||||
await live.syncNow(c.settings.liveSyncPairs.single);
|
||||
expect(live.status[LiveSync.keyOf(c.settings.liveSyncPairs.single)]!.$2, isNot(contains('실패')));
|
||||
expect(treeOf(d).keys.toSet(), {'a.txt'});
|
||||
}, skip: Platform.isWindows ? false : 'Windows 만 (MSYS2 rsync)');
|
||||
|
||||
test("실시간 동기화 (LiveSync rsync 쌍): ' 두 개의 --exclude · ' 가 든 경로", () async {
|
||||
if (rsync == null) return markTestSkipped('앱에 든 rsync 가 없음');
|
||||
final (s, d) = scene("s'1", "d'2", ['a.txt'], ["it's.txt", "Bob's.txt", 'old.txt']);
|
||||
|
|
|
|||
Loading…
Reference in New Issue