From 84863c1569439984dc8258cbadc1f3576f317326 Mon Sep 17 00:00:00 2001 From: RyuJongJin <266630912+RyuJongJin@users.noreply.github.com> Date: Tue, 20 Oct 2026 12:39:46 +0900 Subject: [PATCH] =?UTF-8?q?WIP=20=EC=8B=9C=ED=97=98=20=EC=9E=84=EC=8B=9C?= =?UTF-8?q?=20=ED=8F=B4=EB=8D=94=20=EA=B2=80=EC=82=AC:=20=EC=8B=A4?= =?UTF-8?q?=ED=96=89=EB=A7=88=EB=8B=A4=20%TEMP%\t=5Fjjrun=5F<=ED=91=9C>=20?= =?UTF-8?q?=EB=A1=9C=20TEMP=20=C2=B7=20TMP=20=EB=A5=BC=20=EB=8F=8C?= =?UTF-8?q?=EB=A0=A4=20=EA=B7=B8=20=EC=95=88=EB=A7=8C=20=EC=85=88=20-=20?= =?UTF-8?q?=EB=8B=A4=EB=A5=B8=20=EC=84=B8=EC=85=98=20=EC=8B=9C=ED=97=98?= =?UTF-8?q?=EA=B3=BC=20=EC=84=9E=EC=97=AC=20=EA=B1=B0=EC=A7=93=20=EC=8B=A4?= =?UTF-8?q?=ED=8C=A8=ED=95=98=EB=8D=98=20=EA=B2=83=20=C2=B7=20=EC=95=B1=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EA=B0=80=20=EB=82=A8=EA=B8=B4=20=EA=B2=83?= =?UTF-8?q?=EB=8F=84=20=EC=9E=A1=EC=9D=8C=20=C2=B7=20=EB=8F=84=EC=9A=B0?= =?UTF-8?q?=EB=AF=B8=EA=B0=80=20=ED=91=9C=20=EB=B0=96=EC=97=90=20=EC=83=9D?= =?UTF-8?q?=EA=B8=B0=EB=A9=B4=20=EC=8B=A4=ED=8C=A8=20=C2=B7=20Edge=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=ED=8F=B4=EB=8D=94=20(=EC=95=B1?= =?UTF-8?q?=EC=9D=B4=20=EB=81=9D=EB=82=A0=20=EB=95=8C=EA=B9=8C=EC=A7=80=20?= =?UTF-8?q?=EC=9E=A1=ED=9E=98)=20=EB=8A=94=20jj=5Fheld=5F=20=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=B4=EC=9D=B4=EA=B8=B0=EB=A7=8C=20=ED=95=98=EA=B3=A0=20?= =?UTF-8?q?=ED=95=A8=EA=BB=98=20=EC=A7=80=EC=9B=80=20(=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=EC=9E=90=20=EC=A7=80=EC=8B=9C=2010/20,=20=EA=B2=80=ED=86=A0=20?= =?UTF-8?q?=EB=8C=80=EA=B8=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- integration_test/browser_keep_test.dart | 2 +- integration_test/browser_real_test.dart | 2 +- integration_test/com_guard_test.dart | 3 +- integration_test/support/temp_dir.dart | 16 ++++- integration_test/translate_split_test.dart | 2 +- integration_test/web_translate_test.dart | 2 +- test/support/temp_dirs.dart | 10 +++ tool/test_temp_check.ps1 | 79 ++++++++++------------ 8 files changed, 67 insertions(+), 49 deletions(-) diff --git a/integration_test/browser_keep_test.dart b/integration_test/browser_keep_test.dart index 64a6837..93d1aab 100644 --- a/integration_test/browser_keep_test.dart +++ b/integration_test/browser_keep_test.dart @@ -28,7 +28,7 @@ void main() { await req.response.close(); }); final url = 'http://127.0.0.1:${server.port}/page'; - final dataDir = testTempDir('jj_keep_').path; + final dataDir = testTempDir('jj_keep_', heldByApp: true).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 d9c3a6c..2902388 100644 --- a/integration_test/browser_real_test.dart +++ b/integration_test/browser_real_test.dart @@ -32,7 +32,7 @@ void main() { await req.response.close(); }); final url = 'http://127.0.0.1:${server.port}/'; - final dataDir = testTempDir('jj_webview_').path; + final dataDir = testTempDir('jj_webview_', heldByApp: true).path; final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService())); c.settings.webViewDataDir = dataDir; diff --git a/integration_test/com_guard_test.dart b/integration_test/com_guard_test.dart index 819e2f9..7a05d7f 100644 --- a/integration_test/com_guard_test.dart +++ b/integration_test/com_guard_test.dart @@ -25,7 +25,8 @@ void main() { for (var i = 0; i < 8; i++) { ComGuard.releaseForTest(); } - final dir = testTempDir('jj_com_').path; + // Edge 가 앱이 끝날 때까지 사용자 데이터 폴더를 잡는다 (test_temp_check 가 찾음, 관리자 지시 10/20) + final dir = testTempDir('jj_com_', heldByApp: true).path; final raw = await tester.runAsync(() async { try { await WebViewEnvironment.create(settings: WebViewEnvironmentSettings(userDataFolder: '${dir}_raw')); diff --git a/integration_test/support/temp_dir.dart b/integration_test/support/temp_dir.dart index 63cb631..6756c2c 100644 --- a/integration_test/support/temp_dir.dart +++ b/integration_test/support/temp_dir.dart @@ -4,13 +4,25 @@ import 'package:flutter_test/flutter_test.dart'; /// 관리자 지시 (10/19, 28 이 찾음): 통합 시험 · 캡처가 %TEMP%\jj_* 시험 폴더를 남겼다 - 시험 끝 (실패해도) 에 지운다. /// [base] 가 있으면 그 아래 (짧은 경로가 필요한 캡처). 반드시 시험 (testWidgets · test) 안에서 부른다 (addTearDown). -Directory testTempDir(String prefix, {String? base}) { +/// [heldByApp]: 앱이 끝날 때까지 놓지 않는 폴더 (Edge 사용자 데이터 - Windows 판 플러그인에는 환경 닫기가 없다). +/// 이름 앞에 jj_held_ 를 붙이고, tool/test_temp_check.ps1 은 이것을 실패로 세지 않고 앱이 끝난 뒤 실행 폴더와 함께 지운다 +Directory testTempDir(String prefix, {String? base, bool heldByApp = false}) { final root = base == null || base.isEmpty ? Directory.systemTemp : (Directory(base)..createSync(recursive: true)); - final dir = root.createTempSync(prefix); + final dir = root.createTempSync(heldByApp ? 'jj_held_$prefix' : prefix); addTearDown(() => deleteTestTemp(dir)); + if (base == null || base.isEmpty) expectInTaggedTemp(dir); return dir; } +/// 관리자 지시 (10/20): tool/test_temp_check.ps1 이 TEMP · TMP 를 실행 폴더 (%TEMP%\t_jjrun_<표>) 로 돌리고 표를 JJ_TEST_TMP_TAG 로 넘긴다. +/// 표가 있는데 임시 폴더가 그 밖에 생기면 (환경 변수가 시험 · 앱까지 오지 않음) 다른 세션 시험과 섞이므로 시험을 실패시킨다 +void expectInTaggedTemp(Directory dir) { + final tag = Platform.environment['JJ_TEST_TMP_TAG'] ?? ''; + if (tag.isEmpty) return; + expect(dir.absolute.path.toLowerCase(), contains('t_jjrun_$tag'.toLowerCase()), + reason: 'TEMP 가 시험 실행 폴더로 오지 않음 (JJ_TEST_TMP_TAG=$tag)'); +} + /// 시험 폴더 지우기 - Windows 에서 앱 · WebView · 백신이 파일을 잠깐 잡고 있으면 몇 번 다시 해 본다 (기다림은 실제 시간: 가짜 시계 밖). /// 긴 경로는 \\?\ 로. 끝내 못 지우면 알리기만 하고 시험은 실패시키지 않는다 (tool/test_temp_check.ps1 이 남은 것을 잡는다) void deleteTestTemp(Directory dir) { diff --git a/integration_test/translate_split_test.dart b/integration_test/translate_split_test.dart index 1ca12f2..5360ada 100644 --- a/integration_test/translate_split_test.dart +++ b/integration_test/translate_split_test.dart @@ -43,7 +43,7 @@ void main() { await c.init(); await c.addVideos([video]); }); - c.settings.webViewDataDir = testTempDir('jj_webview_').path; + c.settings.webViewDataDir = testTempDir('jj_webview_', heldByApp: true).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/web_translate_test.dart b/integration_test/web_translate_test.dart index 7761425..f5cc3c8 100644 --- a/integration_test/web_translate_test.dart +++ b/integration_test/web_translate_test.dart @@ -51,7 +51,7 @@ void main() { await req.response.close(); }); final url = 'http://127.0.0.1:${server.port}/'; - final dataDir = testTempDir('jj_webview_').path; + final dataDir = testTempDir('jj_webview_', heldByApp: true).path; final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService())); c.settings diff --git a/test/support/temp_dirs.dart b/test/support/temp_dirs.dart index 2982681..2af9195 100644 --- a/test/support/temp_dirs.dart +++ b/test/support/temp_dirs.dart @@ -10,9 +10,19 @@ final _later = []; Directory tempDirUntilEnd(String prefix) { final d = Directory.systemTemp.createTempSync(prefix); _later.add(d); + expectInTaggedTemp(d); return d; } +/// 관리자 지시 (10/20): tool/test_temp_check.ps1 이 TEMP · TMP 를 실행 폴더 (%TEMP%\t_jjrun_<표>) 로 돌리고 표를 JJ_TEST_TMP_TAG 로 넘긴다. +/// 표가 있는데 임시 폴더가 그 밖에 생기면 (환경 변수가 시험 · 앱까지 오지 않음) 다른 세션 시험과 섞이므로 시험을 실패시킨다 +void expectInTaggedTemp(Directory dir) { + final tag = Platform.environment['JJ_TEST_TMP_TAG'] ?? ''; + if (tag.isEmpty) return; + expect(dir.absolute.path.toLowerCase(), contains('t_jjrun_$tag'.toLowerCase()), + reason: 'TEMP 가 시험 실행 폴더로 오지 않음 (JJ_TEST_TMP_TAG=$tag)'); +} + /// main() 에서 한 번 부른다: 파일의 시험이 다 끝나면 [tempDirUntilEnd] 로 만든 폴더를 지운다 /// (늦게 끝난 저장이 다시 만들 수 있어 잠깐씩 기다리며 몇 번 본다) void deleteTempDirsAtEnd() { diff --git a/tool/test_temp_check.ps1 b/tool/test_temp_check.ps1 index 7504eeb..d9a4638 100644 --- a/tool/test_temp_check.ps1 +++ b/tool/test_temp_check.ps1 @@ -1,66 +1,61 @@ -# 전체 시험 + 임시 폴더 흔적 검사 (관리자 지시 10/17): 시험이 %TEMP%\jj_* 폴더를 남기지 않는지. -# - 시험 전 · 뒤의 %TEMP% 를 비교해, 새로 남은 폴더 중 이름 앞부분이 시험 소스 (test\*.dart) 의 createTemp('jj…') 와 같은 것만 본다 -# (앱 · 다른 프로그램이 만든 jj_mkvmaker 등은 세지 않음). 하나라도 남으면 이름을 보이고 실패 (exit 2). -# - 시험 파일들은 동시에 돌아 파일마다 스스로 확인할 수 없어 (누가 만든 폴더인지 모름) 전체 실행 앞뒤로 본다. -# - 다른 worktree · 세션이 같은 시험을 동시에 돌리면 그쪽 폴더가 섞일 수 있다 (이름으로 확인). -# - 이미 있던 폴더는 건드리지 않는다 (지우지 않음). +# 전체 시험 + 임시 폴더 흔적 검사 (관리자 지시 10/17): 시험이 임시 폴더 (jj_*) 를 남기지 않는지. +# - 관리자 지시 (10/20): 실행마다 %TEMP%\t_jjrun_<표> 를 만들고 TEMP · TMP 를 그쪽으로 돌려 flutter test 를 돌린다. +# 그 폴더 안만 보므로 다른 worktree · 세션이 같은 %TEMP% 에서 동시에 돌린 시험의 폴더가 섞이지 않는다 +# (예전에는 앞뒤 %TEMP% 를 비교해 다른 세션의 jj_explorer_ · jj_sync_ · jj_fake_recycle_ 가 섞여 거짓 실패). +# - 앱 코드 (lib) 가 시험 중에 만든 임시 폴더 (jj_up_ · jj_7z_ 등) 도 같은 곳에 생기므로 함께 잡힌다. +# - 표는 환경 변수 JJ_TEST_TMP_TAG 로도 시험에 넘긴다: 시험 도우미 (test/support/temp_dirs.dart · integration_test/support/temp_dir.dart) +# 가 임시 폴더가 그 안에 생기는지 확인한다 (통합 시험 -d windows 에서 앱까지 환경 변수가 가는지). +# - 끝나면 남은 jj 폴더의 이름을 보이고 (하나라도 있으면 실패, exit 2) 실행 폴더를 통째로 지운다. # 사용 (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_')) -# 넘겨받은 시험 파일 (.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 -and $named.Count -eq 0) { throw '시험 소스에서 임시 폴더 이름을 찾지 못했습니다' } +# 표: 짧게 (긴 경로 시험이 경로 길이에 민감하다) - 이름이 jj 로 시작하지 않게 (예전 판 검사가 이 폴더를 시험 폴더로 세지 않게) +$tag = -join ((1..6) | ForEach-Object { '{0:x}' -f (Get-Random -Maximum 16) }) +$run = Join-Path $temp "t_jjrun_$tag" +New-Item -ItemType Directory -Path $run | Out-Null +$saved = @{ TEMP = $env:TEMP; TMP = $env:TMP; JJ_TEST_TMP_TAG = $env:JJ_TEST_TMP_TAG } +Write-Host "시험 임시 폴더: $run" -function Get-TestTemp { - @(Get-ChildItem $temp -Directory -Filter 'jj*' -ErrorAction SilentlyContinue | ForEach-Object { $_.Name }) -} - -$before = @{} -foreach ($n in Get-TestTemp) { $before[$n] = $true } +$env:TEMP = $run +$env:TMP = $run +$env:JJ_TEST_TMP_TAG = $tag Push-Location $app try { flutter test @args $code = $LASTEXITCODE } finally { Pop-Location + $env:TEMP = $saved.TEMP + $env:TMP = $saved.TMP + $env:JJ_TEST_TMP_TAG = $saved.JJ_TEST_TMP_TAG } # 시험이 끝난 뒤 지우기가 늦게 끝나는 것 (백신 · 색인) 을 잠깐 기다린다 Start-Sleep -Seconds 3 -$left = @(Get-TestTemp | Where-Object { -not $before.ContainsKey($_) } | Where-Object { - $n = $_ - @($prefixes | Where-Object { $n.StartsWith($_) }).Count -gt 0 - }) -# 참고: 시험 소스의 이름이 아닌 새 jj 폴더 (앱 코드가 만든 것일 수 있음 - 실패로 세지 않고 보이기만) -$other = @(Get-TestTemp | Where-Object { -not $before.ContainsKey($_) -and $left -notcontains $_ }) +$items = @(Get-ChildItem -LiteralPath $run -Force -ErrorAction SilentlyContinue) +# jj_held_ 는 앱이 끝날 때까지 놓지 않는 폴더 (integration_test/support/temp_dir.dart 의 heldByApp - Edge 사용자 데이터) - 보이기만 하고 아래에서 함께 지운다 +$held = @($items | Where-Object { $_.Name -like 'jj_held_*' } | ForEach-Object { $_.Name }) +$left = @($items | Where-Object { $_.Name -like 'jj*' -and $_.Name -notlike 'jj_held_*' } | ForEach-Object { $_.Name }) +# 참고: jj 가 아닌 것 (flutter 도구 · 다른 프로그램이 만든 것 - 실패로 세지 않고 보이기만) +$other = @($items | Where-Object { $_.Name -notlike 'jj*' } | ForEach-Object { $_.Name }) if ($other.Count -gt 0) { - Write-Host ("참고 - 시험 소스에 없는 이름의 새 jj 폴더 {0}개:" -f $other.Count) + Write-Host ("참고 - jj 가 아닌 남은 것 {0}개:" -f $other.Count) $other | Sort-Object | ForEach-Object { Write-Host " $_" } } +if ($held.Count -gt 0) { + Write-Host ("참고 - 앱이 끝날 때까지 잡혀 있던 것 {0}개 (실행 폴더와 함께 지움):" -f $held.Count) + $held | Sort-Object | ForEach-Object { Write-Host " $_" } +} if ($left.Count -gt 0) { - Write-Host ("시험이 남긴 임시 폴더 {0}개 (%TEMP%):" -f $left.Count) + Write-Host ("시험이 남긴 임시 폴더 {0}개 ({1}):" -f $left.Count, $run) $left | Sort-Object | ForEach-Object { Write-Host " $_" } +} +# 실행 폴더는 이 스크립트가 만든 것이라 통째로 지운다 (긴 경로는 \\?\ 로) +cmd /c "rd /s /q `"\\?\$run`"" 2>$null +if (Test-Path -LiteralPath $run) { Write-Host "실행 폴더를 다 지우지 못함: $run" } +if ($left.Count -gt 0) { if ($code -eq 0) { exit 2 } } else { Write-Host '임시 폴더 흔적: 새로 남은 것 0'