JJ_MKVMaker/test/vault_modules_test.dart

162 lines
7.9 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:jj_mkvmaker/core/vault_store.dart';
import 'package:jj_mkvmaker/core/vault_view.dart';
import 'package:jj_mkvmaker/ui/vault/vault_session.dart';
import 'package:jj_mkvmaker/ui/vault/vault_views.dart';
import 'package:path/path.dart' as p;
/// 보관함 나누기 (vault_page 1,095줄 → lib/ui/vault/) 의 모듈 약속 시험. 실제 시험 폴더 · 실제 보관함 (PBKDF2 1000번) 만 쓴다.
/// 화면 전체 흐름 (만들기 · 열기 · 넣기 · 보기 · 꺼내기 · 폴더 · 열쇠) 은 vault_page_test 가 본다
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
late Directory tmp;
final wasView = vaultViewRootOf;
setUp(() {
tmp = Directory.systemTemp.createTempSync('jj_vaultmod_');
vaultViewRootOf = () async => Directory(p.join(tmp.path, 'view'));
});
tearDown(() async {
vaultViewRootOf = wasView;
await removeTempDirRetry(tmp);
});
test('vault_session (상태): 폴더 글 · 위 폴더 · 잇기 · 상태를 바꾸면 다시 그림', () {
expect(vaultParentOf('가/나/다'), '가/나');
expect(vaultParentOf('가'), '');
expect(vaultJoin('', '가'), '가');
expect(vaultJoin('가', '나'), '가/나');
expect(vaultFolderLabel('가/나'), contains('가 › 나'));
var n = 0;
final said = <String>[];
final s = VaultUiState(changed: () => n++, snack: said.add);
s.setBusy(true);
s.setProgress('1 / 2');
expect((s.busy, s.progress, n), (true, '1 / 2', 2));
s.setBusy(false);
expect(s.progress, '1 / 2', reason: 'setBusy 는 진행 글을 그대로 둔다');
s.setBusyProgress(false, null);
expect((s.busy, s.progress, n), (false, null, 4));
});
test('vault_session (보기 폴더): 한 번 연 동안 같은 폴더 · 지우면 잊고 다음은 새 폴더 · 없는 폴더 지우기는 조용히', () async {
final v = VaultViewSession();
await v.clear(); // 만든 적 없음
final a = await v.dir(), b = await v.dir();
expect(a.path, b.path);
expect(p.basename(a.path), startsWith(vaultViewPrefix));
expect(p.dirname(a.path), p.join(tmp.path, 'view'));
File(p.join(a.path, 'x.bin')).writeAsBytesSync([1, 2, 3]);
await v.clear();
expect(a.existsSync(), isFalse);
final c = await v.dir();
expect(c.path == a.path, isFalse);
await removeTempDirRetry(Directory(p.join(tmp.path, '없음')));
});
test('vault_session (넣기): 펼치기 (폴더 구조 · 겹치는 이름 " (2)" · 읽지 못한 것은 빼고) → 넣기 → 잠그면 멈춤', () async {
final vault = await Vault.create(Directory(p.join(tmp.path, 'vault')), 'pw-for-test', iterations: 1000);
final src = Directory(p.join(tmp.path, 'src'))..createSync();
final one = File(p.join(src.path, 'a.txt'))..writeAsStringSync('하나');
final dir = Directory(p.join(src.path, '사진'))..createSync();
File(p.join(dir.path, 'b.txt')).writeAsStringSync('둘');
Directory(p.join(dir.path, '안')).createSync();
File(p.join(dir.path, '안', 'c.txt')).writeAsStringSync('셋');
Directory(p.join(dir.path, '빈')).createSync();
await vault.mkdir('사진'); // 맨 위에 같은 이름이 이미 있음
final missing = p.join(src.path, '없는 폴더');
Directory(missing).createSync();
Directory(missing).deleteSync();
final plan = await planVaultAdd(vault, '', [one.path, dir.path, missing]);
expect(plan.folders.first, '사진 (2)');
expect(plan.folders, unorderedEquals(['사진 (2)', '사진 (2)/안', '사진 (2)/빈']), reason: '안의 순서는 폴더 목록 순서');
// 없는 것은 폴더가 아니라 파일로 보여 펼치기에서는 실패하지 않는다 - 넣기에서 실패해 bad 에 남는다
expect([for (final x in plan.files) '${x.folder}|${p.basename(x.src)}'],
unorderedEquals(['|a.txt', '사진 (2)|b.txt', '사진 (2)/안|c.txt', '|없는 폴더']));
final said = <String>[];
final s = VaultUiState(changed: () {}, snack: said.add)..vault = vault;
final thumbs = Directory(p.join(tmp.path, 'thumbs'))..createSync();
final r = await putVaultAdd(s, [one.path, dir.path, missing], plan, thumbTemp: () async => thumbs.path);
expect(r.done, 3);
expect(r.bad, {missing}, reason: '넣지 못한 것만 (그 원본은 화면이 지우지 않음)');
expect(said, hasLength(1), reason: '처음 실패만 알림');
expect((s.busy, s.progress), (false, null));
expect([for (final e in vault.filesUnder('사진 (2)')) e.path], unorderedEquals(['사진 (2)/b.txt', '사진 (2)/안/c.txt']));
expect(vault.folders, contains('사진 (2)/빈'));
expect(one.existsSync(), isTrue, reason: '넣기는 원본을 지우지 않는다 (지우기는 화면이 deleteOriginals 로)');
// 보관함이 잠겨 있으면 (vault = null) 하나도 넣지 않고 멈춘다
final p3 = VaultAddPlan()..files.add((src: one.path, folder: '', top: one.path));
final s3 = VaultUiState(changed: () {}, snack: said.add)..vault = null;
final r3 = await putVaultAdd(s3, [one.path], p3, thumbTemp: () async => thumbs.path);
expect(r3.done, 0);
});
testWidgets('vault_views (묻는 창): 옮길 곳은 자기와 그 안을 빼고 · 지금 있는 곳은 꺼짐 · 이름 창의 빈 칸은 null', (t) async {
String? picked = 'x', named = 'x';
late BuildContext ctx;
await t.pumpWidget(MaterialApp(home: Builder(builder: (c) {
ctx = c;
return const Scaffold();
})));
final f = pickVaultTarget(ctx, ['가', '가/나', '다'], except: '가', from: '');
await t.pumpAndSettle();
expect(find.text('나'), findsNothing);
expect(find.text('가'), findsNothing);
expect(t.widget<ListTile>(find.widgetWithText(ListTile, '보관함 맨 위')).enabled, isFalse);
await t.tap(find.text('다'));
await t.pumpAndSettle();
picked = await f;
expect(picked, '다');
final n = askVaultName(ctx, '새 폴더');
await t.pumpAndSettle();
await t.tap(find.text('확인'));
await t.pumpAndSettle();
named = await n;
expect(named, isNull);
});
testWidgets('vault_views (목록): 폴더 먼저 (이름 순) · 파일은 정렬 따라 · 바쁘면 누르기 꺼짐 · 누른 것은 콜백으로', (t) async {
final vault = await t.runAsync(() async {
final v = await Vault.create(Directory(p.join(tmp.path, 'vault')), 'pw-for-test', iterations: 1000);
final src = Directory(p.join(tmp.path, 'src'))..createSync();
for (final n in ['A.txt', 'b.txt']) {
File(p.join(src.path, n)).writeAsStringSync(n);
await v.add(p.join(src.path, n), name: n, folder: '');
await Future<void>.delayed(const Duration(milliseconds: 20));
}
await v.mkdir('나');
await v.mkdir('가');
return v;
});
final got = <String>[];
Future<void> show(String sort, {bool busy = false}) => t.pumpWidget(MaterialApp(
home: Scaffold(
body: VaultListView(
vault: vault!,
cwd: '',
sort: sort,
busy: busy,
onCwd: (f) => got.add('cwd $f'),
onSort: (x) => got.add('sort $x'),
onFolder: (f, w) => got.add('$w $f'),
onEntry: (e, w) => got.add('$w ${e.name}')))));
List<String> titles() => [
for (final w in t.widgetList<ListTile>(find.byType(ListTile))) ((w.title as Text).data ?? ''),
];
await show('name');
expect(titles(), ['가', '나', 'A.txt', 'b.txt']);
await show('added');
expect(titles(), ['가', '나', 'b.txt', 'A.txt'], reason: '넣은 날 새것 먼저 (b 를 나중에 넣음)');
await t.tap(find.text('가'));
await t.tap(find.text('b.txt'));
expect(got, ['cwd 가', 'view b.txt']);
await show('added', busy: true);
await t.tap(find.text('나'));
expect(got, hasLength(2), reason: '바쁜 동안은 누르기가 꺼짐');
});
}