diff --git a/components/board/client.go b/components/board/client.go index defb5e6d212..189c48c139e 100644 --- a/components/board/client.go +++ b/components/board/client.go @@ -4,6 +4,7 @@ package board import ( "context" "math" + "slices" "sync" "time" @@ -67,7 +68,9 @@ func NewClientFromConn( } func (c *client) AnalogByName(name string) (Analog, error) { - c.info.analogNames = append(c.info.analogNames, name) + if !slices.Contains(c.info.analogNames, name) { + c.info.analogNames = append(c.info.analogNames, name) + } return &analogClient{ client: c, boardName: c.info.name, @@ -76,7 +79,9 @@ func (c *client) AnalogByName(name string) (Analog, error) { } func (c *client) DigitalInterruptByName(name string) (DigitalInterrupt, error) { - c.info.digitalInterruptNames = append(c.info.digitalInterruptNames, name) + if !slices.Contains(c.info.digitalInterruptNames, name) { + c.info.digitalInterruptNames = append(c.info.digitalInterruptNames, name) + } return &digitalInterruptClient{ client: c, boardName: c.info.name, diff --git a/components/board/client_test.go b/components/board/client_test.go index 1b0c004cc1d..2bfe24f1f84 100644 --- a/components/board/client_test.go +++ b/components/board/client_test.go @@ -3,6 +3,7 @@ package board_test import ( "context" "net" + "slices" "testing" "time" @@ -219,3 +220,69 @@ func TestWorkingClient(t *testing.T) { test.That(t, conn.Close(), test.ShouldBeNil) }) } + +// these tests validate that for modular boards(which rely on client.go's board interface) +// we will only add new pins to the cached name lists. +func TestClientNames(t *testing.T) { + logger := logging.NewTestLogger(t) + injectBoard := &inject.Board{} + + listener, cleanup := setupService(t, injectBoard) + defer cleanup() + t.Run("test analog names are cached correctly in the client", func(t *testing.T) { + ctx := context.Background() + conn, err := viamgrpc.Dial(ctx, listener.Addr().String(), logger) + test.That(t, err, test.ShouldBeNil) + client, err := board.NewClientFromConn(ctx, conn, "", board.Named(testBoardName), logger) + test.That(t, err, test.ShouldBeNil) + + nameFunc := func(name string) error { + _, err = client.AnalogByName(name) + return err + } + testNamesAPI(t, client.AnalogNames, nameFunc, "Analog") + + test.That(t, conn.Close(), test.ShouldBeNil) + }) + + t.Run("test interrupt names are cached correctly in the client", func(t *testing.T) { + ctx := context.Background() + conn, err := viamgrpc.Dial(ctx, listener.Addr().String(), logger) + test.That(t, err, test.ShouldBeNil) + client, err := board.NewClientFromConn(ctx, conn, "", board.Named(testBoardName), logger) + test.That(t, err, test.ShouldBeNil) + + nameFunc := func(name string) error { + _, err = client.DigitalInterruptByName(name) + return err + } + testNamesAPI(t, client.DigitalInterruptNames, nameFunc, "DigitalInterrupt") + test.That(t, conn.Close(), test.ShouldBeNil) + }) +} + +func testNamesAPI(t *testing.T, namesFunc func() []string, nameFunc func(string) error, name string) { + t.Helper() + names := namesFunc() + test.That(t, len(names), test.ShouldEqual, 0) + name1 := name + "1" + err := nameFunc(name1) + test.That(t, err, test.ShouldBeNil) + names = namesFunc() + test.That(t, len(names), test.ShouldEqual, 1) + test.That(t, slices.Contains(names, name1), test.ShouldBeTrue) + + name2 := name + "2" + err = nameFunc(name2) + test.That(t, err, test.ShouldBeNil) + + names = namesFunc() + test.That(t, len(names), test.ShouldEqual, 2) + test.That(t, slices.Contains(names, name2), test.ShouldBeTrue) + + err = nameFunc(name1) + test.That(t, err, test.ShouldBeNil) + names = namesFunc() + test.That(t, len(names), test.ShouldEqual, 2) + test.That(t, slices.Contains(names, name1), test.ShouldBeTrue) +}