apa102: increase coverage to 100%

Make Write() handle when buffer is too long.
pull/1/head
Marc-Antoine Ruel 9 years ago
parent ce258e061b
commit 9fe596f933

@ -96,12 +96,11 @@ func (l *lut) init(i uint8, t uint16) {
//
// dst is in APA102 SPI 32 bits word format. src is in RGB 24 bits word format.
// maxR, maxG and maxB are the maximum light intensity to use per channel.
//
// src cannot be longer in pixel count than dst.
func (l *lut) raster(dst []byte, src []byte) {
// Whichever is the shortest.
length := len(src) / 3
if o := len(dst) / 4; o < length {
length = o
}
for i := 0; i < length; i++ {
// Converts a color into the 4 bytes needed to control an APA-102 LED.
//
@ -221,7 +220,8 @@ type Dev struct {
s spi.Conn
l lut // Updated at each .Write() call.
numLights int
buf []byte
rawBuf []byte
pixels []byte
}
// ColorModel implements devices.Display. There's no surprise, it is
@ -250,8 +250,8 @@ func (d *Dev) Draw(r image.Rectangle, src image.Image, sp image.Point) {
srcR.Max.Y = srcR.Min.Y + dY
}
d.l.init(d.Intensity, d.Temperature)
d.l.rasterImg(d.buf[4:4+4*d.numLights], r, src, srcR)
_ = d.s.Tx(d.buf, nil)
d.l.rasterImg(d.pixels, r, src, srcR)
_ = d.s.Tx(d.rawBuf, nil)
}
// Write accepts a stream of raw RGB pixels and sends it as APA102 encoded
@ -261,8 +261,13 @@ func (d *Dev) Write(pixels []byte) (int, error) {
return 0, errLength
}
d.l.init(d.Intensity, d.Temperature)
d.l.raster(d.buf[4:4+4*d.numLights], pixels)
err := d.s.Tx(d.buf, nil)
// Trying to write more pixels than defined?
if o := len(d.pixels) / 4; o < len(pixels)/3 {
pixels = pixels[:o*3]
}
// Do not touch header and footer.
d.l.raster(d.pixels, pixels)
err := d.s.Tx(d.rawBuf, nil)
return len(pixels), err
}
@ -294,7 +299,8 @@ func New(s spi.Conn, numLights int, intensity uint8, temperature uint16) (*Dev,
Temperature: temperature,
s: s,
numLights: numLights,
buf: buf,
rawBuf: buf,
pixels: buf[4 : 4+4*numLights],
}, nil
}

