evcc-io/server/eebus/eebus_test.go

78 lines
2.3 KiB
Go

package eebus
import (
"testing"
"time"
eebusapi "github.com/enbility/eebus-go/api"
eebusmocks "github.com/enbility/eebus-go/mocks"
shipapi "github.com/enbility/ship-go/api"
spineapi "github.com/enbility/spine-go/api"
"github.com/evcc-io/evcc/util"
"github.com/stretchr/testify/require"
"go.yaml.in/yaml/v4"
)
func TestConfig(t *testing.T) {
conf := `
certificate:
private: |
-----BEGIN EC PRIVATE KEY-----
MHcCfoo==
-----END EC PRIVATE KEY-----
public: |
-----BEGIN CERTIFICATE-----
MIIBbar=
-----END CERTIFICATE-----
`
var res Config
require.NoError(t, yaml.Unmarshal([]byte(conf), &res))
}
// mockDevice implements Device for testing
type mockDevice struct{}
func (d *mockDevice) Connect(connected bool) {}
func (d *mockDevice) UseCaseEvent(_ spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event eebusapi.EventType) {
}
var _ Device = (*mockDevice)(nil)
// TestUnregisterDevice_MutexNotHeldDuringShipCall is the regression guard
// for issue #28942. It asserts that c.mux is NOT held at the point
// UnregisterRemoteService is called. The pre-fix code held c.mux across that
// cross-layer call, and ship-go's synchronous HandleConnectionClosed
// callback chain re-entered connect(ski, false) on the same goroutine,
// which then deadlocked on c.mux.Lock() (Go mutexes are non-reentrant).
//
// The assertion uses a goroutine that tries to briefly acquire c.mux from
// inside the mock's UnregisterRemoteService implementation; if the lock is
// held, the acquisition times out and the test fails.
func TestUnregisterDevice_MutexNotHeldDuringShipCall(t *testing.T) {
dev := &mockDevice{}
c := &EEBus{
log: util.NewLogger("test"),
clients: map[string][]Device{"aabbcc": {dev}},
}
service := eebusmocks.NewServiceInterface(t)
service.EXPECT().UnregisterRemoteService(shipapi.NewServiceIdentity("aabbcc", "", "")).Run(func(shipapi.ServiceIdentity) {
acquired := make(chan struct{})
go func() {
c.mux.Lock()
defer c.mux.Unlock()
close(acquired)
}()
select {
case <-acquired:
// good — mutex was free
case <-time.After(100 * time.Millisecond):
t.Errorf("c.mux was held while UnregisterRemoteSKI was called — " +
"regression to the cross-layer lock hold that caused #28942")
}
}).Once()
c.service = service
c.UnregisterDevice("aabbcc", dev)
}