From 43c8dab04cc26e60b6e7902fcd5bd18a5f5d42d2 Mon Sep 17 00:00:00 2001 From: RyuJongJin <266630912+RyuJongJin@users.noreply.github.com> Date: Thu, 22 Oct 2026 20:07:38 +0900 Subject: [PATCH] =?UTF-8?q?=EB=B3=B4=EA=B4=80=ED=95=A8=20=EB=84=A3?= =?UTF-8?q?=EA=B8=B0:=20=EB=84=A3=EB=8B=A4=EA=B0=80=20=EC=9E=A0=EA=B0=80?= =?UTF-8?q?=20=EB=A9=88=EC=B6=94=EB=A9=B4=20=EB=84=A3=EC=A7=80=20=EB=AA=BB?= =?UTF-8?q?=ED=95=9C=20=EC=9B=90=EB=B3=B8=EC=9D=80=20=EC=A7=80=EC=9A=B0?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 예전부터 있던 버그: [원본 지우기 (이동)] 로 넣는 도중에 잠그면 넣기는 멈추지만, 넣지 못한 원본이 bad 에 들어가지 않아 그 원본까지 지워졌다 (데이터 잃음) - putVaultAdd: 잠겨 멈출 때 남은 항목의 top 을 bad 에 넣는다 → 넣은 것만 원본을 지우고 나머지는 그대로 - 약속 시험 (실제 시험 폴더 · 실제 보관함): 원본 4개를 넣다가 3번째에서 잠그면 1 · 2 만 지워지고 3 · 4 는 바이트까지 그대로. 고치기 전 코드에서는 4.bin 이 지워져 실패함을 확인 Co-Authored-By: Claude Opus 5.5 --- lib/ui/vault/vault_session.dart | 10 ++++-- test/vault_modules_test.dart | 58 +++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/lib/ui/vault/vault_session.dart b/lib/ui/vault/vault_session.dart index 69829cd..dc27f86 100644 --- a/lib/ui/vault/vault_session.dart +++ b/lib/ui/vault/vault_session.dart @@ -213,7 +213,7 @@ Future<({bool move, bool permanent})?> askVaultAdd(BuildContext context, List bad})> putVaultAdd(VaultUiState s, List paths, VaultAddPlan plan, {required Future Function() thumbTemp}) async { s.setBusy(true); @@ -226,7 +226,13 @@ Future<({int done, Set bad})> putVaultAdd(VaultUiState s, List p } for (var i = 0; i < plan.files.length; i++) { final x = plan.files[i]; - if (s.vault == null) break; // 넣는 동안 잠갔으면 멈춤 + if (s.vault == null) { + // 넣는 동안 잠갔으면 멈춤 - 넣지 못한 남은 것은 bad 로 (그 원본은 [원본 지우기 (이동)] 로 지우지 않게) + for (final rest in plan.files.skip(i)) { + bad.add(rest.top); + } + break; + } s.setProgress('${i + 1} / ${plan.files.length} · ${vBasename(x.src)}'); String? fetched; try { diff --git a/test/vault_modules_test.dart b/test/vault_modules_test.dart index 3e72980..ba5420e 100644 --- a/test/vault_modules_test.dart +++ b/test/vault_modules_test.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; @@ -158,4 +159,61 @@ void main() { await t.tap(find.text('나')); expect(got, hasLength(2), reason: '바쁜 동안은 누르기가 꺼짐'); }); + + testWidgets('vault_session (넣기): [원본 지우기 (이동)] 로 넣다가 중간에 잠그면 넣은 것만 원본을 지우고, 넣지 못한 원본은 바이트까지 그대로', (t) async { + final src = Directory(p.join(tmp.path, 'src')); + final names = ['1.bin', '2.bin', '3.bin', '4.bin']; + final bytes = {for (final n in names) n: List.generate(4096, (i) => (i * 7 + n.codeUnitAt(0)) % 256)}; + final vault = await t.runAsync(() async { + src.createSync(); + for (final n in names) { + File(p.join(src.path, n)).writeAsBytesSync(bytes[n]!); + } + Directory(p.join(tmp.path, 'thumbs')).createSync(); + return Vault.create(Directory(p.join(tmp.path, 'vault')), 'pw-for-test', iterations: 1000); + }); + final paths = [for (final n in names) p.join(src.path, n)]; + late VaultUiState s; + // 화면의 잠그기와 같게: 열쇠를 버린다 (vault = null). "3 / 4" 를 보이는 때 = 1 · 2 를 넣은 뒤 + s = VaultUiState( + changed: () { + if (s.progress?.startsWith('3 / ') ?? false) s.vault = null; + }, + snack: (_) {}) + ..vault = vault; + final deleted = []; + late BuildContext ctx; + await t.pumpWidget(MaterialApp(home: Builder(builder: (c) { + ctx = c; + return const Scaffold(); + }))); + var finished = false; + unawaited(vaultAddFlow(ctx, s, paths, thumbTemp: () async => p.join(tmp.path, 'thumbs'), deleteOriginals: (ok, {required permanent}) async { + deleted.addAll(ok); + for (final x in ok) { + File(x).deleteSync(); // 실제로 지운다 (탐색기의 지우기 대신) + } + }).whenComplete(() => finished = true)); + Future until(bool Function() ok) async { + for (var i = 0; i < 300 && !ok(); i++) { + await t.runAsync(() => Future.delayed(const Duration(milliseconds: 20))); + await t.pump(const Duration(milliseconds: 20)); + } + } + + await until(() => find.text('원본 지우기 (이동)').evaluate().isNotEmpty); + await t.tap(find.text('원본 지우기 (이동)')); + await t.pump(); + await t.tap(find.widgetWithText(FilledButton, '넣기')); + await until(() => finished); + expect(finished, isTrue); + expect(s.vault, isNull, reason: '중간에 잠금'); + expect([for (final e in vault!.entries) e.name], unorderedEquals(['1.bin', '2.bin']), reason: '잠그기 전 둘만 들어감'); + expect(deleted, unorderedEquals([paths[0], paths[1]]), reason: '넣은 것만 원본 지우기로'); + for (final n in ['3.bin', '4.bin']) { + final f = File(p.join(src.path, n)); + expect(f.existsSync(), isTrue, reason: '넣지 못한 원본 $n 은 남음'); + expect(f.readAsBytesSync(), bytes[n], reason: '$n 바이트 그대로'); + } + }); }