173 lines
7.8 KiB
Dart
173 lines
7.8 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:jj_mkvmaker/app/app_controller.dart';
|
|
import 'package:jj_mkvmaker/app/cleanup.dart';
|
|
import 'package:jj_mkvmaker/app/download_manager.dart';
|
|
import 'package:jj_mkvmaker/core/download_detect.dart';
|
|
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/downloader.dart';
|
|
import 'package:jj_mkvmaker/services/model_store.dart';
|
|
import 'package:jj_mkvmaker/services/platform_services.dart';
|
|
import 'package:path/path.dart' as p;
|
|
|
|
/// 앱 임시 폴더를 시험 폴더로
|
|
class _Storage extends DesktopStorageService {
|
|
final String tmp;
|
|
_Storage(this.tmp);
|
|
@override
|
|
Future<String> tempDirectory() async => tmp;
|
|
}
|
|
|
|
class _Backend implements DownloadBackend {
|
|
@override
|
|
DownloadKind get kind => DownloadKind.video;
|
|
@override
|
|
Future<(String, List<PlaylistEntry>)?> expandPlaylist(String url) async => null;
|
|
@override
|
|
Future<void> start(DownloadTask t, void Function() changed) async => t.state = DownloadState.downloading;
|
|
@override
|
|
Future<void> pause(DownloadTask t) async {}
|
|
@override
|
|
Future<void> cancel(DownloadTask t) async {}
|
|
@override
|
|
Future<void> shutdown() async {}
|
|
}
|
|
|
|
void main() {
|
|
late Directory root;
|
|
late AppController c;
|
|
late String tmp, data, models, sysTmp, logs;
|
|
|
|
File make(String path, [int size = 100]) => File(path)
|
|
..parent.createSync(recursive: true)
|
|
..writeAsBytesSync(List.filled(size, 1));
|
|
|
|
setUp(() {
|
|
root = Directory.systemTemp.createTempSync('jj_clean_');
|
|
tmp = p.join(root.path, 'apptmp');
|
|
data = p.join(root.path, 'data');
|
|
models = p.join(root.path, 'models');
|
|
sysTmp = p.join(root.path, 'systemp');
|
|
logs = p.join(data, 'Logs');
|
|
c = AppController(PlatformServices(
|
|
mediaTool: ProcessMediaTool('x', 'y'),
|
|
storage: _Storage(tmp),
|
|
models: ModelStore(models),
|
|
));
|
|
c.settings.downloadRoot = p.join(root.path, 'dl');
|
|
c.logFile = p.join(logs, 'app.log');
|
|
|
|
// 지울 것
|
|
make(p.join(tmp, 'ai_123', 'audio.wav'), 5000);
|
|
make(p.join(tmp, 'jj_adjust_after_1.png'));
|
|
make(p.join(data, 'settings.json.4567.tmp'));
|
|
make(p.join(c.settings.ytDlpDir, '영상 [abc].mp4.part'), 3000);
|
|
make(p.join(c.settings.ytDlpDir, '영상 [abc].f137.mp4'), 2000);
|
|
make(p.join(c.settings.ytDlpDir, '영상 [abc].mp4.ytdl'));
|
|
make(p.join(c.settings.ytDlpDir, '재생목록', '두번째 [def].webm.part-Frag12'));
|
|
make(p.join(c.settings.aria2Dir, 'movie.mkv.aria2'));
|
|
make(p.join(models, 'whisper', 'ggml-small.bin.part'), 4000);
|
|
make(p.join(sysTmp, 'jj_mkvmaker_update_2026.10.04_001', 'JJ_MKVMaker.zip'), 6000);
|
|
make(p.join(sysTmp, 'jj_ffmpeg.download'));
|
|
make(p.join(logs, 'app.log.1'), 700);
|
|
|
|
// 남길 것
|
|
make(p.join(c.settings.ytDlpDir, '영상 [abc].mp4'), 9000);
|
|
make(p.join(c.settings.ytDlpDir, 'jj_mkv', '영상 [abc].mkv'));
|
|
make(p.join(c.settings.aria2Dir, 'movie.mkv'));
|
|
make(p.join(models, 'whisper', 'ggml-base.bin'));
|
|
make(p.join(data, 'settings.json'));
|
|
make(p.join(sysTmp, 'other_app.tmp'));
|
|
make(p.join(logs, 'app.log'));
|
|
});
|
|
tearDown(() => root.deleteSync(recursive: true));
|
|
|
|
test('찾기: 묶음별로 남은 조각만 찾고, 받은 동영상 · MKV · 모델 · 설정 · 지금 기록은 건드리지 않는다', () async {
|
|
final groups = await Cleaner(c, dataDir: data, systemTemp: sysTmp).scan();
|
|
Map<String, List<String>> names = {
|
|
for (final g in groups) g.id: [for (final i in g.items) p.basename(i.path)]..sort(),
|
|
};
|
|
expect(names['work'], ['ai_123', 'jj_adjust_after_1.png', 'settings.json.4567.tmp']);
|
|
expect(names['download'], [
|
|
'movie.mkv.aria2',
|
|
'두번째 [def].webm.part-Frag12',
|
|
'영상 [abc].f137.mp4',
|
|
'영상 [abc].mp4.part',
|
|
'영상 [abc].mp4.ytdl',
|
|
]);
|
|
expect(names['models'], ['ggml-small.bin.part']);
|
|
expect(names['update'], ['jj_ffmpeg.download', 'jj_mkvmaker_update_2026.10.04_001']);
|
|
expect(names['logs'], ['app.log.1']);
|
|
expect(groups.firstWhere((g) => g.id == 'work').bytes, 5000 + 100 + 100);
|
|
expect(groups.every((g) => g.skipped == null), isTrue);
|
|
|
|
final (n, bytes) = await Cleaner(c, dataDir: data, systemTemp: sysTmp).clean(groups);
|
|
expect(n, 3 + 5 + 1 + 2 + 1);
|
|
expect(bytes, greaterThan(20000));
|
|
// 남길 것은 그대로
|
|
for (final keep in [
|
|
p.join(c.settings.ytDlpDir, '영상 [abc].mp4'),
|
|
p.join(c.settings.ytDlpDir, 'jj_mkv', '영상 [abc].mkv'),
|
|
p.join(c.settings.aria2Dir, 'movie.mkv'),
|
|
p.join(models, 'whisper', 'ggml-base.bin'),
|
|
p.join(data, 'settings.json'),
|
|
p.join(sysTmp, 'other_app.tmp'),
|
|
p.join(logs, 'app.log'),
|
|
]) {
|
|
expect(File(keep).existsSync(), isTrue, reason: keep);
|
|
}
|
|
// 다시 찾으면 없음
|
|
final again = await Cleaner(c, dataDir: data, systemTemp: sysTmp).scan();
|
|
expect(again.every((g) => g.items.isEmpty), isTrue);
|
|
});
|
|
|
|
test('다운로드 중 (일시정지 포함) 이면 받다 만 다운로드는 건너뛴다', () async {
|
|
final d = DownloadManager(backends: [_Backend()], settings: () => c.settings, readClipboard: () async => null);
|
|
addTearDown(d.dispose);
|
|
d.addPage('https://www.youtube.com/watch?v=abc');
|
|
await Future<void>.delayed(Duration.zero);
|
|
expect(d.activeCount, 1);
|
|
final groups = await Cleaner(c, downloads: d, dataDir: data, systemTemp: sysTmp).scan();
|
|
final dl = groups.firstWhere((g) => g.id == 'download');
|
|
expect(dl.skipped, isNotNull);
|
|
await Cleaner(c, downloads: d, dataDir: data, systemTemp: sysTmp).clean(groups);
|
|
expect(File(p.join(c.settings.ytDlpDir, '영상 [abc].mp4.part')).existsSync(), isTrue, reason: '이어받기용 조각은 그대로');
|
|
expect(Directory(p.join(tmp, 'ai_123')).existsSync(), isFalse, reason: '다른 묶음은 정리');
|
|
});
|
|
|
|
test('크기 글', () {
|
|
expect(Cleaner.sizeText(512), '512 B');
|
|
expect(Cleaner.sizeText(2048), '2 KB');
|
|
expect(Cleaner.sizeText(5 * 1024 * 1024), '5.0 MB');
|
|
expect(Cleaner.sizeText(3 * 1024 * 1024 * 1024), '3.0 GB');
|
|
});
|
|
test('관리자 지시 (10/20): 앱이 켜질 때 지난 업데이트가 남긴 것 (풀어 둔 새 판 · .ps1 · .log) 을 지운다 - 막 생긴 것 · 다른 것은 남김', () {
|
|
final tmp = Directory.systemTemp.createTempSync('jj_upd_left_');
|
|
addTearDown(() => tmp.deleteSync(recursive: true));
|
|
final old = DateTime.now().subtract(const Duration(hours: 1));
|
|
void make(String rel, {bool fresh = false}) {
|
|
final f = File(p.join(tmp.path, rel))..createSync(recursive: true);
|
|
if (!fresh) f.setLastModifiedSync(old);
|
|
}
|
|
|
|
make(p.join('jj_mkvmaker_update_2026.10.18_979', 'extract', 'jj_mkvmaker.exe'));
|
|
make('jj_mkvmaker_update.ps1');
|
|
make('jj_mkvmaker_update.log', fresh: true); // 교체 스크립트가 막 쓴 것
|
|
make('jj_mkvmaker_updater.txt');
|
|
make(p.join('jj_mkvmaker', 'a.wav'));
|
|
// 폴더는 안에 파일을 만들어 바뀐 때가 지금이다 - 5분 뒤로 보고 지우는지 본다
|
|
final dir = Directory(p.join(tmp.path, 'jj_mkvmaker_update_2026.10.18_979'));
|
|
final gone = removeUpdateLeftovers(tmp.path, now: DateTime.now().add(const Duration(minutes: 5)));
|
|
expect(gone..sort(), ['jj_mkvmaker_update.log', 'jj_mkvmaker_update.ps1', 'jj_mkvmaker_update_2026.10.18_979'],
|
|
reason: '5분 뒤로 보면 막 생긴 기록도 2분이 지남');
|
|
expect(dir.existsSync(), isFalse);
|
|
expect(File(p.join(tmp.path, 'jj_mkvmaker_updater.txt')).existsSync(), isTrue);
|
|
expect(Directory(p.join(tmp.path, 'jj_mkvmaker')).existsSync(), isTrue);
|
|
|
|
make('jj_mkvmaker_update.log', fresh: true);
|
|
expect(removeUpdateLeftovers(tmp.path), isEmpty, reason: '2분이 안 된 것은 남김 (스크립트가 끝나는 중일 수 있음)');
|
|
});
|
|
}
|