tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/adafruit4650/device_test.go (about) 1 package adafruit4650 2 3 import ( 4 "bytes" 5 _ "embed" 6 "encoding/hex" 7 "fmt" 8 "image" 9 "image/color" 10 "image/draw" 11 "image/png" 12 "os" 13 "testing" 14 "time" 15 "tinygo.org/x/drivers" 16 "tinygo.org/x/tinyfont" 17 "tinygo.org/x/tinyfont/freemono" 18 ) 19 20 //go:embed expected_hello_world.png 21 var expectedHelloWorld []byte 22 23 // mockBus mocks a fake i2c device adafruit4650 display. 24 // The memory layout assumes that clients set up the device in a particular way and always send complete 25 // pages to the device buffer. 26 type mockBus struct { 27 img draw.Image 28 line int 29 addr uint8 30 currentPage int 31 currentColumn int 32 } 33 34 func (m *mockBus) Tx(addr uint16, w, r []byte) error { 35 if addr != uint16(m.addr) { 36 panic("unexpected address") 37 } 38 if r != nil { 39 panic("mock does not support reads") 40 } 41 42 if w[0] == 0x00 { 43 if w[1]&0xf0 == 0xb0 { 44 m.currentPage = int(w[1] & 0x0f) 45 46 lo := w[2] & 0x0f 47 hi := w[2] & 0x07 48 m.currentColumn = int(hi<<4 | lo) 49 } 50 return nil 51 } 52 if w[0] != 0x40 { 53 panic("unexpected first byte: " + hex.EncodeToString(w[0:1])) 54 } 55 56 return m.writeRAM(w[1:]) 57 } 58 59 func newMock() *mockBus { 60 61 m := image.NewRGBA(image.Rect(0, 0, width, height)) 62 return &mockBus{img: m, addr: DefaultAddress, currentPage: -1, currentColumn: -1} 63 } 64 65 func (m *mockBus) writeRAM(data []byte) error { 66 67 // RAM layout 68 // *-----> y 69 // | 70 // x| col0 col1 ... col63 71 // v p0 a0 b0 .. 72 // a1 b1 .. 73 // .. .. .. 74 // a7 b7 .. 75 // p1 a0 b0 76 // a1 b1 77 // 78 79 fmt.Printf("writing page %d\n", m.currentPage) 80 // assuming entire pages will be written 81 for x := 0; x < 8; x++ { 82 for y := 0; y < height; y++ { 83 84 col := data[y] 85 86 c := color.Black 87 if col&(1<<x) != 0 { 88 c = color.White 89 } 90 91 m.img.Set(x+m.currentPage*8, height-y-1, c) 92 } 93 } 94 95 return nil 96 } 97 98 func (m *mockBus) toImage() *image.RGBA { 99 100 container := image.NewRGBA(m.img.Bounds().Inset(-1)) 101 draw.Draw(container, container.Bounds(), image.NewUniform(color.RGBA{G: 255, A: 255}), image.Point{}, draw.Over) 102 draw.Draw(container, m.img.Bounds(), m.img, image.Point{}, draw.Over) 103 return container 104 } 105 106 func TestDevice_Display(t *testing.T) { 107 108 bus := newMock() 109 dev := New(bus) 110 111 dev.Configure() 112 113 drawPlus(&dev) 114 drawHellowWorld(&dev) 115 116 //when 117 dev.Display() 118 119 //then 120 actual := bus.toImage() 121 122 expected, err := png.Decode(bytes.NewReader(expectedHelloWorld)) 123 if err != nil { 124 panic(err) 125 } 126 127 assertEqualImages(t, actual, expected) 128 } 129 130 func drawPlus(d drivers.Displayer) { 131 for i := int16(0); i < 128; i++ { 132 d.SetPixel(i, 32, color.RGBA{R: 1}) 133 } 134 for i := int16(0); i < 64; i++ { 135 d.SetPixel(64, i, color.RGBA{R: 1}) 136 } 137 } 138 139 func drawHellowWorld(d drivers.Displayer) { 140 tinyfont.WriteLine(d, &freemono.Regular9pt7b, 0, 32, "Hello World!", color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}) 141 } 142 143 func assertEqualImages(t testing.TB, actual, expected image.Image) { 144 145 if actual.Bounds().Dx() != expected.Bounds().Dx() || actual.Bounds().Dy() != expected.Bounds().Dy() { 146 f := writeImage(actual) 147 t.Fatalf("differing size: was %v, expected %v, saved actual to %s", actual.Bounds(), expected.Bounds(), f) 148 } 149 150 bb := expected.Bounds() 151 for x := bb.Min.X; x < bb.Max.X; x++ { 152 for y := bb.Min.Y; y < bb.Max.Y; y++ { 153 actualBB := actual.Bounds() 154 if actual.At(x+actualBB.Min.X, y+actualBB.Min.Y) != expected.At(x, y) { 155 f := writeImage(actual) 156 t.Fatalf("different pixel at %d/%d: %v != %v, saved actual at %s", x, y, actual.At(x, y), expected.At(x, y), f) 157 } 158 } 159 } 160 } 161 162 func writeImage(img image.Image) string { 163 164 fn := fmt.Sprintf("%d.png", time.Now().Unix()) 165 f, err := os.OpenFile(fn, os.O_RDWR|os.O_CREATE, 0644) 166 if err != nil { 167 panic(err) 168 } 169 defer f.Close() 170 171 err = png.Encode(f, img) 172 if err != nil { 173 panic(err) 174 } 175 return fn 176 }