JJ_MKVMaker/test/batch_rename_date_test.dart

76 lines
4.5 KiB
Dart

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:image/image.dart' as img;
import 'package:jj_mkvmaker/core/batch_rename.dart';
import 'package:jj_mkvmaker/core/photo_date.dart';
import 'package:jj_mkvmaker/ui/batch_rename_dialog.dart';
import 'package:path/path.dart' as p;
/// X-plore 비교 i: 일괄 이름 바꾸기의 날짜 · 시각 ([D] · [T], 사진은 찍은 때) · 마지막 규칙 기억
void main() {
late Directory tmp;
setUp(() => tmp = Directory.systemTemp.createTempSync('jj_rendate_'));
tearDown(() => tmp.deleteSync(recursive: true));
test('[D] · [T]: 날짜 2026-10-18 · 시각 13-05-09 (":" 는 못 써서 "-"), 날짜가 없으면 빈칸', () {
const r = RenameRule(template: '[D] [T] [N]');
expect(r.usesDate, isTrue);
expect(r.apply('a.jpg', isDir: false, index: 0, date: DateTime(2026, 10, 18, 13, 5, 9)), '2026-10-18 13-05-09 a.jpg');
expect(const RenameRule(template: '[d]_[C]').apply('a.jpg', isDir: false, index: 0, date: DateTime(2026, 1, 2)), '2026-01-02_01.jpg');
expect(const RenameRule(template: 'x[D]').apply('a.jpg', isDir: false, index: 0), 'x.jpg');
expect(const RenameRule().usesDate, isFalse);
});
test('규칙 기억: toJson → fromJson 그대로, 틀린 값은 처음 값으로', () {
const r = RenameRule(template: '[D]_[C]', start: 3, step: 2, digits: 4, find: 'a', replace: 'b', ignoreCase: false, caseMode: 'upper', dateSource: 'modified');
expect(RenameRule.fromJson(r.toJson()).toJson(), r.toJson());
final bad = RenameRule.fromJson({'template': '', 'digits': 99, 'caseMode': '??', 'dateSource': 1, 'start': 'x'});
expect((bad.template, bad.digits, bad.caseMode, bad.dateSource, bad.start), ('[N]', 9, 'keep', 'photo', 1));
});
test('사진의 찍은 때 (JPEG Exif DateTimeOriginal) · 없거나 JPEG 이 아니면 null', () async {
expect(parseExifDate('2024:05:01 12:34:56'), DateTime(2024, 5, 1, 12, 34, 56));
expect(parseExifDate('0000:00:00 00:00:00'), isNull);
final image = img.Image(width: 8, height: 8);
image.exif.exifIfd['DateTimeOriginal'] = img.IfdValueAscii('2024:05:01 12:34:56');
final withExif = File(p.join(tmp.path, 'a.jpg'))..writeAsBytesSync(img.encodeJpg(image));
expect(await photoTakenAt(withExif.path), DateTime(2024, 5, 1, 12, 34, 56));
final plain = File(p.join(tmp.path, 'b.jpg'))..writeAsBytesSync(img.encodeJpg(img.Image(width: 8, height: 8)));
expect(await photoTakenAt(plain.path), isNull);
expect(await photoTakenAt(p.join(tmp.path, 'none.png')), isNull);
});
testWidgets('창: 마지막 규칙으로 열리고, [바꾸기] 때의 규칙을 돌려준다 · [D] 는 사진의 찍은 때', (tester) async {
tester.view.physicalSize = const Size(1000, 900);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.reset);
final image = img.Image(width: 8, height: 8);
image.exif.exifIfd['DateTimeOriginal'] = img.IfdValueAscii('2024:05:01 12:34:56');
final photo = File(p.join(tmp.path, 'IMG_1.jpg'))..writeAsBytesSync(img.encodeJpg(image));
RenameRule? used;
final nav = GlobalKey<NavigatorState>();
await tester.pumpWidget(MaterialApp(navigatorKey: nav, home: const Scaffold()));
showBatchRename(nav.currentContext!, [photo.path],
isDir: (_) => false, initial: const RenameRule(template: '[D]_[N]'), onRun: (r) => used = r);
bool ready() =>
find.text('2024-05-01_IMG_1.jpg').evaluate().isNotEmpty &&
tester.widget<FilledButton>(find.widgetWithText(FilledButton, '바꾸기 (1개)')).onPressed != null;
for (var i = 0; i < 60 && !ready(); i++) {
await tester.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 30)));
await tester.pump();
}
expect(find.widgetWithText(TextField, '[D]_[N]'), findsOneWidget, reason: '마지막 규칙으로 열림');
expect(find.text('2024-05-01_IMG_1.jpg'), findsOneWidget);
expect(tester.widget<FilledButton>(find.widgetWithText(FilledButton, '바꾸기 (1개)')).onPressed, isNotNull, reason: '찍은 때를 다 읽으면 켜짐');
await tester.tap(find.widgetWithText(FilledButton, '바꾸기 (1개)'));
for (var i = 0; i < 100 && !File(p.join(tmp.path, '2024-05-01_IMG_1.jpg')).existsSync(); i++) {
await tester.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 30)));
await tester.pump();
}
expect(File(p.join(tmp.path, '2024-05-01_IMG_1.jpg')).existsSync(), isTrue);
expect(used?.template, '[D]_[N]');
});
}