JJ_MKVMaker/test/app_manager_test.dart

118 lines
5.1 KiB
Dart

import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:jj_mkvmaker/core/app_manager.dart';
import 'package:jj_mkvmaker/ui/app_manager_page.dart';
import 'package:jj_mkvmaker/ui/explorer_page.dart' show ExplorerButton;
import 'package:path/path.dart' as p;
/// X-plore 비교 5-2 앱 관리자 (사용자 결정 10/21, Android): 보이는 앱만 · APK 백업 · 앱 정보 · 지우기 (시스템 창)
class _Fake implements AppManagerBackend {
final List<InstalledApp> apps;
final String dir;
final calls = <String>[];
_Fake(this.apps, this.dir);
@override
Future<List<InstalledApp>> list() async => apps;
@override
Future<Uint8List?> icon(String package) async => null;
@override
Future<bool> details(String package) async {
calls.add('details:$package');
return true;
}
@override
Future<bool> uninstall(String package) async {
calls.add('uninstall:$package');
return true;
}
@override
Future<String> backupDir() async => dir;
}
void main() {
late Directory tmp;
setUp(() => tmp = Directory.systemTemp.createTempSync('jj_apps_'));
tearDown(() => tmp.deleteSync(recursive: true));
InstalledApp app(String label, {int size = 1000, int day = 1, bool system = false, int splits = 0, String? apk}) => InstalledApp(
package: 'com.x.${label.toLowerCase()}',
label: label,
version: '1.2',
apk: apk ?? '',
installed: DateTime(2026, 1, day),
updated: DateTime(2026, 1, day),
size: size,
system: system,
splits: splits,
);
test('기기 답 (지도) 읽기 · APK 백업 이름 (못 쓰는 글자 _ · 이미 있으면 (2))', () async {
final a = InstalledApp.fromMap({'package': 'p', 'label': 'A/B', 'version': '3', 'apk': '/x', 'installed': 0, 'updated': 0, 'size': 5, 'splits': 2, 'system': true});
expect((a.label, a.size, a.splits, a.system), ('A/B', 5, 2, true));
final src = File(p.join(tmp.path, 'base.apk'))..writeAsStringSync('apk');
final b = app('A/B', apk: src.path);
final out = p.join(tmp.path, 'out');
final first = await backupApk(b, out);
final second = await backupApk(b, out);
expect(p.basename(first), 'A_B_1.2.apk');
expect(p.basename(second), 'A_B_1.2 (2).apk');
expect(File(second).readAsStringSync(), 'apk');
});
testWidgets('화면: 안내 · 이름 / 크기 / 설치일 정렬 · 찾기 · [APK 백업] · [앱 정보] · 시스템 앱은 [지우기] 없음', (tester) async {
tester.view.physicalSize = const Size(900, 1400);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.reset);
final src = File(p.join(tmp.path, 'b.apk'))..writeAsStringSync('apk');
final fake = _Fake([
app('Banana', size: 300, day: 5, apk: src.path, splits: 3),
app('Apple', size: 900, day: 1),
app('Settings', size: 100, day: 2, system: true),
], p.join(tmp.path, 'Download', 'JJ_MKVMaker', 'apk'));
await tester.pumpWidget(MaterialApp(home: AppManagerPage(backend: fake)));
await tester.pumpAndSettle();
expect(find.text('보이는 앱만 나옵니다 (모든 앱 목록 권한은 쓰지 않음)'), findsOneWidget);
double y(String t) => tester.getTopLeft(find.text(t)).dy;
expect(y('Apple') < y('Banana') && y('Banana') < y('Settings'), isTrue, reason: '처음은 이름 순');
await tester.tap(find.text('이름'));
await tester.pumpAndSettle();
await tester.tap(find.text('크기').last);
await tester.pumpAndSettle();
expect(y('Apple') < y('Banana'), isTrue, reason: '큰 것부터');
await tester.enterText(find.byType(TextField), 'ban');
await tester.pump();
expect(find.text('Apple'), findsNothing);
// APK 백업 (나눠진 앱이면 알림)
await tester.tap(find.text('Banana'));
await tester.pumpAndSettle();
expect(find.text('지우기 (시스템 창에서 확인)'), findsOneWidget);
await tester.tap(find.text('APK 백업'));
for (var i = 0; i < 20; i++) {
await tester.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 20)));
await tester.pump();
}
expect(File(p.join(fake.dir, 'Banana_1.2.apk')).existsSync(), isTrue);
expect(find.textContaining('나눠진 앱 (조각 3개)'), findsOneWidget);
// 시스템 앱: 지우기 없음 · 앱 정보는 시스템 화면
await tester.enterText(find.byType(TextField), 'set');
await tester.pump();
await tester.tap(find.text('Settings'));
await tester.pumpAndSettle();
expect(find.text('지우기 (시스템 창에서 확인)'), findsNothing);
await tester.tap(find.text('앱 정보 (시스템 화면)'));
await tester.pumpAndSettle();
expect(fake.calls, ['details:com.x.settings']);
});
test('가운데 버튼 [앱 관리자] 는 Android 에서만 고를 수 있음', () {
// 설계 M4: Android 인지는 부르는 쪽이 넘긴다 (c.services.platform.isAndroid)
expect(ExplorerButton.explorerValues(android: false), isNot(contains(ExplorerButton.apps)));
expect(ExplorerButton.explorerValues(android: true), contains(ExplorerButton.apps));
});
}