@ -6,6 +6,7 @@ package apa102
import (
"bytes"
"errors"
"fmt"
"image"
"image/color"
@ -334,6 +335,12 @@ func TestDevEmpty(t *testing.T) {
}
}
func TestConfigureFail(t *testing.T) {
if d, err := New(&configFail{}, 150, 255, 6500); d != nil || err == nil {
t.Fatal("Configure() call have failed")
}
}
func TestDevLen(t *testing.T) {
buf := bytes.Buffer{}
d, _ := New(spitest.NewRecordRaw(&buf), 1, 255, 6500)
@ -445,8 +452,17 @@ func TestDevLong(t *testing.T) {
}
}
// Expected output for 3 test cases. Each test case use a completely different
// code path so make sure each code path results in the exact same output.
func TestDevWriteShort(t *testing.T) {
buf := bytes.Buffer{}
d, _ := New(spitest.NewRecordRaw(&buf), 1, 250, 6500)
if n, err := d.Write([]byte{0, 0, 0, 1, 1, 1}); n != 3 || err != nil {
t.Fatal(n, err)
}
}
// expectedi250t5000 is the expected output for 3 test cases. Each test case
// use a completely different code path so make sure each code path results in
// the exact same output.
var expectedi250t5000 = []byte{
0x00, 0x00, 0x00, 0x00, 0xE1, 0x08, 0x04, 0x00, 0xE1, 0x14, 0x10, 0xC, 0xE1,
0x20, 0x1C, 0x18, 0xE1, 0x2C, 0x28, 0x24, 0xE1, 0x38, 0x34, 0x30, 0xE1, 0x41,
@ -456,6 +472,16 @@ var expectedi250t5000 = []byte{
0x3A, 0x36, 0x32, 0xFF, 0xFF,
}
// expectedi250t6500 is the default color temperature.
var expectedi250t6500 = []byte{
0x00, 0x00, 0x00, 0x00, 0xE1, 0x08, 0x04, 0x00, 0xE1, 0x14, 0x10, 0x0C, 0xE1,
0x20, 0x1C, 0x18, 0xE1, 0x2C, 0x28, 0x24, 0xE1, 0x38, 0x34, 0x30, 0xE1, 0x44,
0x40, 0x3C, 0xE1, 0x4E, 0x4C, 0x48, 0xE1, 0x52, 0x4F, 0x4E, 0xE1, 0x66, 0x5C,
0x56, 0xE1, 0x9A, 0x84, 0x73, 0xE1, 0xFB, 0xD4, 0xB4, 0xE2, 0xCB, 0xAE, 0x94,
0xE4, 0xA0, 0x8A, 0x77, 0xE4, 0xF0, 0xD2, 0xB8, 0xFF, 0x2D, 0x28, 0x23, 0xFF,
0x3E, 0x38, 0x32, 0xFF, 0xFF,
}
func TestDevTemperatureWarm(t *testing.T) {
buf := bytes.Buffer{}
pixels := make([]byte, 16*3)
@ -489,6 +515,20 @@ func TestDrawNRGBA(t *testing.T) {
}
}
func TestDrawNRGBA_wide(t *testing.T) {
img := image.NewNRGBA(image.Rect(0, 0, 17, 2))
for x := 0; x < 16; x++ {
// Test all intensity code paths. Confirm that alpha is ignored.
img.SetNRGBA(x, 0, color.NRGBA{uint8((3 * x) << 2), uint8((3*x + 1) << 2), uint8((3*x + 2) << 2), 0})
}
buf := bytes.Buffer{}
d, _ := New(spitest.NewRecordRaw(&buf), 16, 250, 6500)
d.Draw(d.Bounds(), img, image.Point{})
if !bytes.Equal(expectedi250t6500, buf.Bytes()) {
t.Fatalf("%#v != %#v", expectedi250t5000, buf.Bytes())
}
}
func TestDrawRGBA(t *testing.T) {
img := image.NewRGBA(image.Rect(0, 0, 16, 1))
for i := 0; i < 16; i++ {
@ -506,6 +546,15 @@ func TestDrawRGBA(t *testing.T) {
}
}
func TestInit(t *testing.T) {
// Catch the "maxB == maxG" line.
l := lut{}
l.init(255, 6000)
if equalUint16(l.r[:], l.g[:]) || !equalUint16(l.g[:], l.b[:]) {
t.Fatal("test case is for only when maxG == maxB but maxR != maxG")
}
}
//
func Example() {
@ -625,3 +674,25 @@ func BenchmarkWriteColorfulVariation(b *testing.B) {
_, _ = d.Write(pixels)
}
}
//
type configFail struct {
spitest.Record
}
func (c *configFail) Configure(mode spi.Mode, bits int) error {
return errors.New("injected error")
}
func equalUint16(a, b []uint16) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}

@ -0,0 +1,17 @@
// Copyright 2017 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 apa102
import "testing"
func TestToRGBFast_limits(t *testing.T) {
if r, g, b := toRGBFast(999); r != 255 || g != 83 || b != 0 {
t.Fatal(r, g, b)
}
if r, g, b := toRGBFast(30000); r != 159 || g != 191 || b != 255 {
t.Fatal(r, g, b)
}
}
Loading…
Cancel
Save