JJ_MKVMaker/test/settings_json_contract_test...

71 lines
3.8 KiB
Dart

import 'dart:convert';
import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
import 'package:jj_mkvmaker/app/settings.dart';
import 'package:path/path.dart' as p;
/// 설정 A 약속 시험: 설정 파일 (settings.json) 을 읽고 다시 쓴 결과가 나누기 전 판과 같다 (키 순서를 뺀 바이트 - 키를 정렬해 견줌).
/// - 합성: 모든 키의 값을 바꾼 입력 (test/fixtures/settings_golden_in.json) → 나누기 전 판의 출력 (…_out.json)
/// - 사용자 파일: JJ_USER_SETTINGS_EXPECT 가 있으면 사용자 설정 파일 (com.jj\jj_mkvmaker\settings.json) 을 시험 폴더에 복사해 읽기만 하고,
/// 나누기 전 판이 같은 파일로 낸 출력 (scratch, 커밋하지 않음) 과 견준다. 사용자 파일에는 쓰지 않는다
/// JJ_SETTINGS_GOLDEN_WRITE=1 이면 견주지 않고 기준을 만든다 (나누기 전 판에서 한 번)
void main() {
final write = Platform.environment['JJ_SETTINGS_GOLDEN_WRITE'] == '1';
const fixtures = 'test/fixtures';
/// 키를 정렬한 JSON (키 순서는 분야를 옮기면 바뀐다 - 값 · 키 집합만 견줌)
String canonical(Object? v) {
Object? sort(Object? x) => x is Map
? {for (final k in (x.keys.map((e) => '$e').toList()..sort())) k: sort(x[k])}
: x is List
? [for (final e in x) sort(e)]
: x;
return const JsonEncoder.withIndent(' ').convert(sort(v));
}
String roundTrip(String text) => canonical(AppSettings.fromJson(jsonDecode(text) as Map<String, dynamic>).toJson());
/// 모든 키의 값을 바꾼 입력 (bool 은 반대, 수는 +7, 글은 앞에 x_, 안쪽 묶음도) - 읽기의 검사 · 고침도 함께 지나게
Object? change(Object? v) => switch (v) {
final bool b => !b,
final int n => n + 7,
final double d => d + 0.5,
final String s => 'x_$s',
final Map<dynamic, dynamic> m => {for (final e in m.entries) '${e.key}': change(e.value)},
_ => v,
};
Map<String, Object?> mutated() => {for (final e in AppSettings().toJson(plainSecrets: true).entries) e.key: change(e.value)};
test('합성: 모든 키를 바꾼 설정을 읽고 쓰면 나누기 전 판과 같다 · 처음 값도', () {
final inFile = File(p.join(fixtures, 'settings_golden_in.json'));
final outFile = File(p.join(fixtures, 'settings_golden_out.json'));
final defFile = File(p.join(fixtures, 'settings_golden_default.json'));
if (write) {
Directory(fixtures).createSync(recursive: true);
inFile.writeAsStringSync(const JsonEncoder.withIndent(' ').convert(mutated()));
outFile.writeAsStringSync(roundTrip(inFile.readAsStringSync()));
defFile.writeAsStringSync(canonical(AppSettings().toJson()));
return;
}
// git 이 줄 끝을 CRLF 로 바꿔 꺼낼 수 있다 - LF 로 맞춰 견줌
String read(File f) => f.readAsStringSync().replaceAll('\r\n', '\n');
expect(roundTrip(read(inFile)), read(outFile));
expect(canonical(AppSettings().toJson()), read(defFile));
});
final expectPath = Platform.environment['JJ_USER_SETTINGS_EXPECT'];
test('사용자 설정 파일 (복사본): 읽고 쓰면 나누기 전 판과 같다', () {
final user = File(p.join(Platform.environment['APPDATA'] ?? '', 'com.jj', 'jj_mkvmaker', 'settings.json'));
final tmp = Directory.systemTemp.createTempSync('jj_settings_user_');
addTearDown(() => tmp.deleteSync(recursive: true));
final copy = user.copySync(p.join(tmp.path, 'settings.json'));
final got = roundTrip(copy.readAsStringSync());
if (write) {
File(expectPath!).writeAsStringSync(got);
return;
}
expect(got, File(expectPath!).readAsStringSync().replaceAll('\r\n', '\n'));
}, skip: expectPath == null ? '사용자 파일 기준 (JJ_USER_SETTINGS_EXPECT) 이 없으면 건너뜀 - 이 PC 에서만' : null);
}