JJ_MKVMaker/test/reader_modules_test.dart

126 lines
6.4 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.

// 보기 화면 모듈 (lib/ui/reader/) 의 약속 시험 - 실제 구성 요소로 (가짜 원본 없음, 사용자 설정 저장소 쓰지 않음)
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:image/image.dart' as img;
import 'package:jj_mkvmaker/core/reader_sources.dart';
import 'package:jj_mkvmaker/ui/reader/reader_bars.dart';
import 'package:jj_mkvmaker/ui/reader/reader_controls.dart';
import 'package:jj_mkvmaker/ui/reader/reader_images.dart';
import 'package:jj_mkvmaker/ui/reader/reader_slots.dart';
import 'package:path/path.dart' as p;
String _png(Directory dir, String name, {int w = 40, int h = 60}) {
final im = img.Image(width: w, height: h);
img.fill(im, color: img.ColorRgb8(200, 10, 10));
return (File(p.join(dir.path, name))..writeAsBytesSync(img.encodePng(im))).path;
}
void main() {
late Directory tmp;
setUp(() => tmp = Directory.systemTemp.createTempSync('jj_reader_mod_'));
tearDown(() => tmp.deleteSync(recursive: true));
test('reader_slots: 쪽 만들기 · relocateSlot (반쪽 ↔ 통째) · pageAfterRemoval · 이름', () {
final split = buildSlots(3, (i) => i == 1);
expect(split, [(page: 0, part: -1), (page: 1, part: 0), (page: 1, part: 1), (page: 2, part: -1)]);
final whole = buildSlots(3, (_) => false);
// 둘째 반쪽을 보다 나누기를 끄면 그 장 통째로, 통째를 보다 켜면 첫 반쪽으로
expect(relocateSlot(whole, (page: 1, part: 1), 2), 1);
expect(relocateSlot(split, (page: 2, part: -1), 2), 3);
// 장이 없어지면 옛 번호를 범위 안으로
expect(relocateSlot(buildSlots(1, (_) => false), (page: 5, part: -1), 4), 0);
// 보던 1 이 없어지면 뒤의 2 (새 번호 1), 뒤가 다 없으면 앞, 모두 없으면 -1
expect(pageAfterRemoval(1, {1}, 3), 1);
expect(pageAfterRemoval(2, {1, 2}, 3), 0);
expect(pageAfterRemoval(0, {0, 1}, 2), -1);
expect([slotLabel((page: 11, part: -1)), slotLabel((page: 11, part: 1))], ['12', '12-2']);
expect(slotAt(const [], 3), (page: 0, part: -1));
expect(slotBaseWidth((page: 0, part: 0), 5000), 8192);
});
test('reader_slots: splitsPage - 켜기 · 끄기 · 자동 (회전 반영)', () {
final dims = {0: const Size(200, 100), 1: const Size(100, 200), 2: const Size(100, 200)};
expect(splitsPage('on', 1, dims, turns: 0, bundle: false), isTrue);
expect(splitsPage('off', 0, dims, turns: 0, bundle: true), isFalse);
expect(splitsPage('auto', 0, dims, turns: 0, bundle: false), isTrue, reason: '세로 사이의 가로 한 장');
expect(splitsPage('auto', 0, dims, turns: 1, bundle: false), isFalse, reason: '돌리면 세로');
expect(splitsPage('auto', 9, dims, turns: 0, bundle: true), isFalse, reason: '크기를 모르면 나누지 않음');
});
test('reader_controls: tapZone · volumeKeyStep', () {
expect([tapZone(90, 100, rtl: false), tapZone(10, 100, rtl: false), tapZone(50, 100, rtl: false)], [1, -1, 0]);
expect([tapZone(90, 100, rtl: true), tapZone(10, 100, rtl: true)], [-1, 1]);
const down = KeyDownEvent(
physicalKey: PhysicalKeyboardKey.audioVolumeDown, logicalKey: LogicalKeyboardKey.audioVolumeDown, timeStamp: Duration.zero);
const upRelease = KeyUpEvent(
physicalKey: PhysicalKeyboardKey.audioVolumeUp, logicalKey: LogicalKeyboardKey.audioVolumeUp, timeStamp: Duration.zero);
const other = KeyDownEvent(physicalKey: PhysicalKeyboardKey.keyA, logicalKey: LogicalKeyboardKey.keyA, timeStamp: Duration.zero);
expect(volumeKeyStep(down, enabled: true), 1);
expect(volumeKeyStep(upRelease, enabled: true), 0, reason: '뗄 때도 처리 (볼륨 안 바뀜)');
expect(volumeKeyStep(down, enabled: false), isNull);
expect(volumeKeyStep(other, enabled: true), isNull);
});
testWidgets('reader_controls: 계속 보기 시계는 장이 보인 뒤부터 재고, 끄면 멈춘다', (tester) async {
var ticks = 0;
final show = ReaderSlideshow(seconds: () => 2, onTick: () => ticks++);
addTearDown(show.dispose);
expect(show.set(true, '0:0'), isTrue);
await tester.pump(const Duration(seconds: 3));
expect(ticks, 0, reason: '아직 보이지 않은 장');
show.markShown('0:0');
await tester.pump(const Duration(seconds: 2));
expect(ticks, 1);
show.markShown('0:1');
expect(show.set(false, '0:1'), isTrue);
await tester.pump(const Duration(seconds: 3));
expect(ticks, 1);
final z = ReaderZoom();
expect([z.finger(1), z.finger(1), z.holdPage], [false, true, true]);
z.reset();
});
testWidgets('reader_images: 장마다 한 번 읽기 · forget 뒤 새로 · 그림 크기 (실제 PNG)', (tester) async {
final src = ImageFilesSource([_png(tmp, 'a.png', w: 40, h: 60), _png(tmp, 'b.png')], tempDir: tmp.path, title: 't');
final cache = ReaderImageCache(src);
final a = cache.full(0, 800);
expect(identical(a, cache.full(0, 800)), isTrue);
cache.forget(0, 0);
final b = cache.full(0, 800);
expect(identical(a, b), isFalse);
final size = await tester.runAsync(() async => ReaderImageCache.imageSize(await b));
expect(size, const Size(40, 60));
expect(cache.sharpWidthFor(0, 1.2, 800), isNull);
expect(cache.sharpWidthFor(0, 2, 800), 1600);
await tester.runAsync(() async {
await a;
cache.dispose();
});
});
testWidgets('reader_bars: fitBarItems (넘치면 ⋮ 자리 빼고) · 갤러리 줄 키 · 누름', (tester) async {
final items = [for (var i = 0; i < 5; i++) ReaderBarItem(48, [ReaderAct(const Icon(Icons.add), 't$i', () {})])];
expect(fitBarItems(items, 240), 5);
expect(fitBarItems(items, 239), 3, reason: '48 (⋮) + 48 × 3 = 192');
final src = ImageFilesSource([_png(tmp, 'a.png'), _png(tmp, 'b.png'), _png(tmp, 'c.png')], tempDir: tmp.path, title: 't');
final cache = ReaderImageCache(src);
final gallery = ScrollController();
addTearDown(gallery.dispose);
int? tapped;
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: ReaderGalleryStrip(controller: gallery, count: 3, current: 1, rtl: false, thumb: cache.thumb, onTap: (i) => tapped = i),
),
));
expect(find.byKey(const ValueKey('thumb2')), findsOneWidget);
await tester.tap(find.byKey(const ValueKey('thumb2')));
expect(tapped, 2);
expect(galleryOffset(1, 128, 1000), 32);
await tester.pumpWidget(const SizedBox());
await tester.runAsync(() async => cache.dispose());
});
}