penguin/homekey-go

homekey-go

internal/app/physical_bluez_test.go

raw ยท 2099 bytes

package app

import (
	"context"
	"errors"
	"testing"
	"time"

	"homekey.local/provisioner/drivers/sesame"
	"homekey.local/provisioner/internal/physical"
)

// A failed preparation owns no Link. Retiring it must not short-circuit the
// actual BlueZ disconnect, which was the reason repeated clicks were needed.
func TestDisconnectFailedPreparationReleasesBlueZLink(t *testing.T) {
	m, err := physical.Open(context.Background(), t.TempDir(), func(context.Context, sesame.Target) (sesame.Link, sesame.Target, error) {
		return nil, sesame.Target{}, errors.New("Sesame is already connected through this adapter")
	})
	if err != nil {
		t.Fatal(err)
	}
	defer m.Close()
	target := sesame.Target{UUID: "00112233-4455-6677-8899-aabbccddeeff", Model: "sesame_6_pro", Adapter: "hci0", Address: "AA:BB:CC:DD:EE:FF"}
	m.Prepare([]sesame.Target{target})
	deadline := time.Now().Add(time.Second)
	for time.Now().Before(deadline) {
		states := m.RegistrationConnections()
		if len(states) == 1 && states[0].Phase == "failed" {
			break
		}
		time.Sleep(time.Millisecond)
	}
	if states := m.RegistrationConnections(); len(states) != 1 || states[0].Phase != "failed" {
		t.Fatal("preparation did not fail", states)
	}
	connected := true
	calls := 0
	release := func(ctx context.Context, adapter, address string) error {
		if adapter != target.Adapter || address != target.Address {
			t.Fatal("released wrong lock", adapter, address)
		}
		if len(m.RegistrationConnections()) != 0 {
			t.Fatal("released before retiring preparation")
		}
		calls++
		connected = false
		return nil
	}
	if err = disconnectSesame(context.Background(), m, target.Adapter, target.Address, release); err != nil {
		t.Fatal(err)
	}
	if connected || calls != 1 {
		t.Fatal("failed preparation hid the live BlueZ link", connected, calls)
	}
	transportError := errors.New("disconnect failed")
	if err = disconnectSesame(context.Background(), m, target.Adapter, target.Address, func(context.Context, string, string) error { return transportError }); !errors.Is(err, transportError) {
		t.Fatal("hid transport disconnect failure", err)
	}
}