JJ_MKVMaker/test/support/rsync_scenes.dart

103 lines
4.7 KiB
Dart

import 'dart:convert' show utf8;
import 'dart:io';
import 'package:jj_mkvmaker/core/sync_tools.dart' show splitOptions, toCygwinPath;
import 'package:path/path.dart' as p;
/// 28 (10/19) 이 원격 맞추기 vs 실제 rsync 비교에 쓴 장면 · 옵션 (rsync_filter_test 의 "28 의 장면" 과 같은 것).
/// 원격 (WebDAV · SFTP · FTP · SMB) 맞추기의 결과 트리를 실제 rsync 를 로컬에서 돌린 결과 트리와 비교할 때 쓴다.
/// 보내는 쪽 파일
const sceneSrc = [
'a.txt', 'b.log', 'build/x.o', 'build/sub/y.o', 'sub/c.txt', 'sub/deep/d.txt', 'sub/deep/e.log', 'node_modules/m/i.js',
'src/node_modules/n.js', 'docs/readme.md', 'docs/img/p.png', '한글 폴더/가.txt', 'space dir/s.bak', 'Keep.TXT', 'tmp/t.txt',
'x/tmp/t2.txt', 'foo/bar', 'foo/q/bar',
];
/// 받는 쪽에만 있는 파일 (지우기 · 거른 것 지키기 · 거른 것이 든 폴더)
const sceneDst = [
'extra.txt', 'keep.log', 'build/old.o', 'old/keep.log', 'old/gone.txt', 'sub/deep/z.txt', 'tmp/old.txt', 'onlydst/a.bak',
'old2/deep/x/keep.log', 'old2/deep/x/g.txt', 'old2/deep/y/h.txt', 'old2/e.txt',
];
/// 옵션 (그대로 rsync 에 · 원격 맞추기는 그 옵션의 지우기 · 거르기)
const sceneOptions = [
'-a --delete --exclude=*.log --exclude=build/ --exclude=/sub/deep/',
'-a --delete --exclude=*.log',
'-a --delete --include=*/ --include=*.txt --exclude=*',
'-a --delete --exclude=node_modules/',
'-a --delete --exclude=/tmp/',
'-a --delete --exclude=tmp/',
'-a --delete --exclude=sub/deep',
'-a --delete --exclude=docs/***',
'-a --delete --exclude=foo/**/bar',
'-a --delete --exclude=**/bar',
'-a --delete --exclude=*.[lb][oa][gk]',
'-a --delete --exclude="space dir/"',
'-a --delete --exclude=한글*/',
'-a --delete --exclude=*.bak --delete-excluded',
'-a --delete --exclude=*.log --delete-excluded',
'-a --delete --filter="- *.o" --filter="+ build/"',
'-a --delete --include=sub/deep/d.txt --exclude=sub/deep/*',
'-a --delete --exclude=?.txt',
'-a --delete --exclude=*.TXT',
'-a --delete --exclude=deep/',
'-a --delete --exclude=/old2/deep/x/keep.log',
'-a --del',
'-a --delete-after --exclude=*.log',
'-a --delete --exclude=**/x/',
'-a --delete --include=*.txt --exclude=*.log --exclude=*/',
'-a --delete -f"+ *.txt" --exclude=*',
'-a --delete -f"- *.log"',
// 지우기 없음 (거른 것만)
'-a --exclude=*.log --exclude=build/',
];
/// 앱에 들어 있는 rsync (Windows). 없으면 null
String? get sceneRsync {
final f = p.join(Directory.current.path, 'third_party', 'rsync', 'windows', 'rsync.exe');
return Platform.isWindows && File(f).existsSync() ? f : null;
}
/// 장면을 만든다 (있던 것은 지우고). 보내는 쪽 글은 "s:경로", 받는 쪽은 "d:경로"
/// [srcFiles] · [dstFiles] 로 다른 장면도 (받는 쪽 글은 "dd:경로" 로 - 양쪽에 같은 이름이 있으면 크기가 달라 늘 바뀜으로 보이게)
void writeScene(String src, String dst, {List<String> srcFiles = sceneSrc, List<String>? dstFiles}) {
for (final (dir, list, tag) in [(src, srcFiles, 's'), (dst, dstFiles ?? sceneDst, dstFiles == null ? 'd' : 'dd')]) {
final d = Directory(dir);
if (d.existsSync()) d.deleteSync(recursive: true);
d.createSync(recursive: true);
for (final r in list) {
File(p.join(dir, r))
..createSync(recursive: true)
..writeAsStringSync('$tag:$r');
}
}
}
/// 폴더 안의 트리: 상대 경로 (/) → 폴더면 "/", 파일이면 그 글
Map<String, String> treeOf(String dir) {
final out = <String, String>{};
final root = Directory(dir);
if (!root.existsSync()) return out;
for (final e in root.listSync(recursive: true)) {
final rel = p.relative(e.path, from: dir).replaceAll(r'\', '/');
out[rel] = e is Directory ? '/' : File(e.path).readAsStringSync();
}
return out;
}
/// 실제 rsync 로 src/ → dst/ 를 맞춘 뒤 dst 의 트리 (src · dst 는 writeScene 으로 만든 로컬 폴더)
Future<Map<String, String>> realRsyncTree(String rsync, String src, String dst, String opts) async {
final r = await Process.run(rsync, ['-8', ...splitOptions(opts), '${toCygwinPath(src)}/', '${toCygwinPath(dst)}/'],
stdoutEncoding: utf8, stderrEncoding: utf8);
if (r.exitCode != 0) throw StateError('rsync $opts → ${r.exitCode}: ${r.stderr}');
return treeOf(dst);
}
/// 두 트리의 다른 점 (없으면 빈 목록)
List<String> treeDiff(Map<String, String> want, Map<String, String> got) => [
for (final k in {...want.keys, ...got.keys}.toList()..sort())
if (want[k] != got[k])
'$k: rsync=${want[k] == null ? '없음' : want[k] == '/' ? '폴더' : '"${want[k]}"'} · 앱=${got[k] == null ? '없음' : got[k] == '/' ? '폴더' : '"${got[k]}"'}',
];