mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-07-26 21:46:38 +10:00
Add error handling tests for USB connection and listing ports
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
@@ -23,6 +24,8 @@ class _FakeMeshCoreConnector extends MeshCoreConnector {
|
|||||||
String? fakeActiveUsbPort;
|
String? fakeActiveUsbPort;
|
||||||
String? fakeActiveUsbPortDisplayLabel;
|
String? fakeActiveUsbPortDisplayLabel;
|
||||||
bool fakeUsbTransportConnected = false;
|
bool fakeUsbTransportConnected = false;
|
||||||
|
Future<List<String>> Function()? listUsbPortsImpl;
|
||||||
|
Future<void> Function({required String portName})? connectUsbImpl;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MeshCoreConnectionState get state => initialState;
|
MeshCoreConnectionState get state => initialState;
|
||||||
@@ -38,13 +41,21 @@ class _FakeMeshCoreConnector extends MeshCoreConnector {
|
|||||||
bool get isUsbTransportConnected => fakeUsbTransportConnected;
|
bool get isUsbTransportConnected => fakeUsbTransportConnected;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<String>> listUsbPorts() async => List<String>.from(_ports);
|
Future<List<String>> listUsbPorts() async {
|
||||||
|
if (listUsbPortsImpl != null) {
|
||||||
|
return listUsbPortsImpl!();
|
||||||
|
}
|
||||||
|
return List<String>.from(_ports);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> connectUsb({
|
Future<void> connectUsb({
|
||||||
required String portName,
|
required String portName,
|
||||||
int baudRate = 115200,
|
int baudRate = 115200,
|
||||||
}) async {
|
}) async {
|
||||||
|
if (connectUsbImpl != null) {
|
||||||
|
return connectUsbImpl!(portName: portName);
|
||||||
|
}
|
||||||
connectUsbCalls += 1;
|
connectUsbCalls += 1;
|
||||||
lastConnectPortName = portName;
|
lastConnectPortName = portName;
|
||||||
}
|
}
|
||||||
@@ -145,4 +156,50 @@ void main() {
|
|||||||
expect(find.widgetWithText(FloatingActionButton, 'USB'), findsNothing);
|
expect(find.widgetWithText(FloatingActionButton, 'USB'), findsNothing);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('Error Handling', () {
|
||||||
|
testWidgets('shows error message when listing ports fails', (tester) async {
|
||||||
|
final connector = _FakeMeshCoreConnector();
|
||||||
|
connector.listUsbPortsImpl = () async {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'usb_permission_denied',
|
||||||
|
message: 'Permission denied',
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
_buildTestApp(connector: connector, child: const UsbScreen()),
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
find.text('USB permission was denied.'),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
'connection failure completes without leaving loading state',
|
||||||
|
(tester) async {
|
||||||
|
final connector = _FakeMeshCoreConnector(
|
||||||
|
ports: <String>['COM1'],
|
||||||
|
);
|
||||||
|
var connectAttempted = false;
|
||||||
|
connector.connectUsbImpl = ({required String portName}) async {
|
||||||
|
connectAttempted = true;
|
||||||
|
throw PlatformException(code: 'usb_busy', message: 'Device is busy');
|
||||||
|
};
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
_buildTestApp(connector: connector, child: const UsbScreen()),
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
await tester.tap(find.widgetWithText(FilledButton, 'Connect'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(connectAttempted, isTrue);
|
||||||
|
expect(find.byType(CircularProgressIndicator), findsNothing);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,4 +138,25 @@ void main() {
|
|||||||
expect(packets, hasLength(1));
|
expect(packets, hasLength(1));
|
||||||
expect(packets.single.payload, orderedEquals(<int>[0x55]));
|
expect(packets.single.payload, orderedEquals(<int>[0x55]));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('recovers from invalid frame header', () {
|
||||||
|
final decoder = UsbSerialFrameDecoder();
|
||||||
|
|
||||||
|
final packets = decoder.ingest(
|
||||||
|
Uint8List.fromList(<int>[
|
||||||
|
// First, a malformed frame (e.g. from a partial TX echo)
|
||||||
|
usbSerialRxFrameStart,
|
||||||
|
usbSerialTxFrameStart,
|
||||||
|
// Then, a valid frame
|
||||||
|
usbSerialRxFrameStart,
|
||||||
|
0x01,
|
||||||
|
0x00,
|
||||||
|
0x88,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(packets, hasLength(1));
|
||||||
|
expect(packets.single.isRxFrame, isTrue);
|
||||||
|
expect(packets.single.payload, orderedEquals(<int>[0x88]));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,24 @@ void main() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test('friendlyUsbPortName works for Linux-style label', () {
|
||||||
|
expect(
|
||||||
|
friendlyUsbPortName(
|
||||||
|
'/dev/ttyACM0 - RAK4631 - USB VID:PID=239A:8029 SER=xxxxxxxx',
|
||||||
|
),
|
||||||
|
'RAK4631',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('friendlyUsbPortName trims whitespace from label parts', () {
|
||||||
|
expect(
|
||||||
|
friendlyUsbPortName(
|
||||||
|
' /dev/ttyS0 - My Serial Port - n/a ',
|
||||||
|
),
|
||||||
|
'My Serial Port',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'friendlyUsbPortName falls back to port name when description is n/a',
|
'friendlyUsbPortName falls back to port name when description is n/a',
|
||||||
() {
|
() {
|
||||||
|
|||||||
Reference in New Issue
Block a user