108 lines
4.2 KiB
Dart
108 lines
4.2 KiB
Dart
import 'dart:async';
|
|
import 'dart:io';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:jj_mkvmaker/app/app_controller.dart';
|
|
import 'package:jj_mkvmaker/app/local_fs.dart';
|
|
import 'package:jj_mkvmaker/core/file_ops.dart' show FileEntry;
|
|
import 'package:jj_mkvmaker/platform/windows/desktop_storage_service.dart';
|
|
import 'package:jj_mkvmaker/platform/windows/process_media_tool.dart';
|
|
import 'package:jj_mkvmaker/services/platform_services.dart';
|
|
import 'package:jj_mkvmaker/ui/explorer/explorer_controller.dart';
|
|
import 'package:jj_mkvmaker/ui/explorer/explorer_host.dart';
|
|
import 'package:jj_mkvmaker/ui/explorer/explorer_pane.dart';
|
|
import 'package:path/path.dart' as p;
|
|
|
|
/// M2 빈 목록 경합 (10/21): 앱으로 돌아와 다시 읽는 동안 (reloadOpen) 사용자가 펼친 폴더가, 다시 읽기가 끝날 때 잊혀 비어 보였다.
|
|
/// 다시 읽기를 붙잡는 느린 목록을 생성자 (ExplorerController.fs) 로 넘겨 겹침을 매번 만든다. 폴더 · 목록은 실제 (RealLocalFs)
|
|
void main() {
|
|
late Directory tmp;
|
|
late String a, b, show;
|
|
setUp(() {
|
|
tmp = Directory.systemTemp.createTempSync('jj_race_');
|
|
a = p.join(tmp.path, 'A');
|
|
b = p.join(tmp.path, 'B');
|
|
show = p.join(a, 'Show');
|
|
Directory(show).createSync(recursive: true);
|
|
Directory(b).createSync();
|
|
File(p.join(show, 'ep1.mkv')).writeAsStringSync('1');
|
|
});
|
|
tearDown(() => tmp.deleteSync(recursive: true));
|
|
|
|
testWidgets('다시 읽는 동안 펼친 폴더는 남는다 · 시작할 때 읽어 둔 것 중 닫힌 것만 잊는다', (t) async {
|
|
final fs = _HeldFs();
|
|
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
|
|
final key = GlobalKey<_HostState>();
|
|
await t.pumpWidget(MaterialApp(home: _Host(key: key)));
|
|
final ctl = ExplorerController(c, rsync: false, host: key.currentState!, fs: fs)..local = [(a, 'A:'), (b, 'B:')];
|
|
addTearDown(ctl.dispose);
|
|
final q = ctl.panes[0];
|
|
await t.runAsync(() async {
|
|
await ctl.goTo(q, a);
|
|
await ctl.load(q, b); // 읽어 두었지만 열려 있지 않음 - 다시 읽기 뒤 잊어야 함
|
|
expect(q.cache.containsKey(b), isTrue);
|
|
// 다시 읽기를 A 에서 붙잡는다
|
|
fs.hold(a);
|
|
final reload = ctl.reloadOpen();
|
|
await fs.reached.future;
|
|
// 그동안 사용자가 Show 를 펼침
|
|
q.expanded.add(show);
|
|
await ctl.load(q, show);
|
|
fs.release.complete();
|
|
await reload;
|
|
});
|
|
expect([for (final e in q.cache[show] ?? const <FileEntry>[]) p.basename(e.path)], ['ep1.mkv'], reason: '펼친 폴더가 비어 보이지 않음');
|
|
expect(q.cache.containsKey(b), isFalse, reason: '시작할 때 있던 것 중 닫힌 것은 잊음');
|
|
expect(q.cache.containsKey(a), isTrue);
|
|
});
|
|
}
|
|
|
|
/// 실제 목록 ([RealLocalFs]) 인데 [hold] 로 정한 폴더 한 번은 [release] 까지 붙잡는다
|
|
class _HeldFs implements LocalFs {
|
|
static const _real = RealLocalFs();
|
|
String? holdAt;
|
|
late Completer<void> reached, release;
|
|
|
|
/// [dir] 를 다음 한 번 붙잡는다 (Completer 는 실제 시간 영역 - runAsync 안에서 부른다)
|
|
void hold(String dir) {
|
|
holdAt = dir;
|
|
reached = Completer<void>();
|
|
release = Completer<void>();
|
|
}
|
|
|
|
@override
|
|
Future<List<FileEntry>> list(String dir, {bool showHidden = false}) async {
|
|
if (dir == holdAt) {
|
|
holdAt = null;
|
|
reached.complete();
|
|
await release.future;
|
|
}
|
|
return _real.list(dir, showHidden: showHidden);
|
|
}
|
|
|
|
@override
|
|
Future<List<(String, String)>> volumes() => _real.volumes();
|
|
@override
|
|
Future<bool> probe(String path) => _real.probe(path);
|
|
}
|
|
|
|
class _Host extends StatefulWidget {
|
|
const _Host({super.key});
|
|
@override
|
|
State<_Host> createState() => _HostState();
|
|
}
|
|
|
|
class _HostState extends State<_Host> implements ExplorerHost {
|
|
@override
|
|
bool get alive => mounted;
|
|
@override
|
|
void snack(String text) {}
|
|
@override
|
|
void reveal(ExplorerPane pane, String path) {}
|
|
@override
|
|
void afterFrame(VoidCallback f) => WidgetsBinding.instance.addPostFrameCallback((_) => f());
|
|
@override
|
|
Widget build(BuildContext context) => const Scaffold(body: SizedBox());
|
|
}
|