import 'dart:io'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:jj_mkvmaker/app/app_controller.dart'; import 'package:jj_mkvmaker/app/i18n_controller.dart'; import 'package:jj_mkvmaker/app/live_sync.dart'; import 'package:jj_mkvmaker/app/settings.dart'; import 'package:jj_mkvmaker/core/sync_tools.dart' show toCygwinPath; import 'package:jj_mkvmaker/l10n/tr.dart'; import 'package:jj_mkvmaker/platform/windows/desktop_storage_service.dart'; import 'package:jj_mkvmaker/platform/windows/process_media_tool.dart'; import 'package:jj_mkvmaker/services/platform_services.dart'; import 'package:jj_mkvmaker/ui/monitor_page.dart'; import 'package:jj_mkvmaker/ui/theme.dart'; import 'package:path/path.dart' as p; import 'support/temp_dir.dart'; /// P0 (관리자 3a 10/20): 미리 볼 수 없는 거르기 규칙 (merge 파일 - 안에 "H a.txt") 이 있는 지우기 포함 쌍 - 모니터링 카드의 빨간 줄 /// "자동으로 지우지 않습니다" · [지금 지우기 (확인)] 창 (FilterLimitNote · 체크) 을 412 · 960, ko · en · ja 로 찍는다. /// 시험 폴더 안에서만 (창은 [나중에] - 지우지 않음). /// 실행: flutter test integration_test\windows_lsync_p0_shot_test.dart -d windows (JJ_SHOT_DIR 에 PNG) void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); final out = Platform.environment['JJ_SHOT_DIR'] ?? p.join(Directory.systemTemp.path, 'jj_shots_lsync_p0'); final rsync = p.join(Directory.current.path, 'third_party', 'rsync', 'windows', 'rsync.exe'); for (final lang in ['ko', 'en', 'ja']) { for (final (w, h) in [(412, 900), (960, 600)]) { testWidgets('미리 볼 수 없는 규칙의 쌍 $lang ${w}x$h', (t) async { if (!File(rsync).existsSync()) return markTestSkipped('앱에 든 rsync 가 없음'); t.view.physicalSize = Size(w.toDouble(), h.toDouble()); t.view.devicePixelRatio = 1; addTearDown(t.view.reset); await t.runAsync(() => i18n.apply(lang, save: false)); addTearDown(() => i18n.apply('ko', save: false)); final tmp = testTempDir('jj_lsyncp0_shot_'); final src = p.join(tmp.path, 's'), dst = p.join(tmp.path, 'd'); for (final (dir, rel) in [(src, 'a.txt'), (src, 'b.txt'), (dst, 'a.txt'), (dst, 'old.log'), (dst, 'extra.txt')]) { File(p.join(dir, rel)) ..createSync(recursive: true) ..writeAsStringSync('$dir/$rel'); } File(p.join(tmp.path, 'rules.txt')).writeAsStringSync('H a.txt\n'); final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService())); c.settings ..copy.rsyncSource = 'custom' ..copy.rsyncPath = rsync ..copy.rsyncOptions = '-a --filter="merge ${toCygwinPath(p.join(tmp.path, 'rules.txt'))}"' ..copy.liveSyncPairs = [LiveSyncPair(src, dst, method: 'rsync', delete: true, deleteConfirmed: true)]; final live = LiveSync(c)..start(); addTearDown(live.dispose); final pair = c.settings.copy.liveSyncPairs.single; await t.runAsync(() => live.syncNow(pair)); expect(live.noAutoDelete[LiveSync.keyOf(pair)], isNotEmpty); final shotKey = GlobalKey(); await t.pumpWidget(MaterialApp( theme: buildTheme(), debugShowCheckedModeBanner: false, builder: (_, child) => RepaintBoundary(key: shotKey, child: child), home: MonitorPage(c: c), )); Future settle([int n = 8]) async { for (var i = 0; i < n; i++) { await t.runAsync(() => Future.delayed(const Duration(milliseconds: 80))); await t.pump(const Duration(milliseconds: 80)); } } Future shot(String name) async { await settle(3); expect(t.takeException(), isNull, reason: '$name 넘침'); final b = shotKey.currentContext!.findRenderObject()! as RenderRepaintBoundary; final img = await t.runAsync(() => b.toImage()); final bytes = await t.runAsync(() => img!.toByteData(format: ui.ImageByteFormat.png)); Directory(out).createSync(recursive: true); File(p.join(out, 'lsync_p0_${name}_${lang}_$w.png')).writeAsBytesSync(bytes!.buffer.asUint8List()); } await t.tap(find.text(tr('lsync (실시간 동기화)'))); await settle(); expect(find.text(tr('이 옵션의 일부 거르기 규칙은 미리 볼 수 없어 자동으로 지우지 않습니다')), findsOneWidget); await shot('1_card'); await t.tap(find.text(tr('지금 지우기 (확인)'))); await settle(12); expect(find.textContaining(tr('알고 실행합니다 (지울 목록이 실제와 다를 수 있음)')), findsOneWidget); await shot('2_now'); await t.tap(find.text(tr('나중에'))); await settle(); expect(File(p.join(dst, 'old.log')).existsSync() && File(p.join(dst, 'a.txt')).existsSync(), isTrue, reason: '찍기만 - 지우지 않음'); }); } } }