From 5d61ae25578b69e60b26687663744f982413cff6 Mon Sep 17 00:00:00 2001 From: John Hobbs Date: Tue, 18 Feb 2025 14:37:35 -0600 Subject: [PATCH 1/3] Initial expectation based SPI test helper --- max6675/max6675.go | 5 ++--- max6675/max6675_test.go | 45 ++++++++++++++++++++++++++++++++++++++ pin.go | 8 +++++++ tester/pin.go | 20 +++++++++++++++++ tester/spi.go | 48 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 max6675/max6675_test.go create mode 100644 pin.go create mode 100644 tester/pin.go create mode 100644 tester/spi.go diff --git a/max6675/max6675.go b/max6675/max6675.go index eb67596b6..e016ce5da 100644 --- a/max6675/max6675.go +++ b/max6675/max6675.go @@ -3,7 +3,6 @@ package max6675 import ( "errors" - "machine" "tinygo.org/x/drivers" ) @@ -14,13 +13,13 @@ var ErrThermocoupleOpen = errors.New("thermocouple input open") type Device struct { bus drivers.SPI - cs machine.Pin + cs drivers.Pin } // Create a new Device to read from a MAX6675 thermocouple. // Pins must be configured before use. Frequency for SPI // should be 4.3MHz maximum. -func NewDevice(bus drivers.SPI, cs machine.Pin) *Device { +func NewDevice(bus drivers.SPI, cs drivers.Pin) *Device { return &Device{ bus: bus, cs: cs, diff --git a/max6675/max6675_test.go b/max6675/max6675_test.go new file mode 100644 index 000000000..23e53e964 --- /dev/null +++ b/max6675/max6675_test.go @@ -0,0 +1,45 @@ +package max6675_test + +import ( + "testing" + + qt "github.com/frankban/quicktest" + "tinygo.org/x/drivers/max6675" + "tinygo.org/x/drivers/tester" +) + +func Test_MAX6675_Read(t *testing.T) { + c := qt.New(t) + spi := tester.NewSPIBus(c) + + var expectedCelsius float32 = 20.25 + + temp := uint16(expectedCelsius/0.25) << 3 + + spi.Expect( + []byte{0, 0}, + []byte{byte(temp >> 8), byte(temp)}, + ) + + dev := max6675.NewDevice(spi, tester.NewNoopPin()) + + actual, err := dev.Read() + c.Assert(err, qt.Equals, nil) + c.Assert(actual, qt.Equals, expectedCelsius) +} + +func Test_MAX6675_Read_ErrThermocoupleOpen(t *testing.T) { + c := qt.New(t) + spi := tester.NewSPIBus(c) + + spi.Expect( + []byte{0, 0}, + []byte{0, 0x04}, + ) + + dev := max6675.NewDevice(spi, tester.NewNoopPin()) + + actual, err := dev.Read() + c.Assert(err, qt.Equals, max6675.ErrThermocoupleOpen) + c.Assert(actual, qt.Equals, float32(0)) +} diff --git a/pin.go b/pin.go new file mode 100644 index 000000000..22383417e --- /dev/null +++ b/pin.go @@ -0,0 +1,8 @@ +package drivers + +type Pin interface { + Get() bool + High() + Low() + Set(high bool) +} diff --git a/tester/pin.go b/tester/pin.go new file mode 100644 index 000000000..6292b2561 --- /dev/null +++ b/tester/pin.go @@ -0,0 +1,20 @@ +package tester + +type Pin struct { + c Failer +} + +func NewPin(c Failer) *Pin { + return &Pin{c} +} + +type NoopPin struct{} + +func NewNoopPin() *NoopPin { + return &NoopPin{} +} + +func (n *NoopPin) Get() bool { return true } +func (n *NoopPin) High() {} +func (n *NoopPin) Low() {} +func (n *NoopPin) Set(high bool) {} diff --git a/tester/spi.go b/tester/spi.go new file mode 100644 index 000000000..7095c9a38 --- /dev/null +++ b/tester/spi.go @@ -0,0 +1,48 @@ +package tester + +import ( + "bytes" +) + +type spiExpectation struct { + write []byte + read []byte +} + +type SPIBus struct { + c Failer + expectations []spiExpectation +} + +func NewSPIBus(c Failer) *SPIBus { + return &SPIBus{ + c: c, + expectations: []spiExpectation{}, + } +} + +func (s *SPIBus) Tx(w, r []byte) error { + if len(s.expectations) == 0 { + s.c.Fatalf("unexpected SPI exchange") + } + ex := s.expectations[0] + if !bytes.Equal(ex.write, w) { + s.c.Fatalf("unexpected SPI write: got %#v, expecting %#v", w, ex.write) + } + copy(r, ex.read) + s.expectations = s.expectations[1:] + return nil +} + +func (s *SPIBus) Transfer(b byte) (byte, error) { + buf := make([]byte, 1) + err := s.Tx([]byte{b}, buf) + return buf[0], err +} + +func (s *SPIBus) Expect(in []byte, out []byte) { + if len(in) != len(out) { + s.c.Fatalf("Expect: input and output slices must be the same length") + } + s.expectations = append(s.expectations, spiExpectation{in, out}) +} From 898be11bf7328d36befd0cb6eccf556ffe7853a6 Mon Sep 17 00:00:00 2001 From: John Hobbs Date: Wed, 12 Mar 2025 22:02:53 -0500 Subject: [PATCH 2/3] Expectation based Pin test helper --- max6675/max6675_test.go | 5 +++- tester/pin.go | 57 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/max6675/max6675_test.go b/max6675/max6675_test.go index 23e53e964..3e77ae430 100644 --- a/max6675/max6675_test.go +++ b/max6675/max6675_test.go @@ -11,17 +11,20 @@ import ( func Test_MAX6675_Read(t *testing.T) { c := qt.New(t) spi := tester.NewSPIBus(c) + cs := tester.NewPin(c) var expectedCelsius float32 = 20.25 temp := uint16(expectedCelsius/0.25) << 3 + cs.ExpectSet(false) spi.Expect( []byte{0, 0}, []byte{byte(temp >> 8), byte(temp)}, ) + cs.ExpectSet(true) - dev := max6675.NewDevice(spi, tester.NewNoopPin()) + dev := max6675.NewDevice(spi, cs) actual, err := dev.Read() c.Assert(err, qt.Equals, nil) diff --git a/tester/pin.go b/tester/pin.go index 6292b2561..ffe13108a 100644 --- a/tester/pin.go +++ b/tester/pin.go @@ -1,11 +1,64 @@ package tester +import "tinygo.org/x/drivers" + +var _ drivers.Pin = (*Pin)(nil) +var _ drivers.Pin = (*NoopPin)(nil) + +type pinExpectation struct { + get bool + value bool +} + type Pin struct { - c Failer + c Failer + expectations []pinExpectation +} + +func (p *Pin) ExpectGet(high bool) { + p.expectations = append(p.expectations, pinExpectation{get: true, value: high}) +} + +func (p *Pin) ExpectSet(high bool) { + p.expectations = append(p.expectations, pinExpectation{get: false, value: high}) +} + +func (p *Pin) Get() bool { + if len(p.expectations) == 0 { + p.c.Fatalf("unexpected pin read") + } + ex := p.expectations[0] + if !ex.get { + p.c.Fatalf("unexpected pin read") + } + p.expectations = p.expectations[1:] + return ex.value +} + +func (p *Pin) Set(high bool) { + if len(p.expectations) == 0 { + p.c.Fatalf("unexpected pin write") + } + ex := p.expectations[0] + if ex.get { + p.c.Fatalf("unexpected pin write") + } + if ex.value != high { + p.c.Fatalf("unexpected pin write: got %v, expecting %v", high, ex.value) + } + p.expectations = p.expectations[1:] +} + +func (p *Pin) High() { + p.Set(true) +} + +func (p *Pin) Low() { + p.Set(false) } func NewPin(c Failer) *Pin { - return &Pin{c} + return &Pin{c, []pinExpectation{}} } type NoopPin struct{} From 6aa1d210af4ebc2dc73e969fc813f2fe42c88072 Mon Sep 17 00:00:00 2001 From: John Hobbs Date: Thu, 20 Mar 2025 13:45:54 -0500 Subject: [PATCH 3/3] Change `high` arg to `value`, add doc comments --- pin.go | 2 +- tester/pin.go | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/pin.go b/pin.go index 22383417e..7442a339e 100644 --- a/pin.go +++ b/pin.go @@ -4,5 +4,5 @@ type Pin interface { Get() bool High() Low() - Set(high bool) + Set(value bool) } diff --git a/tester/pin.go b/tester/pin.go index ffe13108a..f688a4581 100644 --- a/tester/pin.go +++ b/tester/pin.go @@ -15,12 +15,15 @@ type Pin struct { expectations []pinExpectation } -func (p *Pin) ExpectGet(high bool) { - p.expectations = append(p.expectations, pinExpectation{get: true, value: high}) +// Expect a Get() call, and return the provided value. +func (p *Pin) ExpectGet(value bool) { + p.expectations = append(p.expectations, pinExpectation{get: true, value: value}) } -func (p *Pin) ExpectSet(high bool) { - p.expectations = append(p.expectations, pinExpectation{get: false, value: high}) +// Expect a Set(bool) call, with the provided value. +// true is High, false is Low +func (p *Pin) ExpectSet(value bool) { + p.expectations = append(p.expectations, pinExpectation{get: false, value: value}) } func (p *Pin) Get() bool { @@ -35,7 +38,7 @@ func (p *Pin) Get() bool { return ex.value } -func (p *Pin) Set(high bool) { +func (p *Pin) Set(value bool) { if len(p.expectations) == 0 { p.c.Fatalf("unexpected pin write") } @@ -43,8 +46,8 @@ func (p *Pin) Set(high bool) { if ex.get { p.c.Fatalf("unexpected pin write") } - if ex.value != high { - p.c.Fatalf("unexpected pin write: got %v, expecting %v", high, ex.value) + if ex.value != value { + p.c.Fatalf("unexpected pin write: got %v, expecting %v", value, ex.value) } p.expectations = p.expectations[1:] } @@ -61,6 +64,7 @@ func NewPin(c Failer) *Pin { return &Pin{c, []pinExpectation{}} } +// NoopPin is a pin that does nothing, and always returns true for Get() type NoopPin struct{} func NewNoopPin() *NoopPin {