158 lines
7.6 KiB
Dart
158 lines
7.6 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:jj_mkvmaker/core/file_ops.dart';
|
|
import 'package:jj_mkvmaker/ui/explorer_search.dart';
|
|
import 'package:path/path.dart' as p;
|
|
|
|
/// 79: 찾기 조건 (크기 · 바뀐 날 · 종류) · 결과 목록 화면 (2000개마다 멈춤 · 더 보기, 고르고 복사 · 이동 · 지우기, 누르면 열기)
|
|
void main() {
|
|
final now = DateTime(2026, 10, 17, 12);
|
|
FileEntry f(String name, {int size = 0, int daysAgo = 0}) =>
|
|
FileEntry('/x/$name', isDir: false, size: size, modified: now.subtract(Duration(days: daysAgo)));
|
|
|
|
test('조건: "1GB 넘는 30일 지난 영상"', () {
|
|
const gb = 1024 * 1024 * 1024;
|
|
const c = SearchConditions(minBytes: gb, olderDays: 30, kind: 'video');
|
|
expect(c.accepts(f('a.mkv', size: 2 * gb, daysAgo: 40), now: now), isTrue);
|
|
expect(c.accepts(f('a.mkv', size: gb ~/ 2, daysAgo: 40), now: now), isFalse, reason: '작다');
|
|
expect(c.accepts(f('a.mkv', size: 2 * gb, daysAgo: 3), now: now), isFalse, reason: '최근');
|
|
expect(c.accepts(f('a.srt', size: 2 * gb, daysAgo: 40), now: now), isFalse, reason: '동영상 아님');
|
|
expect(c.accepts(FileEntry('/x/d', isDir: true, modified: DateTime(2000)), now: now), isFalse, reason: '조건이 있으면 폴더는 아님');
|
|
expect(c.summary, contains('1.0GB'));
|
|
});
|
|
|
|
test('조건: N일 안 · 확장자 직접 · 조건 없음', () {
|
|
expect(const SearchConditions(newerDays: 7).accepts(f('a', daysAgo: 3), now: now), isTrue);
|
|
expect(const SearchConditions(newerDays: 7).accepts(f('a', daysAgo: 9), now: now), isFalse);
|
|
const custom = SearchConditions(kind: 'custom', exts: '*.MKV, .mp4 webm');
|
|
expect(custom.accepts(f('a.mkv'), now: now), isTrue);
|
|
expect(custom.accepts(f('a.webm'), now: now), isTrue);
|
|
expect(custom.accepts(f('a.avi'), now: now), isFalse);
|
|
expect(const SearchConditions(name: 'x').hasFileConditions, isFalse);
|
|
expect(const SearchConditions().isEmpty, isTrue);
|
|
});
|
|
|
|
test('79-c outermostPaths: 고른 폴더 안의 것 · 같은 것을 빼고 차례는 그대로', () {
|
|
final a = p.join('C:', 'x', 'a');
|
|
final inA = p.join(a, 'f.txt');
|
|
final b2 = p.join('C:', 'x', 'b.txt');
|
|
expect(outermostPaths([inA, b2, a, a]), [b2, a]);
|
|
expect(outermostPaths([b2]), [b2]);
|
|
expect(outermostPaths([p.join('C:', 'x', 'ab'), a]), [p.join('C:', 'x', 'ab'), a], reason: '이름 앞부분만 같은 것은 안이 아님');
|
|
});
|
|
|
|
group('searchFiles + accept', () {
|
|
late Directory tmp;
|
|
setUp(() {
|
|
tmp = Directory.systemTemp.createTempSync('jj_search79_');
|
|
File(p.join(tmp.path, 'a', 'ep1.mkv'))
|
|
..createSync(recursive: true)
|
|
..writeAsBytesSync(List.filled(3000, 0));
|
|
File(p.join(tmp.path, 'a', 'ep1.srt')).writeAsStringSync('x');
|
|
File(p.join(tmp.path, 'small.mkv')).writeAsBytesSync(List.filled(10, 0));
|
|
});
|
|
tearDown(() => tmp.deleteSync(recursive: true));
|
|
|
|
test('이름 없이 조건만 · 이름 + 조건', () async {
|
|
const c = SearchConditions(minBytes: 1000, kind: 'video');
|
|
expect(await searchFiles(tmp.path, '', accept: c.accepts).map((e) => e.name).toList(), ['ep1.mkv']);
|
|
expect(await searchFiles(tmp.path, 'ep1', accept: const SearchConditions(kind: 'subtitle').accepts).map((e) => e.name).toList(),
|
|
['ep1.srt']);
|
|
expect(await searchFiles(tmp.path, '').toList(), isEmpty, reason: '이름도 조건도 없으면 찾지 않는다 (예전 그대로)');
|
|
});
|
|
});
|
|
|
|
group('결과 화면', () {
|
|
late Directory tmp;
|
|
setUp(() {
|
|
tmp = Directory.systemTemp.createTempSync('jj_search79_ui_');
|
|
for (var i = 0; i < 5; i++) {
|
|
File(p.join(tmp.path, 'ep$i.mkv')).writeAsStringSync('$i');
|
|
}
|
|
Directory(p.join(tmp.path, 'ep_dir')).createSync();
|
|
});
|
|
tearDown(() => tmp.deleteSync(recursive: true));
|
|
|
|
Future<(List<FileEntry>, SearchAction? Function())> show(WidgetTester tester, {int chunk = 2000}) async {
|
|
tester.view.physicalSize = const Size(900, 900);
|
|
tester.view.devicePixelRatio = 1;
|
|
addTearDown(tester.view.reset);
|
|
final opened = <FileEntry>[];
|
|
SearchAction? result;
|
|
final nav = GlobalKey<NavigatorState>();
|
|
await tester.pumpWidget(MaterialApp(navigatorKey: nav, home: const Scaffold(body: Text('탐색기'))));
|
|
nav.currentState!
|
|
.push<SearchAction>(MaterialPageRoute(
|
|
builder: (_) => SearchResultsPage(
|
|
root: tmp.path,
|
|
cond: const SearchConditions(name: 'ep'),
|
|
showHidden: false,
|
|
otherDir: p.join(tmp.path, 'dest'),
|
|
chunk: chunk,
|
|
onOpen: (e) async => opened.add(e),
|
|
),
|
|
))
|
|
.then((r) => result = r);
|
|
for (var i = 0; i < 40; i++) {
|
|
await tester.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 20)));
|
|
await tester.pump();
|
|
}
|
|
return (opened, () => result);
|
|
}
|
|
|
|
testWidgets('2개마다 멈추고 [2개 더 보기] - 끝까지', (tester) async {
|
|
await show(tester, chunk: 2);
|
|
expect(find.text('찾기 결과 · 2개'), findsOneWidget);
|
|
expect(find.text('2개에서 멈췄습니다'), findsOneWidget);
|
|
// 79-d: 다 찾기 전에는 "지금까지 찾은 n개 모두"
|
|
expect(find.text('지금까지 찾은 2개 모두'), findsOneWidget);
|
|
await tester.tap(find.text('2개 더 보기'));
|
|
for (var i = 0; i < 20; i++) {
|
|
await tester.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 20)));
|
|
await tester.pump();
|
|
}
|
|
expect(find.text('찾기 결과 · 4개'), findsOneWidget);
|
|
await tester.tap(find.text('2개 더 보기'));
|
|
for (var i = 0; i < 20; i++) {
|
|
await tester.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 20)));
|
|
await tester.pump();
|
|
}
|
|
expect(find.text('찾기 결과 · 6개'), findsOneWidget, reason: '파일 5 + 폴더 1');
|
|
expect(find.textContaining('멈췄습니다'), findsNothing);
|
|
expect(find.widgetWithText(TextButton, '모두'), findsOneWidget, reason: '다 찾으면 그냥 "모두"');
|
|
});
|
|
|
|
testWidgets('누르면 열기 (화면 그대로) · 폴더는 그 자리로 · 고르면 복사 → 다른 창 · 지우기', (tester) async {
|
|
final (opened, result) = await show(tester);
|
|
await tester.tap(find.text('ep1.mkv'));
|
|
await tester.pump();
|
|
expect(opened.single.name, 'ep1.mkv');
|
|
expect(find.text('찾기 결과 · 6개'), findsOneWidget, reason: '열어도 결과 화면은 그대로');
|
|
|
|
// 고르면 아래에 할 일 줄 (개수 · 크기), 고르는 동안 누르기 = 고르기
|
|
expect(find.text('지우기'), findsNothing);
|
|
await tester.longPress(find.text('ep2.mkv'));
|
|
await tester.pump();
|
|
await tester.tap(find.text('ep3.mkv'));
|
|
await tester.pump();
|
|
expect(opened, hasLength(1), reason: '고르는 중에는 열지 않음');
|
|
expect(find.text('파일 2개 · 2B'), findsOneWidget);
|
|
expect(find.text('복사 → dest'), findsOneWidget);
|
|
await tester.tap(find.text('지우기'));
|
|
await tester.pumpAndSettle();
|
|
expect(result()!.kind, 'delete');
|
|
expect(result()!.paths.map(p.basename), unorderedEquals(['ep2.mkv', 'ep3.mkv']), reason: '고른 것만');
|
|
});
|
|
|
|
testWidgets('폴더를 누르면 그 자리로', (tester) async {
|
|
final (_, result) = await show(tester);
|
|
await tester.tap(find.text('ep_dir'));
|
|
await tester.pumpAndSettle();
|
|
expect(result()!.kind, 'reveal');
|
|
expect(p.basename(result()!.paths.single), 'ep_dir');
|
|
});
|
|
});
|
|
}
|