mirror of https://github.com/periph/devices
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
648 lines
16 KiB
Go
648 lines
16 KiB
Go
// Copyright 2016 The Periph Authors. All rights reserved.
|
|
// Use of this source code is governed under the Apache License, Version 2.0
|
|
// that can be found in the LICENSE file.
|
|
|
|
package nrzled
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"image"
|
|
"image/color"
|
|
"image/draw"
|
|
"io/ioutil"
|
|
"testing"
|
|
|
|
"periph.io/x/conn/v3/conntest"
|
|
"periph.io/x/conn/v3/physic"
|
|
"periph.io/x/conn/v3/spi"
|
|
"periph.io/x/conn/v3/spi/spitest"
|
|
)
|
|
|
|
// toRGB converts a slice of color.NRGBA to a byte stream of RGB pixels.
|
|
//
|
|
// Ignores alpha.
|
|
func toRGB(p []color.NRGBA) []byte {
|
|
b := make([]byte, 0, len(p)*3)
|
|
for _, c := range p {
|
|
b = append(b, c.R, c.G, c.B)
|
|
}
|
|
return b
|
|
}
|
|
|
|
func TestSPI_Empty(t *testing.T) {
|
|
buf := bytes.Buffer{}
|
|
o := Opts{NumPixels: 0, Channels: 3, Freq: 2500 * physic.KiloHertz}
|
|
s := spitest.Playback{
|
|
Playback: conntest.Playback{
|
|
Count: 1,
|
|
Ops: []conntest.IO{{W: []byte{0x00, 0x00, 0x00}}},
|
|
},
|
|
}
|
|
d, err := NewSPI(spitest.NewRecordRaw(&buf), &o)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got, expected := d.String(), "nrzled{recordraw}"; got != expected {
|
|
t.Fatalf("\nGot: %s\nWant: %s\n", got, expected)
|
|
}
|
|
|
|
if n, err := d.Write([]byte{}); n != 0 || err != nil {
|
|
t.Fatalf("%d %v", n, err)
|
|
}
|
|
if err := s.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestSPI_fail(t *testing.T) {
|
|
buf := bytes.Buffer{}
|
|
o := Opts{NumPixels: 1, Channels: 3, Freq: 1 * physic.KiloHertz}
|
|
if _, err := NewSPI(spitest.NewRecordRaw(&buf), &o); err == nil {
|
|
t.Fatal("invalid Freq")
|
|
}
|
|
|
|
o = Opts{NumPixels: 1, Channels: 0, Freq: 2500 * physic.KiloHertz}
|
|
if _, err := NewSPI(spitest.NewRecordRaw(&buf), &o); err == nil {
|
|
t.Fatal("invalid Channels")
|
|
}
|
|
|
|
o = Opts{NumPixels: 150, Channels: 3, Freq: 2500 * physic.KiloHertz}
|
|
if d, err := NewSPI(&configFail{}, &o); d != nil || err == nil {
|
|
t.Fatal("Connect() call have failed")
|
|
}
|
|
|
|
o = Opts{NumPixels: 150, Channels: 3, Freq: 2500 * physic.KiloHertz}
|
|
if d, err := NewSPI(&limitLow{}, &o); d != nil || err == nil {
|
|
t.Fatal("MaxTxSize() is too small")
|
|
}
|
|
}
|
|
|
|
func TestSPI_Len(t *testing.T) {
|
|
buf := bytes.Buffer{}
|
|
o := Opts{NumPixels: 1, Channels: 3, Freq: 2500 * physic.KiloHertz}
|
|
d, err := NewSPI(spitest.NewRecordRaw(&buf), &o)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n, err := d.Write([]byte{0}); n != 0 || err == nil {
|
|
t.Fatalf("%d %v", n, err)
|
|
}
|
|
if expected := []byte{}; !bytes.Equal(expected, buf.Bytes()) {
|
|
t.Fatalf("\nGot: %#02v\nWant: %#02v\n", buf.Bytes(), expected)
|
|
}
|
|
}
|
|
|
|
var writeTests = []struct {
|
|
name string
|
|
pixels []byte
|
|
want []byte
|
|
opts Opts
|
|
}{
|
|
{
|
|
name: "1 pixel to #FFFFFF",
|
|
pixels: toRGB([]color.NRGBA{
|
|
{0xFF, 0xFF, 0xFF, 0x00},
|
|
}),
|
|
want: []byte{
|
|
/*FF*/ 0xEE, 0xEE, 0xEE, 0xEE /*FF*/, 0xEE, 0xEE, 0xEE, 0xEE /*FF*/, 0xEE, 0xEE, 0xEE, 0xEE,
|
|
/*EOF*/ 0x00, 0x00, 0x00,
|
|
},
|
|
opts: Opts{
|
|
NumPixels: 1,
|
|
Channels: 3,
|
|
Freq: 2500 * physic.KiloHertz,
|
|
},
|
|
},
|
|
{
|
|
name: "1 pixel to #FEFEFE",
|
|
pixels: toRGB([]color.NRGBA{
|
|
{0xFE, 0xFE, 0xFE, 0x00},
|
|
}),
|
|
want: []byte{
|
|
/*FE*/ 0xEE, 0xEE, 0xEE, 0xE8 /*FE*/, 0xEE, 0xEE, 0xEE, 0xE8 /*FE*/, 0xEE, 0xEE, 0xEE, 0xE8,
|
|
/*EOF*/ 0x00, 0x00, 0x00,
|
|
},
|
|
opts: Opts{
|
|
NumPixels: 1,
|
|
Channels: 3,
|
|
Freq: 2500 * physic.KiloHertz,
|
|
},
|
|
},
|
|
{
|
|
name: "1 pixel to #F0F0F0",
|
|
pixels: toRGB([]color.NRGBA{
|
|
{0xF0, 0xF0, 0xF0, 0x00},
|
|
}),
|
|
want: []byte{
|
|
/*F0*/ 0xEE, 0xEE, 0x88, 0x88 /*F0*/, 0xEE, 0xEE, 0x88, 0x88 /*F0*/, 0xEE, 0xEE, 0x88, 0x88,
|
|
/*EOF*/ 0x00, 0x00, 0x00,
|
|
},
|
|
opts: Opts{
|
|
NumPixels: 1,
|
|
Channels: 3,
|
|
Freq: 2500 * physic.KiloHertz,
|
|
},
|
|
},
|
|
{
|
|
name: "1 pixel to #808080",
|
|
pixels: toRGB([]color.NRGBA{
|
|
{0x80, 0x80, 0x80, 0x00},
|
|
}),
|
|
want: []byte{
|
|
/*80*/ 0xE8, 0x88, 0x88, 0x88 /*80*/, 0xE8, 0x88, 0x88, 0x88 /*80*/, 0xE8, 0x88, 0x88, 0x88,
|
|
/*EOF*/ 0x00, 0x00, 0x00,
|
|
},
|
|
opts: Opts{
|
|
NumPixels: 1,
|
|
Channels: 3,
|
|
Freq: 2500 * physic.KiloHertz,
|
|
},
|
|
},
|
|
{
|
|
name: "1 pixel to #80FF00",
|
|
pixels: toRGB([]color.NRGBA{
|
|
{0x80, 0xFF, 0x00, 0x00},
|
|
}),
|
|
want: []byte{
|
|
/*FF*/ 0xEE, 0xEE, 0xEE, 0xEE /*80*/, 0xE8, 0x88, 0x88, 0x88 /*00*/, 0x88, 0x88, 0x88, 0x88,
|
|
/*EOF*/ 0x00, 0x00, 0x00,
|
|
},
|
|
opts: Opts{
|
|
NumPixels: 1,
|
|
Channels: 3,
|
|
Freq: 2500 * physic.KiloHertz,
|
|
},
|
|
},
|
|
{
|
|
name: "1 pixel to #800000",
|
|
pixels: toRGB([]color.NRGBA{
|
|
{0x80, 0x00, 0x00, 0x00},
|
|
}),
|
|
want: []byte{
|
|
/*00*/ 0x88, 0x88, 0x88, 0x88 /*80*/, 0xE8, 0x88, 0x88, 0x88 /*00*/, 0x88, 0x88, 0x88, 0x88,
|
|
/*EOF*/ 0x00, 0x00, 0x00,
|
|
},
|
|
opts: Opts{
|
|
NumPixels: 1,
|
|
Channels: 3,
|
|
Freq: 2500 * physic.KiloHertz,
|
|
},
|
|
},
|
|
{
|
|
name: "1 pixel to #008000",
|
|
pixels: toRGB([]color.NRGBA{
|
|
{0x00, 0x80, 0x00, 0x00},
|
|
}),
|
|
want: []byte{
|
|
/*80*/ 0xE8, 0x88, 0x88, 0x88 /*00*/, 0x88, 0x88, 0x88, 0x88 /*00*/, 0x88, 0x88, 0x88, 0x88,
|
|
/*EOF*/ 0x00, 0x00, 0x00,
|
|
},
|
|
opts: Opts{
|
|
NumPixels: 1,
|
|
Channels: 3,
|
|
Freq: 2500 * physic.KiloHertz,
|
|
},
|
|
},
|
|
{
|
|
name: "1 pixel to #000080",
|
|
pixels: toRGB([]color.NRGBA{
|
|
{0x00, 0x00, 0x80, 0x00},
|
|
}),
|
|
want: []byte{
|
|
/*00*/ 0x88, 0x88, 0x88, 0x88 /*00*/, 0x88, 0x88, 0x88, 0x88 /*80*/, 0xE8, 0x88, 0x88, 0x88,
|
|
/*EOF*/ 0x00, 0x00, 0x00,
|
|
},
|
|
opts: Opts{
|
|
NumPixels: 1,
|
|
Channels: 3,
|
|
Freq: 2500 * physic.KiloHertz,
|
|
},
|
|
},
|
|
{
|
|
name: "All at once",
|
|
pixels: toRGB([]color.NRGBA{
|
|
{0xFF, 0xFF, 0xFF, 0x00},
|
|
{0xFE, 0xFE, 0xFE, 0x00},
|
|
{0xF0, 0xF0, 0xF0, 0x00},
|
|
{0x80, 0x80, 0x80, 0x00},
|
|
|
|
{0x80, 0x00, 0x00, 0x00},
|
|
{0x00, 0x80, 0x00, 0x00},
|
|
{0x00, 0x00, 0x80, 0x00},
|
|
|
|
{0x00, 0x00, 0x10, 0x00},
|
|
{0x00, 0x00, 0x01, 0x00},
|
|
{0x00, 0x00, 0x00, 0x00},
|
|
}),
|
|
want: []byte{
|
|
/*FF*/ 0xEE, 0xEE, 0xEE, 0xEE /*FF*/, 0xEE, 0xEE, 0xEE, 0xEE /*FF*/, 0xEE, 0xEE, 0xEE, 0xEE,
|
|
/*FE*/ 0xEE, 0xEE, 0xEE, 0xE8 /*FE*/, 0xEE, 0xEE, 0xEE, 0xE8 /*FE*/, 0xEE, 0xEE, 0xEE, 0xE8,
|
|
/*F0*/ 0xEE, 0xEE, 0x88, 0x88 /*F0*/, 0xEE, 0xEE, 0x88, 0x88 /*F0*/, 0xEE, 0xEE, 0x88, 0x88,
|
|
/*80*/ 0xE8, 0x88, 0x88, 0x88 /*80*/, 0xE8, 0x88, 0x88, 0x88 /*80*/, 0xE8, 0x88, 0x88, 0x88,
|
|
|
|
/*00*/ 0x88, 0x88, 0x88, 0x88 /*80*/, 0xE8, 0x88, 0x88, 0x88 /*00*/, 0x88, 0x88, 0x88, 0x88,
|
|
/*80*/ 0xE8, 0x88, 0x88, 0x88 /*00*/, 0x88, 0x88, 0x88, 0x88 /*00*/, 0x88, 0x88, 0x88, 0x88,
|
|
/*00*/ 0x88, 0x88, 0x88, 0x88 /*00*/, 0x88, 0x88, 0x88, 0x88 /*80*/, 0xE8, 0x88, 0x88, 0x88,
|
|
|
|
/*00*/ 0x88, 0x88, 0x88, 0x88 /*00*/, 0x88, 0x88, 0x88, 0x88 /*10*/, 0x88, 0x8E, 0x88, 0x88,
|
|
/*00*/ 0x88, 0x88, 0x88, 0x88 /*00*/, 0x88, 0x88, 0x88, 0x88 /*01*/, 0x88, 0x88, 0x88, 0x8E,
|
|
/*00*/ 0x88, 0x88, 0x88, 0x88 /*00*/, 0x88, 0x88, 0x88, 0x88 /*00*/, 0x88, 0x88, 0x88, 0x88,
|
|
/*EOF*/ 0x00, 0x00, 0x00,
|
|
},
|
|
opts: Opts{
|
|
NumPixels: 10,
|
|
Channels: 3,
|
|
Freq: 2500 * physic.KiloHertz,
|
|
},
|
|
},
|
|
}
|
|
|
|
func TestSPI_Writes(t *testing.T) {
|
|
for _, tt := range writeTests {
|
|
buf := bytes.Buffer{}
|
|
tt.opts.NumPixels = len(tt.pixels) / 3
|
|
d, err := NewSPI(spitest.NewRecordRaw(&buf), &tt.opts)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
n, err := d.Write(tt.pixels)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n != len(tt.pixels) {
|
|
t.Fatalf("%s: Got %d bytes result, want %d", tt.name, n, len(tt.pixels)*3)
|
|
}
|
|
if got := buf.Bytes(); !bytes.Equal(got, tt.want) {
|
|
t.Logf("%s:\nGot: (%d)%#02v\nWant: (%d)%#02v\n", tt.name, len(got), got, len(tt.want), tt.want)
|
|
for i := range tt.want {
|
|
if got[i] != tt.want[i] {
|
|
t.Logf("(%d) Got: %#02v\tWant: %#02v\n", i, got[i], tt.want[i])
|
|
}
|
|
}
|
|
t.Fatal("expectation failure")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSPI_Color(t *testing.T) {
|
|
if c := (&Dev{}).ColorModel(); c != color.NRGBAModel {
|
|
t.Fatal(c)
|
|
}
|
|
}
|
|
|
|
func TestSPI_Long(t *testing.T) {
|
|
buf := bytes.Buffer{}
|
|
colors := make([]color.NRGBA, 256)
|
|
o := Opts{NumPixels: len(colors), Channels: 3, Freq: 2500 * physic.KiloHertz}
|
|
d, err := NewSPI(spitest.NewRecordRaw(&buf), &o)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n, err := d.Write(toRGB(colors)); n != len(colors)*3 || err != nil {
|
|
t.Fatalf("%d %v", n, err)
|
|
}
|
|
expected := make([]byte, 12*o.NumPixels+3)
|
|
for i := 0; i < 12*o.NumPixels; i += 12 {
|
|
//Each channel should be 0x00
|
|
for j := 0; j < 12; j++ {
|
|
expected[i+j] = 0x88
|
|
}
|
|
}
|
|
trailer := expected[12*o.NumPixels:]
|
|
for i := range trailer {
|
|
trailer[i] = 0x00
|
|
}
|
|
if !bytes.Equal(expected, buf.Bytes()) {
|
|
t.Fatalf("\nGot: %#02v\nWant: %#02v\n", buf.Bytes(), expected)
|
|
}
|
|
}
|
|
|
|
func TestSPI_Write_Long(t *testing.T) {
|
|
buf := bytes.Buffer{}
|
|
o := Opts{NumPixels: 1, Channels: 3, Freq: 2500 * physic.KiloHertz}
|
|
d, err := NewSPI(spitest.NewRecordRaw(&buf), &o)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n, err := d.Write([]byte{0, 0, 0, 1, 1, 1}); n != 0 || err == nil {
|
|
t.Fatal(n, err)
|
|
}
|
|
}
|
|
|
|
var drawTests = []struct {
|
|
name string
|
|
img image.Image
|
|
want []byte
|
|
opts Opts
|
|
}{
|
|
{
|
|
name: "Draw NRGBA",
|
|
img: func() image.Image {
|
|
im := image.NewNRGBA(image.Rect(0, 0, 4, 1))
|
|
for i := 0; i < 4; i++ {
|
|
im.Pix[4*i+0] = 0x00
|
|
im.Pix[4*i+1] = 0x80
|
|
im.Pix[4*i+2] = 0xFF
|
|
im.Pix[4*i+3] = 0
|
|
}
|
|
return im
|
|
}(),
|
|
want: func() []byte {
|
|
var b []byte
|
|
for i := 0; i < 4; i++ {
|
|
b = append(b, 0xE8, 0x88, 0x88, 0x88) //0x80
|
|
b = append(b, 0x88, 0x88, 0x88, 0x88) //0x00
|
|
b = append(b, 0xEE, 0xEE, 0xEE, 0xEE) //0xFF
|
|
}
|
|
for i := 0; i < 3; i++ {
|
|
b = append(b, 0x00)
|
|
}
|
|
return b
|
|
}(),
|
|
opts: Opts{
|
|
NumPixels: 4,
|
|
Channels: 3,
|
|
Freq: 2500 * physic.KiloHertz,
|
|
},
|
|
},
|
|
{
|
|
name: "Draw Empty",
|
|
img: func() image.Image {
|
|
im := image.NewNRGBA(image.Rect(0, 0, 0, 0))
|
|
return im
|
|
}(),
|
|
want: func() []byte {
|
|
var b []byte
|
|
return b
|
|
}(),
|
|
opts: Opts{
|
|
NumPixels: 4,
|
|
Channels: 3,
|
|
Freq: 2500 * physic.KiloHertz,
|
|
},
|
|
},
|
|
}
|
|
|
|
func TestSPI_Draws(t *testing.T) {
|
|
for _, tt := range drawTests {
|
|
buf := bytes.Buffer{}
|
|
d, err := NewSPI(spitest.NewRecordRaw(&buf), &tt.opts)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := d.Draw(d.Bounds(), tt.img, image.Point{}); err != nil {
|
|
t.Fatalf("%s: %v", tt.name, err)
|
|
}
|
|
got := buf.Bytes()
|
|
if !bytes.Equal(got, tt.want) {
|
|
t.Logf("%s:\nGot: (%d)%#02v\nWant: (%d)%#02v\n", tt.name, len(got), got, len(tt.want), tt.want)
|
|
for i := range tt.want {
|
|
if got[i] != tt.want[i] {
|
|
t.Logf("(%d) Got: %#02v\tWant: %#02v\n", i, got[i], tt.want[i])
|
|
}
|
|
}
|
|
t.Fatal("expectation failure")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSPI_Draw_DstEmpty(t *testing.T) {
|
|
buf := bytes.Buffer{}
|
|
o := Opts{NumPixels: 4, Channels: 3, Freq: 2500 * physic.KiloHertz}
|
|
d, err := NewSPI(spitest.NewRecordRaw(&buf), &o)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
img := image.NewNRGBA(image.Rect(0, 0, 1, 1))
|
|
if err := d.Draw(image.Rect(0, 0, 0, 0), img, image.Point{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestSPI_Halt(t *testing.T) {
|
|
s := spitest.Playback{
|
|
Playback: conntest.Playback{
|
|
Count: 1,
|
|
Ops: []conntest.IO{
|
|
{},
|
|
{W: []byte{
|
|
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
|
|
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
|
|
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
|
|
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
|
|
//End of frame
|
|
0x00, 0x00, 0x00,
|
|
}},
|
|
},
|
|
},
|
|
}
|
|
o := Opts{NumPixels: 4, Channels: 3, Freq: 2500 * physic.KiloHertz}
|
|
d, err := NewSPI(&s, &o)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := d.Halt(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestSPI_Halt_fail(t *testing.T) {
|
|
s := spitest.Playback{Playback: conntest.Playback{DontPanic: true}}
|
|
o := Opts{NumPixels: 4, Channels: 3, Freq: 2500 * physic.KiloHertz}
|
|
d, err := NewSPI(&s, &o)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if d.Halt() == nil {
|
|
t.Fatal("expected failure")
|
|
}
|
|
if err := s.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
type genColor func(int) [3]byte
|
|
|
|
func benchmarkSPIWrite(b *testing.B, o Opts, length int, f genColor) {
|
|
var pixels []byte
|
|
for i := 0; i < length; i++ {
|
|
c := f(i)
|
|
pixels = append(pixels, c[:]...)
|
|
}
|
|
o.NumPixels = length
|
|
b.ReportAllocs()
|
|
d, err := NewSPI(spitest.NewRecordRaw(ioutil.Discard), &o)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
if _, err = d.Write(pixels[:]); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
if _, err = d.Write(pixels[:]); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkSPI_WriteWhite(b *testing.B) {
|
|
o := Opts{NumPixels: 150, Channels: 3, Freq: 2500 * physic.KiloHertz}
|
|
benchmarkSPIWrite(b, o, 150, func(i int) [3]byte { return [3]byte{0xFF, 0xFF, 0xFF} })
|
|
}
|
|
|
|
func BenchmarkSPI_WriteDim(b *testing.B) {
|
|
o := Opts{NumPixels: 150, Channels: 3, Freq: 2500 * physic.KiloHertz}
|
|
benchmarkSPIWrite(b, o, 150, func(i int) [3]byte { return [3]byte{0x01, 0x01, 0x01} })
|
|
}
|
|
|
|
func BenchmarkSPI_WriteBlack(b *testing.B) {
|
|
o := Opts{NumPixels: 150, Channels: 3, Freq: 2500 * physic.KiloHertz}
|
|
benchmarkSPIWrite(b, o, 150, func(i int) [3]byte { return [3]byte{0x0, 0x0, 0x0} })
|
|
}
|
|
|
|
func genColorfulPixel(x int) [3]byte {
|
|
i := x * 3
|
|
return [3]byte{uint8(i) + uint8(i>>8),
|
|
uint8(i+1) + uint8(i+1>>8),
|
|
uint8(i+2) + uint8(i+2>>8),
|
|
}
|
|
}
|
|
|
|
func BenchmarkSPI_WriteColorful(b *testing.B) {
|
|
o := Opts{NumPixels: 150, Channels: 3, Freq: 2500 * physic.KiloHertz}
|
|
benchmarkSPIWrite(b, o, 150, genColorfulPixel)
|
|
}
|
|
|
|
func BenchmarkSPI_WriteColorfulPassThru(b *testing.B) {
|
|
o := Opts{NumPixels: 150, Channels: 3, Freq: 2500 * physic.KiloHertz}
|
|
benchmarkSPIWrite(b, o, 150, genColorfulPixel)
|
|
}
|
|
|
|
func BenchmarkSPI_WriteColorfulVariation(b *testing.B) {
|
|
// Continuously vary the lookup tables.
|
|
b.ReportAllocs()
|
|
pixels := [256 * 3]byte{}
|
|
for i := range pixels {
|
|
pixels[i] = uint8(i) + uint8(i>>8)
|
|
}
|
|
o := Opts{NumPixels: len(pixels) / 3, Channels: 3, Freq: 2500 * physic.KiloHertz}
|
|
d, err := NewSPI(spitest.NewRecordRaw(ioutil.Discard), &o)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
if _, err = d.Write(pixels[:]); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
if _, err = d.Write(pixels[:]); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func benchmarkSPIDraw(b *testing.B, o Opts, img draw.Image, f genColor) {
|
|
for x := 0; x < img.Bounds().Dx(); x++ {
|
|
for y := 0; y < img.Bounds().Dy(); y++ {
|
|
pix := f(x)
|
|
c := color.NRGBA{R: pix[0], G: pix[1], B: pix[2], A: 255}
|
|
img.Set(x, y, c)
|
|
}
|
|
}
|
|
o.NumPixels = img.Bounds().Max.X
|
|
b.ReportAllocs()
|
|
d, _ := NewSPI(spitest.NewRecordRaw(ioutil.Discard), &o)
|
|
r := d.Bounds()
|
|
p := image.Point{}
|
|
if err := d.Draw(r, img, p); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
if err := d.Draw(r, img, p); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkSPI_DrawNRGBAColorful(b *testing.B) {
|
|
o := Opts{NumPixels: 150, Channels: 3, Freq: 2500 * physic.KiloHertz}
|
|
benchmarkSPIDraw(b, o, image.NewNRGBA(image.Rect(0, 0, 150, 1)), genColorfulPixel)
|
|
}
|
|
|
|
func BenchmarkSPI_DrawNRGBAWhite(b *testing.B) {
|
|
o := Opts{NumPixels: 150, Channels: 3, Freq: 2500 * physic.KiloHertz}
|
|
benchmarkSPIDraw(b, o, image.NewNRGBA(image.Rect(0, 0, 150, 1)), func(i int) [3]byte { return [3]byte{0xFF, 0xFF, 0xFF} })
|
|
}
|
|
|
|
func BenchmarkDrawRGBAColorful(b *testing.B) {
|
|
o := Opts{NumPixels: 150, Channels: 3, Freq: 2500 * physic.KiloHertz}
|
|
benchmarkSPIDraw(b, o, image.NewRGBA(image.Rect(0, 0, 256, 1)), genColorfulPixel)
|
|
}
|
|
|
|
func BenchmarkSPI_DrawSlowpath(b *testing.B) {
|
|
// Should be an image type that doesn't have a fast path
|
|
img := image.NewGray(image.Rect(0, 0, 150, 1))
|
|
for x := 0; x < img.Bounds().Dx(); x++ {
|
|
for y := 0; y < img.Bounds().Dy(); y++ {
|
|
pix := genColorfulPixel(x)
|
|
img.Set(x, y, color.Gray{pix[0]})
|
|
}
|
|
}
|
|
o := Opts{NumPixels: img.Bounds().Max.X, Channels: 3, Freq: 2500 * physic.KiloHertz}
|
|
b.ReportAllocs()
|
|
d, err := NewSPI(spitest.NewRecordRaw(ioutil.Discard), &o)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
r := d.Bounds()
|
|
p := image.Point{}
|
|
if err := d.Draw(r, img, p); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
if err := d.Draw(r, img, p); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkSPI_Halt(b *testing.B) {
|
|
b.ReportAllocs()
|
|
o := &Opts{NumPixels: 150, Channels: 3, Freq: 2500 * physic.KiloHertz}
|
|
d, err := NewSPI(spitest.NewRecordRaw(ioutil.Discard), o)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
if err := d.Halt(); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
|
|
type configFail struct {
|
|
spitest.Record
|
|
}
|
|
|
|
func (c *configFail) Connect(f physic.Frequency, mode spi.Mode, bits int) (spi.Conn, error) {
|
|
return nil, errors.New("injected error")
|
|
}
|
|
|
|
type limitLow struct {
|
|
spitest.Record
|
|
}
|
|
|
|
func (c *limitLow) MaxTxSize() int {
|
|
return 1
|
|
}
|