diff --git a/integration_test/ai_e2e_test.dart b/integration_test/ai_e2e_test.dart index 1f662c6..5eef314 100644 --- a/integration_test/ai_e2e_test.dart +++ b/integration_test/ai_e2e_test.dart @@ -7,6 +7,7 @@ import 'package:jj_mkvmaker/core/languages.dart'; import 'package:jj_mkvmaker/core/models.dart'; import 'package:jj_mkvmaker/core/output_paths.dart'; import 'package:jj_mkvmaker/services/platform_services.dart'; +import 'support/temp_dir.dart'; import 'package:path/path.dart' as p; /// 실제 Whisper + NLLB 로 AI 자막 → MKV (모델이 JJ_MKVMAKER_MODELS 에 있어야 함) @@ -16,7 +17,7 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('영어 음성 영상 → AI 자막(ko/en/ja) → MKV', (tester) async { - final dir = Directory.systemTemp.createTempSync('jj_e2e_'); + final dir = testTempDir('jj_e2e_'); final video = p.join(dir.path, '영어 강의.mp4'); final speech = p.join(Directory.current.path, 'test_assets', 'speech_en.wav'); final r = await Process.run('ffmpeg', [ diff --git a/integration_test/browser_keep_test.dart b/integration_test/browser_keep_test.dart index 97ff79e..64a6837 100644 --- a/integration_test/browser_keep_test.dart +++ b/integration_test/browser_keep_test.dart @@ -10,6 +10,7 @@ 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/browser_page.dart'; +import 'support/temp_dir.dart'; import 'package:path/path.dart' as p; /// 실제 Edge(WebView2): MKV 화면 ↔ 브라우저를 여러 번 오가도 보던 페이지를 다시 읽지 않고 그대로 이어진다 @@ -27,7 +28,7 @@ void main() { await req.response.close(); }); final url = 'http://127.0.0.1:${server.port}/page'; - final dataDir = Directory.systemTemp.createTempSync('jj_keep_').path; + final dataDir = testTempDir('jj_keep_').path; final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService())); c.settings diff --git a/integration_test/browser_real_test.dart b/integration_test/browser_real_test.dart index 3e8a1bd..d9c3a6c 100644 --- a/integration_test/browser_real_test.dart +++ b/integration_test/browser_real_test.dart @@ -13,6 +13,7 @@ import 'package:jj_mkvmaker/platform/windows/process_media_tool.dart'; import 'package:jj_mkvmaker/platform/windows/ytdlp_backend.dart'; import 'package:jj_mkvmaker/services/platform_services.dart'; import 'package:jj_mkvmaker/ui/browser_page.dart'; +import 'support/temp_dir.dart'; import 'package:path/path.dart' as p; /// 실제 Edge(WebView2): 페이지 열기 · 동영상 감지 · 쿠키 저장 → yt-dlp 가 같은 쿠키를 읽는지 @@ -31,7 +32,7 @@ void main() { await req.response.close(); }); final url = 'http://127.0.0.1:${server.port}/'; - final dataDir = Directory.systemTemp.createTempSync('jj_webview_').path; + final dataDir = testTempDir('jj_webview_').path; final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService())); c.settings.webViewDataDir = dataDir; diff --git a/integration_test/cef_dns_real_test.dart b/integration_test/cef_dns_real_test.dart index c1967ab..e5f74d2 100644 --- a/integration_test/cef_dns_real_test.dart +++ b/integration_test/cef_dns_real_test.dart @@ -13,6 +13,7 @@ import 'package:jj_mkvmaker/platform/windows/process_media_tool.dart'; import 'package:jj_mkvmaker/services/platform_services.dart'; import 'package:jj_mkvmaker/ui/app_actions.dart'; import 'package:jj_mkvmaker/ui/browser_page.dart'; +import 'support/temp_dir.dart'; import 'package:path/path.dart' as p; /// 실제 망 (Windows): DNS 강제 (8.8.8.8) 상태에서 Chrome 엔진 (CEF) 이 앱 안 프록시의 인증 (비밀값) 에 답해 페이지를 여는지. @@ -33,7 +34,7 @@ void main() { } CefRuntime.rememberStartState(); expect(CefRuntime.readyThisRun, isTrue); - final data = Platform.environment['JJ_MKVMAKER_DATA'] ?? Directory.systemTemp.createTempSync('jj_cef_real_').path; + final data = Platform.environment['JJ_MKVMAKER_DATA'] ?? testTempDir('jj_cef_real_').path; Directory(data).createSync(recursive: true); final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService())); c.settings diff --git a/integration_test/com_guard_test.dart b/integration_test/com_guard_test.dart index 4517322..819e2f9 100644 --- a/integration_test/com_guard_test.dart +++ b/integration_test/com_guard_test.dart @@ -1,11 +1,10 @@ -import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:jj_mkvmaker/platform/windows/com_guard.dart'; import 'package:jj_mkvmaker/ui/browser_page.dart'; +import 'support/temp_dir.dart'; /// 실제 Edge(WebView2): 다른 구성 요소가 COM 준비 상태를 풀어 버린 상황을 만들고 /// (브라우저가 "CoInitialize 가 호출되지 않았습니다" 로 안 뜨던 문제) 그래도 브라우저 환경이 만들어지는지 @@ -26,7 +25,7 @@ void main() { for (var i = 0; i < 8; i++) { ComGuard.releaseForTest(); } - final dir = Directory.systemTemp.createTempSync('jj_com_').path; + final dir = testTempDir('jj_com_').path; final raw = await tester.runAsync(() async { try { await WebViewEnvironment.create(settings: WebViewEnvironmentSettings(userDataFolder: '${dir}_raw')); diff --git a/integration_test/dns_block_real_test.dart b/integration_test/dns_block_real_test.dart index 58c7485..f38fd6b 100644 --- a/integration_test/dns_block_real_test.dart +++ b/integration_test/dns_block_real_test.dart @@ -11,6 +11,7 @@ import 'package:jj_mkvmaker/platform/windows/process_media_tool.dart'; import 'package:jj_mkvmaker/services/platform_services.dart'; import 'package:jj_mkvmaker/ui/app_actions.dart'; import 'package:jj_mkvmaker/ui/browser_page.dart'; +import 'support/temp_dir.dart'; import 'package:path/path.dart' as p; /// 250 실제 엔진 (Windows Edge WebView2): DNS 강제 서버에 닿지 않으면 (TEST-NET 192.0.2.1) 엔진의 오류 화면 @@ -20,7 +21,7 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('250: 닿지 않는 DNS → 브라우저에 앱 덮개 (Edge 실제)', (tester) async { - final data = Platform.environment['JJ_MKVMAKER_DATA'] ?? Directory.systemTemp.createTempSync('jj_dns_block_').path; + final data = Platform.environment['JJ_MKVMAKER_DATA'] ?? testTempDir('jj_dns_block_').path; Directory(data).createSync(recursive: true); final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService())); c.settings diff --git a/integration_test/full_player_test.dart b/integration_test/full_player_test.dart index 4df2e85..2c45854 100644 --- a/integration_test/full_player_test.dart +++ b/integration_test/full_player_test.dart @@ -9,6 +9,7 @@ import 'package:jj_mkvmaker/platform/windows/desktop_shell.dart'; import 'package:jj_mkvmaker/services/media_player.dart'; import 'package:path/path.dart' as p; import 'package:window_manager/window_manager.dart'; +import 'support/temp_dir.dart'; /// 실제 mpv 플레이어 + 창 모드 void main() { @@ -16,7 +17,7 @@ void main() { testWidgets('재생 목록 · 자막 트랙 · 음량 · 전체 화면 · 팝업 · 창 크기', (tester) async { MediaKitPreviewPlayer.ensureInitialized(); - final dir = Directory.systemTemp.createTempSync('jj_fp_'); + final dir = testTempDir('jj_fp_'); final srt = p.join(dir.path, 's.srt'); File(srt).writeAsStringSync('1\n00:00:00,000 --> 00:00:05,000\n내장 자막\n'); final ext = p.join(dir.path, 'ep1_ko.srt'); diff --git a/integration_test/monitor_shot_test.dart b/integration_test/monitor_shot_test.dart index 1b15e36..9e994c7 100644 --- a/integration_test/monitor_shot_test.dart +++ b/integration_test/monitor_shot_test.dart @@ -13,6 +13,7 @@ import 'package:jj_mkvmaker/services/platform_services.dart'; import 'package:jj_mkvmaker/ui/monitor_page.dart'; import 'package:jj_mkvmaker/ui/schedule_editor.dart'; import 'package:jj_mkvmaker/ui/theme.dart'; +import 'support/temp_dir.dart'; import 'package:path/path.dart' as p; /// 모니터링 화면 (복사 진행 중 · lsync · 일정 격자) 을 실제 글꼴로 그려 PNG 로 (JJ_SHOT_DIR). 화면 배치 확인용. @@ -21,7 +22,7 @@ void main() { testWidgets('모니터링 화면 그림', (tester) async { final out = Platform.environment['JJ_SHOT_DIR'] ?? Directory.systemTemp.path; - final tmp = Directory.systemTemp.createTempSync('jj_monshot_'); + final tmp = testTempDir('jj_monshot_'); final src = p.join(tmp.path, '영상 원본'), dst = p.join(tmp.path, '백업'); for (var i = 0; i < 6; i++) { File(p.join(src, i < 3 ? 'A' : 'B', 'clip$i.mkv')) diff --git a/integration_test/pdf_test.dart b/integration_test/pdf_test.dart index 40dec40..c8a13e0 100644 --- a/integration_test/pdf_test.dart +++ b/integration_test/pdf_test.dart @@ -5,6 +5,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:image/image.dart' as img; import 'package:integration_test/integration_test.dart'; import 'package:jj_mkvmaker/core/reader_sources.dart'; +import 'support/temp_dir.dart'; import 'package:path/path.dart' as p; /// 실제 pdfium: 그림 → PDF, 열기 · 그리기, 페이지 지우기 · 그림 넣기 · 저장 @@ -12,8 +13,7 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('PDF: 만들기 · 열기 · 지우기 · 넣기 · 저장', (tester) async { - final dir = Directory.systemTemp.createTempSync('jj_pdf_'); - addTearDown(() => dir.deleteSync(recursive: true)); + final dir = testTempDir('jj_pdf_'); String image(String name, int w, int h, {bool jpg = false}) { final im = img.Image(width: w, height: h); img.fill(im, color: img.ColorRgb8(200, 50, 50)); diff --git a/integration_test/player_test.dart b/integration_test/player_test.dart index 41eefb0..e0dd52f 100644 --- a/integration_test/player_test.dart +++ b/integration_test/player_test.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:jj_mkvmaker/platform/common/media_kit_player.dart'; +import 'support/temp_dir.dart'; import 'package:path/path.dart' as p; /// 실제 Windows 앱에서 libmpv 재생 확인 @@ -14,7 +15,7 @@ void main() { testWidgets('media_kit: 영상 열기 · 재생 · 이동', (tester) async { MediaKitPreviewPlayer.ensureInitialized(); - final dir = Directory.systemTemp.createTempSync('jj_play_'); + final dir = testTempDir('jj_play_'); final video = p.join(dir.path, '재생 테스트.mp4'); final r = await Process.run('ffmpeg', [ '-y', '-f', 'lavfi', '-i', 'testsrc=size=320x240:rate=25', diff --git a/integration_test/rsync_page_test.dart b/integration_test/rsync_page_test.dart index ff95e49..26d8c56 100644 --- a/integration_test/rsync_page_test.dart +++ b/integration_test/rsync_page_test.dart @@ -12,6 +12,7 @@ import 'package:jj_mkvmaker/services/platform_services.dart'; import 'package:jj_mkvmaker/ui/explorer_page.dart'; import 'package:jj_mkvmaker/ui/monitor_page.dart'; import 'package:jj_mkvmaker/ui/theme.dart'; +import 'support/temp_dir.dart'; import 'package:path/path.dart' as p; /// Rsync 화면을 실제 rsync (앱에 들어 있는 것) 로: → 한 방향, ⇄ 양쪽 (-u), 모니터링에 남는지. 화면은 JJ_SHOT_DIR 에 PNG. @@ -20,7 +21,7 @@ void main() { testWidgets('Rsync 화면: → · ⇄ 실제 실행', (tester) async { final out = Platform.environment['JJ_SHOT_DIR'] ?? Directory.systemTemp.path; - final tmp = Directory.systemTemp.createTempSync('jj_rsyncpage_'); + final tmp = testTempDir('jj_rsyncpage_'); final a = p.join(tmp.path, '왼쪽 폴더'), b = p.join(tmp.path, 'right'); File(p.join(a, 'sub', '한글 파일.txt')) ..createSync(recursive: true) @@ -108,7 +109,7 @@ void main() { testWidgets('원본 파일 지우기 (--remove-source-files) · 빈 폴더 정리 · lsync 최종 정리', (tester) async { final out = Platform.environment['JJ_SHOT_DIR'] ?? Directory.systemTemp.path; - final tmp = Directory.systemTemp.createTempSync('jj_rsyncmove_'); + final tmp = testTempDir('jj_rsyncmove_'); final a = p.join(tmp.path, 'srcA'), b = p.join(tmp.path, 'dstB'); final cDir = p.join(tmp.path, 'liveC'), d = p.join(tmp.path, 'liveD'); for (final (dir, name) in [(a, 'x'), (cDir, 'y')]) { diff --git a/integration_test/secret_store_platform_test.dart b/integration_test/secret_store_platform_test.dart index 4541124..4b53a61 100644 --- a/integration_test/secret_store_platform_test.dart +++ b/integration_test/secret_store_platform_test.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:jj_mkvmaker/services/secret_store.dart'; +import 'support/temp_dir.dart'; /// 54: 이 기기의 안전 저장소 (Windows DPAPI · Android Keystore) 에 실제로 쓰고 읽고 지운다. /// 시험용 열쇠 하나만 쓰고 끝에 지운다 (사용자가 저장한 값은 건드리지 않음). @@ -13,7 +14,7 @@ void main() { Directory? tmp; SecretStore store() { if (!Platform.isWindows) return SecretStore.platform(); - return SecretStore.dpapiAt((tmp ??= Directory.systemTemp.createTempSync('jj_secretit_')).path); + return SecretStore.dpapiAt((tmp ??= testTempDir('jj_secretit_')).path); } tearDownAll(() { diff --git a/integration_test/support/temp_dir.dart b/integration_test/support/temp_dir.dart new file mode 100644 index 0000000..63cb631 --- /dev/null +++ b/integration_test/support/temp_dir.dart @@ -0,0 +1,29 @@ +import 'dart:io'; + +import 'package:flutter_test/flutter_test.dart'; + +/// 관리자 지시 (10/19, 28 이 찾음): 통합 시험 · 캡처가 %TEMP%\jj_* 시험 폴더를 남겼다 - 시험 끝 (실패해도) 에 지운다. +/// [base] 가 있으면 그 아래 (짧은 경로가 필요한 캡처). 반드시 시험 (testWidgets · test) 안에서 부른다 (addTearDown). +Directory testTempDir(String prefix, {String? base}) { + final root = base == null || base.isEmpty ? Directory.systemTemp : (Directory(base)..createSync(recursive: true)); + final dir = root.createTempSync(prefix); + addTearDown(() => deleteTestTemp(dir)); + return dir; +} + +/// 시험 폴더 지우기 - Windows 에서 앱 · WebView · 백신이 파일을 잠깐 잡고 있으면 몇 번 다시 해 본다 (기다림은 실제 시간: 가짜 시계 밖). +/// 긴 경로는 \\?\ 로. 끝내 못 지우면 알리기만 하고 시험은 실패시키지 않는다 (tool/test_temp_check.ps1 이 남은 것을 잡는다) +void deleteTestTemp(Directory dir) { + final path = dir.absolute.path; + final target = Platform.isWindows && !path.startsWith(r'\\?\') ? Directory('\\\\?\\$path') : dir; + for (var i = 0; i < 20; i++) { + try { + if (!target.existsSync()) return; + target.deleteSync(recursive: true); + return; + } on FileSystemException { + sleep(const Duration(milliseconds: 250)); + } + } + stderr.writeln('시험 임시 폴더를 지우지 못함: $path'); +} diff --git a/integration_test/tor_dns_real_test.dart b/integration_test/tor_dns_real_test.dart index acadafb..7570d25 100644 --- a/integration_test/tor_dns_real_test.dart +++ b/integration_test/tor_dns_real_test.dart @@ -17,6 +17,7 @@ import 'package:jj_mkvmaker/services/platform_services.dart'; import 'package:jj_mkvmaker/ui/app_actions.dart'; import 'package:jj_mkvmaker/ui/browser_page.dart'; import 'package:jj_mkvmaker/ui/tor_page.dart'; +import 'support/temp_dir.dart'; import 'package:path/path.dart' as p; /// 실제 망 (Windows): DNS 강제 (8.8.8.8) 로 앱의 HTTP · 일반 브라우저 (Edge) 가 이름을 푸는지, 닿지 않는 DNS 는 실패로 알리는지, @@ -26,7 +27,7 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('DNS 강제 · Tor 화면 실제 연결', (tester) async { - final data = Platform.environment['JJ_MKVMAKER_DATA'] ?? Directory.systemTemp.createTempSync('jj_tor_real_').path; + final data = Platform.environment['JJ_MKVMAKER_DATA'] ?? testTempDir('jj_tor_real_').path; Directory(data).createSync(recursive: true); ComponentStore.dataDirectory = data; ComponentStore.shared = ComponentStore(data); diff --git a/integration_test/translate_split_test.dart b/integration_test/translate_split_test.dart index 0d7da6d..1ca12f2 100644 --- a/integration_test/translate_split_test.dart +++ b/integration_test/translate_split_test.dart @@ -11,6 +11,7 @@ import 'package:jj_mkvmaker/core/models.dart'; import 'package:jj_mkvmaker/core/output_paths.dart'; import 'package:jj_mkvmaker/services/platform_services.dart'; import 'package:jj_mkvmaker/ui/browser_page.dart'; +import 'support/temp_dir.dart'; import 'package:path/path.dart' as p; /// 실제 NLLB + 실제 Edge(WebView2): 브라우저를 보면서 영어 자막 → 한국어 번역 → MKV 를 @@ -19,7 +20,7 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('브라우저 + 작업 현황: 영어 자막 → 한국어 번역 → MKV (대기열)', (tester) async { - final dir = Directory.systemTemp.createTempSync('jj_split_'); + final dir = testTempDir('jj_split_'); final video = p.join(dir.path, 'movie.mp4'); final r = await Process.run('ffmpeg', [ '-y', '-f', 'lavfi', '-i', 'testsrc=size=320x240:rate=10', '-f', 'lavfi', '-i', 'sine', @@ -42,7 +43,7 @@ void main() { await c.init(); await c.addVideos([video]); }); - c.settings.webViewDataDir = Directory.systemTemp.createTempSync('jj_webview_').path; + c.settings.webViewDataDir = testTempDir('jj_webview_').path; final v = c.videos.single; // 같은 폴더의 movie.en.srt 가 자동으로 잡힘 final s = v.subtitles.firstWhere((x) => x.path != null && p.equals(x.path!, en)); diff --git a/integration_test/video_adjust_e2e_test.dart b/integration_test/video_adjust_e2e_test.dart index ea70673..29219f6 100644 --- a/integration_test/video_adjust_e2e_test.dart +++ b/integration_test/video_adjust_e2e_test.dart @@ -12,6 +12,7 @@ import 'package:jj_mkvmaker/core/output_paths.dart'; import 'package:jj_mkvmaker/services/platform_services.dart'; import 'package:jj_mkvmaker/ui/theme.dart'; import 'package:jj_mkvmaker/ui/video_adjust_dialog.dart'; +import 'support/temp_dir.dart'; import 'package:path/path.dart' as p; /// 실제 FFmpeg: 가로 영상 → 화면 · 색 보정 창 미리보기 → 세로 9:16 + 채도 MKV 만들기 @@ -19,7 +20,7 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('화면 · 색 보정: 미리보기와 세로 9:16 MKV', (tester) async { - final dir = Directory.systemTemp.createTempSync('jj_adj_e2e_'); + final dir = testTempDir('jj_adj_e2e_'); final video = p.join(dir.path, '가로 영상.mp4'); final ffmpeg = p.join(Directory.current.path, 'third_party', 'ffmpeg', 'windows', 'ffmpeg.exe'); final r = await Process.run(ffmpeg, [ diff --git a/integration_test/web_translate_test.dart b/integration_test/web_translate_test.dart index d2cd6f1..7761425 100644 --- a/integration_test/web_translate_test.dart +++ b/integration_test/web_translate_test.dart @@ -10,6 +10,7 @@ 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/browser_page.dart'; +import 'support/temp_dir.dart'; import 'package:path/path.dart' as p; /// 실제 웹뷰 (Windows: Edge WebView2 · Android: 시스템 WebView) + 실제 Google 번역: @@ -50,7 +51,7 @@ void main() { await req.response.close(); }); final url = 'http://127.0.0.1:${server.port}/'; - final dataDir = Directory.systemTemp.createTempSync('jj_webview_').path; + final dataDir = testTempDir('jj_webview_').path; final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService())); c.settings diff --git a/integration_test/whisper_probe_test.dart b/integration_test/whisper_probe_test.dart index 0429a42..7a1bf2b 100644 --- a/integration_test/whisper_probe_test.dart +++ b/integration_test/whisper_probe_test.dart @@ -4,6 +4,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:path/path.dart' as p; import 'package:whisper_ggml/whisper_ggml.dart'; +import 'support/temp_dir.dart'; /// whisper.cpp 동작 확인 (스마트 앱 컨트롤·속도) void main() { @@ -14,7 +15,7 @@ void main() { 'whisper', 'ggml-base.bin'); final assets = p.join(Directory.current.path, 'test_assets'); for (final name in ['speech_en.wav', 'speech_ko.wav']) { - final tmp = Directory.systemTemp.createTempSync('jj_w_'); + final tmp = testTempDir('jj_w_'); final wav = File(p.join(assets, name)).copySync(p.join(tmp.path, name)).path; final sw = Stopwatch()..start(); final r = await tester.runAsync(() => const Whisper(model: WhisperModel.base).transcribe( diff --git a/integration_test/windows_662_shot_test.dart b/integration_test/windows_662_shot_test.dart index 5980132..080f16e 100644 --- a/integration_test/windows_662_shot_test.dart +++ b/integration_test/windows_662_shot_test.dart @@ -14,6 +14,7 @@ import 'package:jj_mkvmaker/ui/app_actions.dart'; import 'package:jj_mkvmaker/ui/browser_page.dart'; import 'package:jj_mkvmaker/ui/home_page.dart'; import 'package:jj_mkvmaker/ui/theme.dart'; +import 'support/temp_dir.dart'; import 'package:path/path.dart' as p; /// 66-2 기기 감독 확인용 (Windows): 지금 화면 아이콘 (넓은 · 좁은 화면) · 처음으로 알림 · 브라우저 안내 한 줄을 PNG (JJ_SHOT_DIR) 로. @@ -83,9 +84,7 @@ void main() { ..components = ['mkv', 'browser', 'explorer', 'rsync'] ..navOrder = ['mkv', 'browser', 'explorer', 'rsync']; final base = Platform.environment['JJ_SHOT_TMP']; - final dir = (base == null || base.isEmpty ? Directory.systemTemp : (Directory(base)..createSync(recursive: true))) - .createTempSync('jj_shot_662_'); - addTearDown(() => dir.deleteSync(recursive: true)); + final dir = testTempDir('jj_shot_662_', base: base); final bm = BookmarksController(p.join(dir.path, 'bookmarks.json')); t.view.physicalSize = const Size(1280, 800); t.view.devicePixelRatio = 1; diff --git a/integration_test/windows_b3_shot_test.dart b/integration_test/windows_b3_shot_test.dart index f6c9399..562c199 100644 --- a/integration_test/windows_b3_shot_test.dart +++ b/integration_test/windows_b3_shot_test.dart @@ -14,6 +14,7 @@ import 'package:path/path.dart' as p; import '../test/support/fake_recycle_bin.dart'; import '../test/support/recycle_leftovers.dart'; +import 'support/temp_dir.dart'; /// 묶음 3 (데이터 쪽) 기기 감독 확인용 (Windows): 실제 앱 화면을 띄워 누르고 단계마다 PNG (JJ_SHOT_DIR) 로 남긴다. /// 165 (드라이브 · 연결 안 됨) · 48 (같은 이름) · 52 (돌아와도 진행 막대) · 148 (휴지통 되돌리기) · 144 (긴 경로). @@ -23,9 +24,7 @@ void main() { final out = Platform.environment['JJ_SHOT_DIR'] ?? p.join(Directory.systemTemp.path, 'jj_shots_b3'); Directory tempDir(String prefix) { final base = Platform.environment['JJ_SHOT_TMP']; - if (base == null || base.isEmpty) return Directory.systemTemp.createTempSync(prefix); - Directory(base).createSync(recursive: true); - return Directory(base).createTempSync(prefix); + return testTempDir(prefix, base: base); } final key = GlobalKey(); diff --git a/integration_test/windows_b6_input_test.dart b/integration_test/windows_b6_input_test.dart index fda1d56..1b74e68 100644 --- a/integration_test/windows_b6_input_test.dart +++ b/integration_test/windows_b6_input_test.dart @@ -12,6 +12,7 @@ import 'package:jj_mkvmaker/platform/windows/process_media_tool.dart'; import 'package:jj_mkvmaker/services/platform_services.dart'; import 'package:jj_mkvmaker/ui/explorer_page.dart'; import 'package:jj_mkvmaker/ui/theme.dart'; +import 'support/temp_dir.dart'; import 'package:path/path.dart' as p; /// 관리자 지시 9 (10/17): 82 끌어 놓기 · 75 마우스 4 · 5 번을 Windows 실제 빌드에서 가짜 포인터로 (실제 마우스 · 키보드는 쓰지 않음). @@ -26,7 +27,7 @@ void main() { setUp(() async { await i18n.apply('ko', save: false); - tmp = Directory.systemTemp.createTempSync('jj_b6input_'); + tmp = testTempDir('jj_b6input_'); left = p.join(tmp.path, 'left'); right = p.join(tmp.path, 'right'); File(p.join(left, 'doc.txt')) diff --git a/integration_test/windows_b6_shot_test.dart b/integration_test/windows_b6_shot_test.dart index bd103a0..56363f3 100644 --- a/integration_test/windows_b6_shot_test.dart +++ b/integration_test/windows_b6_shot_test.dart @@ -21,6 +21,7 @@ import 'package:jj_mkvmaker/ui/explorer_search.dart'; import 'package:jj_mkvmaker/ui/quick_look.dart'; import 'package:jj_mkvmaker/ui/storage_page.dart'; import 'package:jj_mkvmaker/ui/theme.dart'; +import 'support/temp_dir.dart'; import 'package:path/path.dart' as p; /// 관리자 지시 5 (10/17): 기기 없이 15 가 볼 수 있게 묶음6 새 화면을 PNG 로 (JJ_SHOT_DIR, 없으면 임시 폴더). @@ -38,8 +39,7 @@ void main() { addTearDown(t.view.reset); await t.runAsync(() => i18n.apply(lang, save: false)); addTearDown(() => i18n.apply('ko', save: false)); - final tmp = Directory.systemTemp.createTempSync('jj_b6shot_'); - addTearDown(() => tmp.deleteSync(recursive: true)); + final tmp = testTempDir('jj_b6shot_'); // 시험 파일 File(p.join(tmp.path, 'Show', 'ep01.mkv')) ..createSync(recursive: true) diff --git a/integration_test/windows_check_shot_test.dart b/integration_test/windows_check_shot_test.dart index fc82b1a..a16fca4 100644 --- a/integration_test/windows_check_shot_test.dart +++ b/integration_test/windows_check_shot_test.dart @@ -20,6 +20,7 @@ import 'package:jj_mkvmaker/ui/theme.dart'; import 'package:path/path.dart' as p; import '../test/support/fake_recycle_bin.dart'; +import 'support/temp_dir.dart'; /// 기기 감독 (Windows) 확인용: 실제 앱 화면을 띄워 누르고 단계마다 PNG (JJ_SHOT_DIR) 로 남긴다. /// 65 · 94 · 98 (휴지통 · 휴지통 없는 위치 · 오류 문구), 37 (경로 입력), 124 · 130 (마스터 · 최상), 123 (AI 그림). @@ -30,9 +31,7 @@ void main() { // 시험 폴더: 항목이 아주 많은 시스템 임시 폴더 대신 (JJ_SHOT_TMP, 없으면 시스템 임시 폴더) Directory tempDir(String prefix) { final base = Platform.environment['JJ_SHOT_TMP']; - if (base == null || base.isEmpty) return Directory.systemTemp.createTempSync(prefix); - Directory(base).createSync(recursive: true); - return Directory(base).createTempSync(prefix); + return testTempDir(prefix, base: base); } final key = GlobalKey(); const sizes = [('narrow', Size(700, 900)), ('wide', Size(1280, 800))]; diff --git a/integration_test/windows_p3_shot_test.dart b/integration_test/windows_p3_shot_test.dart index 0fc0f72..b9961aa 100644 --- a/integration_test/windows_p3_shot_test.dart +++ b/integration_test/windows_p3_shot_test.dart @@ -8,6 +8,7 @@ import 'package:integration_test/integration_test.dart'; import 'package:jj_mkvmaker/app/i18n_controller.dart'; import 'package:jj_mkvmaker/ui/file_tools.dart'; import 'package:jj_mkvmaker/ui/theme.dart'; +import 'support/temp_dir.dart'; import 'package:path/path.dart' as p; /// 관리자 지시 (10/18): X-plore P3 화면을 PNG 로 (JJ_SHOT_DIR, 없으면 임시 폴더) - 해시 창 · 폴더 속성 · 16진 보기. @@ -23,8 +24,7 @@ void main() { addTearDown(t.view.reset); await t.runAsync(() => i18n.apply(lang, save: false)); addTearDown(() => i18n.apply('ko', save: false)); - final tmp = Directory.systemTemp.createTempSync('jj_p3shot_'); - addTearDown(() => tmp.deleteSync(recursive: true)); + final tmp = testTempDir('jj_p3shot_'); final file = File(p.join(tmp.path, '영상 폴더', 'ep01 긴 이름의 동영상 파일.mkv')) ..createSync(recursive: true) ..writeAsBytesSync(List.generate(5000, (i) => i % 256)); diff --git a/integration_test/windows_wifi_shot_test.dart b/integration_test/windows_wifi_shot_test.dart index c0f589a..78aa826 100644 --- a/integration_test/windows_wifi_shot_test.dart +++ b/integration_test/windows_wifi_shot_test.dart @@ -13,6 +13,7 @@ import 'package:jj_mkvmaker/platform/windows/process_media_tool.dart'; import 'package:jj_mkvmaker/services/platform_services.dart'; import 'package:jj_mkvmaker/ui/theme.dart'; import 'package:jj_mkvmaker/ui/wifi_share_page.dart'; +import 'support/temp_dir.dart'; import 'package:path/path.dart' as p; /// 관리자 지시 (10/18): 89 Wi-Fi 공유 화면을 PNG 로 (JJ_SHOT_DIR) - 켜기 전 · 켠 뒤 (코드 · 주소 · QR · 남은 시간 · 접속 기록). @@ -37,8 +38,7 @@ void main() { WifiShare.debugLoopbackOnly = false; WifiShare.debugAddresses = null; }); - final tmp = Directory.systemTemp.createTempSync('jj_wifishot_'); - addTearDown(() => tmp.deleteSync(recursive: true)); + final tmp = testTempDir('jj_wifishot_'); final dir = Directory(p.join(tmp.path, '여행 사진'))..createSync(); final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService())); final shotKey = GlobalKey(); diff --git a/integration_test/windows_xplore_shot_test.dart b/integration_test/windows_xplore_shot_test.dart index 17f8d19..03b573e 100644 --- a/integration_test/windows_xplore_shot_test.dart +++ b/integration_test/windows_xplore_shot_test.dart @@ -14,6 +14,7 @@ import 'package:jj_mkvmaker/platform/windows/process_media_tool.dart'; import 'package:jj_mkvmaker/services/platform_services.dart'; import 'package:jj_mkvmaker/ui/explorer_page.dart'; import 'package:jj_mkvmaker/ui/theme.dart'; +import 'support/temp_dir.dart'; import 'package:path/path.dart' as p; /// X-plore 비교 P1 (관리자 지시 10/18): X-plore 스타일 탐색기를 PNG 로 (JJ_SHOT_DIR, 없으면 임시 폴더). @@ -31,8 +32,7 @@ void main() { addTearDown(t.view.reset); await t.runAsync(() => i18n.apply(lang, save: false)); addTearDown(() => i18n.apply('ko', save: false)); - final tmp = Directory.systemTemp.createTempSync('jj_xpshot_'); - addTearDown(() => tmp.deleteSync(recursive: true)); + final tmp = testTempDir('jj_xpshot_'); final a = Directory(p.join(tmp.path, 'Videos'))..createSync(); final b = Directory(p.join(tmp.path, 'Backup'))..createSync(); for (final n in ['Show S01', 'Movies']) { diff --git a/integration_test/windows_xplore_tools_shot_test.dart b/integration_test/windows_xplore_tools_shot_test.dart index d32f3b7..240b0b2 100644 --- a/integration_test/windows_xplore_tools_shot_test.dart +++ b/integration_test/windows_xplore_tools_shot_test.dart @@ -15,6 +15,7 @@ import 'package:jj_mkvmaker/ui/explorer_grid.dart'; import 'package:jj_mkvmaker/ui/storage_page.dart'; import 'package:jj_mkvmaker/ui/text_viewer.dart'; import 'package:jj_mkvmaker/ui/theme.dart'; +import 'support/temp_dir.dart'; import 'package:path/path.dart' as p; /// X-plore 비교 g · h · i · P3 (관리자 지시 10/18): 격자 보기 · 글 뷰어 · 일괄 이름 (날짜) · 저장 공간 그림을 PNG 로 @@ -31,8 +32,7 @@ void main() { addTearDown(t.view.reset); await t.runAsync(() => i18n.apply(lang, save: false)); addTearDown(() => i18n.apply('ko', save: false)); - final tmp = Directory.systemTemp.createTempSync('jj_xptshot_'); - addTearDown(() => tmp.deleteSync(recursive: true)); + final tmp = testTempDir('jj_xptshot_'); // 그림 (색이 다른 작은 그림) · 동영상 이름 · 글 (CP949) final pics = []; for (var i = 0; i < 7; i++) { diff --git a/tool/test_temp_check.ps1 b/tool/test_temp_check.ps1 index adaa7f7..7504eeb 100644 --- a/tool/test_temp_check.ps1 +++ b/tool/test_temp_check.ps1 @@ -5,15 +5,33 @@ # - 다른 worktree · 세션이 같은 시험을 동시에 돌리면 그쪽 폴더가 섞일 수 있다 (이름으로 확인). # - 이미 있던 폴더는 건드리지 않는다 (지우지 않음). # 사용 (app 폴더에서): powershell -ExecutionPolicy Bypass -File tool\test_temp_check.ps1 [flutter test 인자…] +# 통합 시험 · 캡처도 (관리자 지시 10/19, 28 이 찾음): … test_temp_check.ps1 integration_test\windows_b6_shot_test.dart -d windows +# - 폴더 이름 앞부분은 test 와 integration_test 두 곳의 시험 소스에서 모은다 $ErrorActionPreference = 'Stop' $app = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path $temp = [System.IO.Path]::GetTempPath() # 시험 소스가 쓰는 임시 폴더 이름 앞부분 (createTemp('jj_x_') · createTempSync('jj_x_')) -$prefixes = @(Get-ChildItem (Join-Path $app 'test') -Recurse -Filter *.dart | - ForEach-Object { [regex]::Matches((Get-Content $_.FullName -Raw -Encoding utf8), 'createTemp(?:Sync)?\(''(jj[^''$]*)''') } | +# 넘겨받은 시험 파일 (.dart) 이 있으면 그 파일들과 support 폴더만 본다 - 다른 세션 · worktree 가 같은 때 돌린 시험의 폴더를 세지 않게 +# (10/19: b6 캡처 앞뒤에 다른 세션의 단위 시험 폴더 jj_explorer_ · jj_sync_ 가 섞여 거짓 실패) +$named = @($args | Where-Object { "$_" -like '*.dart' } | ForEach-Object { Join-Path $app "$_" } | Where-Object { Test-Path $_ }) +$sources = if ($named.Count -gt 0) { + # 그 파일들이 가져다 쓰는 support 파일만 함께 (import 'support/…') - 다른 세션의 시험과 이름이 겹치지 않게 (10/19: jj_fake_recycle_) + $support = @($named | ForEach-Object { + $dir = Split-Path $_ + [regex]::Matches((Get-Content $_ -Raw -Encoding utf8), "import '(support/[^']+\.dart)'") | + ForEach-Object { Join-Path $dir $_.Groups[1].Value } }) + @($named) + @($support | Where-Object { Test-Path $_ }) +} else { + @(@('test', 'integration_test') | ForEach-Object { Join-Path $app $_ } | Where-Object { Test-Path $_ } | + ForEach-Object { Get-ChildItem $_ -Recurse -Filter *.dart | ForEach-Object { $_.FullName } }) +} +# 이름이 하나도 없으면 (임시 폴더를 만들지 않는 시험) 새 jj 폴더는 참고로만 보인다 +# 이름 앞부분: createTemp('jj_x_') · createTempSync('jj_x_') · testTempDir('jj_x_') (integration_test/support/temp_dir.dart) +$prefixes = @($sources | + ForEach-Object { [regex]::Matches((Get-Content $_ -Raw -Encoding utf8), '(?:createTemp(?:Sync)?|testTempDir)\(''(jj[^''$]*)''') } | ForEach-Object { $_.Groups[1].Value } | Sort-Object -Unique) -if ($prefixes.Count -eq 0) { throw '시험 소스에서 임시 폴더 이름을 찾지 못했습니다' } +if ($prefixes.Count -eq 0 -and $named.Count -eq 0) { throw '시험 소스에서 임시 폴더 이름을 찾지 못했습니다' } function Get-TestTemp { @(Get-ChildItem $temp -Directory -Filter 'jj*' -ErrorAction SilentlyContinue | ForEach-Object { $_.Name }) @@ -34,6 +52,12 @@ $left = @(Get-TestTemp | Where-Object { -not $before.ContainsKey($_) } | Where-O $n = $_ @($prefixes | Where-Object { $n.StartsWith($_) }).Count -gt 0 }) +# 참고: 시험 소스의 이름이 아닌 새 jj 폴더 (앱 코드가 만든 것일 수 있음 - 실패로 세지 않고 보이기만) +$other = @(Get-TestTemp | Where-Object { -not $before.ContainsKey($_) -and $left -notcontains $_ }) +if ($other.Count -gt 0) { + Write-Host ("참고 - 시험 소스에 없는 이름의 새 jj 폴더 {0}개:" -f $other.Count) + $other | Sort-Object | ForEach-Object { Write-Host " $_" } +} if ($left.Count -gt 0) { Write-Host ("시험이 남긴 임시 폴더 {0}개 (%TEMP%):" -f $left.Count) $left | Sort-Object | ForEach-Object { Write-Host " $_" }