@@ -326,6 +326,81 @@ func TestUseCase26_TimelapseCapture(t *testing.T) {
326326 }
327327}
328328
329+ // ucCleanupLeakedScanners unregisters scanner IDs 0..15 to free BLE
330+ // resources leaked by previous test runs that failed to clean up.
331+ func ucCleanupLeakedScanners (
332+ ctx context.Context ,
333+ scanProxy * genBluetooth.BluetoothScanProxy ,
334+ ) {
335+ attr := ucShellAttribution ()
336+ for id := int32 (0 ); id < 16 ; id ++ {
337+ _ = scanProxy .UnregisterScanner (ctx , id , attr )
338+ }
339+ }
340+
341+ // ucRegisterBLEScanner registers a BLE scanner using the provided callback
342+ // spy and returns the scanner ID. If the first registration attempt fails
343+ // with status 128 (GATT_NO_RESOURCES), it cleans up leaked scanners from
344+ // previous test runs and retries.
345+ func ucRegisterBLEScanner (
346+ ctx context.Context ,
347+ t * testing.T ,
348+ scanProxy * genBluetooth.BluetoothScanProxy ,
349+ spy * scanCallbackSpy ,
350+ ) int32 {
351+ t .Helper ()
352+ attr := ucShellAttribution ()
353+ cb := genLE .NewScannerCallbackStub (spy )
354+
355+ err := scanProxy .RegisterScanner (ctx , cb , genOs.WorkSource {}, attr )
356+ requireOrSkip (t , err )
357+
358+ var status , scannerID int32
359+ select {
360+ case ev := <- spy .registeredCh :
361+ status = ev .status
362+ scannerID = ev .scannerID
363+ case <- time .After (5 * time .Second ):
364+ t .Fatal ("OnScannerRegistered callback never arrived" )
365+ }
366+
367+ if status == 0 {
368+ return scannerID
369+ }
370+
371+ // Status 128 = GATT_NO_RESOURCES. Clean up leaked scanners from
372+ // previous test runs, then retry with a fresh callback.
373+ if status == 128 {
374+ t .Log ("scanner registration got status 128; cleaning up leaked scanners" )
375+ ucCleanupLeakedScanners (ctx , scanProxy )
376+ time .Sleep (500 * time .Millisecond )
377+
378+ spy2 := & scanCallbackSpy {
379+ registeredCh : make (chan scanRegisteredEvent , 1 ),
380+ resultCh : spy .resultCh ,
381+ }
382+ cb2 := genLE .NewScannerCallbackStub (spy2 )
383+
384+ err = scanProxy .RegisterScanner (ctx , cb2 , genOs.WorkSource {}, attr )
385+ requireOrSkip (t , err )
386+
387+ select {
388+ case ev := <- spy2 .registeredCh :
389+ status = ev .status
390+ scannerID = ev .scannerID
391+ case <- time .After (5 * time .Second ):
392+ t .Fatal ("OnScannerRegistered callback never arrived after cleanup" )
393+ }
394+
395+ if status == 0 {
396+ return scannerID
397+ }
398+ }
399+
400+ t .Fatalf ("scanner registration failed after cleanup: status %d" , status )
401+ return - 1
402+ }
403+
329404// ---------------------------------------------------------------------------
330405// #27: BLE scanning
331406// ---------------------------------------------------------------------------
@@ -349,22 +424,7 @@ func TestUseCase27_BLEScanning(t *testing.T) {
349424 registeredCh : make (chan scanRegisteredEvent , 1 ),
350425 resultCh : make (chan genLE.ScanResult , 100 ),
351426 }
352- scanCallback := genLE .NewScannerCallbackStub (spy )
353-
354- err = scanProxy .RegisterScanner (ctx , scanCallback , genOs.WorkSource {}, ucShellAttribution ())
355- requireOrSkip (t , err )
356-
357- var scannerID int32
358- select {
359- case event := <- spy .registeredCh :
360- t .Logf ("OnScannerRegistered: status=%d scannerID=%d" , event .status , event .scannerID )
361- if event .status != 0 {
362- t .Skipf ("scanner registration failed: status %d" , event .status )
363- }
364- scannerID = event .scannerID
365- case <- time .After (5 * time .Second ):
366- t .Fatal ("OnScannerRegistered callback never arrived" )
367- }
427+ scannerID := ucRegisterBLEScanner (ctx , t , scanProxy , spy )
368428
369429 ss := genLE.ScanSettings {
370430 CallbackType : 1 ,
@@ -390,6 +450,9 @@ collectLoop:
390450
391451 err = scanProxy .StopScan (ctx , scannerID , ucShellAttribution ())
392452 requireOrSkip (t , err )
453+
454+ err = scanProxy .UnregisterScanner (ctx , scannerID , ucShellAttribution ())
455+ requireOrSkip (t , err )
393456}
394457
395458// ---------------------------------------------------------------------------
@@ -553,22 +616,8 @@ func TestUseCase30_BLESensorCollector(t *testing.T) {
553616 registeredCh : make (chan scanRegisteredEvent , 1 ),
554617 resultCh : make (chan genLE.ScanResult , 100 ),
555618 }
556- scanCallback := genLE .NewScannerCallbackStub (spy )
557-
558- err = scanProxy .RegisterScanner (ctx , scanCallback , genOs.WorkSource {}, ucShellAttribution ())
559- requireOrSkip (t , err )
560-
561- var scannerID int32
562- select {
563- case event := <- spy .registeredCh :
564- if event .status != 0 {
565- t .Skipf ("scanner registration failed: status %d" , event .status )
566- }
567- scannerID = event .scannerID
568- t .Logf ("Scanner registered: id=%d" , scannerID )
569- case <- time .After (5 * time .Second ):
570- t .Fatal ("scanner registration timed out" )
571- }
619+ scannerID := ucRegisterBLEScanner (ctx , t , scanProxy , spy )
620+ t .Logf ("Scanner registered: id=%d" , scannerID )
572621
573622 ss := genLE.ScanSettings {
574623 CallbackType : 1 ,
@@ -607,6 +656,9 @@ func TestUseCase30_BLESensorCollector(t *testing.T) {
607656
608657 err = scanProxy .StopScan (ctx , scannerID , ucShellAttribution ())
609658 requireOrSkip (t , err )
659+
660+ err = scanProxy .UnregisterScanner (ctx , scannerID , ucShellAttribution ())
661+ requireOrSkip (t , err )
610662 })
611663}
612664
0 commit comments