import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; import 'package:jj_mkvmaker/core/lan_scan.dart'; import 'package:jj_mkvmaker/core/mdns_smb.dart'; /// X-plore j 보충: mDNS (_smb._tcp.local) 질의 · 응답 읽기 (이름 압축 포함), 찾기 합치기 void main() { List name(String n) => [ for (final p in n.split('.')) ...[p.length, ...p.codeUnits], 0, ]; test('질의: PTR · QU (유니캐스트로 답해 달라)', () { final q = mdnsQuery(0x1234, '_smb._tcp.local', 12); expect(q.sublist(0, 6), [0x12, 0x34, 0, 0, 0, 1]); expect(q.sublist(q.length - 4), [0, 12, 0x80, 1]); }); test('응답: PTR → 인스턴스, SRV → 호스트 (이름 압축), A → 주소', () { final b = BytesBuilder(); b.add([0, 0, 0x84, 0, 0, 0, 0, 1, 0, 0, 0, 2]); // 답 1 · 추가 2 final ptrOwner = b.length; b.add(name('_smb._tcp.local')); b.add([0, 12, 0, 1, 0, 0, 0, 120]); final inst = [3, ...'NAS'.codeUnits, 0xc0, ptrOwner]; // "NAS" + 압축 (_smb._tcp.local) b.add([0, inst.length, ...inst]); final instAt = b.length - inst.length; // SRV: 이름 = 인스턴스 (압축), 자료 = 우선 · 무게 · 포트 · 대상 "ds1815.local" b.add([0xc0, instAt, 0, 33, 0, 1, 0, 0, 0, 120]); final target = name('ds1815.local'); b.add([0, 6 + target.length, 0, 0, 0, 0, 1, 189, ...target]); final targetAt = b.length - target.length; b.add([0xc0, targetAt, 0, 1, 0, 1, 0, 0, 0, 120, 0, 4, 192, 168, 0, 15]); final r = parseMdns(Uint8List.fromList(b.toBytes())); expect(r.map((x) => (x.name, x.type, x.data)), [ ('_smb._tcp.local', 12, 'NAS._smb._tcp.local'), ('NAS._smb._tcp.local', 33, 'ds1815.local'), ('ds1815.local', 1, '192.168.0.15'), ]); }); test('합치기: mDNS 이름이 포트 훑기 결과를 덮고, 주소 순서대로', () async { final out = await findSmbServersBoth(({cancelled, onProgress}) async => const [LanHost('192.168.0.20', null), LanHost('192.168.0.3', null)], cancelled: () => false); // 이 PC 의 망에 실제 mDNS 응답자가 있으면 더 나올 수 있다 - 훑기 결과 두 개는 늘 있어야 함 expect(out.map((h) => h.address), containsAllInOrder(['192.168.0.3', '192.168.0.20'])); }); }