240 화면 시험: 여럿이 돌 때 ■ 창 · 줄의 ■
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>fix_mkv
parent
54c4d6f97f
commit
b0c3a4cee2
|
|
@ -1,3 +1,4 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
@ -15,6 +16,7 @@ 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/ai_services.dart';
|
||||
import 'package:jj_mkvmaker/services/downloader.dart';
|
||||
import 'package:jj_mkvmaker/services/media_tool.dart';
|
||||
import 'package:jj_mkvmaker/services/model_store.dart';
|
||||
import 'package:jj_mkvmaker/services/platform_services.dart';
|
||||
import 'package:jj_mkvmaker/ui/app_actions.dart';
|
||||
|
|
@ -36,6 +38,35 @@ class _Recognizer implements SpeechRecognizer {
|
|||
}
|
||||
}
|
||||
|
||||
/// 240: 취소될 때까지 끝나지 않는 FFmpeg (여럿이 동시에 도는 상태를 만든다)
|
||||
class _HoldTool implements MediaTool {
|
||||
final runs = <Object?, Completer<void>>{};
|
||||
@override
|
||||
Future<String?> version() async => 'ffmpeg test';
|
||||
@override
|
||||
Future<Set<String>> encoders() async => {'libx264'};
|
||||
@override
|
||||
Future<MediaInfo> probe(String path) async => const MediaInfo(duration: Duration(seconds: 1));
|
||||
@override
|
||||
Future<void> runFfmpeg(List<String> args, {Duration? duration, ProgressCallback? onProgress, Object? tag}) async {
|
||||
final c = runs[tag] = Completer<void>();
|
||||
try {
|
||||
await c.future;
|
||||
} finally {
|
||||
runs.remove(tag);
|
||||
}
|
||||
}
|
||||
|
||||
void _stop(Completer<void>? c) {
|
||||
if (c != null && !c.isCompleted) c.completeError(MediaToolException('사용자가 취소했습니다.'));
|
||||
}
|
||||
|
||||
@override
|
||||
void cancel() => runs.values.toList().forEach(_stop);
|
||||
@override
|
||||
void cancelTagged(Object tag) => _stop(runs[tag]);
|
||||
}
|
||||
|
||||
class _Translator implements Translator {
|
||||
@override
|
||||
Future<void> load(String modelDir) async {}
|
||||
|
|
@ -504,4 +535,50 @@ void main() {
|
|||
await tester.pumpWidget(const SizedBox());
|
||||
d.dispose();
|
||||
});
|
||||
|
||||
testWidgets('240: 여럿이 동시에 돌 때 ■ 는 묻는다 ([그대로 두기] · [모두 취소 (n개)]) · 목록 줄의 ■ 는 그 동영상만', (tester) async {
|
||||
tester.view.physicalSize = const Size(1600, 900);
|
||||
tester.view.devicePixelRatio = 1;
|
||||
addTearDown(tester.view.reset);
|
||||
final tool = _HoldTool();
|
||||
final dir = Directory.systemTemp.createTempSync('jj_batch240_');
|
||||
addTearDown(() => dir.deleteSync(recursive: true));
|
||||
final files = [
|
||||
for (final n in ['a', 'b']) (File(p.join(dir.path, '$n.mp4'))..writeAsBytesSync([0])).path,
|
||||
];
|
||||
final c = AppController(PlatformServices(mediaTool: tool, storage: DesktopStorageService()));
|
||||
late Future<void> building;
|
||||
await tester.runAsync(() async {
|
||||
await c.init();
|
||||
c.settings.maxParallelJobs = 5;
|
||||
await c.addVideos(files);
|
||||
building = c.buildVideos([...c.videos]);
|
||||
for (var i = 0; i < 200 && tool.runs.length < 2; i++) {
|
||||
await Future<void>.delayed(const Duration(milliseconds: 10));
|
||||
}
|
||||
});
|
||||
final [a, b] = c.videos;
|
||||
expect([a.status, b.status], [JobStatus.running, JobStatus.running]);
|
||||
await tester.pumpWidget(MaterialApp(home: HomePage(c: c)));
|
||||
await tester.pump();
|
||||
// ■ (위 막대): 둘이 돌고 남은 것은 없음 → 묻는다
|
||||
await tester.tap(find.byTooltip('작업 취소').first);
|
||||
await tester.pump(const Duration(milliseconds: 400)); // 도는 동안은 진행 막대가 움직여 pumpAndSettle 이 끝나지 않는다
|
||||
expect(find.text('모두 취소 (2개)'), findsOneWidget);
|
||||
await tester.tap(find.text('그대로 두기'));
|
||||
await tester.pump(const Duration(milliseconds: 400)); // 도는 동안은 진행 막대가 움직여 pumpAndSettle 이 끝나지 않는다
|
||||
expect(tool.runs.length, 2, reason: '[그대로 두기] 는 아무것도 취소하지 않음');
|
||||
// 목록 줄의 ■: 그 동영상만
|
||||
expect(find.byTooltip('이 동영상만 취소'), findsNWidgets(2));
|
||||
await tester.tap(find.byTooltip('이 동영상만 취소').first);
|
||||
await tester.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 100)));
|
||||
await tester.pump();
|
||||
expect([a.status, b.status], [JobStatus.cancelled, JobStatus.running]);
|
||||
// 하나만 남으면 ■ 는 묻지 않고 바로 취소
|
||||
await tester.tap(find.byTooltip('작업 취소').first);
|
||||
await tester.runAsync(() => building);
|
||||
await tester.pump();
|
||||
expect(b.status, JobStatus.cancelled);
|
||||
expect(find.text('모두 취소 (2개)'), findsNothing);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue