You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
125 lines
4.2 KiB
125 lines
4.2 KiB
import 'dart:typed_data';
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:meshcore_open/connector/meshcore_protocol.dart';
|
|
import 'package:meshcore_open/models/channel_message.dart';
|
|
|
|
// #985: a 13-hop message with 3-byte path hashes (packed path-len 0x8D)
|
|
// rendered as "39 hops". Three defects: the V3 path_len field was read
|
|
// signed (0x8D became -115, swallowed as the flood sentinel), the log-RX
|
|
// ingress stored the width-stripped hop count, and the repeat merge
|
|
// substituted the path BYTE length into the packed field.
|
|
|
|
Uint8List _v3Frame({required int pathLen, String text = 'Dee3: hi'}) {
|
|
final textBytes = text.codeUnits;
|
|
return Uint8List.fromList([
|
|
respCodeChannelMsgRecvV3,
|
|
0, // SNR
|
|
0, // reserved1 (bit0 = outgoing)
|
|
0, // reserved2
|
|
2, // channel_idx
|
|
pathLen,
|
|
txtTypePlain,
|
|
0x40, 0xE2, 0x01, 0x00, // timestamp LE
|
|
...textBytes,
|
|
]);
|
|
}
|
|
|
|
Uint8List _legacyFrame({required int pathLen, String text = 'Dee3: hi'}) {
|
|
final textBytes = text.codeUnits;
|
|
return Uint8List.fromList([
|
|
respCodeChannelMsgRecv,
|
|
2, // channel_idx
|
|
pathLen,
|
|
txtTypePlain,
|
|
0x40, 0xE2, 0x01, 0x00, // timestamp LE
|
|
...textBytes,
|
|
]);
|
|
}
|
|
|
|
void main() {
|
|
group('ChannelMessage.fromFrame path-len decode (#985)', () {
|
|
test(
|
|
'3-byte-width packed byte stays unsigned: 0x8D = 13 hops, width 3',
|
|
() {
|
|
final m = ChannelMessage.fromFrame(_v3Frame(pathLen: 0x8D))!;
|
|
expect(m.pathLength, 0x8D);
|
|
expect(m.hopCount, 13);
|
|
expect(m.pathHashSize, 3);
|
|
},
|
|
);
|
|
|
|
test('2-byte-width packed byte: 0x53 = 19 hops, width 2', () {
|
|
final m = ChannelMessage.fromFrame(_v3Frame(pathLen: 0x53))!;
|
|
expect(m.hopCount, 19);
|
|
expect(m.pathHashSize, 2);
|
|
});
|
|
|
|
test('0xFF stays the direct-route sentinel (-1)', () {
|
|
final m = ChannelMessage.fromFrame(_v3Frame(pathLen: 0xFF))!;
|
|
expect(m.pathLength, -1);
|
|
expect(m.hopCount, -1);
|
|
});
|
|
|
|
test('legacy (non-V3) frame decodes the same way', () {
|
|
final m = ChannelMessage.fromFrame(_legacyFrame(pathLen: 0x8D))!;
|
|
expect(m.pathLength, 0x8D);
|
|
expect(m.hopCount, 13);
|
|
expect(m.pathHashSize, 3);
|
|
});
|
|
});
|
|
|
|
group('effectivePathHashWidth (#985)', () {
|
|
ChannelMessage msg(int? pathLength) => ChannelMessage(
|
|
senderName: 'Dee3',
|
|
text: 'hi',
|
|
timestamp: DateTime.fromMillisecondsSinceEpoch(0),
|
|
isOutgoing: false,
|
|
status: ChannelMessageStatus.sent,
|
|
pathLength: pathLength,
|
|
);
|
|
|
|
test('uses the packet\'s own width when a path-len byte is stored', () {
|
|
expect(msg(0x8D).effectivePathHashWidth(2), 3);
|
|
expect(msg(0x53).effectivePathHashWidth(1), 2);
|
|
expect(msg(13).effectivePathHashWidth(2), 1);
|
|
});
|
|
|
|
test('falls back to the device width for legacy/sentinel records', () {
|
|
expect(msg(null).effectivePathHashWidth(2), 2);
|
|
expect(msg(-1).effectivePathHashWidth(3), 3);
|
|
expect(msg(null).effectivePathHashWidth(0), 1);
|
|
});
|
|
});
|
|
|
|
group('mergePathLenRaw (#985)', () {
|
|
test('null and sentinel yield to the real packed byte', () {
|
|
expect(mergePathLenRaw(null, 0x8D), 0x8D);
|
|
expect(mergePathLenRaw(0x8D, null), 0x8D);
|
|
expect(mergePathLenRaw(-1, 0x8D), 0x8D);
|
|
expect(mergePathLenRaw(0x8D, -1), 0x8D);
|
|
expect(mergePathLenRaw(null, null), isNull);
|
|
});
|
|
|
|
test('higher decoded hop count wins, compared in hops not raw bytes', () {
|
|
// 0x8E = 14 hops at width 3; 0x8D = 13 hops at width 3.
|
|
expect(mergePathLenRaw(0x8D, 0x8E), 0x8E);
|
|
// 19 hops at width 2 (0x53) beats 13 hops at width 2 (0x4D).
|
|
expect(mergePathLenRaw(0x4D, 0x53), 0x53);
|
|
// A width-1 count is NOT outranked by a packed byte's raw magnitude:
|
|
// 20 hops at width 1 beats 13 hops at width 3 (raw 0x8D = 141).
|
|
expect(mergePathLenRaw(0x8D, 20), 20);
|
|
});
|
|
|
|
test('regression: the Dee3 scenario decodes to 13 hops, never 39', () {
|
|
// Two repeats of the same packet, both carrying packed 0x8D. The old
|
|
// merge substituted the 39-byte path length, which decoded as
|
|
// "39 hops at width 1".
|
|
final merged = mergePathLenRaw(0x8D, 0x8D)!;
|
|
expect(pathHopCount(merged), 13);
|
|
expect(pathHashSizeBytes(merged), 3);
|
|
expect(pathByteLength(merged), 39);
|
|
});
|
|
});
|
|
}
|