Add fuzz tests for config, remap, and sacn packages
This commit is contained in:
153
config/fuzz_test.go
Normal file
153
config/fuzz_test.go
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FuzzParseUniverse(f *testing.F) {
|
||||||
|
f.Add("artnet:0.0.0")
|
||||||
|
f.Add("artnet:0.0.1")
|
||||||
|
f.Add("artnet:127.15.15")
|
||||||
|
f.Add("artnet:0")
|
||||||
|
f.Add("artnet:32767")
|
||||||
|
f.Add("sacn:1")
|
||||||
|
f.Add("sacn:63999")
|
||||||
|
f.Add("sacn:100")
|
||||||
|
f.Add("")
|
||||||
|
f.Add("invalid")
|
||||||
|
f.Add("artnet:")
|
||||||
|
f.Add("sacn:")
|
||||||
|
f.Add("artnet:a.b.c")
|
||||||
|
f.Add("artnet:-1")
|
||||||
|
f.Add("sacn:0")
|
||||||
|
f.Add("sacn:64000")
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, input string) {
|
||||||
|
u, err := ParseUniverse(input)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s := u.String()
|
||||||
|
u2, err := ParseUniverse(s)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("roundtrip failed: parsed %q -> %v -> %q, but re-parse failed: %v", input, u, s, err)
|
||||||
|
}
|
||||||
|
if u != u2 {
|
||||||
|
t.Fatalf("roundtrip mismatch: %v != %v", u, u2)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzFromAddrParse(f *testing.F) {
|
||||||
|
f.Add("artnet:0.0.0")
|
||||||
|
f.Add("artnet:0.0.1:1-512")
|
||||||
|
f.Add("artnet:0.0.1:50-100")
|
||||||
|
f.Add("artnet:0.0.1:1")
|
||||||
|
f.Add("artnet:0.0.1:512")
|
||||||
|
f.Add("artnet:0.0.1:1-")
|
||||||
|
f.Add("sacn:1:100-200")
|
||||||
|
f.Add("sacn:100")
|
||||||
|
f.Add("")
|
||||||
|
f.Add("artnet:0.0.0:0")
|
||||||
|
f.Add("artnet:0.0.0:513")
|
||||||
|
f.Add("artnet:0.0.0:-1")
|
||||||
|
f.Add("artnet:0.0.0:abc")
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, input string) {
|
||||||
|
var addr FromAddr
|
||||||
|
err := addr.parse(input)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if addr.ChannelStart > addr.ChannelEnd {
|
||||||
|
t.Fatalf("ChannelStart > ChannelEnd: %d > %d", addr.ChannelStart, addr.ChannelEnd)
|
||||||
|
}
|
||||||
|
if addr.ChannelStart < 1 || addr.ChannelEnd > 512 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s := addr.String()
|
||||||
|
var addr2 FromAddr
|
||||||
|
if err := addr2.parse(s); err != nil {
|
||||||
|
t.Fatalf("roundtrip failed: parsed %q -> %v -> %q, but re-parse failed: %v", input, addr, s, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzToAddrParse(f *testing.F) {
|
||||||
|
f.Add("artnet:0.0.0")
|
||||||
|
f.Add("artnet:0.0.1:1")
|
||||||
|
f.Add("artnet:0.0.1:512")
|
||||||
|
f.Add("sacn:1:100")
|
||||||
|
f.Add("sacn:100")
|
||||||
|
f.Add("")
|
||||||
|
f.Add("artnet:0.0.0:0")
|
||||||
|
f.Add("artnet:0.0.0:1-100")
|
||||||
|
f.Add("artnet:0.0.0:513")
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, input string) {
|
||||||
|
var addr ToAddr
|
||||||
|
err := addr.parse(input)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if addr.ChannelStart < 1 || addr.ChannelStart > 512 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s := addr.String()
|
||||||
|
var addr2 ToAddr
|
||||||
|
if err := addr2.parse(s); err != nil {
|
||||||
|
t.Fatalf("roundtrip failed: parsed %q -> %v -> %q, but re-parse failed: %v", input, addr, s, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzParseUniverseNumber(f *testing.F) {
|
||||||
|
f.Add("0.0.0", string(ProtocolArtNet))
|
||||||
|
f.Add("127.15.15", string(ProtocolArtNet))
|
||||||
|
f.Add("0", string(ProtocolArtNet))
|
||||||
|
f.Add("32767", string(ProtocolArtNet))
|
||||||
|
f.Add("1", string(ProtocolSACN))
|
||||||
|
f.Add("63999", string(ProtocolSACN))
|
||||||
|
f.Add("", string(ProtocolArtNet))
|
||||||
|
f.Add("invalid", string(ProtocolArtNet))
|
||||||
|
f.Add("0.0", string(ProtocolArtNet))
|
||||||
|
f.Add("0.0.0.0", string(ProtocolArtNet))
|
||||||
|
f.Add("-1", string(ProtocolArtNet))
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, input string, protoStr string) {
|
||||||
|
proto := Protocol(protoStr)
|
||||||
|
if proto != ProtocolArtNet && proto != ProtocolSACN {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _ = parseUniverseNumber(input, proto)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzParseChannelRange(f *testing.F) {
|
||||||
|
f.Add("1-512")
|
||||||
|
f.Add("1")
|
||||||
|
f.Add("512")
|
||||||
|
f.Add("1-")
|
||||||
|
f.Add("100-200")
|
||||||
|
f.Add("")
|
||||||
|
f.Add("-")
|
||||||
|
f.Add("abc")
|
||||||
|
f.Add("1-abc")
|
||||||
|
f.Add("-1")
|
||||||
|
f.Add("0")
|
||||||
|
f.Add("513")
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, input string) {
|
||||||
|
var start, end int
|
||||||
|
err := parseChannelRange(input, &start, &end)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if start < 0 {
|
||||||
|
t.Fatalf("start should not be negative: %d", start)
|
||||||
|
}
|
||||||
|
if end < 0 {
|
||||||
|
t.Fatalf("end should not be negative: %d", end)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
137
remap/fuzz_test.go
Normal file
137
remap/fuzz_test.go
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
package remap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gopatchy/artmap/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FuzzRemap(f *testing.F) {
|
||||||
|
f.Add(uint16(0), uint16(1), 0, 0, 512, make([]byte, 512))
|
||||||
|
f.Add(uint16(0), uint16(1), 100, 200, 100, make([]byte, 512))
|
||||||
|
f.Add(uint16(0), uint16(1), 511, 511, 1, make([]byte, 512))
|
||||||
|
f.Add(uint16(100), uint16(200), 0, 0, 256, make([]byte, 512))
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, srcUni, dstUni uint16, fromChan, toChan, count int, inputData []byte) {
|
||||||
|
if fromChan < 0 || fromChan >= 512 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if toChan < 0 || toChan >= 512 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if count < 1 || count > 512 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if fromChan+count > 512 || toChan+count > 512 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(inputData) < 512 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
srcU, _ := config.NewUniverse(config.ProtocolArtNet, srcUni)
|
||||||
|
dstU, _ := config.NewUniverse(config.ProtocolArtNet, dstUni)
|
||||||
|
|
||||||
|
mappings := []config.NormalizedMapping{{
|
||||||
|
From: srcU,
|
||||||
|
FromChan: fromChan,
|
||||||
|
To: dstU,
|
||||||
|
ToChan: toChan,
|
||||||
|
Count: count,
|
||||||
|
}}
|
||||||
|
|
||||||
|
engine := NewEngine(mappings)
|
||||||
|
|
||||||
|
var srcData [512]byte
|
||||||
|
copy(srcData[:], inputData[:512])
|
||||||
|
|
||||||
|
outputs := engine.Remap(srcU, srcData)
|
||||||
|
|
||||||
|
if len(outputs) != 1 {
|
||||||
|
t.Fatalf("expected 1 output, got %d", len(outputs))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
srcIdx := fromChan + i
|
||||||
|
dstIdx := toChan + i
|
||||||
|
if outputs[0].Data[dstIdx] != srcData[srcIdx] {
|
||||||
|
t.Fatalf("channel mismatch at offset %d: src[%d]=%d != dst[%d]=%d",
|
||||||
|
i, srcIdx, srcData[srcIdx], dstIdx, outputs[0].Data[dstIdx])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzRemapMultipleMappings(f *testing.F) {
|
||||||
|
f.Add(make([]byte, 512))
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, inputData []byte) {
|
||||||
|
if len(inputData) < 512 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
srcU, _ := config.NewUniverse(config.ProtocolArtNet, 0)
|
||||||
|
dstU1, _ := config.NewUniverse(config.ProtocolArtNet, 1)
|
||||||
|
dstU2, _ := config.NewUniverse(config.ProtocolSACN, 1)
|
||||||
|
|
||||||
|
mappings := []config.NormalizedMapping{
|
||||||
|
{From: srcU, FromChan: 0, To: dstU1, ToChan: 0, Count: 256},
|
||||||
|
{From: srcU, FromChan: 256, To: dstU2, ToChan: 0, Count: 256},
|
||||||
|
}
|
||||||
|
|
||||||
|
engine := NewEngine(mappings)
|
||||||
|
|
||||||
|
var srcData [512]byte
|
||||||
|
copy(srcData[:], inputData[:512])
|
||||||
|
|
||||||
|
outputs := engine.Remap(srcU, srcData)
|
||||||
|
|
||||||
|
if len(outputs) != 2 {
|
||||||
|
t.Fatalf("expected 2 outputs, got %d", len(outputs))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, out := range outputs {
|
||||||
|
if out.Universe == dstU1 {
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
if out.Data[i] != srcData[i] {
|
||||||
|
t.Fatalf("dstU1 mismatch at %d", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if out.Universe == dstU2 {
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
if out.Data[i] != srcData[256+i] {
|
||||||
|
t.Fatalf("dstU2 mismatch at %d", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzRemapUnmatchedUniverse(f *testing.F) {
|
||||||
|
f.Add(make([]byte, 512))
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, inputData []byte) {
|
||||||
|
if len(inputData) < 512 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
srcU, _ := config.NewUniverse(config.ProtocolArtNet, 0)
|
||||||
|
otherU, _ := config.NewUniverse(config.ProtocolArtNet, 99)
|
||||||
|
dstU, _ := config.NewUniverse(config.ProtocolArtNet, 1)
|
||||||
|
|
||||||
|
mappings := []config.NormalizedMapping{{
|
||||||
|
From: srcU, FromChan: 0, To: dstU, ToChan: 0, Count: 512,
|
||||||
|
}}
|
||||||
|
|
||||||
|
engine := NewEngine(mappings)
|
||||||
|
|
||||||
|
var srcData [512]byte
|
||||||
|
copy(srcData[:], inputData[:512])
|
||||||
|
|
||||||
|
outputs := engine.Remap(otherU, srcData)
|
||||||
|
if outputs != nil {
|
||||||
|
t.Fatalf("expected nil output for unmatched universe, got %d outputs", len(outputs))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
91
sacn/fuzz_test.go
Normal file
91
sacn/fuzz_test.go
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package sacn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FuzzParsePacket(f *testing.F) {
|
||||||
|
cid := [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
|
||||||
|
validPacket := BuildDataPacket(1, 0, "test", cid, make([]byte, 512))
|
||||||
|
f.Add(validPacket)
|
||||||
|
f.Add(BuildDataPacket(1, 0, "test", cid, make([]byte, 100)))
|
||||||
|
f.Add(BuildDataPacket(63999, 255, "long source name here", cid, make([]byte, 512)))
|
||||||
|
f.Add([]byte{})
|
||||||
|
f.Add(make([]byte, 125))
|
||||||
|
f.Add(make([]byte, 126))
|
||||||
|
f.Add(make([]byte, 638))
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
_, dmxData, ok := ParsePacket(data)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(dmxData) != 512 {
|
||||||
|
t.Fatalf("dmx data should be 512 bytes, got %d", len(dmxData))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzBuildParseRoundtrip(f *testing.F) {
|
||||||
|
f.Add(uint16(1), uint8(0), "test", make([]byte, 512))
|
||||||
|
f.Add(uint16(63999), uint8(255), "source", make([]byte, 100))
|
||||||
|
f.Add(uint16(100), uint8(128), "", make([]byte, 0))
|
||||||
|
f.Add(uint16(1), uint8(0), "a]very long source name that exceeds normal limits", make([]byte, 512))
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, universe uint16, seq uint8, sourceName string, dmxInput []byte) {
|
||||||
|
if universe < 1 || universe > 63999 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cid := [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
|
||||||
|
packet := BuildDataPacket(universe, seq, sourceName, cid, dmxInput)
|
||||||
|
parsedUniverse, parsedData, ok := ParsePacket(packet)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("failed to parse packet we just built")
|
||||||
|
}
|
||||||
|
if parsedUniverse != universe {
|
||||||
|
t.Fatalf("universe mismatch: sent %d, got %d", universe, parsedUniverse)
|
||||||
|
}
|
||||||
|
expectedLen := len(dmxInput)
|
||||||
|
if expectedLen > 512 {
|
||||||
|
expectedLen = 512
|
||||||
|
}
|
||||||
|
if !bytes.Equal(parsedData[:expectedLen], dmxInput[:expectedLen]) {
|
||||||
|
t.Fatalf("dmx data mismatch")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParsePacket(data []byte) (universe uint16, dmxData [512]byte, ok bool) {
|
||||||
|
if len(data) < 126 {
|
||||||
|
return 0, dmxData, false
|
||||||
|
}
|
||||||
|
if data[4] != 0x41 || data[5] != 0x53 || data[6] != 0x43 {
|
||||||
|
return 0, dmxData, false
|
||||||
|
}
|
||||||
|
rootVector := uint32(data[18])<<24 | uint32(data[19])<<16 | uint32(data[20])<<8 | uint32(data[21])
|
||||||
|
if rootVector != VectorRootE131Data {
|
||||||
|
return 0, dmxData, false
|
||||||
|
}
|
||||||
|
framingVector := uint32(data[40])<<24 | uint32(data[41])<<16 | uint32(data[42])<<8 | uint32(data[43])
|
||||||
|
if framingVector != VectorE131DataPacket {
|
||||||
|
return 0, dmxData, false
|
||||||
|
}
|
||||||
|
universe = uint16(data[113])<<8 | uint16(data[114])
|
||||||
|
if data[117] != VectorDMPSetProperty {
|
||||||
|
return 0, dmxData, false
|
||||||
|
}
|
||||||
|
propCount := uint16(data[123])<<8 | uint16(data[124])
|
||||||
|
if propCount < 1 {
|
||||||
|
return 0, dmxData, false
|
||||||
|
}
|
||||||
|
dmxLen := int(propCount) - 1
|
||||||
|
if dmxLen > 512 {
|
||||||
|
dmxLen = 512
|
||||||
|
}
|
||||||
|
if len(data) < 126+dmxLen {
|
||||||
|
return 0, dmxData, false
|
||||||
|
}
|
||||||
|
copy(dmxData[:], data[126:126+dmxLen])
|
||||||
|
return universe, dmxData, true
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user