chore(#462): remove em-dash characters from test/ (and lib top-up)

Follow-up to #457. Swept the em-dash character (U+2014) out of the test
tree (test descriptions and comments) and cleaned 8 em-dashes that
landed in lib comments via the #456 refactor after #457 merged, so the
tree is back to zero. Same rules: replaced with commas/colons/periods,
preserved the lone "no data" glyph placeholders, left non-English ARB
untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
release/1.3.0
Strycher 2 months ago
parent 48a9d79d5a
commit 3b39a16edd

@ -100,7 +100,7 @@ ProfileDiff buildProfileDiff(
),
);
}
// Broker enabled is not a profile field (#456) — never diffed/applied.
// Broker enabled is not a profile field (#456), never diffed/applied.
}
return ProfileDiff(rows);

@ -20,7 +20,7 @@ class ConfigProfileFormatException implements Exception {
/// skip. Only keys present in the document appear in the model; everything else
/// stays null so the apply engines touch only what the profile sets.
///
/// Expected shape (v2 — capability sections, #456):
/// Expected shape (v2, capability sections, #456):
/// ```yaml
/// schema_version: 2
/// name: "US wide-area" # optional label, not applied

@ -32,7 +32,7 @@ class FlatWrite {
}
/// The writes for one broker slot. [fields] excludes `jwt_token` (never written)
/// and the broker `enabled` flag (#456: not a profile field — apply preserves
/// and the broker `enabled` flag (#456: not a profile field, apply preserves
/// the device's current enabled state).
class BrokerWrites {
const BrokerWrites({

@ -113,7 +113,7 @@ class BrokerConfig {
final String? iataOverride;
}
/// The `mqtt` section — observer/MQTT capability. Shared by any device running
/// The `mqtt` section, observer/MQTT capability. Shared by any device running
/// the observer role (observer, observer-repeater, observer-companion); never a
/// plain companion or plain repeater (#456).
class MqttSection {
@ -136,7 +136,7 @@ class MqttSection {
regionIata == null && statusInterval == null && brokers.isEmpty;
}
/// A complete importable config profile — a set of capability-scoped sections
/// A complete importable config profile, a set of capability-scoped sections
/// (#456). A device applies the sections it supports; the apply *mechanism* is
/// per-device, the section *schema* is per-capability and shared. Future
/// sections (`radio`, `repeater`, `companion`, `display`) slot in alongside.
@ -153,10 +153,10 @@ class ConfigProfile {
/// Optional human label (not applied to the device).
final String? name;
/// `wifi` section — any wifi-capable device.
/// `wifi` section, any wifi-capable device.
final WifiConfig? wifi;
/// `mqtt` section — observer/MQTT capability.
/// `mqtt` section, observer/MQTT capability.
final MqttSection? mqtt;
}

@ -38,7 +38,7 @@ class ObserverApplyService {
}
for (final b in writes.brokers) {
// Profiles never set broker enabled (#456) — preserve the device's current
// Profiles never set broker enabled (#456), preserve the device's current
// state: read wasLive for the safe-save dance and re-enable to the same.
final current = await _svc.getBroker(b.slot);
final wasLive = current?.enabled ?? false;

@ -122,7 +122,7 @@ void main() {
// 8 zero bytes for lat/lon + 4 bytes timestamp
expect(frame.length, baseFrameLength + 8 + 4);
// Verify the lat/lon slot is actually zero — guards against a
// Verify the lat/lon slot is actually zero, guards against a
// regression where the function writes garbage into those bytes.
final tailStart = baseFrameLength;
for (var i = tailStart; i < tailStart + 8; i++) {

@ -6,7 +6,7 @@ import 'package:meshcore_open/connector/meshcore_connector.dart';
void main() {
group('DirectRepeater.ranking (SNR-first)', () {
test('a stronger signal outranks a more-recent but weaker one', () {
// 5 dB better SNR, but heard 24 min earlier — signal margin must win.
// 5 dB better SNR, but heard 24 min earlier, signal margin must win.
final strongOld = DirectRepeater(
pubkeyFirstByte: 1,
pubkeyPrefix: Uint8List.fromList([1]),
@ -49,7 +49,7 @@ void main() {
});
test('a live repeater always outranks a stale one', () {
// Weak but live vs strong but stale — the live one must win, so the
// Weak but live vs strong but stale, the live one must win, so the
// stale sentinel (-1) has to sit below every live ranking.
final weakLive = DirectRepeater(
pubkeyFirstByte: 1,
@ -94,15 +94,15 @@ void main() {
snr: 12.0,
);
test('width 1 — matches on the single prefix byte', () {
test('width 1, matches on the single prefix byte', () {
expect(repeater([0x84]).matchesPathStart([0x84, 0xab]), isTrue);
expect(repeater([0x84]).matchesPathStart([0x99]), isFalse);
});
test('width 2 — both bytes must match, so a 1-byte collision misses', () {
test('width 2, both bytes must match, so a 1-byte collision misses', () {
final r = repeater([0x84, 0xab]);
expect(r.matchesPathStart([0x84, 0xab, 0xc1]), isTrue);
// Shares only the first byte — the #156 collision that a 1-byte match
// Shares only the first byte, the #156 collision that a 1-byte match
// would have falsely accepted.
expect(r.matchesPathStart([0x84, 0x99]), isFalse);
});
@ -114,7 +114,7 @@ void main() {
});
group(
'DirectRepeater.recordHop (#150 — RX-fed nearest-repeater detection)',
'DirectRepeater.recordHop (#150, RX-fed nearest-repeater detection)',
() {
DirectRepeater rep(List<int> prefix, double snr) => DirectRepeater(
pubkeyFirstByte: prefix.last,

@ -10,7 +10,7 @@ import 'package:meshcore_open/connector/meshcore_protocol.dart';
import 'package:meshcore_open/helpers/cayenne_lpp.dart';
void main() {
group('buildSendTelemetryReq(null) — the self request', () {
group('buildSendTelemetryReq(null), the self request', () {
test(
'is CMD_SEND_TELEMETRY_REQ + 3 reserved = 4-byte frame (firmware len==4)',
() {

@ -5,7 +5,7 @@
// waits out the 2s timeout + 3 retries on EVERY empty slot, so a device with a
// few populated channels out of a large capacity takes minutes to sync (~8s per
// empty slot). This pins the decision: advance on ERR only while a channel GET
// is in flight AND no generic-ack command is waiting — a pending generic-ack
// is in flight AND no generic-ack command is waiting, a pending generic-ack
// (a SET / channel-text send) is order-correlated to the ERR first.
import 'package:flutter_test/flutter_test.dart';

@ -23,7 +23,7 @@ void main() {
return f;
}
group('parseOffbandCaps — offset 82 + short-frame robustness', () {
group('parseOffbandCaps, offset 82 + short-frame robustness', () {
test('pre-v14 frame without the caps byte (len 82) -> null', () {
expect(MeshCoreConnector.parseOffbandCaps(frame(82)), isNull);
});

@ -1,6 +1,6 @@
// Adversarial + correctness tests for the Offband config codec (#64 A1).
//
// TDD: the "hostile device" group is written to FAIL on the un-hardened codec —
// TDD: the "hostile device" group is written to FAIL on the un-hardened codec,
// it documents the Gemini BLOCKER (unbounded BrokerListDecoder / DoS) and MINOR
// (out-of-range slot accepted). C1 (#72) hardens the codec until these pass.
// The builder / parsing / happy-path groups assert the contract and should be
@ -107,7 +107,7 @@ void main() {
});
});
group('broker decode — happy path', () {
group('broker decode, happy path', () {
test('START -> KV -> END assembles a broker', () {
final d = BrokerListDecoder();
expect(
@ -133,7 +133,7 @@ void main() {
// ---- ADVERSARIAL: assume a malicious/compromised device controls the wire ----
group('adversarial: hostile device', () {
test(
'DoS — flood of BROKER_KV must not retain slots past brokerSlotCount',
'DoS, flood of BROKER_KV must not retain slots past brokerSlotCount',
() {
final d = BrokerListDecoder();
d.add(
@ -199,7 +199,7 @@ void main() {
test('KV before START is ignored (no implicit accumulation)', () {
final d = BrokerListDecoder();
// No START — a stray KV must not be retained.
// No START, a stray KV must not be retained.
d.add(ObserverConfigClient.parse(brokerKv(0, 'url=mqtt://stray')));
final list = d.add(
ObserverConfigClient.parse(resp(ObserverConfigClient.rBrokersEnd)),

@ -65,7 +65,7 @@ void main() {
test('byte is present on non-capable boards and reads as bypassed', () {
// Firmware appends it unconditionally, so presence indicates version,
// not capability — the cap bit is what gates the UI.
// not capability, the cap bit is what gates the UI.
final frame = deviceInfo(length: 84, femByte: 0);
frame[82] = 0x00;
expect(MeshCoreConnector.parseFemLnaState(frame), isFalse);

@ -116,7 +116,7 @@ mqtt:
);
});
test('rejects broker "enabled" (#456 — not a profile field)', () {
test('rejects broker "enabled" (#456, not a profile field)', () {
expect(
() => parseConfigProfile(
'schema_version: 2\nmqtt:\n brokers:\n - slot: 0\n enabled: true\n',

@ -49,7 +49,7 @@ void main() {
expect(b.fields[ConfigKeys.brokerUrl], 'h');
});
test('broker enabled is never emitted (#456 — not a profile field)', () {
test('broker enabled is never emitted (#456, not a profile field)', () {
final w = enumerateProfileWrites(
const ConfigProfile(
schemaVersion: 2,

@ -26,18 +26,18 @@ Contact _contact({
void main() {
group('PathHelper.formatPathHex (#154)', () {
test('width 1 — one hop per byte, comma-separated', () {
test('width 1, one hop per byte, comma-separated', () {
expect(PathHelper.formatPathHex([0x84, 0xab, 0x12], 1), '84,AB,12');
});
test('width 2 — groups two bytes per hop', () {
test('width 2, groups two bytes per hop', () {
expect(
PathHelper.formatPathHex([0x84, 0xab, 0x12, 0x34], 2),
'84AB,1234',
);
});
test('width 3 — groups three bytes per hop', () {
test('width 3, groups three bytes per hop', () {
expect(
PathHelper.formatPathHex([0x01, 0x02, 0x03, 0x04, 0x05, 0x06], 3),
'010203,040506',
@ -54,7 +54,7 @@ void main() {
});
group('PathHelper.resolvePathNames (#154)', () {
test('width 1 — resolves per-byte, ignoring chat nodes', () {
test('width 1, resolves per-byte, ignoring chat nodes', () {
final contacts = [
_contact(prefix: [0xF2], name: 'MunTui', type: advTypeChat),
_contact(prefix: [0x7E], name: 'zrepeater', type: advTypeRepeater),
@ -68,14 +68,14 @@ void main() {
expect(resolved, equals('F2 → zrepeater → USS Ronald Reagan'));
});
test('width 2 — resolves by the full 2-byte hop prefix', () {
test('width 2, resolves by the full 2-byte hop prefix', () {
final contacts = [
_contact(
prefix: [0x7E, 0xAB],
name: 'zrepeater',
type: advTypeRepeater,
),
// Shares the first byte but differs in the second — must NOT match.
// Shares the first byte but differs in the second, must NOT match.
_contact(prefix: [0x7E, 0x01], name: 'decoy', type: advTypeRepeater),
];
final resolved = PathHelper.resolvePathNames([0x7E, 0xAB], contacts, 2);
@ -120,7 +120,7 @@ void main() {
]);
});
test('buildTraceRoundTrip — repeater width 1: out + target + reverse', () {
test('buildTraceRoundTrip, repeater width 1: out + target + reverse', () {
expect(
PathHelper.buildTraceRoundTrip(
routingPath: [0x0a, 0x0b],
@ -133,7 +133,7 @@ void main() {
);
});
test('buildTraceRoundTrip — repeater width 2 mirrors by hop, not byte', () {
test('buildTraceRoundTrip, repeater width 2 mirrors by hop, not byte', () {
expect(
PathHelper.buildTraceRoundTrip(
routingPath: [0xa1, 0xa2, 0xb1, 0xb2],
@ -147,7 +147,7 @@ void main() {
});
test(
'buildTraceRoundTrip — via-repeater: [via]+target → [via,target,via]',
'buildTraceRoundTrip, via-repeater: [via]+target → [via,target,via]',
() {
// #150: with no committed route, trace a repeater through our strongest
// direct repeater as the first hop (Ben's mesh: 6A3D fronting AC01).
@ -164,7 +164,7 @@ void main() {
},
);
test('buildTraceRoundTrip — chat: far hop is the turnaround', () {
test('buildTraceRoundTrip, chat: far hop is the turnaround', () {
expect(
PathHelper.buildTraceRoundTrip(
routingPath: [0x0a, 0x0b],

@ -2,7 +2,7 @@
// enable/disable as success but not apply it (HV4 desync,
// meshcore-firmware#179). After a toggle/save the UI re-reads the slot and
// compares to what it asked for; this classifier turns (intended, re-read) into
// the outcome the UI reports. It treats every device identically — it only ever
// the outcome the UI reports. It treats every device identically, it only ever
// reflects the re-read, never assuming the change took.
import 'package:flutter_test/flutter_test.dart';

@ -38,7 +38,7 @@ Uint8List _buildContactFrame({
}
void main() {
group('Contact.fromFrame — pathLen mapping', () {
group('Contact.fromFrame, pathLen mapping', () {
test('pathLen == 0 → pathLength == 0 (direct, NOT flood)', () {
final frame = _buildContactFrame(pathLen: 0);
final contact = Contact.fromFrame(frame);
@ -83,7 +83,7 @@ void main() {
);
});
group('Contact.fromFrame — corrupt contact guards', () {
group('Contact.fromFrame, corrupt contact guards', () {
test('all-zero public key → returns null', () {
final zeroPubKey = Uint8List(32); // all zeros
final frame = _buildContactFrame(pubKey: zeroPubKey);
@ -161,7 +161,7 @@ void main() {
// Build a name with mostly printable chars and one replacement char (0xFFFD in codeUnits).
// utf8 allowMalformed: true maps invalid sequences to U+FFFD.
// We embed one invalid UTF-8 byte (0x80) among valid ASCII bytes.
// The decoded string will be "Hi\uFFFDThere" — not ALL bad, so should be accepted.
// The decoded string will be "Hi\uFFFDThere", not ALL bad, so should be accepted.
final nameBytes = Uint8List(32);
nameBytes[0] = 0x48; // 'H'
nameBytes[1] = 0x69; // 'i'
@ -190,7 +190,7 @@ void main() {
);
});
group('PathRecord — routeWeight field', () {
group('PathRecord, routeWeight field', () {
test('default routeWeight is 1.0', () {
final record = PathRecord(
hopCount: 2,
@ -268,7 +268,7 @@ void main() {
);
});
group('AppSettings — new fields', () {
group('AppSettings, new fields', () {
test('default values are correct', () {
final settings = AppSettings();
expect(settings.maxRouteWeight, equals(5.0));

@ -2,7 +2,7 @@
//
// The models are the data structures untrusted wire fields decode into, so the
// defensive behavior (enum fallback, numeric fallback, secret presence) is
// pinned here. All green — M1 confirms the existing draft is contract-correct.
// pinned here. All green, M1 confirms the existing draft is contract-correct.
import 'package:flutter_test/flutter_test.dart';
import 'package:meshcore_open/models/observer_config.dart';

@ -49,7 +49,7 @@ void main() {
expect(build().copyWith(rxTime: rx).rxTime, rx);
});
test('copyWith(packetHash:) — the live ingest path — preserves rxTime', () {
test('copyWith(packetHash:), the live ingest path, preserves rxTime', () {
final msg = build(rxTime: rx).copyWith(packetHash: 'abc123');
expect(msg.rxTime, rx);
expect(msg.timestamp, claimed);

@ -228,7 +228,7 @@ void main() {
fake.saveCalls,
hasLength(1),
reason:
'the client no longer gates JWT fields — the firmware enforces them '
'the client no longer gates JWT fields, the firmware enforces them '
'with its defaults (owner -> device pubkey)',
);
expect(fake.saveCalls.single.enable, isTrue);

@ -23,7 +23,7 @@ class _FakeSvc extends ObserverConfigService {
String? errorText;
/// When false, the device ACKs an enabled-SET but the verify re-read reports
/// the slot UNCHANGED — models the HV4 ack-but-doesn't-apply bug (#89).
/// the slot UNCHANGED, models the HV4 ack-but-doesn't-apply bug (#89).
bool deviceApplies = true;
final Map<int, bool> _enabledOverride = {};

@ -94,7 +94,7 @@ void main() {
test('heals a self-block even when the self key is unchanged', () async {
await service.setSelfKey(selfKey);
// Stale persisted state reloaded while the self key is already known —
// Stale persisted state reloaded while the self key is already known,
// load() does not filter, so this lands a self-block behind the guards.
store.keys = {selfKey, otherKey};
await service.load();

@ -1,7 +1,7 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:meshcore_open/services/mesh_topology_service.dart';
/// #186 Phase B — the passive-topology route oracle.
/// #186 Phase B, the passive-topology route oracle.
/// Path orientation used throughout: a received path is `origin → h1 → … → hk → us`,
/// so `hk` (the path's LAST hop) is our direct neighbour.
void main() {
@ -10,7 +10,7 @@ void main() {
const r1 = [0x11, 0x11];
const r2 = [0x22, 0x22];
const r3 = [0x33, 0x33];
const t = [0xCC, 0xCC]; // target — only ever seen relaying
const t = [0xCC, 0xCC]; // target, only ever seen relaying
const u = [0x99, 0x99]; // never observed
List<int> path(List<List<int>> hops) => hops.expand((h) => h).toList();
@ -23,7 +23,7 @@ void main() {
return s;
}
group('observePath — orientation + graph growth', () {
group('observePath, orientation + graph growth', () {
test('last hop is our neighbour (SNR); mids are relays (no SNR)', () {
final s = svc();
// packet: origin → R2 → T → R1 → us (R1 = neighbour, T relayed)
@ -36,7 +36,7 @@ void main() {
expect(s.nodeCount, 4); // self, R2, T, R1
expect(s.edgeCount, 3); // R2~T, T~R1, self~R1
expect(s.neighbourSnrOf(r1), 6.5); // last hop = measured neighbour
expect(s.neighbourSnrOf(t), isNull); // relayed — no measurable SNR
expect(s.neighbourSnrOf(t), isNull); // relayed, no measurable SNR
expect(s.neighbourSnrOf(r2), isNull);
});
@ -60,7 +60,7 @@ void main() {
test('KEY: routes to a node only ever heard RELAYING', () {
final s = svc();
// T never arrives as a last hop — only mid-path — yet we can reach it.
// T never arrives as a last hop, only mid-path, yet we can reach it.
s.observePath(path([r2, t, r1]), width: 2); // origin→R2→T→R1→us
expect(s.neighbourSnrOf(t), isNull); // proof T was never our neighbour
// us → R1 → T

@ -1,7 +1,7 @@
// Adversarial + correctness tests for ObserverConfigService (#64 A2).
//
// TDD: the concurrency and secret-redaction tests are written to FAIL on the
// current service — they document the Gemini BLOCKER (#1 race: "one request in
// current service, they document the Gemini BLOCKER (#1 race: "one request in
// flight" is assumed, not enforced) and MINOR (#6: secret key names leak into
// error strings). S1 (#73) hardens the service until these pass.
//
@ -198,7 +198,7 @@ void main() {
await setF,
isTrue,
reason:
'setFlat must receive its OWN ACK, not the GET value — cross-talk means single-flight is unenforced',
'setFlat must receive its OWN ACK, not the GET value, cross-talk means single-flight is unenforced',
);
});
@ -357,7 +357,7 @@ void main() {
expect(
setKeys(auto),
isNot(contains('mqtt.broker.2.enabled')),
reason: 'a failed field must never reach enabled=1 — slot stays safe',
reason: 'a failed field must never reach enabled=1, slot stays safe',
);
auto.closeStream();
});
@ -395,7 +395,7 @@ void main() {
// ---- #103: a slow-but-steady dump must outlast the fixed timeout ----
test('getBrokers completes a dump that outlasts the per-frame timeout while '
'frames keep arriving (re-armed inactivity watchdog) — #103', () async {
'frames keep arriving (re-armed inactivity watchdog), #103', () async {
svc.timeout = const Duration(milliseconds: 80);
final future = svc.getBrokers();
final frames = <Uint8List>[

@ -8,7 +8,7 @@ import 'package:meshcore_open/services/path_history_service.dart';
import 'package:meshcore_open/services/storage_service.dart';
// ---------------------------------------------------------------------------
// Fake storage — no SharedPreferences dependency, all in-memory.
// Fake storage, no SharedPreferences dependency, all in-memory.
// ---------------------------------------------------------------------------
class FakeStorageService extends StorageService {
final Map<String, ContactPathHistory> _store = {};
@ -450,7 +450,7 @@ void main() {
});
// -------------------------------------------------------------------------
// Group 3: recordPathResult — weight adjustment
// Group 3: recordPathResult, weight adjustment
// -------------------------------------------------------------------------
group('recordPathResult weight adjustment', () {
test('success increments weight by successIncrement', () async {

@ -82,7 +82,7 @@ void main() {
final Uint8List recipientKey = _makeRecipientKey();
// -------------------------------------------------------------------------
group('computeExpectedAckHash — attempt masking', () {
group('computeExpectedAckHash, attempt masking', () {
test('attempts 0–3 all produce different hashes', () {
final hashes = List.generate(
4,
@ -239,7 +239,7 @@ void main() {
});
// -------------------------------------------------------------------------
group('buildSendTextMsgFrame — attempt encoding', () {
group('buildSendTextMsgFrame, attempt encoding', () {
// Frame layout: [cmd(1)][txtType(1)][attempt(1)][timestamp(4)][pubKeyPrefix(6)][text][null(1)]
// So byte index 2 carries the raw attempt & 0xFF.
@ -387,7 +387,7 @@ void main() {
test(
'attempt 3: flutter hash equals hash computed directly for attempt 3',
() {
// 3 & 3 == 3, so no wrapping — both sides agree.
// 3 & 3 == 3, so no wrapping, both sides agree.
final hashFor3 = MessageRetryService.computeExpectedAckHash(
fixedTs,
3,
@ -486,7 +486,7 @@ void main() {
);
// -------------------------------------------------------------------------
group('_AckHashMapping.attemptIndex — indirect verification via public API', () {
group('_AckHashMapping.attemptIndex, indirect verification via public API', () {
// _AckHashMapping is private; we validate its purpose indirectly: that
// computeExpectedAckHash records the correct per-attempt hash so that the
// right hash is matched when an ACK arrives.
@ -532,7 +532,7 @@ void main() {
);
test(
'attempt index 1 and 5 map to the same slot — ACK from either retry is matched',
'attempt index 1 and 5 map to the same slot, ACK from either retry is matched',
() {
final hashForAttempt1 = MessageRetryService.computeExpectedAckHash(
fixedTs,
@ -553,7 +553,7 @@ void main() {
);
});
group('sendMessageWithRetry — auto path fallback', () {
group('sendMessageWithRetry, auto path fallback', () {
test(
'preserves the contact path when auto-selection returns null',
() async {

@ -105,7 +105,7 @@ void main() {
// A frame claiming a payload above the max must be dropped, and the
// decoder must resync to the next valid frame. Length is derived from the
// ceiling so this stays a genuine over-max after it was raised to 176
// (#430) — a hard-coded 173 is now a valid length.
// (#430), a hard-coded 173 is now a valid length.
final oversized = usbSerialMaxPayloadLength + 1;
final packets = decoder.ingest(
Uint8List.fromList(<int>[

@ -72,7 +72,7 @@ void main() {
orderedEquals([0x00, 0x61, 0x73, 0x6d]),
reason:
'web/sqlite3.wasm does not start with the WebAssembly magic '
'bytes (\\0asm) — the download is corrupt or is not a wasm file.',
'bytes (\\0asm), the download is corrupt or is not a wasm file.',
);
});
}

@ -7,7 +7,7 @@ import 'package:meshcore_open/storage/drift/offband_database.dart';
///
/// This runs against an in-memory SQLite instance so it exercises the drift
/// layer and generated code on any host. It does NOT prove the per-platform
/// backends (native libs on desktop/mobile, WASM on web) — those are proven by
/// backends (native libs on desktop/mobile, WASM on web), those are proven by
/// building each target and by `verifyReadWrite()` at runtime.
void main() {
late OffbandDatabase db;

@ -1,4 +1,4 @@
// Rotating file-log writer (#97). Exercised against a real temp directory —
// Rotating file-log writer (#97). Exercised against a real temp directory,
// dart:io File works in flutter_test on desktop, so the rotation behaviour is
// integration-tested, not mocked.

@ -6,7 +6,7 @@ import 'package:meshcore_open/widgets/app_shell.dart';
import 'package:provider/provider.dart';
/// #389: Back must pop out of a pushed detail (a channel chat) to its list, and
/// background the app only from a genuine top-level tab — not the other way
/// background the app only from a genuine top-level tab, not the other way
/// round. These pin the full decision matrix.
void main() {
group('AppShell.backAction', () {

@ -3,7 +3,7 @@ import 'package:meshcore_open/widgets/path_selection_dialog.dart';
void main() {
group('PathSelectionDialog.parsePathPrefixes (#155)', () {
test('width 1 — single-byte hops', () {
test('width 1, single-byte hops', () {
final invalid = <String>[];
expect(PathSelectionDialog.parsePathPrefixes('A1,F2,3C', 1, invalid), [
0xA1,
@ -13,7 +13,7 @@ void main() {
expect(invalid, isEmpty);
});
test('width 2 — two-byte hops, each entry 4 hex chars', () {
test('width 2, two-byte hops, each entry 4 hex chars', () {
final invalid = <String>[];
expect(PathSelectionDialog.parsePathPrefixes('84AB,C1D2', 2, invalid), [
0x84,

Loading…
Cancel
Save

Powered by TurnKey Linux.