diff --git a/lib/ui/monitor_page.dart b/lib/ui/monitor_page.dart index 42a8cd0..4fbc595 100644 --- a/lib/ui/monitor_page.dart +++ b/lib/ui/monitor_page.dart @@ -19,19 +19,11 @@ import 'copy_sync_settings.dart' show addLiveSyncPairDialog, editLiveSyncPairDia import 'path_label.dart'; import 'rsync_setup.dart'; import 'schedule_editor.dart'; -import '../core/sync_preview.dart' show withoutSourceRemovingOptions, optionsRemoveSource; -import 'sync_preview_view.dart' show confirmDeletingRun; +import '../core/sync_preview.dart' show optionsRemoveSource; +import 'sync_preview_view.dart' show confirmDeletingRun, effectiveRunText; import 'theme.dart'; /// 모니터링 (파일 탐색기 > 모니터링): 복사 · rsync 와 lsync (실시간 동기화) 를 따로 보여 준다. -/// 178 · 209: 실제로 실행할 방법 · 옵션 (원본 지우기 옵션은 빼고, 이동일 때만 그 방법의 원본 지우기를 붙인다) -String effectiveRunText(String method, String options, {required bool move}) { - final name = method == 'builtin' ? tr('현재 방식') : method; - if (method == 'builtin') return name; - final opts = withoutSourceRemovingOptions(options); - final extra = move ? (method == 'robocopy' ? ' /MOVE' : method == 'rsync' ? ' --remove-source-files' : '') : ''; - return '$name $opts$extra'.trim(); -} class MonitorPage extends StatelessWidget { final AppController c; diff --git a/lib/ui/sync_preview_view.dart b/lib/ui/sync_preview_view.dart index a5b57e5..20a670d 100644 --- a/lib/ui/sync_preview_view.dart +++ b/lib/ui/sync_preview_view.dart @@ -182,6 +182,15 @@ bool taskDeletes(CopyTask t) { /// 104: 지우기가 들어 있는 작업은 어디서 실행하든 (모니터링 [실행] · 옵션 저장 뒤 실행 · 최종 정리) Rsync 화면과 같은 미리 보기를 거친다. /// 지울 목록이 나온 뒤에만 [실행] 이 켜진다. 지우기가 없으면 묻지 않고 true +/// 178 · 209: 실제로 실행할 방법 · 옵션 (원본 지우기 옵션은 빼고, 이동일 때만 그 방법의 원본 지우기를 붙인다) +String effectiveRunText(String method, String options, {required bool move}) { + final name = method == 'builtin' ? tr('현재 방식') : method; + if (method == 'builtin') return name; + final opts = withoutSourceRemovingOptions(options); + final extra = move ? (method == 'robocopy' ? ' /MOVE' : method == 'rsync' ? ' --remove-source-files' : '') : ''; + return '$name $opts$extra'.trim(); +} + /// [includeMove] (210): 이동 (원본 파일 지우기) 도 미리 보기 · 확인을 거친다 - 모니터링의 [실행] 처럼 따로 묻는 창이 없을 때. /// [skipNewer] (229): 앱이 직접 할 때 (현재 방식 · WebDAV) 받는 쪽이 더 새것이면 건너뛰는지 (환경 설정) Future confirmDeletingRun(BuildContext context, CopyTask t, {bool includeMove = false, bool skipNewer = false}) async { @@ -210,7 +219,8 @@ Future confirmDeletingRun(BuildContext context, CopyTask t, {bool includeM Text('→ ${shortPath(dstOf(s))}', style: const TextStyle(fontWeight: FontWeight.w600)), Text('${vDisplay(s)} → ${vDisplay(dstOf(s))}', style: const TextStyle(fontSize: 11, color: JjColors.textDim)), ], - Text('${t.method} ${t.options}', style: const TextStyle(fontSize: 12, fontFamily: 'monospace', color: JjColors.textDim)), + // 230: 칸에 적힌 옵션이 아니라 실제로 실행할 것 (원본 지우기 옵션은 이동일 때만 붙는다) + Text(effectiveRunText(t.method, t.options, move: t.move), style: const TextStyle(fontSize: 12, fontFamily: 'monospace', color: JjColors.textDim)), // 210: 이동이면 실행 뒤 원본에서 지워진다는 것을 분명히 if (t.move) Padding( diff --git a/test/dismiss_test.dart b/test/dismiss_test.dart index 8548860..bd3d0f0 100644 --- a/test/dismiss_test.dart +++ b/test/dismiss_test.dart @@ -193,6 +193,30 @@ void main() { expect(find.text('아직 실행 안 함'), findsOneWidget); }); + testWidgets('230: 이동 확인 창의 명령 줄은 실제로 실행할 것 (rsync 는 --remove-source-files 를 붙이고, 복사면 원본 지우기 옵션을 뺀다)', (tester) async { + final src = p.join(tmp.path, 'r'), dst = p.join(tmp.path, 'rd'); + File(p.join(src, 'a.txt')) + ..createSync(recursive: true) + ..writeAsStringSync('a'); + Directory(dst).createSync(); + bool? r; + final mv = CopyTask(id: '1', sources: [src], dest: dst, contents: true, move: true, method: 'rsync', options: '-avPog'); + await host(tester, (ctx) async => r = await confirmDeletingRun(ctx, mv, includeMove: true)); + await settle(tester, () => find.textContaining('비교하는 중').evaluate().isEmpty, rounds: 200); + expect(find.text('rsync -avPog --remove-source-files'), findsOneWidget); + expect(find.text('rsync -avPog'), findsNothing); + await tester.tap(find.text('취소').last); + await tester.pumpAndSettle(); + expect(r, isFalse); + // 복사 (지우기 있음) 인데 칸에 원본 지우기 옵션이 남아 있으면 - 실제로는 빼고 실행하므로 창에도 빼고 + final cp = CopyTask(id: '2', sources: [src], dest: dst, contents: true, method: 'rsync', options: '-avPog --delete --remove-source-files'); + await host(tester, (ctx) async => r = await confirmDeletingRun(ctx, cp)); + await settle(tester, () => find.textContaining('비교하는 중').evaluate().isEmpty, rounds: 200); + expect(find.text('rsync -avPog --delete'), findsOneWidget); + await tester.tap(find.text('취소').last); + await tester.pumpAndSettle(); + }); + testWidgets('105: 원본이 대상 안 + 창에서 [원본 파일 지우기] 로 지우기 옵션이 생기면 막는다', (tester) async { final outer = p.join(tmp.path, 'O'), inner = p.join(outer, 'in'); File(p.join(inner, 'n.txt'))