import 'dart:io'; import 'package:flutter_test/flutter_test.dart'; import 'package:jj_mkvmaker/core/files/any_transfer.dart'; import 'package:jj_mkvmaker/core/files/file_listing.dart'; import 'package:jj_mkvmaker/core/files/local_transfer.dart'; import 'package:jj_mkvmaker/core/files/name_ops.dart'; import 'package:jj_mkvmaker/core/files/path_rules.dart'; import 'package:jj_mkvmaker/core/files/transfer_control.dart'; import 'package:jj_mkvmaker/core/files/transfer_guard.dart'; import 'package:jj_mkvmaker/core/files/windows_drives.dart'; import 'package:path/path.dart' as p; /// 나누기 file_ops 약속 시험: lib/core/files/ 모듈마다 맨 위 약속 (입력 · 출력 · 오류) - 실제 시험 폴더 · 실제 파일. void main() { late Directory tmp; setUp(() => tmp = Directory.systemTemp.createTempSync('jj_files_')); tearDown(() => tmp.deleteSync(recursive: true)); String mk(String rel, [String text = 'x']) { final f = File(p.join(tmp.path, rel))..createSync(recursive: true); f.writeAsStringSync(text); return f.path; } test('file_listing: 읽기 · 숨김 · 정렬 (폴더 먼저 · 숫자) · 찾기 · 읽지 못함은 던짐 · 크기 글', () async { mk('d/file10.txt'); mk('d/file2.txt'); mk('d/.hidden'); Directory(p.join(tmp.path, 'd', 'sub')).createSync(); final d = p.join(tmp.path, 'd'); final list = await listEntries(d); expect(list.map((e) => e.name).toSet(), {'file10.txt', 'file2.txt', 'sub'}); expect((await listEntries(d, showHidden: true)).length, 4); expect(sortEntries(list, SortBy.name).map((e) => e.name), ['sub', 'file2.txt', 'file10.txt']); expect(await searchFiles(tmp.path, 'FILE*').map((e) => e.name).toList(), ['file2.txt', 'file10.txt']); await expectLater(listEntries(p.join(tmp.path, 'none')), throwsA(isA())); expect([formatSize(-1), formatSize(12), formatSize(2048), formatSize(3 * 1024 * 1024 * 1024)], ['—', '12B', '2KB', '3.0GB']); }); test('windows_drives: 목록은 비지 않음 · 종류는 열지 않고 숫자 · 공유 맨 위', () { final drives = windowsDrives(); expect(drives, isNotEmpty); if (Platform.isWindows) expect(driveType(drives.first.$1), greaterThan(0)); expect(uncRoot(r'\\nas\share\a\b'), r'\\nas\share'); expect(uncRoot('x'), 'x'); }); test('path_rules: 겹치지 않는 이름 · 같은 경로 · 안에 있음 · 바깥 것만 · 전송 전 확인', () { mk('a.txt'); expect(p.basename(uniqueTarget(tmp.path, 'a.txt')), 'a (2).txt'); final a = p.join(tmp.path, 'A'), inner = p.join(a, 'b'); Directory(inner).createSync(recursive: true); expect(isSameOrInside(inner, a), isTrue); expect(isSameOrInside(a, inner), isFalse); expect(outermostPaths([inner, a, a]), [a]); if (Platform.isWindows) expect(samePath(a, a.toLowerCase()), isTrue); expect(transferProblem([a], inner, move: false), isNotNull, reason: '자기 안으로'); expect(transferProblem([p.join(tmp.path, 'none')], tmp.path, move: false), isNotNull, reason: '원본 없음'); expect(transferProblem([p.join(tmp.path, 'a.txt')], a, move: true), isNull); expect(isAppTempName('~jjren_1') && isPartialFile('x.jjpart'), isTrue); }); test('name_ops: 쓸 수 없는 이름 · 이름 바꾸기 · 새 폴더 · 같은 이름은 던짐', () async { expect(nameProblem('a/b', windowsRules: false), isNotNull); expect(nameProblem('CON.txt', windowsRules: true), isNotNull); expect(nameProblem('ok.txt', windowsRules: true), isNull); final f = mk('x.txt'); mk('y.txt'); expect(p.basename(await renameInPlace(f, ' z.txt ')), 'z.txt'); await expectLater(renameInPlace(p.join(tmp.path, 'z.txt'), 'y.txt'), throwsA(isA())); final dir = await createFolder(tmp.path, '새 폴더'); expect(Directory(dir).existsSync(), isTrue); await expectLater(createFolder(tmp.path, '새 폴더'), throwsA(isA())); }); test('transfer_guard: 원본 없음 · 빈 원본은 던지고 지우지 않음 · 빈 폴더 정리', () async { final src = p.join(tmp.path, 'src'), dst = p.join(tmp.path, 'dst'); mk('dst/keep.txt'); await expectLater(ensureSourceForDelete(src, dst), throwsA(isA())); Directory(src).createSync(); await expectLater(ensureSourceForDelete(src, dst), throwsA(isA().having((e) => e.empty, 'empty', isTrue))); await ensureSourceForDelete(src, dst, allowEmpty: true); Directory(p.join(src, 'e1', 'e2')).createSync(recursive: true); expect(await removeEmptyDirs(src), 2); expect(Directory(src).existsSync(), isTrue); }); test('transfer_control: 같은 이름 건너뛰기는 null 과 기록 · 취소 뒤 check 는 던짐', () async { final f = mk('s/a.txt'); mk('d/a.txt'); final c = TransferControl(conflict: NameConflict.skip); expect(await c.targetFor(f, p.join(tmp.path, 'd')), isNull); expect(c.skipped, [f]); c.check(); c.cancel(); expect(c.cancelled, isTrue); expect(c.check, throwsA(isA())); }); test('local_transfer: 복사 (진행 · 바뀐 때) · 이동 · 맞추기 (지우기) · 취소는 반쯤 쓴 파일을 남기지 않음', () async { final a = mk('s/dir/a.txt', 'hello'); final dst = p.join(tmp.path, 'd'); Directory(dst).createSync(); var last = 0; final made = await localCopy(TransferControl(), [p.join(tmp.path, 's', 'dir')], dst, onProgress: (_, done, _) => last = done); expect(File(p.join(made.single, 'a.txt')).readAsStringSync(), 'hello'); expect(last, 5); expect(await localTotalSize([p.join(tmp.path, 's')]), 5); final moved = await localMove(TransferControl(), [a], dst); expect(File(a).existsSync(), isFalse); expect(File(moved.single).readAsStringSync(), 'hello'); final m = p.join(tmp.path, 'm'); mk('m/old.txt'); expect(await anyMirror(TransferControl(), p.join(tmp.path, 'd'), m, delete: true), 2, reason: 'F1: 로컬 맞추기도 anyMirror 하나'); expect(File(p.join(m, 'old.txt')).existsSync(), isFalse); final c = TransferControl()..cancel(); await expectLater(localCopy(c, [moved.single], p.join(tmp.path, 's')), throwsA(isA())); expect(Directory(p.join(tmp.path, 's')).listSync(recursive: true).where((e) => e.path.endsWith('.jjpart')), isEmpty); }); test('any_transfer: 로컬 경로로도 복사 · 이동 · 맞추기 · 크기, 원본 없음은 던짐', () async { mk('s/x/b.txt', 'abc'); final dst = p.join(tmp.path, 'd'); Directory(dst).createSync(); expect(await anyTotalSize([p.join(tmp.path, 's')]), 3); final made = await anyCopy(TransferControl(), [p.join(tmp.path, 's', 'x')], dst); expect(File(p.join(made.single, 'b.txt')).readAsStringSync(), 'abc'); final m = p.join(tmp.path, 'm'); expect(await anyMirror(TransferControl(), p.join(tmp.path, 's'), m), 1); expect(await anyMirror(TransferControl(), p.join(tmp.path, 's'), m), 0, reason: '같으면 다시 보내지 않음'); final m2 = Directory(p.join(tmp.path, 'm2'))..createSync(); final moved = await anyMove(TransferControl(), [p.join(tmp.path, 's', 'x')], m2.path); expect(Directory(p.join(tmp.path, 's', 'x')).existsSync(), isFalse); expect(File(p.join(moved.single, 'b.txt')).readAsStringSync(), 'abc'); await expectLater(anyCopy(TransferControl(), [p.join(tmp.path, 'none')], dst), throwsA(isA())); }); }