Skip to content

Commit 019ea52

Browse files
committed
fix(ota): Prevent test hangs and improve OTA resource management
This commit addresses a race condition in the BLE OTA tests and improves resource handling in the transport layer. The `BleOtaTransport` now explicitly closes its `responseChannel` upon disconnection. This prevents potential resource leaks and ensures that downstream consumers are properly notified when the transport is closed. The BLE OTA tests have been made more robust by adding a check to ensure the peripheral is still connected (`otaPeripheral.isConnected`) before attempting to simulate a value update. This prevents tests from hanging or throwing exceptions due to race conditions where a response is simulated after the connection has already been terminated. Signed-off-by: James Rich <[email protected]>
1 parent 16f5b85 commit 019ea52

File tree

4 files changed

+28
-9
lines changed

4 files changed

+28
-9
lines changed

feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/ota/BleOtaTransport.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ class BleOtaTransport(
303303
bleConnection.disconnect()
304304
isConnected = false
305305
transportScope.cancel()
306+
responseChannel.close()
306307
}
307308

308309
private suspend fun sendCommand(command: OtaCommand) {

feature/firmware/src/test/kotlin/org/meshtastic/feature/firmware/ota/BleOtaTransportErrorTest.kt

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ class BleOtaTransportErrorTest {
6767
if (command.startsWith("OTA")) {
6868
backgroundScope.launch {
6969
delay(50.milliseconds)
70-
otaPeripheral.simulateValueUpdate(txCharHandle, "ERR Hash Rejected\n".toByteArray())
70+
if (otaPeripheral.isConnected) {
71+
otaPeripheral.simulateValueUpdate(txCharHandle, "ERR Hash Rejected\n".toByteArray())
72+
}
7173
}
7274
}
7375
return WriteResponse.Success
@@ -130,7 +132,9 @@ class BleOtaTransportErrorTest {
130132
): WriteResponse {
131133
backgroundScope.launch {
132134
delay(50.milliseconds)
133-
otaPeripheral.simulateValueUpdate(txCharHandle, "OK\n".toByteArray())
135+
if (otaPeripheral.isConnected) {
136+
otaPeripheral.simulateValueUpdate(txCharHandle, "OK\n".toByteArray())
137+
}
134138
}
135139
return WriteResponse.Success
136140
}
@@ -204,15 +208,19 @@ class BleOtaTransportErrorTest {
204208
): WriteResponse {
205209
backgroundScope.launch {
206210
delay(50.milliseconds)
207-
otaPeripheral.simulateValueUpdate(txCharHandle, "OK\n".toByteArray())
211+
if (otaPeripheral.isConnected) {
212+
otaPeripheral.simulateValueUpdate(txCharHandle, "OK\n".toByteArray())
213+
}
208214
}
209215
return WriteResponse.Success
210216
}
211217

212218
override fun onWriteCommand(characteristic: MockRemoteCharacteristic, value: ByteArray) {
213219
backgroundScope.launch {
214220
delay(10.milliseconds)
215-
otaPeripheral.simulateValueUpdate(txCharHandle, "ACK\n".toByteArray())
221+
if (otaPeripheral.isConnected) {
222+
otaPeripheral.simulateValueUpdate(txCharHandle, "ACK\n".toByteArray())
223+
}
216224
}
217225
}
218226
}
@@ -252,7 +260,9 @@ class BleOtaTransportErrorTest {
252260
// Setup final response to be a Hash Mismatch error after chunks are sent
253261
backgroundScope.launch {
254262
delay(1000.milliseconds)
255-
otaPeripheral.simulateValueUpdate(txCharHandle, "ERR Hash Mismatch\n".toByteArray())
263+
if (otaPeripheral.isConnected) {
264+
otaPeripheral.simulateValueUpdate(txCharHandle, "ERR Hash Mismatch\n".toByteArray())
265+
}
256266
}
257267

258268
val data = ByteArray(1024) { it.toByte() }

feature/firmware/src/test/kotlin/org/meshtastic/feature/firmware/ota/BleOtaTransportNordicMockTest.kt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ class BleOtaTransportNordicMockTest {
9494
}
9595
backgroundScope.launch(testDispatcher) {
9696
delay(50.milliseconds)
97-
otaPeripheral.simulateValueUpdate(txCharHandle, "OK\n".toByteArray())
97+
if (otaPeripheral.isConnected) {
98+
otaPeripheral.simulateValueUpdate(txCharHandle, "OK\n".toByteArray())
99+
}
98100
}
99101
}
100102
return WriteResponse.Success
@@ -106,12 +108,16 @@ class BleOtaTransportNordicMockTest {
106108
println("Mock: Received chunk size=${value.size}, total=$currentTotal/$expected")
107109
backgroundScope.launch(testDispatcher) {
108110
delay(5.milliseconds)
109-
otaPeripheral.simulateValueUpdate(txCharHandle, "ACK\n".toByteArray())
111+
if (otaPeripheral.isConnected) {
112+
otaPeripheral.simulateValueUpdate(txCharHandle, "ACK\n".toByteArray())
113+
}
110114

111115
if (currentTotal >= expected && expected > 0) {
112116
delay(10.milliseconds)
113117
println("Mock: Sending final OK")
114-
otaPeripheral.simulateValueUpdate(txCharHandle, "OK\n".toByteArray())
118+
if (otaPeripheral.isConnected) {
119+
otaPeripheral.simulateValueUpdate(txCharHandle, "OK\n".toByteArray())
120+
}
115121
}
116122
}
117123
}

feature/firmware/src/test/kotlin/org/meshtastic/feature/firmware/ota/BleOtaTransportTest.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ class BleOtaTransportTest {
7070
backgroundScope.launch(testDispatcher) {
7171
// Use a very small delay to simulate high speed
7272
delay(1.milliseconds)
73-
otaPeripheral.simulateValueUpdate(txCharHandle, "OK\n".toByteArray())
73+
if (otaPeripheral.isConnected) {
74+
otaPeripheral.simulateValueUpdate(txCharHandle, "OK\n".toByteArray())
75+
}
7476
}
7577
return WriteResponse.Success
7678
}

0 commit comments

Comments
 (0)