124 lines
5.6 KiB
Dart
124 lines
5.6 KiB
Dart
import 'dart:io';
|
|
import 'dart:ui' as ui;
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:image/image.dart' as img;
|
|
import 'package:integration_test/integration_test.dart';
|
|
import 'package:jj_mkvmaker/app/i18n_controller.dart';
|
|
import 'package:jj_mkvmaker/core/batch_rename.dart';
|
|
import 'package:jj_mkvmaker/core/file_ops.dart';
|
|
import 'package:jj_mkvmaker/core/text_codec.dart';
|
|
import 'package:jj_mkvmaker/ui/batch_rename_dialog.dart';
|
|
import 'package:jj_mkvmaker/ui/explorer_grid.dart';
|
|
import 'package:jj_mkvmaker/ui/storage_page.dart';
|
|
import 'package:jj_mkvmaker/ui/text_viewer.dart';
|
|
import 'package:jj_mkvmaker/ui/theme.dart';
|
|
import 'support/temp_dir.dart';
|
|
import 'package:path/path.dart' as p;
|
|
|
|
/// X-plore 비교 g · h · i · P3 (관리자 지시 10/18): 격자 보기 · 글 뷰어 · 일괄 이름 (날짜) · 저장 공간 그림을 PNG 로
|
|
/// (JJ_SHOT_DIR, 없으면 임시 폴더). 크기 412 · 960, 언어 ko · en. 시험 파일은 임시 폴더에만.
|
|
void main() {
|
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
|
final out = Platform.environment['JJ_SHOT_DIR'] ?? p.join(Directory.systemTemp.path, 'jj_shots_xptools');
|
|
|
|
for (final lang in ['ko', 'en']) {
|
|
for (final (w, h) in [(412, 900), (960, 600)]) {
|
|
testWidgets('X-plore 도구 $lang ${w}x$h', (t) async {
|
|
t.view.physicalSize = Size(w.toDouble(), h.toDouble());
|
|
t.view.devicePixelRatio = 1;
|
|
addTearDown(t.view.reset);
|
|
await t.runAsync(() => i18n.apply(lang, save: false));
|
|
addTearDown(() => i18n.apply('ko', save: false));
|
|
final tmp = testTempDir('jj_xptshot_');
|
|
// 그림 (색이 다른 작은 그림) · 동영상 이름 · 글 (CP949)
|
|
final pics = <String>[];
|
|
for (var i = 0; i < 7; i++) {
|
|
final im = img.Image(width: 64, height: 48);
|
|
img.fill(im, color: img.ColorRgb8(40 + i * 30, 120, 200 - i * 20));
|
|
if (i == 0) im.exif.exifIfd['DateTimeOriginal'] = img.IfdValueAscii('2024:05:01 12:34:56');
|
|
final f = File(p.join(tmp.path, 'IMG_${1000 + i}.jpg'))..writeAsBytesSync(img.encodeJpg(im));
|
|
pics.add(f.path);
|
|
}
|
|
File(p.join(tmp.path, 'clip.mkv')).writeAsBytesSync(List.filled(10, 0));
|
|
final txt = File(p.join(tmp.path, 'memo.txt'))
|
|
..writeAsBytesSync(encodeText(List.generate(30, (i) => '${i + 1}번째 줄 - 한글 메모 line $i').join('\n'), 'CP949').bytes);
|
|
for (final (n, size) in [('Videos', 900000), ('Photos', 400000), ('Docs', 120000), ('Music', 60000)]) {
|
|
final d = Directory(p.join(tmp.path, 'disk', n))..createSync(recursive: true);
|
|
File(p.join(d.path, 'big.bin')).writeAsBytesSync(List.filled(size, 0));
|
|
}
|
|
File(p.join(tmp.path, 'disk', 'loose.bin')).writeAsBytesSync(List.filled(80000, 0));
|
|
|
|
final shotKey = GlobalKey();
|
|
final nav = GlobalKey<NavigatorState>();
|
|
await t.runAsync(() => t.pumpWidget(RepaintBoundary(
|
|
key: shotKey,
|
|
child: MaterialApp(navigatorKey: nav, theme: buildTheme(), debugShowCheckedModeBanner: false, home: const Scaffold()),
|
|
)));
|
|
Future<void> settle([int n = 12]) async {
|
|
for (var i = 0; i < n; i++) {
|
|
await t.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 60)));
|
|
await t.pump();
|
|
}
|
|
}
|
|
|
|
Future<void> shot(String name) async {
|
|
await settle(4);
|
|
final r = shotKey.currentContext!.findRenderObject()! as RenderRepaintBoundary;
|
|
final image = await t.runAsync(() => r.toImage());
|
|
final bytes = await t.runAsync(() => image!.toByteData(format: ui.ImageByteFormat.png));
|
|
Directory(out).createSync(recursive: true);
|
|
File(p.join(out, 'xptools_${name}_${lang}_$w.png')).writeAsBytesSync(bytes!.buffer.asUint8List());
|
|
}
|
|
|
|
// g 격자 보기
|
|
final entries = [
|
|
for (final x in [...pics, p.join(tmp.path, 'clip.mkv'), txt.path])
|
|
FileEntry(x, isDir: false, size: File(x).lengthSync(), modified: DateTime(2026, 10, 18)),
|
|
];
|
|
nav.currentState!.push(MaterialPageRoute<void>(
|
|
builder: (_) => ExplorerGridPage(
|
|
title: 'Photos',
|
|
entries: entries,
|
|
imageExts: const ['jpg'],
|
|
videoThumb: (_) async => null,
|
|
onOpen: (_) async {},
|
|
),
|
|
));
|
|
await settle(20);
|
|
await shot('grid');
|
|
nav.currentState!.pop();
|
|
await settle();
|
|
|
|
// h 글 뷰어 (CP949 를 스스로 알아봄)
|
|
nav.currentState!.push(MaterialPageRoute<void>(builder: (_) => TextViewerPage(path: txt.path)));
|
|
await settle(20);
|
|
await shot('text');
|
|
nav.currentState!.pop();
|
|
await settle();
|
|
|
|
// i 일괄 이름 바꾸기 - [D] (사진은 찍은 때)
|
|
showBatchRename(nav.currentContext!, pics.take(4).toList(),
|
|
isDir: (_) => false, initial: const RenameRule(template: '[D]_[T]_[C]'));
|
|
await settle(30);
|
|
await shot('rename_date');
|
|
await t.tap(find.text(lang == 'ko' ? '취소' : 'Cancel').last);
|
|
await settle();
|
|
|
|
// P3 저장 공간 그림
|
|
nav.currentState!.push(MaterialPageRoute<void>(builder: (_) => StorageAnalysisPage(root: p.join(tmp.path, 'disk'))));
|
|
await settle(30);
|
|
await t.tap(find.text(lang == 'ko' ? '칸 그림' : 'Treemap'));
|
|
await settle();
|
|
await shot('storage_chart');
|
|
nav.currentState!.pop();
|
|
await settle();
|
|
await t.pumpWidget(const SizedBox());
|
|
await settle(4);
|
|
});
|
|
}
|
|
}
|
|
}
|