// -*- Mode: Go; indent-tabs-mode: t -*- // // Copyright (C) 2019-2023 IOTech Ltd // // SPDX-License-Identifier: Apache-2.0 package transformer import ( "math" "testing" "github.com/edgexfoundry/go-mod-core-contracts/v4/common" "github.com/edgexfoundry/go-mod-core-contracts/v4/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/edgexfoundry/device-sdk-go/v4/pkg/models" ) func Test_transformReadBase(t *testing.T) { tests := []struct { name string value interface{} base float64 expected interface{} expectedErr bool }{ {"valid - uint8 base transformation", uint8(2), 10, uint8(100), false}, {"invalid - uint8 base transformation overflow", uint8(10), 10, nil, true}, {"valid - uint16 base transformation", uint16(2), 100, uint16(10000), false}, {"invalid - uint16 base transformation overflow", uint16(100), 10, nil, true}, {"valid - uint32 base transformation", uint32(2), 10000, uint32(100000000), false}, {"invalid - uint32 base transformation overflow", uint32(10000), 10, nil, true}, {"valid - uint64 base transformation", uint64(2), 100000, uint64(10000000000), false}, {"invalid - uint64 base transformation overflow", uint64(100000000), 10, nil, true}, {"valid - int8 base transformation", int8(2), 10, int8(100), false}, {"invalid - int8 base transformation overflow", int8(10), 10, nil, true}, {"valid - int16 base transformation", int16(2), 100, int16(10000), false}, {"invalid - int16 base transformation overflow", int16(100), 10, nil, true}, {"valid - int32 base transformation", int32(2), 10000, int32(100000000), false}, {"invalid - int32 base transformation overflow", int32(10000), 10, nil, true}, {"valid - int64 base transformation", int64(2), 100000, int64(10000000000), false}, {"invalid - int64 base transformation overflow", int64(100000000), 10, nil, true}, {"valid - float32 base transformation", float32(1.1), 2, float32(2.143547), false}, {"invalid - float32 base transformation overflow", math.MaxFloat32, 2, nil, true}, {"valid - float64 base transformation", float64(1.1), 2, float64(2.1435469250725863), false}, {"invalid - float64 base transformation overflow", math.MaxFloat64, 2, nil, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { res, err := transformBase(tt.value, tt.base, true) if !tt.expectedErr { require.NoError(t, err) assert.Equal(t, tt.expected, res) } else { require.Error(t, err) assert.Equal(t, errors.Kind(err), errors.KindOverflowError, "expect Overflow ErrKind") } }) } } func Test_transformReadScale(t *testing.T) { tests := []struct { name string value interface{} scale float64 expected interface{} expectedErr bool }{ {"valid - uint8 scale transformation", uint8(math.MaxUint8 / 5), 5, uint8(math.MaxUint8), false}, {"invalid - uint8 scale transformation overflow", uint8(math.MaxUint8 / 5), 6, nil, true}, {"valid - uint16 scale transformation", uint16(math.MaxUint16 / 5), 5, uint16(math.MaxUint16), false}, {"invalid - uint16 scale transformation overflow", uint16(math.MaxUint16 / 5), 6, nil, true}, {"valid - uint32 scale transformation", uint32(math.MaxUint32 / 5), 5, uint32(math.MaxUint32), false}, {"invalid - uint32 scale transformation overflow", uint32(math.MaxUint32 / 5), 6, nil, true}, {"valid - uint64 scale transformation", uint64(math.MaxUint64 / 5), 5, uint64(math.MaxUint64), false}, {"valid - int8 scale transformation", int8(10), 10, int8(100), false}, {"invalid - int8 scale transformation overflow", int8(10), 30, nil, true}, {"valid - int16 scale transformation", int16(10000), 3, int16(30000), false}, {"invalid - int16 scale transformation overflow", int16(10000), 4, nil, true}, {"valid - int32 scale transformation", int32(1000000000), 2, int32(2000000000), false}, {"invalid - int32 scale transformation overflow", int32(1000000000), 3, nil, true}, {"valid - int64 scale transformation", int64(1000000000), 1000000000, int64(1000000000000000000), false}, {"valid - float32 scale transformation", float32(12.1), 10, float32(121), false}, {"invalid - float32 scale transformation overflow", float32(math.MaxFloat32 / 2), 3, nil, true}, {"valid - float64 scale transformation", float64(111111111.1), 2, float64(222222222.2), false}, {"invalid - float64 scale transformation overflow", float64(math.MaxFloat64), 2, nil, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { res, err := transformScale(tt.value, tt.scale, true) if !tt.expectedErr { require.NoError(t, err) assert.Equal(t, tt.expected, res) } else { require.Error(t, err) assert.Equal(t, errors.Kind(err), errors.KindOverflowError, "expect Overflow ErrKind") } }) } } func Test_transformReadOffset(t *testing.T) { tests := []struct { name string value interface{} offset float64 expected interface{} expectedErr bool }{ {"valid - uint8 offset transformation", uint8(math.MaxUint8 - 1), 1, uint8(math.MaxUint8), false}, {"invalid - uint8 offset transformation overflow", uint8(math.MaxUint8), 1, nil, true}, {"valid - uint16 offset transformation", uint16(math.MaxUint16 - 1), 1, uint16(math.MaxUint16), false}, {"invalid - uint16 offset transformation overflow", uint16(math.MaxUint16), 1, nil, true}, {"valid - uint32 offset transformation", uint32(math.MaxUint32 - 1), 1, uint32(math.MaxUint32), false}, {"invalid - uint32 offset transformation overflow", uint32(math.MaxUint32), 1, nil, true}, {"valid - uint64 offset transformation", uint64(math.MaxUint64) - uint64(1), 1, uint64(math.MaxUint64), false}, {"valid - int8 offset transformation", int8(math.MaxInt8 - 1), 1, int8(math.MaxInt8), false}, {"invalid - int8 offset transformation overflow", int8(math.MaxInt8), 1, nil, true}, {"valid - int16 offset transformation", int16(math.MaxInt16 - 1), 1, int16(math.MaxInt16), false}, {"invalid - int16 offset transformation overflow", int16(math.MaxInt16), 1, nil, true}, {"valid - int32 offset transformation", int32(math.MaxInt32 - 1), 1, int32(math.MaxInt32), false}, {"invalid - int32 offset transformation overflow", int32(math.MaxInt32), 1, nil, true}, {"valid - int64 offset transformation", int64(math.MaxInt64 - 1), 1, int64(math.MaxInt64), false}, {"valid - float32 offset transformation", float32(1.1), 1, float32(2.1), false}, {"invalid - float32 offset transformation overflow", float32(math.MaxFloat32), math.MaxFloat32, nil, true}, {"valid - float64 offset transformation", float64(1.1), 1, float64(2.1), false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { res, err := transformOffset(tt.value, tt.offset, true) if !tt.expectedErr { require.NoError(t, err) assert.Equal(t, tt.expected, res) } else { require.Error(t, err) assert.Equal(t, errors.Kind(err), errors.KindOverflowError, "expect Overflow ErrKind") } }) } } func Test_transformReadMask(t *testing.T) { tests := []struct { name string value interface{} mask uint64 expected interface{} expectedErr bool }{ {"valid - uint8 mask transformation", uint8(math.MaxUint8), 15, uint8(15), false}, {"valid - uint16 mask transformation", uint16(math.MaxUint16), 256, uint16(256), false}, {"valid - uint32 mask transformation", uint32(math.MaxUint32), 256, uint32(256), false}, {"valid - uint64 mask transformation", uint64(math.MaxUint64), 256, uint64(256), false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { res, err := transformReadMask(tt.value, tt.mask) if !tt.expectedErr { require.NoError(t, err) assert.Equal(t, tt.expected, res) } else { require.Error(t, err) } }) } } func Test_transformReadShift(t *testing.T) { tests := []struct { name string value interface{} shift int64 expected interface{} expectedErr bool }{ {"valid - uint8 shift transformation with positive shift value", uint8(0b1), 4, uint8(0b10000), false}, {"valid - uint8 shift transformation with negative shift value", uint8(0b11111111), -4, uint8(0b00001111), false}, {"valid - uint16 shift transformation with positive shift value", uint16(0b1), 8, uint16(0b100000000), false}, {"valid - uint16 shift transformation with negative shift value", uint16(0b1111111100000000), -8, uint16(0b0000000011111111), false}, {"valid - uint32 shift transformation with positive shift value", uint32(0b1), 16, uint32(0b10000000000000000), false}, {"valid - uint32 shift transformation with negative shift value", uint32(0b11111111111111110000000000000000), -16, uint32(0b00000000000000001111111111111111), false}, {"valid - uint64 shift transformation with positive shift value", uint64(0b1), 32, uint64(0b100000000000000000000000000000000), false}, {"valid - uint64 shift transformation with negative shift value", uint64(0b1111111111111111111111111111111100000000000000000000000000000000), -32, uint64(0b0000000000000000000000000000000011111111111111111111111111111111), false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { res, err := transformReadShift(tt.value, tt.shift) if !tt.expectedErr { require.NoError(t, err) assert.Equal(t, tt.expected, res) } else { require.Error(t, err) } }) } } func Test_commandValueForTransform(t *testing.T) { u8, err := models.NewCommandValue("test-resource", common.ValueTypeUint8, uint8(0)) require.NoError(t, err) u16, err := models.NewCommandValue("test-resource", common.ValueTypeUint16, uint16(0)) require.NoError(t, err) u32, err := models.NewCommandValue("test-resource", common.ValueTypeUint32, uint32(0)) require.NoError(t, err) u64, err := models.NewCommandValue("test-resource", common.ValueTypeUint64, uint64(0)) require.NoError(t, err) i8, err := models.NewCommandValue("test-resource", common.ValueTypeInt8, int8(0)) require.NoError(t, err) i16, err := models.NewCommandValue("test-resource", common.ValueTypeInt16, int16(0)) require.NoError(t, err) i32, err := models.NewCommandValue("test-resource", common.ValueTypeInt32, int32(0)) require.NoError(t, err) i64, err := models.NewCommandValue("test-resource", common.ValueTypeInt64, int64(0)) require.NoError(t, err) f32, err := models.NewCommandValue("test-resource", common.ValueTypeFloat32, float32(0)) require.NoError(t, err) f64, err := models.NewCommandValue("test-resource", common.ValueTypeFloat64, float64(0)) require.NoError(t, err) s, err := models.NewCommandValue("test-resource", common.ValueTypeString, "invalid") require.NoError(t, err) tests := []struct { name string cv *models.CommandValue expectedValue interface{} expectedErr bool }{ {"valid - uint8 CommandValue", u8, uint8(0), false}, {"valid - uint16 CommandValue", u16, uint16(0), false}, {"valid - uint32 CommandValue", u32, uint32(0), false}, {"valid - uint64 CommandValue", u64, uint64(0), false}, {"valid - int8 CommandValue", i8, int8(0), false}, {"valid - int16 CommandValue", i16, int16(0), false}, {"valid - int32 CommandValue", i32, int32(0), false}, {"valid - int64 CommandValue", i64, int64(0), false}, {"valid - float32 CommandValue", f32, float32(0), false}, {"valid - float64 CommandValue", f64, float64(0), false}, {"invalid - unsupported type for transformation", s, nil, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := commandValueForTransform(tt.cv) if !tt.expectedErr { assert.NoError(t, err) } else { assert.NotNil(t, err) } }) } } func Test_mapCommandValue(t *testing.T) { numericValue, err := models.NewCommandValue("test-resource", common.ValueTypeFloat32, float32(123.456)) require.NoError(t, err) stringValue, err := models.NewCommandValue("test-resource", common.ValueTypeString, "key") require.NoError(t, err) arrayValue, err := models.NewCommandValue("test-resource", common.ValueTypeInt8Array, []int8{1, 2, 3}) require.NoError(t, err) invalid, err := models.NewCommandValue("test-resource", common.ValueTypeString, "invalid") require.NoError(t, err) mappings := map[string]string{ "123.456": "value", "key": "value", "[1 2 3]": "value", } tests := []struct { name string cv *models.CommandValue success bool }{ {"valid - CommandValue with numeric mapping", numericValue, true}, {"valid - CommandValue with string mapping", stringValue, true}, {"valid - CommandValue with array mapping", arrayValue, true}, {"invalid - mapping not found", invalid, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { res, ok := mapCommandValue(tt.cv, mappings) require.Equal(t, ok, tt.success) if ok { assert.Equal(t, res.Value, "value") } }) } }