telegraf/plugins/inputs/sflow/decoder/directives_test.go

583 lines
16 KiB
Go

package decoder
import (
"bytes"
"encoding/binary"
"fmt"
"math"
"testing"
"github.com/influxdata/telegraf"
"github.com/stretchr/testify/require"
)
// Execute will ececute the decode directive relative to the supplied buffer
func Execute(dd Directive, buffer *bytes.Buffer) error {
dc := &DecodeContext{}
return dd.Execute(buffer, dc)
}
func Test_basicUI32NotEnoughBytes(t *testing.T) {
dd := U32()
value := uint16(1001) // not enough bytes to read a U32 out as only a U16 in
var buffer bytes.Buffer
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &value))
require.Error(t, Execute(dd, &buffer))
}
func Test_basicUI32(t *testing.T) {
dd := U32()
value := uint32(1001)
var buffer bytes.Buffer
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &value))
require.NoError(t, Execute(dd, &buffer))
require.Equal(t, 0, buffer.Len())
x, _ := dd.(*valueDirective)
require.Equal(t, &value, x.value)
}
func Test_basicBytes(t *testing.T) {
dd := Bytes(4)
value := []byte{0x01, 0x02, 0x03, 0x04}
var buffer bytes.Buffer
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &value))
require.NoError(t, Execute(dd, &buffer))
require.Equal(t, 0, buffer.Len())
x, _ := dd.(*valueDirective)
require.Equal(t, value, x.value)
}
func Test_basicSeq(t *testing.T) {
// Seq with no members compiles and executed but buffer is left untouched
dd := Seq()
value := uint32(1001)
var buffer bytes.Buffer
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &value))
originalLen := buffer.Len()
require.NoError(t, Execute(dd, &buffer))
require.Equal(t, originalLen, buffer.Len())
u := U32()
dd = Seq(
u,
)
value = uint32(1001)
buffer.Reset()
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &value))
require.NoError(t, Execute(dd, &buffer))
require.Equal(t, 0, buffer.Len())
x, _ := u.(*valueDirective)
require.Equal(t, &value, x.value)
}
func Test_basicSeqOf(t *testing.T) {
// SeqOf with no members compiles and executed but buffer is left untouched
dd := SeqOf([]Directive{})
value := uint32(1001)
var buffer bytes.Buffer
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &value))
originalLen := buffer.Len()
require.NoError(t, Execute(dd, &buffer))
require.Equal(t, originalLen, buffer.Len())
u := U32()
dd = SeqOf(
[]Directive{u},
)
value = uint32(1001)
buffer.Reset()
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &value))
require.NoError(t, Execute(dd, &buffer))
require.Equal(t, 0, buffer.Len())
x, _ := u.(*valueDirective)
require.Equal(t, &value, x.value)
}
func Test_errorInSeq(t *testing.T) {
// Seq with no members compiles and executed but buffer is left untouched
dd := Seq(U32(), ErrorDirective())
value := uint32(1001)
var buffer bytes.Buffer
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &value))
require.Error(t, Execute(dd, &buffer))
}
func Test_basicU32Switch(t *testing.T) {
c1 := U32()
c2 := U32()
dd := U32().Switch(
Case(uint32(1), c1),
Case(uint32(2), c2),
)
value1 := uint32(3)
var buffer bytes.Buffer
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &value1))
value2 := uint32(4)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &value2))
require.Error(t, Execute(dd, &buffer)) // should error as no path
value1 = uint32(1)
buffer.Reset()
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &value1))
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &value2))
require.NoError(t, Execute(dd, &buffer))
x, _ := c1.(*valueDirective)
y, _ := c2.(*valueDirective)
value0 := uint32(0)
require.Equal(t, &value2, x.value)
require.Equal(t, &value0, y.value)
// bad path shoudl raise error
// path 1 should be able to fina value in c1 and not in c2
// then other way around
}
func Test_basicBinSwitch(t *testing.T) {
c1 := U32()
c2 := U32()
dd := Bytes(1).Switch(
Case(byte(1), c1),
Case(byte(2), c2),
)
value1 := byte(3)
var buffer bytes.Buffer
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &value1))
value2 := uint32(4)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &value2))
require.Error(t, Execute(dd, &buffer)) // should error as no path
value1 = byte(1)
buffer.Reset()
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &value1))
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &value2))
require.NoError(t, Execute(dd, &buffer))
x, _ := c1.(*valueDirective)
y, _ := c2.(*valueDirective)
value0 := uint32(0)
require.Equal(t, &value2, x.value)
require.Equal(t, &value0, y.value)
// bad path shoudl raise error
// path 1 should be able to fina value in c1 and not in c2
// then other way around
}
func Test_basicIter(t *testing.T) {
innerDD := U32()
dd := U32().Iter(math.MaxInt32, innerDD)
var buffer bytes.Buffer
iterations := uint32(2)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &iterations))
it1Val := uint32(3)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &it1Val))
it2Val := uint32(4)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &it2Val))
require.NoError(t, Execute(dd, &buffer))
x, _ := dd.(*valueDirective)
require.Equal(t, &iterations, x.value)
y, _ := innerDD.(*valueDirective)
// we can't test it1Val as it gets overwritten!
require.Equal(t, &it2Val, y.value)
}
func Test_IterLimit(t *testing.T) {
innerDD := U32()
dd := U32().Iter(1, innerDD) // limit set at 1
var buffer bytes.Buffer
iterations := uint32(2)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &iterations))
it1Val := uint32(3)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &it1Val))
it2Val := uint32(4)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &it2Val))
require.Error(t, Execute(dd, &buffer))
}
func Test_errorWithinIter(t *testing.T) {
dd := U32().Iter(math.MaxInt32, ErrorDirective())
var buffer bytes.Buffer
iterations := uint32(1)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &iterations))
require.Error(t, Execute(dd, &buffer))
}
func Test_errorWithinIter2(t *testing.T) {
dd := U32().Iter(math.MaxInt32, U32().Do(ErrorOp(false)))
var buffer bytes.Buffer
iterations := uint32(1)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &iterations))
innerValue := uint32(1)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &innerValue))
require.Error(t, Execute(dd, &buffer))
}
func Test_errorWithinIter3(t *testing.T) {
defer expectPanic(t, "Test_cantIterBytes")
U32().Iter(math.MaxInt32, U32().Do(ErrorOp(true)))
}
func Test_alreadyEncapsulated(t *testing.T) {
defer expectPanic(t, "Test_cantIterBytes")
u := U32()
inner := U32()
u.Encapsulated(math.MaxInt32, inner)
u.Encapsulated(math.MaxInt32, inner)
}
func Test_alreadyDoAssigned(t *testing.T) {
defer expectPanic(t, "Test_cantIterBytes")
u := U32()
u.Do(AsF("foo"))
inner := U32()
u.Encapsulated(math.MaxInt32, inner)
}
func Test_cantIterBytes(t *testing.T) {
defer expectPanic(t, "Test_cantIterBytes")
_ = Bytes(1).Iter(math.MaxInt32, U32())
}
// then open metric
func Test_OpenMetric(t *testing.T) {
innerDD := U32()
dd := U32().Iter(math.MaxInt32, Seq(
OpenMetric(""),
innerDD,
CloseMetric(),
))
var buffer bytes.Buffer
iterations := uint32(2)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &iterations))
it1Val := uint32(3)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &it1Val))
it2Val := uint32(3)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &it2Val))
dc := NewDecodeContext()
require.NoError(t, dc.Decode(dd, &buffer))
require.Equal(t, 2, len(dc.GetMetrics()))
}
func Test_AsF(t *testing.T) {
innerDD := U32().Do(AsF("foo"))
dd := U32().Iter(math.MaxInt32, Seq(
OpenMetric(""),
innerDD,
CloseMetric(),
))
var buffer bytes.Buffer
iterations := uint32(2)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &iterations))
it1Val := uint32(3)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &it1Val))
it2Val := uint32(3)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &it2Val))
dc := NewDecodeContext()
require.NoError(t, dc.Decode(dd, &buffer))
require.Equal(t, 2, len(dc.GetMetrics()))
m := dc.GetMetrics()
require.Equal(t, uint64(it1Val), getField(m[0], "foo"))
require.Equal(t, uint64(it2Val), getField(m[1], "foo"))
}
func Test_AsT(t *testing.T) {
innerDD := U32().Do(AsT("foo"))
dd := U32().Iter(math.MaxInt32, Seq(
OpenMetric(""),
innerDD,
CloseMetric(),
))
var buffer bytes.Buffer
iterations := uint32(2)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &iterations))
it1Val := uint32(3)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &it1Val))
it2Val := uint32(3)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &it2Val))
dc := NewDecodeContext()
require.NoError(t, dc.Decode(dd, &buffer))
require.Equal(t, 2, len(dc.GetMetrics()))
m := dc.GetMetrics()
require.Equal(t, fmt.Sprintf("%d", it1Val), getTag(m[0], "foo"))
require.Equal(t, fmt.Sprintf("%d", it2Val), getTag(m[1], "foo"))
}
func getField(m telegraf.Metric, name string) interface{} {
v, _ := m.GetField(name)
return v
}
func getTag(m telegraf.Metric, name string) string {
v, _ := m.GetTag(name)
return v
}
func Test_preMetricNesting(t *testing.T) {
innerDD := U32().Do(AsF("foo"))
dd := Seq(
U32().Do(AsF("bar")),
U32().Do(AsT("baz")),
U32().Iter(math.MaxInt32,
Seq(
OpenMetric(""),
innerDD,
CloseMetric(),
),
),
)
var buffer bytes.Buffer
barVal := uint32(55)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &barVal))
bazVal := uint32(56)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &bazVal))
iterations := uint32(2)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &iterations))
it1Val := uint32(3)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &it1Val))
it2Val := uint32(3)
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &it2Val))
dc := NewDecodeContext()
require.NoError(t, dc.Decode(dd, &buffer))
require.Equal(t, 2, len(dc.GetMetrics()))
m := dc.GetMetrics()
require.Equal(t, uint64(barVal), getField(m[0], "bar"))
require.Equal(t, fmt.Sprintf("%d", bazVal), getTag(m[0], "baz"))
require.Equal(t, uint64(it1Val), getField(m[0], "foo"))
require.Equal(t, uint64(barVal), getField(m[1], "bar"))
require.Equal(t, fmt.Sprintf("%d", bazVal), getTag(m[1], "baz"))
require.Equal(t, uint64(it2Val), getField(m[1], "foo"))
}
func Test_BasicEncapsulated(t *testing.T) {
encap1Value := uint32(2)
encap2Value := uint32(3)
var encapBuffer bytes.Buffer
require.NoError(t, binary.Write(&encapBuffer, binary.BigEndian, &encap1Value))
require.NoError(t, binary.Write(&encapBuffer, binary.BigEndian, &encap2Value))
encapSize := uint32(encapBuffer.Len())
envelopeValue := uint32(4)
var envelopeBuffer bytes.Buffer
require.NoError(t, binary.Write(&envelopeBuffer, binary.BigEndian, &encapSize))
l, e := envelopeBuffer.Write(encapBuffer.Bytes())
require.NoError(t, e)
require.Equal(t, encapSize, uint32(l))
require.NoError(t, binary.Write(&envelopeBuffer, binary.BigEndian, &envelopeValue))
innerDD := U32()
envelopeDD := U32() // the buffer contains another U32 but the encpaultation will ignore it
dd := Seq(
U32().Encapsulated(math.MaxInt32, innerDD),
envelopeDD,
)
require.NoError(t, Execute(dd, &envelopeBuffer))
require.Equal(t, 0, envelopeBuffer.Len())
x, _ := envelopeDD.(*valueDirective)
require.Equal(t, &envelopeValue, x.value)
y, _ := innerDD.(*valueDirective)
require.Equal(t, &encap1Value, y.value)
}
func Test_EncapsulationLimit(t *testing.T) {
encap1Value := uint32(2)
encap2Value := uint32(3)
var encapBuffer bytes.Buffer
require.NoError(t, binary.Write(&encapBuffer, binary.BigEndian, &encap1Value))
require.NoError(t, binary.Write(&encapBuffer, binary.BigEndian, &encap2Value))
encapSize := uint32(encapBuffer.Len())
envelopeValue := uint32(4)
var envelopeBuffer bytes.Buffer
require.NoError(t, binary.Write(&envelopeBuffer, binary.BigEndian, &encapSize))
l, e := envelopeBuffer.Write(encapBuffer.Bytes())
require.NoError(t, e)
require.Equal(t, encapSize, uint32(l))
require.NoError(t, binary.Write(&envelopeBuffer, binary.BigEndian, &envelopeValue))
innerDD := U32()
envelopeDD := U32()
dd := Seq(
U32().Encapsulated(4, innerDD), // 4 bytes, not 8 bytes or higher as max
envelopeDD,
)
require.Error(t, Execute(dd, &envelopeBuffer))
}
func Test_cantEncapulatedBytes(t *testing.T) {
defer expectPanic(t, "cantEncapulatedBytes")
_ = Bytes(1).Encapsulated(math.MaxInt32, U32())
}
func Test_BasicRef(t *testing.T) {
var x interface{}
dd1 := U32().Ref(&x)
dd2 := Ref(x)
dd := Seq(
dd1,
dd2,
)
y, ok := dd2.(*valueDirective)
require.True(t, ok)
require.Equal(t, y.reference, x)
value := uint32(1001)
var buffer bytes.Buffer
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &value))
require.NoError(t, Execute(dd, &buffer))
y, _ = dd1.(*valueDirective)
require.Equal(t, &value, y.value)
y, _ = dd2.(*valueDirective)
require.Equal(t, &value, y.value)
}
func Test_RefReassignError(t *testing.T) {
defer expectPanic(t, "iter iter")
var x interface{}
U32().Ref(&x)
U32().Ref(&x)
}
func Test_ToU32(t *testing.T) {
u := U32().Do(U32ToU32(func(in uint32) uint32 { return in >> 2 }).AsF("x"))
dd := Seq(OpenMetric(""), u, CloseMetric())
value := uint32(1001)
var buffer bytes.Buffer
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &value))
dc := NewDecodeContext()
require.NoError(t, dc.Decode(dd, &buffer))
// require original value decoded
x, _ := u.(*valueDirective)
require.Equal(t, &value, x.value)
// require field ejected
require.Equal(t, 1, len(dc.GetMetrics()))
m := dc.GetMetrics()
require.Equal(t, uint64(value>>2), getField(m[0], "x"))
}
func expectPanic(t *testing.T, msg string) {
if r := recover(); r == nil {
t.Errorf(msg)
}
}
func Test_U32BlankCanvasIter(t *testing.T) {
u := U32().Iter(math.MaxInt32, U32())
func() {
defer expectPanic(t, "iter iter")
u.Iter(math.MaxInt32, U32())
}()
func() {
defer expectPanic(t, "iter switch")
u.Switch(Case(uint32(0), U32()))
}()
func() {
defer expectPanic(t, "iter encap")
u.Encapsulated(math.MaxInt32, U32())
}()
func() {
defer expectPanic(t, "iter do")
u.Do(AsF("foo"))
}()
}
func Test_U32BlankCanvasSwitch(t *testing.T) {
u := U32().Switch(Case(uint32(0), U32()))
func() {
defer expectPanic(t, "switch iter")
u.Iter(math.MaxInt32, U32())
}()
func() {
defer expectPanic(t, "switch switch")
u.Switch(Case(uint32(0), U32()))
}()
func() {
defer expectPanic(t, "switch encap")
u.Encapsulated(math.MaxInt32, U32())
}()
func() {
defer expectPanic(t, "switch do")
u.Do(AsF("foo"))
}()
}
func Test_U32BasicSwitch(t *testing.T) {
s := U32().Switch(Case(uint32(0), nil))
value := uint32(0)
var buffer bytes.Buffer
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &value))
dc := NewDecodeContext()
require.NoError(t, dc.Decode(s, &buffer))
}
func Test_U32BasicSwitchDefault(t *testing.T) {
s := U32().Switch(Case(uint32(0), nil), DefaultCase(nil))
value := uint32(2)
var buffer bytes.Buffer
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &value))
dc := NewDecodeContext()
require.NoError(t, dc.Decode(s, &buffer))
}
func Test_U16(t *testing.T) {
dd := U16()
value := uint16(1001)
var buffer bytes.Buffer
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &value))
require.NoError(t, Execute(dd, &buffer))
require.Equal(t, 0, buffer.Len())
x, _ := dd.(*valueDirective)
require.Equal(t, &value, x.value)
}
func Test_U16Value(t *testing.T) {
myU16 := uint16(5)
dd := U16Value(&myU16)
var buffer bytes.Buffer
require.NoError(t, Execute(dd, &buffer))
x, _ := dd.(*valueDirective)
require.Equal(t, &myU16, x.value)
}
func Test_Bytes(t *testing.T) {
dd := Bytes(4)
value := []byte{0x01, 0x02, 0x03, 0x04}
var buffer bytes.Buffer
require.NoError(t, binary.Write(&buffer, binary.BigEndian, &value))
require.NoError(t, Execute(dd, &buffer))
require.Equal(t, 0, buffer.Len())
x, _ := dd.(*valueDirective)
require.Equal(t, value, x.value)
}
func Test_nilRefAnfWongTypeRef(t *testing.T) {
func() {
defer expectPanic(t, "Test_nilRef")
Ref(nil)
}()
func() {
defer expectPanic(t, "Test_nilRef")
f := new(uint32)
Ref(f)
}()
}