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  }