diff --git a/pdata/pcommon/common_test.go b/pdata/pcommon/common_test.go deleted file mode 100644 index fb76847f440..00000000000 --- a/pdata/pcommon/common_test.go +++ /dev/null @@ -1,1132 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package pcommon - -import ( - "encoding/base64" - "math" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "go.opentelemetry.io/collector/pdata/internal" - otlpcommon "go.opentelemetry.io/collector/pdata/internal/data/protogen/common/v1" -) - -func TestValue(t *testing.T) { - v := NewValueStr("abc") - assert.EqualValues(t, ValueTypeStr, v.Type()) - assert.EqualValues(t, "abc", v.Str()) - - v = NewValueInt(123) - assert.EqualValues(t, ValueTypeInt, v.Type()) - assert.EqualValues(t, 123, v.Int()) - - v = NewValueDouble(3.4) - assert.EqualValues(t, ValueTypeDouble, v.Type()) - assert.EqualValues(t, 3.4, v.Double()) - - v = NewValueBool(true) - assert.EqualValues(t, ValueTypeBool, v.Type()) - assert.True(t, v.Bool()) - - v = NewValueBytes() - assert.EqualValues(t, ValueTypeBytes, v.Type()) - - v = NewValueEmpty() - assert.EqualValues(t, ValueTypeEmpty, v.Type()) - - v = NewValueMap() - assert.EqualValues(t, ValueTypeMap, v.Type()) - - v = NewValueSlice() - assert.EqualValues(t, ValueTypeSlice, v.Type()) -} - -func TestValueType(t *testing.T) { - assert.EqualValues(t, "Empty", ValueTypeEmpty.String()) - assert.EqualValues(t, "Str", ValueTypeStr.String()) - assert.EqualValues(t, "Bool", ValueTypeBool.String()) - assert.EqualValues(t, "Int", ValueTypeInt.String()) - assert.EqualValues(t, "Double", ValueTypeDouble.String()) - assert.EqualValues(t, "Map", ValueTypeMap.String()) - assert.EqualValues(t, "Slice", ValueTypeSlice.String()) - assert.EqualValues(t, "Bytes", ValueTypeBytes.String()) - assert.EqualValues(t, "", ValueType(100).String()) -} - -func TestValueMap(t *testing.T) { - m1 := NewValueMap() - assert.Equal(t, ValueTypeMap, m1.Type()) - assert.Equal(t, NewMap(), m1.Map()) - assert.Equal(t, 0, m1.Map().Len()) - - m1.Map().PutDouble("double_key", 123) - assert.Equal(t, 1, m1.Map().Len()) - got, exists := m1.Map().Get("double_key") - assert.True(t, exists) - assert.Equal(t, NewValueDouble(123), got) - - // Create a second map. - m2 := m1.Map().PutEmptyMap("child_map") - assert.Equal(t, 0, m2.Len()) - - // Modify the source map that was inserted. - m2.PutStr("key_in_child", "somestr") - assert.Equal(t, 1, m2.Len()) - got, exists = m2.Get("key_in_child") - assert.True(t, exists) - assert.Equal(t, NewValueStr("somestr"), got) - - // Insert the second map as a child. This should perform a deep copy. - assert.EqualValues(t, 2, m1.Map().Len()) - got, exists = m1.Map().Get("double_key") - assert.True(t, exists) - assert.Equal(t, NewValueDouble(123), got) - got, exists = m1.Map().Get("child_map") - assert.True(t, exists) - assert.Equal(t, m2, got.Map()) - - // Modify the source map m2 that was inserted into m1. - m2.PutStr("key_in_child", "somestr2") - assert.EqualValues(t, 1, m2.Len()) - got, exists = m2.Get("key_in_child") - assert.True(t, exists) - assert.Equal(t, NewValueStr("somestr2"), got) - - // The child map inside m1 should be modified. - childMap, childMapExists := m1.Map().Get("child_map") - require.True(t, childMapExists) - got, exists = childMap.Map().Get("key_in_child") - require.True(t, exists) - assert.Equal(t, NewValueStr("somestr2"), got) - - // Now modify the inserted map (not the source) - childMap.Map().PutStr("key_in_child", "somestr3") - assert.EqualValues(t, 1, childMap.Map().Len()) - got, exists = childMap.Map().Get("key_in_child") - require.True(t, exists) - assert.Equal(t, NewValueStr("somestr3"), got) - - // The source child map should be modified. - got, exists = m2.Get("key_in_child") - require.True(t, exists) - assert.Equal(t, NewValueStr("somestr3"), got) - - removed := m1.Map().Remove("double_key") - assert.True(t, removed) - assert.EqualValues(t, 1, m1.Map().Len()) - _, exists = m1.Map().Get("double_key") - assert.False(t, exists) - - removed = m1.Map().Remove("child_map") - assert.True(t, removed) - assert.EqualValues(t, 0, m1.Map().Len()) - _, exists = m1.Map().Get("child_map") - assert.False(t, exists) - - // Test nil KvlistValue case for Map() func. - orig := &otlpcommon.AnyValue{Value: &otlpcommon.AnyValue_KvlistValue{KvlistValue: nil}} - m1 = newValue(orig) - assert.EqualValues(t, Map{}, m1.Map()) -} - -func TestNilOrigSetValue(t *testing.T) { - av := NewValueEmpty() - av.SetStr("abc") - assert.EqualValues(t, "abc", av.Str()) - - av = NewValueEmpty() - av.SetInt(123) - assert.EqualValues(t, 123, av.Int()) - - av = NewValueEmpty() - av.SetBool(true) - assert.True(t, av.Bool()) - - av = NewValueEmpty() - av.SetDouble(1.23) - assert.EqualValues(t, 1.23, av.Double()) - - av = NewValueEmpty() - av.SetEmptyBytes().FromRaw([]byte{1, 2, 3}) - assert.Equal(t, []byte{1, 2, 3}, av.Bytes().AsRaw()) - - av = NewValueEmpty() - assert.NoError(t, av.SetEmptyMap().FromRaw(map[string]any{"k": "v"})) - assert.Equal(t, map[string]any{"k": "v"}, av.Map().AsRaw()) - - av = NewValueEmpty() - assert.NoError(t, av.SetEmptySlice().FromRaw([]any{int64(1), "val"})) - assert.Equal(t, []any{int64(1), "val"}, av.Slice().AsRaw()) -} - -func TestMap(t *testing.T) { - assert.EqualValues(t, 0, NewMap().Len()) - - val, exist := NewMap().Get("test_key") - assert.False(t, exist) - assert.EqualValues(t, newValue(nil), val) - - putString := NewMap() - putString.PutStr("k", "v") - assert.EqualValues(t, Map(internal.GenerateTestMap()), putString) - - putInt := NewMap() - putInt.PutInt("k", 123) - assert.EqualValues(t, generateTestIntMap(t), putInt) - - putDouble := NewMap() - putDouble.PutDouble("k", 12.3) - assert.EqualValues(t, generateTestDoubleMap(t), putDouble) - - putBool := NewMap() - putBool.PutBool("k", true) - assert.EqualValues(t, generateTestBoolMap(t), putBool) - - putBytes := NewMap() - putBytes.PutEmptyBytes("k").FromRaw([]byte{1, 2, 3, 4, 5}) - assert.EqualValues(t, generateTestBytesMap(t), putBytes) - - putMap := NewMap() - putMap.PutEmptyMap("k") - assert.EqualValues(t, generateTestEmptyMap(t), putMap) - - putSlice := NewMap() - putSlice.PutEmptySlice("k") - assert.EqualValues(t, generateTestEmptySlice(t), putSlice) - - removeMap := NewMap() - assert.False(t, removeMap.Remove("k")) - assert.EqualValues(t, NewMap(), removeMap) - - // Test Sort - sortMap := NewMap() - sortMap.Sort() - assert.EqualValues(t, NewMap(), sortMap) -} - -func TestMapPutEmpty(t *testing.T) { - m := NewMap() - v := m.PutEmpty("k1") - assert.EqualValues(t, map[string]any{ - "k1": nil, - }, m.AsRaw()) - - v.SetBool(true) - assert.EqualValues(t, map[string]any{ - "k1": true, - }, m.AsRaw()) - - v = m.PutEmpty("k1") - v.SetInt(1) - v2, ok := m.Get("k1") - assert.True(t, ok) - assert.Equal(t, int64(1), v2.Int()) -} - -func TestMapPutEmptyMap(t *testing.T) { - m := NewMap() - childMap := m.PutEmptyMap("k1") - assert.EqualValues(t, map[string]any{ - "k1": map[string]any{}, - }, m.AsRaw()) - childMap.PutEmptySlice("k2").AppendEmpty().SetStr("val") - assert.EqualValues(t, map[string]any{ - "k1": map[string]any{ - "k2": []any{"val"}, - }, - }, m.AsRaw()) - - childMap.PutEmptyMap("k2").PutInt("k3", 1) - assert.EqualValues(t, map[string]any{ - "k1": map[string]any{ - "k2": map[string]any{"k3": int64(1)}, - }, - }, m.AsRaw()) -} - -func TestMapPutEmptySlice(t *testing.T) { - m := NewMap() - childSlice := m.PutEmptySlice("k") - assert.EqualValues(t, map[string]any{ - "k": []any{}, - }, m.AsRaw()) - childSlice.AppendEmpty().SetDouble(1.1) - assert.EqualValues(t, map[string]any{ - "k": []any{1.1}, - }, m.AsRaw()) - - m.PutEmptySlice("k") - assert.EqualValues(t, map[string]any{ - "k": []any{}, - }, m.AsRaw()) - childSliceVal, ok := m.Get("k") - assert.True(t, ok) - childSliceVal.Slice().AppendEmpty().SetEmptySlice().AppendEmpty().SetStr("val") - assert.EqualValues(t, map[string]any{ - "k": []any{[]any{"val"}}, - }, m.AsRaw()) -} - -func TestMapPutEmptyBytes(t *testing.T) { - m := NewMap() - b := m.PutEmptyBytes("k") - bv, ok := m.Get("k") - assert.True(t, ok) - assert.Equal(t, []byte(nil), bv.Bytes().AsRaw()) - b.FromRaw([]byte{1, 2, 3}) - bv, ok = m.Get("k") - assert.True(t, ok) - assert.Equal(t, []byte{1, 2, 3}, bv.Bytes().AsRaw()) - - m.PutEmptyBytes("k") - bv, ok = m.Get("k") - assert.True(t, ok) - assert.Equal(t, []byte(nil), bv.Bytes().AsRaw()) - bv.Bytes().FromRaw([]byte{3, 2, 1}) - bv, ok = m.Get("k") - assert.True(t, ok) - assert.Equal(t, []byte{3, 2, 1}, bv.Bytes().AsRaw()) -} - -func TestMapWithEmpty(t *testing.T) { - origWithNil := []otlpcommon.KeyValue{ - {}, - { - Key: "test_key", - Value: otlpcommon.AnyValue{Value: &otlpcommon.AnyValue_StringValue{StringValue: "test_value"}}, - }, - { - Key: "test_key2", - Value: otlpcommon.AnyValue{Value: nil}, - }, - } - sm := newMap(&origWithNil) - val, exist := sm.Get("test_key") - assert.True(t, exist) - assert.EqualValues(t, ValueTypeStr, val.Type()) - assert.EqualValues(t, "test_value", val.Str()) - - val, exist = sm.Get("test_key2") - assert.True(t, exist) - assert.EqualValues(t, ValueTypeEmpty, val.Type()) - assert.EqualValues(t, "", val.Str()) - - sm.PutStr("other_key_string", "other_value") - val, exist = sm.Get("other_key_string") - assert.True(t, exist) - assert.EqualValues(t, ValueTypeStr, val.Type()) - assert.EqualValues(t, "other_value", val.Str()) - - sm.PutInt("other_key_int", 123) - val, exist = sm.Get("other_key_int") - assert.True(t, exist) - assert.EqualValues(t, ValueTypeInt, val.Type()) - assert.EqualValues(t, 123, val.Int()) - - sm.PutDouble("other_key_double", 1.23) - val, exist = sm.Get("other_key_double") - assert.True(t, exist) - assert.EqualValues(t, ValueTypeDouble, val.Type()) - assert.EqualValues(t, 1.23, val.Double()) - - sm.PutBool("other_key_bool", true) - val, exist = sm.Get("other_key_bool") - assert.True(t, exist) - assert.EqualValues(t, ValueTypeBool, val.Type()) - assert.True(t, val.Bool()) - - sm.PutEmptyBytes("other_key_bytes").FromRaw([]byte{7, 8, 9}) - val, exist = sm.Get("other_key_bytes") - assert.True(t, exist) - assert.EqualValues(t, ValueTypeBytes, val.Type()) - assert.EqualValues(t, []byte{7, 8, 9}, val.Bytes().AsRaw()) - - sm.PutStr("another_key_string", "another_value") - val, exist = sm.Get("another_key_string") - assert.True(t, exist) - assert.EqualValues(t, ValueTypeStr, val.Type()) - assert.EqualValues(t, "another_value", val.Str()) - - sm.PutInt("another_key_int", 456) - val, exist = sm.Get("another_key_int") - assert.True(t, exist) - assert.EqualValues(t, ValueTypeInt, val.Type()) - assert.EqualValues(t, 456, val.Int()) - - sm.PutDouble("another_key_double", 4.56) - val, exist = sm.Get("another_key_double") - assert.True(t, exist) - assert.EqualValues(t, ValueTypeDouble, val.Type()) - assert.EqualValues(t, 4.56, val.Double()) - - sm.PutBool("another_key_bool", false) - val, exist = sm.Get("another_key_bool") - assert.True(t, exist) - assert.EqualValues(t, ValueTypeBool, val.Type()) - assert.False(t, val.Bool()) - - sm.PutEmptyBytes("another_key_bytes").FromRaw([]byte{1}) - val, exist = sm.Get("another_key_bytes") - assert.True(t, exist) - assert.EqualValues(t, ValueTypeBytes, val.Type()) - assert.EqualValues(t, []byte{1}, val.Bytes().AsRaw()) - - assert.True(t, sm.Remove("other_key_string")) - assert.True(t, sm.Remove("other_key_int")) - assert.True(t, sm.Remove("other_key_double")) - assert.True(t, sm.Remove("other_key_bool")) - assert.True(t, sm.Remove("other_key_bytes")) - assert.True(t, sm.Remove("another_key_string")) - assert.True(t, sm.Remove("another_key_int")) - assert.True(t, sm.Remove("another_key_double")) - assert.True(t, sm.Remove("another_key_bool")) - assert.True(t, sm.Remove("another_key_bytes")) - - assert.False(t, sm.Remove("other_key_string")) - assert.False(t, sm.Remove("another_key_string")) - - // Test that the initial key is still there. - val, exist = sm.Get("test_key") - assert.True(t, exist) - assert.EqualValues(t, ValueTypeStr, val.Type()) - assert.EqualValues(t, "test_value", val.Str()) - - val, exist = sm.Get("test_key2") - assert.True(t, exist) - assert.EqualValues(t, ValueTypeEmpty, val.Type()) - assert.EqualValues(t, "", val.Str()) - - _, exist = sm.Get("test_key3") - assert.False(t, exist) - - // Test Sort - sm.Sort() - assert.EqualValues(t, newMap(&origWithNil), sm) -} - -func TestMapIterationNil(t *testing.T) { - NewMap().Range(func(k string, v Value) bool { - // Fail if any element is returned - t.Fail() - return true - }) -} - -func TestMap_Range(t *testing.T) { - rawMap := map[string]any{ - "k_string": "123", - "k_int": int64(123), - "k_double": float64(1.23), - "k_bool": true, - "k_empty": nil, - } - am := NewMap() - assert.NoError(t, am.FromRaw(rawMap)) - assert.Equal(t, 5, am.Len()) - - calls := 0 - am.Range(func(k string, v Value) bool { - calls++ - return false - }) - assert.Equal(t, 1, calls) - - am.Range(func(k string, v Value) bool { - assert.Equal(t, rawMap[k], v.AsRaw()) - delete(rawMap, k) - return true - }) - assert.EqualValues(t, 0, len(rawMap)) -} - -func TestMap_FromRaw(t *testing.T) { - am := NewMap() - assert.NoError(t, am.FromRaw(map[string]any{})) - assert.Equal(t, 0, am.Len()) - am.PutEmpty("k") - assert.Equal(t, 1, am.Len()) - - assert.NoError(t, am.FromRaw(nil)) - assert.Equal(t, 0, am.Len()) - am.PutEmpty("k") - assert.Equal(t, 1, am.Len()) - - assert.NoError(t, am.FromRaw(map[string]any{ - "k_string": "123", - "k_int": 123, - "k_double": 1.23, - "k_bool": true, - "k_null": nil, - "k_bytes": []byte{1, 2, 3}, - "k_slice": []any{1, 2.1, "val"}, - "k_map": map[string]any{ - "k_int": 1, - "k_string": "val", - }, - })) - assert.Equal(t, 8, am.Len()) - v, ok := am.Get("k_string") - assert.True(t, ok) - assert.Equal(t, "123", v.Str()) - v, ok = am.Get("k_int") - assert.True(t, ok) - assert.Equal(t, int64(123), v.Int()) - v, ok = am.Get("k_double") - assert.True(t, ok) - assert.Equal(t, 1.23, v.Double()) - v, ok = am.Get("k_null") - assert.True(t, ok) - assert.Equal(t, ValueTypeEmpty, v.Type()) - v, ok = am.Get("k_bytes") - assert.True(t, ok) - assert.Equal(t, []byte{1, 2, 3}, v.Bytes().AsRaw()) - v, ok = am.Get("k_slice") - assert.True(t, ok) - assert.Equal(t, []any{int64(1), 2.1, "val"}, v.Slice().AsRaw()) - v, ok = am.Get("k_map") - assert.True(t, ok) - assert.Equal(t, map[string]any{ - "k_int": int64(1), - "k_string": "val", - }, v.Map().AsRaw()) -} - -func TestValue_CopyTo(t *testing.T) { - // Test nil KvlistValue case for Map() func. - dest := NewValueEmpty() - orig := &otlpcommon.AnyValue{Value: &otlpcommon.AnyValue_KvlistValue{KvlistValue: nil}} - newValue(orig).CopyTo(dest) - assert.Nil(t, dest.getOrig().Value.(*otlpcommon.AnyValue_KvlistValue).KvlistValue) - - // Test nil ArrayValue case for Slice() func. - dest = NewValueEmpty() - orig = &otlpcommon.AnyValue{Value: &otlpcommon.AnyValue_ArrayValue{ArrayValue: nil}} - newValue(orig).CopyTo(dest) - assert.Nil(t, dest.getOrig().Value.(*otlpcommon.AnyValue_ArrayValue).ArrayValue) - - // Test copy empty value. - orig = &otlpcommon.AnyValue{} - newValue(orig).CopyTo(dest) - assert.Nil(t, dest.getOrig().Value) - - av := NewValueEmpty() - destVal := otlpcommon.AnyValue{Value: &otlpcommon.AnyValue_IntValue{}} - av.CopyTo(newValue(&destVal)) - assert.EqualValues(t, nil, destVal.Value) -} - -func TestMap_CopyTo(t *testing.T) { - dest := NewMap() - // Test CopyTo to empty - NewMap().CopyTo(dest) - assert.EqualValues(t, 0, dest.Len()) - - // Test CopyTo larger slice - Map(internal.GenerateTestMap()).CopyTo(dest) - assert.EqualValues(t, Map(internal.GenerateTestMap()), dest) - - // Test CopyTo same size slice - Map(internal.GenerateTestMap()).CopyTo(dest) - assert.EqualValues(t, Map(internal.GenerateTestMap()), dest) - - // Test CopyTo with an empty Value in the destination - (*dest.getOrig())[0].Value = otlpcommon.AnyValue{} - Map(internal.GenerateTestMap()).CopyTo(dest) - assert.EqualValues(t, Map(internal.GenerateTestMap()), dest) -} - -func TestMap_EnsureCapacity_Zero(t *testing.T) { - am := NewMap() - am.EnsureCapacity(0) - assert.Equal(t, 0, am.Len()) - assert.Equal(t, 0, cap(*am.getOrig())) -} - -func TestMap_EnsureCapacity(t *testing.T) { - am := NewMap() - am.EnsureCapacity(5) - assert.Equal(t, 0, am.Len()) - assert.Equal(t, 5, cap(*am.getOrig())) - am.EnsureCapacity(3) - assert.Equal(t, 0, am.Len()) - assert.Equal(t, 5, cap(*am.getOrig())) - am.EnsureCapacity(8) - assert.Equal(t, 0, am.Len()) - assert.Equal(t, 8, cap(*am.getOrig())) -} - -func TestMap_Clear(t *testing.T) { - am := NewMap() - assert.Nil(t, *am.getOrig()) - am.Clear() - assert.Nil(t, *am.getOrig()) - am.EnsureCapacity(5) - assert.NotNil(t, *am.getOrig()) - am.Clear() - assert.Nil(t, *am.getOrig()) -} - -func TestMap_RemoveIf(t *testing.T) { - am := NewMap() - am.PutStr("k_string", "123") - am.PutInt("k_int", int64(123)) - am.PutDouble("k_double", float64(1.23)) - am.PutBool("k_bool", true) - am.PutEmpty("k_empty") - - assert.Equal(t, 5, am.Len()) - - am.RemoveIf(func(key string, val Value) bool { - return key == "k_int" || val.Type() == ValueTypeBool - }) - assert.Equal(t, 3, am.Len()) - _, exists := am.Get("k_string") - assert.True(t, exists) - _, exists = am.Get("k_int") - assert.False(t, exists) - _, exists = am.Get("k_double") - assert.True(t, exists) - _, exists = am.Get("k_bool") - assert.False(t, exists) - _, exists = am.Get("k_empty") - assert.True(t, exists) -} - -func generateTestEmptyMap(t *testing.T) Map { - m := NewMap() - assert.NoError(t, m.FromRaw(map[string]any{"k": map[string]any(nil)})) - return m -} - -func generateTestEmptySlice(t *testing.T) Map { - m := NewMap() - assert.NoError(t, m.FromRaw(map[string]any{"k": []any(nil)})) - return m -} - -func generateTestIntMap(t *testing.T) Map { - m := NewMap() - assert.NoError(t, m.FromRaw(map[string]any{"k": 123})) - return m -} - -func generateTestDoubleMap(t *testing.T) Map { - m := NewMap() - assert.NoError(t, m.FromRaw(map[string]any{"k": 12.3})) - return m -} - -func generateTestBoolMap(t *testing.T) Map { - m := NewMap() - assert.NoError(t, m.FromRaw(map[string]any{"k": true})) - return m -} - -func generateTestBytesMap(t *testing.T) Map { - m := NewMap() - assert.NoError(t, m.FromRaw(map[string]any{"k": []byte{1, 2, 3, 4, 5}})) - return m -} - -func TestValueSlice(t *testing.T) { - a1 := NewValueSlice() - assert.EqualValues(t, ValueTypeSlice, a1.Type()) - assert.EqualValues(t, NewSlice(), a1.Slice()) - assert.EqualValues(t, 0, a1.Slice().Len()) - - a1.Slice().AppendEmpty().SetDouble(123) - assert.EqualValues(t, 1, a1.Slice().Len()) - assert.EqualValues(t, NewValueDouble(123), a1.Slice().At(0)) - // Create a second array. - a2 := NewValueSlice() - assert.EqualValues(t, 0, a2.Slice().Len()) - - a2.Slice().AppendEmpty().SetStr("somestr") - assert.EqualValues(t, 1, a2.Slice().Len()) - assert.EqualValues(t, NewValueStr("somestr"), a2.Slice().At(0)) - - // Insert the second array as a child. - a2.CopyTo(a1.Slice().AppendEmpty()) - assert.EqualValues(t, 2, a1.Slice().Len()) - assert.EqualValues(t, NewValueDouble(123), a1.Slice().At(0)) - assert.EqualValues(t, a2, a1.Slice().At(1)) - - // Check that the array was correctly inserted. - childArray := a1.Slice().At(1) - assert.EqualValues(t, ValueTypeSlice, childArray.Type()) - assert.EqualValues(t, 1, childArray.Slice().Len()) - - v := childArray.Slice().At(0) - assert.EqualValues(t, ValueTypeStr, v.Type()) - assert.EqualValues(t, "somestr", v.Str()) - - // Test nil values case for Slice() func. - a1 = newValue(&otlpcommon.AnyValue{Value: &otlpcommon.AnyValue_ArrayValue{ArrayValue: nil}}) - assert.EqualValues(t, newSlice(nil), a1.Slice()) -} - -func TestSliceWithNilValues(t *testing.T) { - origWithNil := []otlpcommon.AnyValue{ - {}, - {Value: &otlpcommon.AnyValue_StringValue{StringValue: "test_value"}}, - } - sm := newSlice(&origWithNil) - - val := sm.At(0) - assert.EqualValues(t, ValueTypeEmpty, val.Type()) - assert.EqualValues(t, "", val.Str()) - - val = sm.At(1) - assert.EqualValues(t, ValueTypeStr, val.Type()) - assert.EqualValues(t, "test_value", val.Str()) - - sm.AppendEmpty().SetStr("other_value") - val = sm.At(2) - assert.EqualValues(t, ValueTypeStr, val.Type()) - assert.EqualValues(t, "other_value", val.Str()) -} - -func TestAsString(t *testing.T) { - tests := []struct { - name string - input Value - expected string - }{ - { - name: "string", - input: NewValueStr("string value"), - expected: "string value", - }, - { - name: "int64", - input: NewValueInt(42), - expected: "42", - }, - { - name: "float64", - input: NewValueDouble(1.61803399), - expected: "1.61803399", - }, - { - name: "small float64", - input: NewValueDouble(.000000009), - expected: "9e-9", - }, - { - name: "bad float64", - input: NewValueDouble(math.Inf(1)), - expected: "json: unsupported value: +Inf", - }, - { - name: "boolean", - input: NewValueBool(true), - expected: "true", - }, - { - name: "empty_map", - input: NewValueMap(), - expected: "{}", - }, - { - name: "simple_map", - input: generateTestValueMap(), - expected: "{\"arrKey\":[\"strOne\",\"strTwo\"],\"boolKey\":false,\"floatKey\":18.6,\"intKey\":7,\"mapKey\":{\"keyOne\":\"valOne\",\"keyTwo\":\"valTwo\"},\"nullKey\":null,\"strKey\":\"strVal\"}", - }, - { - name: "empty_array", - input: NewValueSlice(), - expected: "[]", - }, - { - name: "simple_array", - input: generateTestValueSlice(), - expected: "[\"strVal\",7,18.6,false,null]", - }, - { - name: "empty", - input: NewValueEmpty(), - expected: "", - }, - { - name: "bytes", - input: generateTestValueBytes(), - expected: base64.StdEncoding.EncodeToString([]byte("String bytes")), - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - actual := test.input.AsString() - assert.Equal(t, test.expected, actual) - }) - } -} - -func TestValueAsRaw(t *testing.T) { - tests := []struct { - name string - input Value - expected any - }{ - { - name: "string", - input: NewValueStr("value"), - expected: "value", - }, - { - name: "int", - input: NewValueInt(11), - expected: int64(11), - }, - { - name: "double", - input: NewValueDouble(1.2), - expected: 1.2, - }, - { - name: "bytes", - input: generateTestValueBytes(), - expected: []byte("String bytes"), - }, - { - name: "empty", - input: NewValueEmpty(), - expected: nil, - }, - { - name: "slice", - input: generateTestValueSlice(), - expected: []any{"strVal", int64(7), 18.6, false, nil}, - }, - { - name: "map", - input: generateTestValueMap(), - expected: map[string]any{ - "mapKey": map[string]any{"keyOne": "valOne", "keyTwo": "valTwo"}, - "nullKey": nil, - "strKey": "strVal", - "arrKey": []any{"strOne", "strTwo"}, - "boolKey": false, - "floatKey": 18.6, - "intKey": int64(7), - }, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - actual := test.input.AsRaw() - assert.Equal(t, test.expected, actual) - }) - } -} - -func TestMapAsRaw(t *testing.T) { - raw := map[string]any{ - "array": []any{false, []byte("test"), 12.9, int64(91), "another string"}, - "bool": true, - "bytes": []byte("bytes value"), - "double": 1.2, - "empty": nil, - "int": int64(900), - "map": map[string]any{"str": "val"}, - "string": "string value", - } - m := NewMap() - assert.NoError(t, m.FromRaw(raw)) - assert.Equal(t, raw, m.AsRaw()) -} - -func TestNewValueFromRaw(t *testing.T) { - tests := []struct { - name string - input any - expected Value - }{ - { - name: "nil", - input: nil, - expected: NewValueEmpty(), - }, - { - name: "string", - input: "text", - expected: NewValueStr("text"), - }, - { - name: "int", - input: 123, - expected: NewValueInt(int64(123)), - }, - { - name: "int8", - input: int8(12), - expected: NewValueInt(int64(12)), - }, - { - name: "int16", - input: int16(23), - expected: NewValueInt(int64(23)), - }, - { - name: "int32", - input: int32(34), - expected: NewValueInt(int64(34)), - }, - { - name: "int64", - input: int64(45), - expected: NewValueInt(45), - }, - { - name: "uint", - input: uint(56), - expected: NewValueInt(int64(56)), - }, - { - name: "uint8", - input: uint8(67), - expected: NewValueInt(int64(67)), - }, - { - name: "uint16", - input: uint16(78), - expected: NewValueInt(int64(78)), - }, - { - name: "uint32", - input: uint32(89), - expected: NewValueInt(int64(89)), - }, - { - name: "uint64", - input: uint64(90), - expected: NewValueInt(int64(90)), - }, - { - name: "float32", - input: float32(1.234), - expected: NewValueDouble(float64(float32(1.234))), - }, - { - name: "float64", - input: float64(2.345), - expected: NewValueDouble(float64(2.345)), - }, - { - name: "bool", - input: true, - expected: NewValueBool(true), - }, - { - name: "bytes", - input: []byte{1, 2, 3}, - expected: func() Value { - m := NewValueBytes() - m.Bytes().FromRaw([]byte{1, 2, 3}) - return m - }(), - }, - { - name: "map", - input: map[string]any{ - "k": "v", - }, - expected: func() Value { - m := NewValueMap() - assert.NoError(t, m.Map().FromRaw(map[string]any{"k": "v"})) - return m - }(), - }, - { - name: "empty map", - input: map[string]any{}, - expected: func() Value { - m := NewValueMap() - assert.NoError(t, m.Map().FromRaw(map[string]any{})) - return m - }(), - }, - { - name: "slice", - input: []any{"v1", "v2"}, - expected: (func() Value { - s := NewValueSlice() - assert.NoError(t, s.Slice().FromRaw([]any{"v1", "v2"})) - return s - })(), - }, - { - name: "empty slice", - input: []any{}, - expected: (func() Value { - s := NewValueSlice() - assert.NoError(t, s.Slice().FromRaw([]any{})) - return s - })(), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := NewValueEmpty() - assert.NoError(t, actual.FromRaw(tt.input)) - assert.Equal(t, tt.expected, actual) - }) - } -} - -func TestNewValueFromRawInvalid(t *testing.T) { - actual := NewValueEmpty() - assert.EqualError(t, actual.FromRaw(ValueTypeDouble), "") -} - -func generateTestValueMap() Value { - ret := NewValueMap() - attrMap := ret.Map() - attrMap.PutStr("strKey", "strVal") - attrMap.PutInt("intKey", 7) - attrMap.PutDouble("floatKey", 18.6) - attrMap.PutBool("boolKey", false) - attrMap.PutEmpty("nullKey") - - m := attrMap.PutEmptyMap("mapKey") - m.PutStr("keyOne", "valOne") - m.PutStr("keyTwo", "valTwo") - - s := attrMap.PutEmptySlice("arrKey") - s.AppendEmpty().SetStr("strOne") - s.AppendEmpty().SetStr("strTwo") - - return ret -} - -func generateTestValueSlice() Value { - ret := NewValueSlice() - attrArr := ret.Slice() - attrArr.AppendEmpty().SetStr("strVal") - attrArr.AppendEmpty().SetInt(7) - attrArr.AppendEmpty().SetDouble(18.6) - attrArr.AppendEmpty().SetBool(false) - attrArr.AppendEmpty() - return ret -} - -func generateTestValueBytes() Value { - v := NewValueBytes() - v.Bytes().FromRaw([]byte("String bytes")) - return v -} - -func TestSlice(t *testing.T) { - es := NewSlice() - assert.Equal(t, 0, es.Len()) - es = newSlice(&[]otlpcommon.AnyValue{}) - assert.Equal(t, 0, es.Len()) - - es.EnsureCapacity(7) - emptyVal := newValue(&otlpcommon.AnyValue{}) - testVal := Value(internal.GenerateTestValue()) - assert.Equal(t, 7, cap(*es.getOrig())) - for i := 0; i < es.Len(); i++ { - el := es.AppendEmpty() - assert.Equal(t, emptyVal, el) - internal.FillTestValue(internal.Value(el)) - assert.Equal(t, testVal, el) - } -} - -func TestSlice_CopyTo(t *testing.T) { - dest := NewSlice() - // Test CopyTo to empty - NewSlice().CopyTo(dest) - assert.Equal(t, NewSlice(), dest) - - // Test CopyTo larger slice - Slice(internal.GenerateTestSlice()).CopyTo(dest) - assert.Equal(t, Slice(internal.GenerateTestSlice()), dest) - - // Test CopyTo same size slice - Slice(internal.GenerateTestSlice()).CopyTo(dest) - assert.Equal(t, Slice(internal.GenerateTestSlice()), dest) -} - -func TestSlice_EnsureCapacity(t *testing.T) { - es := Slice(internal.GenerateTestSlice()) - // Test ensure smaller capacity. - const ensureSmallLen = 4 - expectedEs := make(map[*otlpcommon.AnyValue]bool) - for i := 0; i < es.Len(); i++ { - expectedEs[es.At(i).getOrig()] = true - } - assert.Equal(t, es.Len(), len(expectedEs)) - es.EnsureCapacity(ensureSmallLen) - assert.Less(t, ensureSmallLen, es.Len()) - foundEs := make(map[*otlpcommon.AnyValue]bool, es.Len()) - for i := 0; i < es.Len(); i++ { - foundEs[es.At(i).getOrig()] = true - } - assert.Equal(t, expectedEs, foundEs) - - // Test ensure larger capacity - const ensureLargeLen = 9 - oldLen := es.Len() - assert.Equal(t, oldLen, len(expectedEs)) - es.EnsureCapacity(ensureLargeLen) - assert.Equal(t, ensureLargeLen, cap(*es.getOrig())) -} - -func TestSlice_MoveAndAppendTo(t *testing.T) { - // Test MoveAndAppendTo to empty - expectedSlice := Slice(internal.GenerateTestSlice()) - dest := NewSlice() - src := Slice(internal.GenerateTestSlice()) - src.MoveAndAppendTo(dest) - assert.Equal(t, Slice(internal.GenerateTestSlice()), dest) - assert.Equal(t, 0, src.Len()) - assert.Equal(t, expectedSlice.Len(), dest.Len()) - - // Test MoveAndAppendTo empty slice - src.MoveAndAppendTo(dest) - assert.Equal(t, Slice(internal.GenerateTestSlice()), dest) - assert.Equal(t, 0, src.Len()) - assert.Equal(t, expectedSlice.Len(), dest.Len()) - - // Test MoveAndAppendTo not empty slice - Slice(internal.GenerateTestSlice()).MoveAndAppendTo(dest) - assert.Equal(t, 2*expectedSlice.Len(), dest.Len()) - for i := 0; i < expectedSlice.Len(); i++ { - assert.Equal(t, expectedSlice.At(i), dest.At(i)) - assert.Equal(t, expectedSlice.At(i), dest.At(i+expectedSlice.Len())) - } -} - -func TestSlice_RemoveIf(t *testing.T) { - // Test RemoveIf on empty slice - emptySlice := NewSlice() - emptySlice.RemoveIf(func(el Value) bool { - t.Fail() - return false - }) - - // Test RemoveIf - filtered := Slice(internal.GenerateTestSlice()) - pos := 0 - filtered.RemoveIf(func(el Value) bool { - pos++ - return pos%3 == 0 - }) - assert.Equal(t, 5, filtered.Len()) -} diff --git a/pdata/pcommon/map.go b/pdata/pcommon/map.go new file mode 100644 index 00000000000..f0f27e080ca --- /dev/null +++ b/pdata/pcommon/map.go @@ -0,0 +1,293 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pcommon // import "go.opentelemetry.io/collector/pdata/pcommon" + +import ( + "sort" + + "go.uber.org/multierr" + + "go.opentelemetry.io/collector/pdata/internal" + otlpcommon "go.opentelemetry.io/collector/pdata/internal/data/protogen/common/v1" +) + +// Map stores a map of string keys to elements of Value type. +type Map internal.Map + +// NewMap creates a Map with 0 elements. +func NewMap() Map { + orig := []otlpcommon.KeyValue(nil) + return Map(internal.NewMap(&orig)) +} + +func (m Map) getOrig() *[]otlpcommon.KeyValue { + return internal.GetOrigMap(internal.Map(m)) +} + +func newMap(orig *[]otlpcommon.KeyValue) Map { + return Map(internal.NewMap(orig)) +} + +// Clear erases any existing entries in this Map instance. +func (m Map) Clear() { + *m.getOrig() = nil +} + +// EnsureCapacity increases the capacity of this Map instance, if necessary, +// to ensure that it can hold at least the number of elements specified by the capacity argument. +func (m Map) EnsureCapacity(capacity int) { + if capacity <= cap(*m.getOrig()) { + return + } + oldOrig := *m.getOrig() + *m.getOrig() = make([]otlpcommon.KeyValue, 0, capacity) + copy(*m.getOrig(), oldOrig) +} + +// Get returns the Value associated with the key and true. Returned +// Value is not a copy, it is a reference to the value stored in this map. +// It is allowed to modify the returned value using Value.Set* functions. +// Such modification will be applied to the value stored in this map. +// +// If the key does not exist returns an invalid instance of the KeyValue and false. +// Calling any functions on the returned invalid instance will cause a panic. +func (m Map) Get(key string) (Value, bool) { + for i := range *m.getOrig() { + akv := &(*m.getOrig())[i] + if akv.Key == key { + return newValue(&akv.Value), true + } + } + return newValue(nil), false +} + +// Remove removes the entry associated with the key and returns true if the key +// was present in the map, otherwise returns false. +func (m Map) Remove(key string) bool { + for i := range *m.getOrig() { + akv := &(*m.getOrig())[i] + if akv.Key == key { + *akv = (*m.getOrig())[len(*m.getOrig())-1] + *m.getOrig() = (*m.getOrig())[:len(*m.getOrig())-1] + return true + } + } + return false +} + +// RemoveIf removes the entries for which the function in question returns true +func (m Map) RemoveIf(f func(string, Value) bool) { + newLen := 0 + for i := 0; i < len(*m.getOrig()); i++ { + akv := &(*m.getOrig())[i] + if f(akv.Key, newValue(&akv.Value)) { + continue + } + if newLen == i { + // Nothing to move, element is at the right place. + newLen++ + continue + } + (*m.getOrig())[newLen] = (*m.getOrig())[i] + newLen++ + } + *m.getOrig() = (*m.getOrig())[:newLen] +} + +// PutEmpty inserts or updates an empty value to the map under given key +// and return the updated/inserted value. +func (m Map) PutEmpty(k string) Value { + if av, existing := m.Get(k); existing { + av.getOrig().Value = nil + return newValue(av.getOrig()) + } + *m.getOrig() = append(*m.getOrig(), otlpcommon.KeyValue{Key: k}) + return newValue(&(*m.getOrig())[len(*m.getOrig())-1].Value) +} + +// PutStr performs the Insert or Update action. The Value is +// inserted to the map that did not originally have the key. The key/value is +// updated to the map where the key already existed. +func (m Map) PutStr(k string, v string) { + if av, existing := m.Get(k); existing { + av.SetStr(v) + } else { + *m.getOrig() = append(*m.getOrig(), newKeyValueString(k, v)) + } +} + +// PutInt performs the Insert or Update action. The int Value is +// inserted to the map that did not originally have the key. The key/value is +// updated to the map where the key already existed. +func (m Map) PutInt(k string, v int64) { + if av, existing := m.Get(k); existing { + av.SetInt(v) + } else { + *m.getOrig() = append(*m.getOrig(), newKeyValueInt(k, v)) + } +} + +// PutDouble performs the Insert or Update action. The double Value is +// inserted to the map that did not originally have the key. The key/value is +// updated to the map where the key already existed. +func (m Map) PutDouble(k string, v float64) { + if av, existing := m.Get(k); existing { + av.SetDouble(v) + } else { + *m.getOrig() = append(*m.getOrig(), newKeyValueDouble(k, v)) + } +} + +// PutBool performs the Insert or Update action. The bool Value is +// inserted to the map that did not originally have the key. The key/value is +// updated to the map where the key already existed. +func (m Map) PutBool(k string, v bool) { + if av, existing := m.Get(k); existing { + av.SetBool(v) + } else { + *m.getOrig() = append(*m.getOrig(), newKeyValueBool(k, v)) + } +} + +// PutEmptyBytes inserts or updates an empty byte slice under given key and returns it. +func (m Map) PutEmptyBytes(k string) ByteSlice { + bv := otlpcommon.AnyValue_BytesValue{} + if av, existing := m.Get(k); existing { + av.getOrig().Value = &bv + } else { + *m.getOrig() = append(*m.getOrig(), otlpcommon.KeyValue{Key: k, Value: otlpcommon.AnyValue{Value: &bv}}) + } + return ByteSlice(internal.NewByteSlice(&bv.BytesValue)) +} + +// PutEmptyMap inserts or updates an empty map under given key and returns it. +func (m Map) PutEmptyMap(k string) Map { + kvl := otlpcommon.AnyValue_KvlistValue{KvlistValue: &otlpcommon.KeyValueList{Values: []otlpcommon.KeyValue(nil)}} + if av, existing := m.Get(k); existing { + av.getOrig().Value = &kvl + } else { + *m.getOrig() = append(*m.getOrig(), otlpcommon.KeyValue{Key: k, Value: otlpcommon.AnyValue{Value: &kvl}}) + } + return Map(internal.NewMap(&kvl.KvlistValue.Values)) +} + +// PutEmptySlice inserts or updates an empty slice under given key and returns it. +func (m Map) PutEmptySlice(k string) Slice { + vl := otlpcommon.AnyValue_ArrayValue{ArrayValue: &otlpcommon.ArrayValue{Values: []otlpcommon.AnyValue(nil)}} + if av, existing := m.Get(k); existing { + av.getOrig().Value = &vl + } else { + *m.getOrig() = append(*m.getOrig(), otlpcommon.KeyValue{Key: k, Value: otlpcommon.AnyValue{Value: &vl}}) + } + return Slice(internal.NewSlice(&vl.ArrayValue.Values)) +} + +// Sort sorts the entries in the Map so two instances can be compared. +// +// Deprecated: [1.0.0-rc4] This method will be removed as it leaks the underlying implementation. +// Please use one of the following alternatives depending on your use case: +// - Map.AsRaw() if you need to compare two maps in tests. +// - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest module if you use Sort() +// to prepare other pdata objects for comparison. +// - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil module if you use Sort() +// to create Map identifiers. +// +// If your use case is not covered by the above alternatives, please comment on the issue +// https://github.com/open-telemetry/opentelemetry-collector/issues/6688. +func (m Map) Sort() { + // Intention is to move the nil values at the end. + sort.SliceStable(*m.getOrig(), func(i, j int) bool { + return (*m.getOrig())[i].Key < (*m.getOrig())[j].Key + }) +} + +// Len returns the length of this map. +// +// Because the Map is represented internally by a slice of pointers, and the data are comping from the wire, +// it is possible that when iterating using "Range" to get access to fewer elements because nil elements are skipped. +func (m Map) Len() int { + return len(*m.getOrig()) +} + +// Range calls f sequentially for each key and value present in the map. If f returns false, range stops the iteration. +// +// Example: +// +// sm.Range(func(k string, v Value) bool { +// ... +// }) +func (m Map) Range(f func(k string, v Value) bool) { + for i := range *m.getOrig() { + kv := &(*m.getOrig())[i] + if !f(kv.Key, Value(internal.NewValue(&kv.Value))) { + break + } + } +} + +// CopyTo copies all elements from the current map overriding the destination. +func (m Map) CopyTo(dest Map) { + newLen := len(*m.getOrig()) + oldCap := cap(*dest.getOrig()) + if newLen <= oldCap { + // New slice fits in existing slice, no need to reallocate. + *dest.getOrig() = (*dest.getOrig())[:newLen:oldCap] + for i := range *m.getOrig() { + akv := &(*m.getOrig())[i] + destAkv := &(*dest.getOrig())[i] + destAkv.Key = akv.Key + newValue(&akv.Value).CopyTo(newValue(&destAkv.Value)) + } + return + } + + // New slice is bigger than exist slice. Allocate new space. + origs := make([]otlpcommon.KeyValue, len(*m.getOrig())) + for i := range *m.getOrig() { + akv := &(*m.getOrig())[i] + origs[i].Key = akv.Key + newValue(&akv.Value).CopyTo(newValue(&origs[i].Value)) + } + *dest.getOrig() = origs +} + +// AsRaw returns a standard go map representation of this Map. +func (m Map) AsRaw() map[string]any { + rawMap := make(map[string]any) + m.Range(func(k string, v Value) bool { + rawMap[k] = v.AsRaw() + return true + }) + return rawMap +} + +// FromRaw overrides this Map instance from a standard go map. +func (m Map) FromRaw(rawMap map[string]any) error { + if len(rawMap) == 0 { + *m.getOrig() = nil + return nil + } + + var errs error + origs := make([]otlpcommon.KeyValue, len(rawMap)) + ix := 0 + for k, iv := range rawMap { + origs[ix].Key = k + errs = multierr.Append(errs, newValue(&origs[ix].Value).FromRaw(iv)) + ix++ + } + *m.getOrig() = origs + return errs +} diff --git a/pdata/pcommon/map_test.go b/pdata/pcommon/map_test.go new file mode 100644 index 00000000000..93b85822fea --- /dev/null +++ b/pdata/pcommon/map_test.go @@ -0,0 +1,469 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pcommon + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "go.opentelemetry.io/collector/pdata/internal" + otlpcommon "go.opentelemetry.io/collector/pdata/internal/data/protogen/common/v1" +) + +func TestMap(t *testing.T) { + assert.EqualValues(t, 0, NewMap().Len()) + + val, exist := NewMap().Get("test_key") + assert.False(t, exist) + assert.EqualValues(t, newValue(nil), val) + + putString := NewMap() + putString.PutStr("k", "v") + assert.EqualValues(t, Map(internal.GenerateTestMap()), putString) + + putInt := NewMap() + putInt.PutInt("k", 123) + assert.EqualValues(t, generateTestIntMap(t), putInt) + + putDouble := NewMap() + putDouble.PutDouble("k", 12.3) + assert.EqualValues(t, generateTestDoubleMap(t), putDouble) + + putBool := NewMap() + putBool.PutBool("k", true) + assert.EqualValues(t, generateTestBoolMap(t), putBool) + + putBytes := NewMap() + putBytes.PutEmptyBytes("k").FromRaw([]byte{1, 2, 3, 4, 5}) + assert.EqualValues(t, generateTestBytesMap(t), putBytes) + + putMap := NewMap() + putMap.PutEmptyMap("k") + assert.EqualValues(t, generateTestEmptyMap(t), putMap) + + putSlice := NewMap() + putSlice.PutEmptySlice("k") + assert.EqualValues(t, generateTestEmptySlice(t), putSlice) + + removeMap := NewMap() + assert.False(t, removeMap.Remove("k")) + assert.EqualValues(t, NewMap(), removeMap) + + // Test Sort + sortMap := NewMap() + sortMap.Sort() + assert.EqualValues(t, NewMap(), sortMap) +} + +func TestMapPutEmpty(t *testing.T) { + m := NewMap() + v := m.PutEmpty("k1") + assert.EqualValues(t, map[string]any{ + "k1": nil, + }, m.AsRaw()) + + v.SetBool(true) + assert.EqualValues(t, map[string]any{ + "k1": true, + }, m.AsRaw()) + + v = m.PutEmpty("k1") + v.SetInt(1) + v2, ok := m.Get("k1") + assert.True(t, ok) + assert.Equal(t, int64(1), v2.Int()) +} + +func TestMapPutEmptyMap(t *testing.T) { + m := NewMap() + childMap := m.PutEmptyMap("k1") + assert.EqualValues(t, map[string]any{ + "k1": map[string]any{}, + }, m.AsRaw()) + childMap.PutEmptySlice("k2").AppendEmpty().SetStr("val") + assert.EqualValues(t, map[string]any{ + "k1": map[string]any{ + "k2": []any{"val"}, + }, + }, m.AsRaw()) + + childMap.PutEmptyMap("k2").PutInt("k3", 1) + assert.EqualValues(t, map[string]any{ + "k1": map[string]any{ + "k2": map[string]any{"k3": int64(1)}, + }, + }, m.AsRaw()) +} + +func TestMapPutEmptySlice(t *testing.T) { + m := NewMap() + childSlice := m.PutEmptySlice("k") + assert.EqualValues(t, map[string]any{ + "k": []any{}, + }, m.AsRaw()) + childSlice.AppendEmpty().SetDouble(1.1) + assert.EqualValues(t, map[string]any{ + "k": []any{1.1}, + }, m.AsRaw()) + + m.PutEmptySlice("k") + assert.EqualValues(t, map[string]any{ + "k": []any{}, + }, m.AsRaw()) + childSliceVal, ok := m.Get("k") + assert.True(t, ok) + childSliceVal.Slice().AppendEmpty().SetEmptySlice().AppendEmpty().SetStr("val") + assert.EqualValues(t, map[string]any{ + "k": []any{[]any{"val"}}, + }, m.AsRaw()) +} + +func TestMapPutEmptyBytes(t *testing.T) { + m := NewMap() + b := m.PutEmptyBytes("k") + bv, ok := m.Get("k") + assert.True(t, ok) + assert.Equal(t, []byte(nil), bv.Bytes().AsRaw()) + b.FromRaw([]byte{1, 2, 3}) + bv, ok = m.Get("k") + assert.True(t, ok) + assert.Equal(t, []byte{1, 2, 3}, bv.Bytes().AsRaw()) + + m.PutEmptyBytes("k") + bv, ok = m.Get("k") + assert.True(t, ok) + assert.Equal(t, []byte(nil), bv.Bytes().AsRaw()) + bv.Bytes().FromRaw([]byte{3, 2, 1}) + bv, ok = m.Get("k") + assert.True(t, ok) + assert.Equal(t, []byte{3, 2, 1}, bv.Bytes().AsRaw()) +} + +func TestMapWithEmpty(t *testing.T) { + origWithNil := []otlpcommon.KeyValue{ + {}, + { + Key: "test_key", + Value: otlpcommon.AnyValue{Value: &otlpcommon.AnyValue_StringValue{StringValue: "test_value"}}, + }, + { + Key: "test_key2", + Value: otlpcommon.AnyValue{Value: nil}, + }, + } + sm := newMap(&origWithNil) + val, exist := sm.Get("test_key") + assert.True(t, exist) + assert.EqualValues(t, ValueTypeStr, val.Type()) + assert.EqualValues(t, "test_value", val.Str()) + + val, exist = sm.Get("test_key2") + assert.True(t, exist) + assert.EqualValues(t, ValueTypeEmpty, val.Type()) + assert.EqualValues(t, "", val.Str()) + + sm.PutStr("other_key_string", "other_value") + val, exist = sm.Get("other_key_string") + assert.True(t, exist) + assert.EqualValues(t, ValueTypeStr, val.Type()) + assert.EqualValues(t, "other_value", val.Str()) + + sm.PutInt("other_key_int", 123) + val, exist = sm.Get("other_key_int") + assert.True(t, exist) + assert.EqualValues(t, ValueTypeInt, val.Type()) + assert.EqualValues(t, 123, val.Int()) + + sm.PutDouble("other_key_double", 1.23) + val, exist = sm.Get("other_key_double") + assert.True(t, exist) + assert.EqualValues(t, ValueTypeDouble, val.Type()) + assert.EqualValues(t, 1.23, val.Double()) + + sm.PutBool("other_key_bool", true) + val, exist = sm.Get("other_key_bool") + assert.True(t, exist) + assert.EqualValues(t, ValueTypeBool, val.Type()) + assert.True(t, val.Bool()) + + sm.PutEmptyBytes("other_key_bytes").FromRaw([]byte{7, 8, 9}) + val, exist = sm.Get("other_key_bytes") + assert.True(t, exist) + assert.EqualValues(t, ValueTypeBytes, val.Type()) + assert.EqualValues(t, []byte{7, 8, 9}, val.Bytes().AsRaw()) + + sm.PutStr("another_key_string", "another_value") + val, exist = sm.Get("another_key_string") + assert.True(t, exist) + assert.EqualValues(t, ValueTypeStr, val.Type()) + assert.EqualValues(t, "another_value", val.Str()) + + sm.PutInt("another_key_int", 456) + val, exist = sm.Get("another_key_int") + assert.True(t, exist) + assert.EqualValues(t, ValueTypeInt, val.Type()) + assert.EqualValues(t, 456, val.Int()) + + sm.PutDouble("another_key_double", 4.56) + val, exist = sm.Get("another_key_double") + assert.True(t, exist) + assert.EqualValues(t, ValueTypeDouble, val.Type()) + assert.EqualValues(t, 4.56, val.Double()) + + sm.PutBool("another_key_bool", false) + val, exist = sm.Get("another_key_bool") + assert.True(t, exist) + assert.EqualValues(t, ValueTypeBool, val.Type()) + assert.False(t, val.Bool()) + + sm.PutEmptyBytes("another_key_bytes").FromRaw([]byte{1}) + val, exist = sm.Get("another_key_bytes") + assert.True(t, exist) + assert.EqualValues(t, ValueTypeBytes, val.Type()) + assert.EqualValues(t, []byte{1}, val.Bytes().AsRaw()) + + assert.True(t, sm.Remove("other_key_string")) + assert.True(t, sm.Remove("other_key_int")) + assert.True(t, sm.Remove("other_key_double")) + assert.True(t, sm.Remove("other_key_bool")) + assert.True(t, sm.Remove("other_key_bytes")) + assert.True(t, sm.Remove("another_key_string")) + assert.True(t, sm.Remove("another_key_int")) + assert.True(t, sm.Remove("another_key_double")) + assert.True(t, sm.Remove("another_key_bool")) + assert.True(t, sm.Remove("another_key_bytes")) + + assert.False(t, sm.Remove("other_key_string")) + assert.False(t, sm.Remove("another_key_string")) + + // Test that the initial key is still there. + val, exist = sm.Get("test_key") + assert.True(t, exist) + assert.EqualValues(t, ValueTypeStr, val.Type()) + assert.EqualValues(t, "test_value", val.Str()) + + val, exist = sm.Get("test_key2") + assert.True(t, exist) + assert.EqualValues(t, ValueTypeEmpty, val.Type()) + assert.EqualValues(t, "", val.Str()) + + _, exist = sm.Get("test_key3") + assert.False(t, exist) + + // Test Sort + sm.Sort() + assert.EqualValues(t, newMap(&origWithNil), sm) +} + +func TestMapIterationNil(t *testing.T) { + NewMap().Range(func(k string, v Value) bool { + // Fail if any element is returned + t.Fail() + return true + }) +} + +func TestMap_Range(t *testing.T) { + rawMap := map[string]any{ + "k_string": "123", + "k_int": int64(123), + "k_double": float64(1.23), + "k_bool": true, + "k_empty": nil, + } + am := NewMap() + assert.NoError(t, am.FromRaw(rawMap)) + assert.Equal(t, 5, am.Len()) + + calls := 0 + am.Range(func(k string, v Value) bool { + calls++ + return false + }) + assert.Equal(t, 1, calls) + + am.Range(func(k string, v Value) bool { + assert.Equal(t, rawMap[k], v.AsRaw()) + delete(rawMap, k) + return true + }) + assert.EqualValues(t, 0, len(rawMap)) +} + +func TestMap_FromRaw(t *testing.T) { + am := NewMap() + assert.NoError(t, am.FromRaw(map[string]any{})) + assert.Equal(t, 0, am.Len()) + am.PutEmpty("k") + assert.Equal(t, 1, am.Len()) + + assert.NoError(t, am.FromRaw(nil)) + assert.Equal(t, 0, am.Len()) + am.PutEmpty("k") + assert.Equal(t, 1, am.Len()) + + assert.NoError(t, am.FromRaw(map[string]any{ + "k_string": "123", + "k_int": 123, + "k_double": 1.23, + "k_bool": true, + "k_null": nil, + "k_bytes": []byte{1, 2, 3}, + "k_slice": []any{1, 2.1, "val"}, + "k_map": map[string]any{ + "k_int": 1, + "k_string": "val", + }, + })) + assert.Equal(t, 8, am.Len()) + v, ok := am.Get("k_string") + assert.True(t, ok) + assert.Equal(t, "123", v.Str()) + v, ok = am.Get("k_int") + assert.True(t, ok) + assert.Equal(t, int64(123), v.Int()) + v, ok = am.Get("k_double") + assert.True(t, ok) + assert.Equal(t, 1.23, v.Double()) + v, ok = am.Get("k_null") + assert.True(t, ok) + assert.Equal(t, ValueTypeEmpty, v.Type()) + v, ok = am.Get("k_bytes") + assert.True(t, ok) + assert.Equal(t, []byte{1, 2, 3}, v.Bytes().AsRaw()) + v, ok = am.Get("k_slice") + assert.True(t, ok) + assert.Equal(t, []any{int64(1), 2.1, "val"}, v.Slice().AsRaw()) + v, ok = am.Get("k_map") + assert.True(t, ok) + assert.Equal(t, map[string]any{ + "k_int": int64(1), + "k_string": "val", + }, v.Map().AsRaw()) +} + +func TestMap_CopyTo(t *testing.T) { + dest := NewMap() + // Test CopyTo to empty + NewMap().CopyTo(dest) + assert.EqualValues(t, 0, dest.Len()) + + // Test CopyTo larger slice + Map(internal.GenerateTestMap()).CopyTo(dest) + assert.EqualValues(t, Map(internal.GenerateTestMap()), dest) + + // Test CopyTo same size slice + Map(internal.GenerateTestMap()).CopyTo(dest) + assert.EqualValues(t, Map(internal.GenerateTestMap()), dest) + + // Test CopyTo with an empty Value in the destination + (*dest.getOrig())[0].Value = otlpcommon.AnyValue{} + Map(internal.GenerateTestMap()).CopyTo(dest) + assert.EqualValues(t, Map(internal.GenerateTestMap()), dest) +} + +func TestMap_EnsureCapacity_Zero(t *testing.T) { + am := NewMap() + am.EnsureCapacity(0) + assert.Equal(t, 0, am.Len()) + assert.Equal(t, 0, cap(*am.getOrig())) +} + +func TestMap_EnsureCapacity(t *testing.T) { + am := NewMap() + am.EnsureCapacity(5) + assert.Equal(t, 0, am.Len()) + assert.Equal(t, 5, cap(*am.getOrig())) + am.EnsureCapacity(3) + assert.Equal(t, 0, am.Len()) + assert.Equal(t, 5, cap(*am.getOrig())) + am.EnsureCapacity(8) + assert.Equal(t, 0, am.Len()) + assert.Equal(t, 8, cap(*am.getOrig())) +} + +func TestMap_Clear(t *testing.T) { + am := NewMap() + assert.Nil(t, *am.getOrig()) + am.Clear() + assert.Nil(t, *am.getOrig()) + am.EnsureCapacity(5) + assert.NotNil(t, *am.getOrig()) + am.Clear() + assert.Nil(t, *am.getOrig()) +} + +func TestMap_RemoveIf(t *testing.T) { + am := NewMap() + am.PutStr("k_string", "123") + am.PutInt("k_int", int64(123)) + am.PutDouble("k_double", float64(1.23)) + am.PutBool("k_bool", true) + am.PutEmpty("k_empty") + + assert.Equal(t, 5, am.Len()) + + am.RemoveIf(func(key string, val Value) bool { + return key == "k_int" || val.Type() == ValueTypeBool + }) + assert.Equal(t, 3, am.Len()) + _, exists := am.Get("k_string") + assert.True(t, exists) + _, exists = am.Get("k_int") + assert.False(t, exists) + _, exists = am.Get("k_double") + assert.True(t, exists) + _, exists = am.Get("k_bool") + assert.False(t, exists) + _, exists = am.Get("k_empty") + assert.True(t, exists) +} + +func generateTestEmptyMap(t *testing.T) Map { + m := NewMap() + assert.NoError(t, m.FromRaw(map[string]any{"k": map[string]any(nil)})) + return m +} + +func generateTestEmptySlice(t *testing.T) Map { + m := NewMap() + assert.NoError(t, m.FromRaw(map[string]any{"k": []any(nil)})) + return m +} + +func generateTestIntMap(t *testing.T) Map { + m := NewMap() + assert.NoError(t, m.FromRaw(map[string]any{"k": 123})) + return m +} + +func generateTestDoubleMap(t *testing.T) Map { + m := NewMap() + assert.NoError(t, m.FromRaw(map[string]any{"k": 12.3})) + return m +} + +func generateTestBoolMap(t *testing.T) Map { + m := NewMap() + assert.NoError(t, m.FromRaw(map[string]any{"k": true})) + return m +} + +func generateTestBytesMap(t *testing.T) Map { + m := NewMap() + assert.NoError(t, m.FromRaw(map[string]any{"k": []byte{1, 2, 3, 4, 5}})) + return m +} diff --git a/pdata/pcommon/slice.go b/pdata/pcommon/slice.go new file mode 100644 index 00000000000..f43aaab133a --- /dev/null +++ b/pdata/pcommon/slice.go @@ -0,0 +1,166 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pcommon // import "go.opentelemetry.io/collector/pdata/pcommon" + +import ( + "go.uber.org/multierr" + + "go.opentelemetry.io/collector/pdata/internal" + otlpcommon "go.opentelemetry.io/collector/pdata/internal/data/protogen/common/v1" +) + +// Slice logically represents a slice of Value. +// +// This is a reference type. If passed by value and callee modifies it, the +// caller will see the modification. +// +// Must use NewSlice function to create new instances. +// Important: zero-initialized instance is not valid for use. +type Slice internal.Slice + +func newSlice(orig *[]otlpcommon.AnyValue) Slice { + return Slice(internal.NewSlice(orig)) +} + +func (es Slice) getOrig() *[]otlpcommon.AnyValue { + return internal.GetOrigSlice(internal.Slice(es)) +} + +// NewSlice creates a Slice with 0 elements. +// Can use "EnsureCapacity" to initialize with a given capacity. +func NewSlice() Slice { + orig := []otlpcommon.AnyValue(nil) + return Slice(internal.NewSlice(&orig)) +} + +// Len returns the number of elements in the slice. +// +// Returns "0" for a newly instance created with "NewSlice()". +func (es Slice) Len() int { + return len(*es.getOrig()) +} + +// At returns the element at the given index. +// +// This function is used mostly for iterating over all the values in the slice: +// +// for i := 0; i < es.Len(); i++ { +// e := es.At(i) +// ... // Do something with the element +// } +func (es Slice) At(ix int) Value { + return newValue(&(*es.getOrig())[ix]) +} + +// CopyTo copies all elements from the current slice overriding the destination. +func (es Slice) CopyTo(dest Slice) { + srcLen := es.Len() + destCap := cap(*dest.getOrig()) + if srcLen <= destCap { + (*dest.getOrig()) = (*dest.getOrig())[:srcLen:destCap] + } else { + (*dest.getOrig()) = make([]otlpcommon.AnyValue, srcLen) + } + + for i := range *es.getOrig() { + newValue(&(*es.getOrig())[i]).CopyTo(newValue(&(*dest.getOrig())[i])) + } +} + +// EnsureCapacity is an operation that ensures the slice has at least the specified capacity. +// 1. If the newCap <= cap then no change in capacity. +// 2. If the newCap > cap then the slice capacity will be expanded to equal newCap. +// +// Here is how a new Slice can be initialized: +// +// es := NewSlice() +// es.EnsureCapacity(4) +// for i := 0; i < 4; i++ { +// e := es.AppendEmpty() +// // Here should set all the values for e. +// } +func (es Slice) EnsureCapacity(newCap int) { + oldCap := cap(*es.getOrig()) + if newCap <= oldCap { + return + } + + newOrig := make([]otlpcommon.AnyValue, len(*es.getOrig()), newCap) + copy(newOrig, *es.getOrig()) + *es.getOrig() = newOrig +} + +// AppendEmpty will append to the end of the slice an empty Value. +// It returns the newly added Value. +func (es Slice) AppendEmpty() Value { + *es.getOrig() = append(*es.getOrig(), otlpcommon.AnyValue{}) + return es.At(es.Len() - 1) +} + +// MoveAndAppendTo moves all elements from the current slice and appends them to the dest. +// The current slice will be cleared. +func (es Slice) MoveAndAppendTo(dest Slice) { + if *dest.getOrig() == nil { + // We can simply move the entire vector and avoid any allocations. + *dest.getOrig() = *es.getOrig() + } else { + *dest.getOrig() = append(*dest.getOrig(), *es.getOrig()...) + } + *es.getOrig() = nil +} + +// RemoveIf calls f sequentially for each element present in the slice. +// If f returns true, the element is removed from the slice. +func (es Slice) RemoveIf(f func(Value) bool) { + newLen := 0 + for i := 0; i < len(*es.getOrig()); i++ { + if f(es.At(i)) { + continue + } + if newLen == i { + // Nothing to move, element is at the right place. + newLen++ + continue + } + (*es.getOrig())[newLen] = (*es.getOrig())[i] + newLen++ + } + // TODO: Prevent memory leak by erasing truncated values. + *es.getOrig() = (*es.getOrig())[:newLen] +} + +// AsRaw return []any copy of the Slice. +func (es Slice) AsRaw() []any { + rawSlice := make([]any, 0, es.Len()) + for i := 0; i < es.Len(); i++ { + rawSlice = append(rawSlice, es.At(i).AsRaw()) + } + return rawSlice +} + +// FromRaw copies []any into the Slice. +func (es Slice) FromRaw(rawSlice []any) error { + if len(rawSlice) == 0 { + *es.getOrig() = nil + return nil + } + var errs error + origs := make([]otlpcommon.AnyValue, len(rawSlice)) + for ix, iv := range rawSlice { + errs = multierr.Append(errs, newValue(&origs[ix]).FromRaw(iv)) + } + *es.getOrig() = origs + return errs +} diff --git a/pdata/pcommon/slice_test.go b/pdata/pcommon/slice_test.go new file mode 100644 index 00000000000..5ddc8a05680 --- /dev/null +++ b/pdata/pcommon/slice_test.go @@ -0,0 +1,125 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pcommon + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "go.opentelemetry.io/collector/pdata/internal" + otlpcommon "go.opentelemetry.io/collector/pdata/internal/data/protogen/common/v1" +) + +func TestSlice(t *testing.T) { + es := NewSlice() + assert.Equal(t, 0, es.Len()) + es = newSlice(&[]otlpcommon.AnyValue{}) + assert.Equal(t, 0, es.Len()) + + es.EnsureCapacity(7) + emptyVal := newValue(&otlpcommon.AnyValue{}) + testVal := Value(internal.GenerateTestValue()) + assert.Equal(t, 7, cap(*es.getOrig())) + for i := 0; i < es.Len(); i++ { + el := es.AppendEmpty() + assert.Equal(t, emptyVal, el) + internal.FillTestValue(internal.Value(el)) + assert.Equal(t, testVal, el) + } +} + +func TestSlice_CopyTo(t *testing.T) { + dest := NewSlice() + // Test CopyTo to empty + NewSlice().CopyTo(dest) + assert.Equal(t, NewSlice(), dest) + + // Test CopyTo larger slice + Slice(internal.GenerateTestSlice()).CopyTo(dest) + assert.Equal(t, Slice(internal.GenerateTestSlice()), dest) + + // Test CopyTo same size slice + Slice(internal.GenerateTestSlice()).CopyTo(dest) + assert.Equal(t, Slice(internal.GenerateTestSlice()), dest) +} + +func TestSlice_EnsureCapacity(t *testing.T) { + es := Slice(internal.GenerateTestSlice()) + // Test ensure smaller capacity. + const ensureSmallLen = 4 + expectedEs := make(map[*otlpcommon.AnyValue]bool) + for i := 0; i < es.Len(); i++ { + expectedEs[es.At(i).getOrig()] = true + } + assert.Equal(t, es.Len(), len(expectedEs)) + es.EnsureCapacity(ensureSmallLen) + assert.Less(t, ensureSmallLen, es.Len()) + foundEs := make(map[*otlpcommon.AnyValue]bool, es.Len()) + for i := 0; i < es.Len(); i++ { + foundEs[es.At(i).getOrig()] = true + } + assert.Equal(t, expectedEs, foundEs) + + // Test ensure larger capacity + const ensureLargeLen = 9 + oldLen := es.Len() + assert.Equal(t, oldLen, len(expectedEs)) + es.EnsureCapacity(ensureLargeLen) + assert.Equal(t, ensureLargeLen, cap(*es.getOrig())) +} + +func TestSlice_MoveAndAppendTo(t *testing.T) { + // Test MoveAndAppendTo to empty + expectedSlice := Slice(internal.GenerateTestSlice()) + dest := NewSlice() + src := Slice(internal.GenerateTestSlice()) + src.MoveAndAppendTo(dest) + assert.Equal(t, Slice(internal.GenerateTestSlice()), dest) + assert.Equal(t, 0, src.Len()) + assert.Equal(t, expectedSlice.Len(), dest.Len()) + + // Test MoveAndAppendTo empty slice + src.MoveAndAppendTo(dest) + assert.Equal(t, Slice(internal.GenerateTestSlice()), dest) + assert.Equal(t, 0, src.Len()) + assert.Equal(t, expectedSlice.Len(), dest.Len()) + + // Test MoveAndAppendTo not empty slice + Slice(internal.GenerateTestSlice()).MoveAndAppendTo(dest) + assert.Equal(t, 2*expectedSlice.Len(), dest.Len()) + for i := 0; i < expectedSlice.Len(); i++ { + assert.Equal(t, expectedSlice.At(i), dest.At(i)) + assert.Equal(t, expectedSlice.At(i), dest.At(i+expectedSlice.Len())) + } +} + +func TestSlice_RemoveIf(t *testing.T) { + // Test RemoveIf on empty slice + emptySlice := NewSlice() + emptySlice.RemoveIf(func(el Value) bool { + t.Fail() + return false + }) + + // Test RemoveIf + filtered := Slice(internal.GenerateTestSlice()) + pos := 0 + filtered.RemoveIf(func(el Value) bool { + pos++ + return pos%3 == 0 + }) + assert.Equal(t, 5, filtered.Len()) +} diff --git a/pdata/pcommon/common.go b/pdata/pcommon/value.go similarity index 52% rename from pdata/pcommon/common.go rename to pdata/pcommon/value.go index 99a510f2935..963c2a82564 100644 --- a/pdata/pcommon/common.go +++ b/pdata/pcommon/value.go @@ -14,19 +14,13 @@ package pcommon // import "go.opentelemetry.io/collector/pdata/pcommon" -// This file contains data structures that are common for all telemetry types, -// such as timestamps, attributes, etc. - import ( "encoding/base64" "encoding/json" "fmt" "math" - "sort" "strconv" - "go.uber.org/multierr" - "go.opentelemetry.io/collector/pdata/internal" otlpcommon "go.opentelemetry.io/collector/pdata/internal/data/protogen/common/v1" ) @@ -480,416 +474,3 @@ func newKeyValueBool(k string, v bool) otlpcommon.KeyValue { akv.SetBool(v) return orig } - -// Map stores a map of string keys to elements of Value type. -type Map internal.Map - -// NewMap creates a Map with 0 elements. -func NewMap() Map { - orig := []otlpcommon.KeyValue(nil) - return Map(internal.NewMap(&orig)) -} - -func (m Map) getOrig() *[]otlpcommon.KeyValue { - return internal.GetOrigMap(internal.Map(m)) -} - -func newMap(orig *[]otlpcommon.KeyValue) Map { - return Map(internal.NewMap(orig)) -} - -// Clear erases any existing entries in this Map instance. -func (m Map) Clear() { - *m.getOrig() = nil -} - -// EnsureCapacity increases the capacity of this Map instance, if necessary, -// to ensure that it can hold at least the number of elements specified by the capacity argument. -func (m Map) EnsureCapacity(capacity int) { - if capacity <= cap(*m.getOrig()) { - return - } - oldOrig := *m.getOrig() - *m.getOrig() = make([]otlpcommon.KeyValue, 0, capacity) - copy(*m.getOrig(), oldOrig) -} - -// Get returns the Value associated with the key and true. Returned -// Value is not a copy, it is a reference to the value stored in this map. -// It is allowed to modify the returned value using Value.Set* functions. -// Such modification will be applied to the value stored in this map. -// -// If the key does not exist returns an invalid instance of the KeyValue and false. -// Calling any functions on the returned invalid instance will cause a panic. -func (m Map) Get(key string) (Value, bool) { - for i := range *m.getOrig() { - akv := &(*m.getOrig())[i] - if akv.Key == key { - return newValue(&akv.Value), true - } - } - return newValue(nil), false -} - -// Remove removes the entry associated with the key and returns true if the key -// was present in the map, otherwise returns false. -func (m Map) Remove(key string) bool { - for i := range *m.getOrig() { - akv := &(*m.getOrig())[i] - if akv.Key == key { - *akv = (*m.getOrig())[len(*m.getOrig())-1] - *m.getOrig() = (*m.getOrig())[:len(*m.getOrig())-1] - return true - } - } - return false -} - -// RemoveIf removes the entries for which the function in question returns true -func (m Map) RemoveIf(f func(string, Value) bool) { - newLen := 0 - for i := 0; i < len(*m.getOrig()); i++ { - akv := &(*m.getOrig())[i] - if f(akv.Key, newValue(&akv.Value)) { - continue - } - if newLen == i { - // Nothing to move, element is at the right place. - newLen++ - continue - } - (*m.getOrig())[newLen] = (*m.getOrig())[i] - newLen++ - } - *m.getOrig() = (*m.getOrig())[:newLen] -} - -// PutEmpty inserts or updates an empty value to the map under given key -// and return the updated/inserted value. -func (m Map) PutEmpty(k string) Value { - if av, existing := m.Get(k); existing { - av.getOrig().Value = nil - return newValue(av.getOrig()) - } - *m.getOrig() = append(*m.getOrig(), otlpcommon.KeyValue{Key: k}) - return newValue(&(*m.getOrig())[len(*m.getOrig())-1].Value) -} - -// PutStr performs the Insert or Update action. The Value is -// inserted to the map that did not originally have the key. The key/value is -// updated to the map where the key already existed. -func (m Map) PutStr(k string, v string) { - if av, existing := m.Get(k); existing { - av.SetStr(v) - } else { - *m.getOrig() = append(*m.getOrig(), newKeyValueString(k, v)) - } -} - -// PutInt performs the Insert or Update action. The int Value is -// inserted to the map that did not originally have the key. The key/value is -// updated to the map where the key already existed. -func (m Map) PutInt(k string, v int64) { - if av, existing := m.Get(k); existing { - av.SetInt(v) - } else { - *m.getOrig() = append(*m.getOrig(), newKeyValueInt(k, v)) - } -} - -// PutDouble performs the Insert or Update action. The double Value is -// inserted to the map that did not originally have the key. The key/value is -// updated to the map where the key already existed. -func (m Map) PutDouble(k string, v float64) { - if av, existing := m.Get(k); existing { - av.SetDouble(v) - } else { - *m.getOrig() = append(*m.getOrig(), newKeyValueDouble(k, v)) - } -} - -// PutBool performs the Insert or Update action. The bool Value is -// inserted to the map that did not originally have the key. The key/value is -// updated to the map where the key already existed. -func (m Map) PutBool(k string, v bool) { - if av, existing := m.Get(k); existing { - av.SetBool(v) - } else { - *m.getOrig() = append(*m.getOrig(), newKeyValueBool(k, v)) - } -} - -// PutEmptyBytes inserts or updates an empty byte slice under given key and returns it. -func (m Map) PutEmptyBytes(k string) ByteSlice { - bv := otlpcommon.AnyValue_BytesValue{} - if av, existing := m.Get(k); existing { - av.getOrig().Value = &bv - } else { - *m.getOrig() = append(*m.getOrig(), otlpcommon.KeyValue{Key: k, Value: otlpcommon.AnyValue{Value: &bv}}) - } - return ByteSlice(internal.NewByteSlice(&bv.BytesValue)) -} - -// PutEmptyMap inserts or updates an empty map under given key and returns it. -func (m Map) PutEmptyMap(k string) Map { - kvl := otlpcommon.AnyValue_KvlistValue{KvlistValue: &otlpcommon.KeyValueList{Values: []otlpcommon.KeyValue(nil)}} - if av, existing := m.Get(k); existing { - av.getOrig().Value = &kvl - } else { - *m.getOrig() = append(*m.getOrig(), otlpcommon.KeyValue{Key: k, Value: otlpcommon.AnyValue{Value: &kvl}}) - } - return Map(internal.NewMap(&kvl.KvlistValue.Values)) -} - -// PutEmptySlice inserts or updates an empty slice under given key and returns it. -func (m Map) PutEmptySlice(k string) Slice { - vl := otlpcommon.AnyValue_ArrayValue{ArrayValue: &otlpcommon.ArrayValue{Values: []otlpcommon.AnyValue(nil)}} - if av, existing := m.Get(k); existing { - av.getOrig().Value = &vl - } else { - *m.getOrig() = append(*m.getOrig(), otlpcommon.KeyValue{Key: k, Value: otlpcommon.AnyValue{Value: &vl}}) - } - return Slice(internal.NewSlice(&vl.ArrayValue.Values)) -} - -// Sort sorts the entries in the Map so two instances can be compared. -// -// Deprecated: [1.0.0-rc4] This method will be removed as it leaks the underlying implementation. -// Please use one of the following alternatives depending on your use case: -// - Map.AsRaw() if you need to compare two maps in tests. -// - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest module if you use Sort() -// to prepare other pdata objects for comparison. -// - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil module if you use Sort() -// to create Map identifiers. -// -// If your use case is not covered by the above alternatives, please comment on the issue -// https://github.com/open-telemetry/opentelemetry-collector/issues/6688. -func (m Map) Sort() { - // Intention is to move the nil values at the end. - sort.SliceStable(*m.getOrig(), func(i, j int) bool { - return (*m.getOrig())[i].Key < (*m.getOrig())[j].Key - }) -} - -// Len returns the length of this map. -// -// Because the Map is represented internally by a slice of pointers, and the data are comping from the wire, -// it is possible that when iterating using "Range" to get access to fewer elements because nil elements are skipped. -func (m Map) Len() int { - return len(*m.getOrig()) -} - -// Range calls f sequentially for each key and value present in the map. If f returns false, range stops the iteration. -// -// Example: -// -// sm.Range(func(k string, v Value) bool { -// ... -// }) -func (m Map) Range(f func(k string, v Value) bool) { - for i := range *m.getOrig() { - kv := &(*m.getOrig())[i] - if !f(kv.Key, Value(internal.NewValue(&kv.Value))) { - break - } - } -} - -// CopyTo copies all elements from the current map overriding the destination. -func (m Map) CopyTo(dest Map) { - newLen := len(*m.getOrig()) - oldCap := cap(*dest.getOrig()) - if newLen <= oldCap { - // New slice fits in existing slice, no need to reallocate. - *dest.getOrig() = (*dest.getOrig())[:newLen:oldCap] - for i := range *m.getOrig() { - akv := &(*m.getOrig())[i] - destAkv := &(*dest.getOrig())[i] - destAkv.Key = akv.Key - newValue(&akv.Value).CopyTo(newValue(&destAkv.Value)) - } - return - } - - // New slice is bigger than exist slice. Allocate new space. - origs := make([]otlpcommon.KeyValue, len(*m.getOrig())) - for i := range *m.getOrig() { - akv := &(*m.getOrig())[i] - origs[i].Key = akv.Key - newValue(&akv.Value).CopyTo(newValue(&origs[i].Value)) - } - *dest.getOrig() = origs -} - -// AsRaw returns a standard go map representation of this Map. -func (m Map) AsRaw() map[string]any { - rawMap := make(map[string]any) - m.Range(func(k string, v Value) bool { - rawMap[k] = v.AsRaw() - return true - }) - return rawMap -} - -// FromRaw overrides this Map instance from a standard go map. -func (m Map) FromRaw(rawMap map[string]any) error { - if len(rawMap) == 0 { - *m.getOrig() = nil - return nil - } - - var errs error - origs := make([]otlpcommon.KeyValue, len(rawMap)) - ix := 0 - for k, iv := range rawMap { - origs[ix].Key = k - errs = multierr.Append(errs, newValue(&origs[ix].Value).FromRaw(iv)) - ix++ - } - *m.getOrig() = origs - return errs -} - -// Slice logically represents a slice of Value. -// -// This is a reference type. If passed by value and callee modifies it, the -// caller will see the modification. -// -// Must use NewSlice function to create new instances. -// Important: zero-initialized instance is not valid for use. -type Slice internal.Slice - -func newSlice(orig *[]otlpcommon.AnyValue) Slice { - return Slice(internal.NewSlice(orig)) -} - -func (es Slice) getOrig() *[]otlpcommon.AnyValue { - return internal.GetOrigSlice(internal.Slice(es)) -} - -// NewSlice creates a Slice with 0 elements. -// Can use "EnsureCapacity" to initialize with a given capacity. -func NewSlice() Slice { - orig := []otlpcommon.AnyValue(nil) - return Slice(internal.NewSlice(&orig)) -} - -// Len returns the number of elements in the slice. -// -// Returns "0" for a newly instance created with "NewSlice()". -func (es Slice) Len() int { - return len(*es.getOrig()) -} - -// At returns the element at the given index. -// -// This function is used mostly for iterating over all the values in the slice: -// -// for i := 0; i < es.Len(); i++ { -// e := es.At(i) -// ... // Do something with the element -// } -func (es Slice) At(ix int) Value { - return newValue(&(*es.getOrig())[ix]) -} - -// CopyTo copies all elements from the current slice overriding the destination. -func (es Slice) CopyTo(dest Slice) { - srcLen := es.Len() - destCap := cap(*dest.getOrig()) - if srcLen <= destCap { - (*dest.getOrig()) = (*dest.getOrig())[:srcLen:destCap] - } else { - (*dest.getOrig()) = make([]otlpcommon.AnyValue, srcLen) - } - - for i := range *es.getOrig() { - newValue(&(*es.getOrig())[i]).CopyTo(newValue(&(*dest.getOrig())[i])) - } -} - -// EnsureCapacity is an operation that ensures the slice has at least the specified capacity. -// 1. If the newCap <= cap then no change in capacity. -// 2. If the newCap > cap then the slice capacity will be expanded to equal newCap. -// -// Here is how a new Slice can be initialized: -// -// es := NewSlice() -// es.EnsureCapacity(4) -// for i := 0; i < 4; i++ { -// e := es.AppendEmpty() -// // Here should set all the values for e. -// } -func (es Slice) EnsureCapacity(newCap int) { - oldCap := cap(*es.getOrig()) - if newCap <= oldCap { - return - } - - newOrig := make([]otlpcommon.AnyValue, len(*es.getOrig()), newCap) - copy(newOrig, *es.getOrig()) - *es.getOrig() = newOrig -} - -// AppendEmpty will append to the end of the slice an empty Value. -// It returns the newly added Value. -func (es Slice) AppendEmpty() Value { - *es.getOrig() = append(*es.getOrig(), otlpcommon.AnyValue{}) - return es.At(es.Len() - 1) -} - -// MoveAndAppendTo moves all elements from the current slice and appends them to the dest. -// The current slice will be cleared. -func (es Slice) MoveAndAppendTo(dest Slice) { - if *dest.getOrig() == nil { - // We can simply move the entire vector and avoid any allocations. - *dest.getOrig() = *es.getOrig() - } else { - *dest.getOrig() = append(*dest.getOrig(), *es.getOrig()...) - } - *es.getOrig() = nil -} - -// RemoveIf calls f sequentially for each element present in the slice. -// If f returns true, the element is removed from the slice. -func (es Slice) RemoveIf(f func(Value) bool) { - newLen := 0 - for i := 0; i < len(*es.getOrig()); i++ { - if f(es.At(i)) { - continue - } - if newLen == i { - // Nothing to move, element is at the right place. - newLen++ - continue - } - (*es.getOrig())[newLen] = (*es.getOrig())[i] - newLen++ - } - // TODO: Prevent memory leak by erasing truncated values. - *es.getOrig() = (*es.getOrig())[:newLen] -} - -// AsRaw return []any copy of the Slice. -func (es Slice) AsRaw() []any { - rawSlice := make([]any, 0, es.Len()) - for i := 0; i < es.Len(); i++ { - rawSlice = append(rawSlice, es.At(i).AsRaw()) - } - return rawSlice -} - -// FromRaw copies []any into the Slice. -func (es Slice) FromRaw(rawSlice []any) error { - if len(rawSlice) == 0 { - *es.getOrig() = nil - return nil - } - var errs error - origs := make([]otlpcommon.AnyValue, len(rawSlice)) - for ix, iv := range rawSlice { - errs = multierr.Append(errs, newValue(&origs[ix]).FromRaw(iv)) - } - *es.getOrig() = origs - return errs -} diff --git a/pdata/pcommon/value_test.go b/pdata/pcommon/value_test.go new file mode 100644 index 00000000000..6ab6bdde41a --- /dev/null +++ b/pdata/pcommon/value_test.go @@ -0,0 +1,569 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pcommon + +import ( + "encoding/base64" + "math" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + otlpcommon "go.opentelemetry.io/collector/pdata/internal/data/protogen/common/v1" +) + +func TestValue(t *testing.T) { + v := NewValueStr("abc") + assert.EqualValues(t, ValueTypeStr, v.Type()) + assert.EqualValues(t, "abc", v.Str()) + + v = NewValueInt(123) + assert.EqualValues(t, ValueTypeInt, v.Type()) + assert.EqualValues(t, 123, v.Int()) + + v = NewValueDouble(3.4) + assert.EqualValues(t, ValueTypeDouble, v.Type()) + assert.EqualValues(t, 3.4, v.Double()) + + v = NewValueBool(true) + assert.EqualValues(t, ValueTypeBool, v.Type()) + assert.True(t, v.Bool()) + + v = NewValueBytes() + assert.EqualValues(t, ValueTypeBytes, v.Type()) + + v = NewValueEmpty() + assert.EqualValues(t, ValueTypeEmpty, v.Type()) + + v = NewValueMap() + assert.EqualValues(t, ValueTypeMap, v.Type()) + + v = NewValueSlice() + assert.EqualValues(t, ValueTypeSlice, v.Type()) +} + +func TestValueType(t *testing.T) { + assert.EqualValues(t, "Empty", ValueTypeEmpty.String()) + assert.EqualValues(t, "Str", ValueTypeStr.String()) + assert.EqualValues(t, "Bool", ValueTypeBool.String()) + assert.EqualValues(t, "Int", ValueTypeInt.String()) + assert.EqualValues(t, "Double", ValueTypeDouble.String()) + assert.EqualValues(t, "Map", ValueTypeMap.String()) + assert.EqualValues(t, "Slice", ValueTypeSlice.String()) + assert.EqualValues(t, "Bytes", ValueTypeBytes.String()) + assert.EqualValues(t, "", ValueType(100).String()) +} + +func TestValueMap(t *testing.T) { + m1 := NewValueMap() + assert.Equal(t, ValueTypeMap, m1.Type()) + assert.Equal(t, NewMap(), m1.Map()) + assert.Equal(t, 0, m1.Map().Len()) + + m1.Map().PutDouble("double_key", 123) + assert.Equal(t, 1, m1.Map().Len()) + got, exists := m1.Map().Get("double_key") + assert.True(t, exists) + assert.Equal(t, NewValueDouble(123), got) + + // Create a second map. + m2 := m1.Map().PutEmptyMap("child_map") + assert.Equal(t, 0, m2.Len()) + + // Modify the source map that was inserted. + m2.PutStr("key_in_child", "somestr") + assert.Equal(t, 1, m2.Len()) + got, exists = m2.Get("key_in_child") + assert.True(t, exists) + assert.Equal(t, NewValueStr("somestr"), got) + + // Insert the second map as a child. This should perform a deep copy. + assert.EqualValues(t, 2, m1.Map().Len()) + got, exists = m1.Map().Get("double_key") + assert.True(t, exists) + assert.Equal(t, NewValueDouble(123), got) + got, exists = m1.Map().Get("child_map") + assert.True(t, exists) + assert.Equal(t, m2, got.Map()) + + // Modify the source map m2 that was inserted into m1. + m2.PutStr("key_in_child", "somestr2") + assert.EqualValues(t, 1, m2.Len()) + got, exists = m2.Get("key_in_child") + assert.True(t, exists) + assert.Equal(t, NewValueStr("somestr2"), got) + + // The child map inside m1 should be modified. + childMap, childMapExists := m1.Map().Get("child_map") + require.True(t, childMapExists) + got, exists = childMap.Map().Get("key_in_child") + require.True(t, exists) + assert.Equal(t, NewValueStr("somestr2"), got) + + // Now modify the inserted map (not the source) + childMap.Map().PutStr("key_in_child", "somestr3") + assert.EqualValues(t, 1, childMap.Map().Len()) + got, exists = childMap.Map().Get("key_in_child") + require.True(t, exists) + assert.Equal(t, NewValueStr("somestr3"), got) + + // The source child map should be modified. + got, exists = m2.Get("key_in_child") + require.True(t, exists) + assert.Equal(t, NewValueStr("somestr3"), got) + + removed := m1.Map().Remove("double_key") + assert.True(t, removed) + assert.EqualValues(t, 1, m1.Map().Len()) + _, exists = m1.Map().Get("double_key") + assert.False(t, exists) + + removed = m1.Map().Remove("child_map") + assert.True(t, removed) + assert.EqualValues(t, 0, m1.Map().Len()) + _, exists = m1.Map().Get("child_map") + assert.False(t, exists) + + // Test nil KvlistValue case for Map() func. + orig := &otlpcommon.AnyValue{Value: &otlpcommon.AnyValue_KvlistValue{KvlistValue: nil}} + m1 = newValue(orig) + assert.EqualValues(t, Map{}, m1.Map()) +} + +func TestValueSlice(t *testing.T) { + a1 := NewValueSlice() + assert.EqualValues(t, ValueTypeSlice, a1.Type()) + assert.EqualValues(t, NewSlice(), a1.Slice()) + assert.EqualValues(t, 0, a1.Slice().Len()) + + a1.Slice().AppendEmpty().SetDouble(123) + assert.EqualValues(t, 1, a1.Slice().Len()) + assert.EqualValues(t, NewValueDouble(123), a1.Slice().At(0)) + // Create a second array. + a2 := NewValueSlice() + assert.EqualValues(t, 0, a2.Slice().Len()) + + a2.Slice().AppendEmpty().SetStr("somestr") + assert.EqualValues(t, 1, a2.Slice().Len()) + assert.EqualValues(t, NewValueStr("somestr"), a2.Slice().At(0)) + + // Insert the second array as a child. + a2.CopyTo(a1.Slice().AppendEmpty()) + assert.EqualValues(t, 2, a1.Slice().Len()) + assert.EqualValues(t, NewValueDouble(123), a1.Slice().At(0)) + assert.EqualValues(t, a2, a1.Slice().At(1)) + + // Check that the array was correctly inserted. + childArray := a1.Slice().At(1) + assert.EqualValues(t, ValueTypeSlice, childArray.Type()) + assert.EqualValues(t, 1, childArray.Slice().Len()) + + v := childArray.Slice().At(0) + assert.EqualValues(t, ValueTypeStr, v.Type()) + assert.EqualValues(t, "somestr", v.Str()) + + // Test nil values case for Slice() func. + a1 = newValue(&otlpcommon.AnyValue{Value: &otlpcommon.AnyValue_ArrayValue{ArrayValue: nil}}) + assert.EqualValues(t, newSlice(nil), a1.Slice()) +} + +func TestNilOrigSetValue(t *testing.T) { + av := NewValueEmpty() + av.SetStr("abc") + assert.EqualValues(t, "abc", av.Str()) + + av = NewValueEmpty() + av.SetInt(123) + assert.EqualValues(t, 123, av.Int()) + + av = NewValueEmpty() + av.SetBool(true) + assert.True(t, av.Bool()) + + av = NewValueEmpty() + av.SetDouble(1.23) + assert.EqualValues(t, 1.23, av.Double()) + + av = NewValueEmpty() + av.SetEmptyBytes().FromRaw([]byte{1, 2, 3}) + assert.Equal(t, []byte{1, 2, 3}, av.Bytes().AsRaw()) + + av = NewValueEmpty() + assert.NoError(t, av.SetEmptyMap().FromRaw(map[string]any{"k": "v"})) + assert.Equal(t, map[string]any{"k": "v"}, av.Map().AsRaw()) + + av = NewValueEmpty() + assert.NoError(t, av.SetEmptySlice().FromRaw([]any{int64(1), "val"})) + assert.Equal(t, []any{int64(1), "val"}, av.Slice().AsRaw()) +} + +func TestValue_CopyTo(t *testing.T) { + // Test nil KvlistValue case for Map() func. + dest := NewValueEmpty() + orig := &otlpcommon.AnyValue{Value: &otlpcommon.AnyValue_KvlistValue{KvlistValue: nil}} + newValue(orig).CopyTo(dest) + assert.Nil(t, dest.getOrig().Value.(*otlpcommon.AnyValue_KvlistValue).KvlistValue) + + // Test nil ArrayValue case for Slice() func. + dest = NewValueEmpty() + orig = &otlpcommon.AnyValue{Value: &otlpcommon.AnyValue_ArrayValue{ArrayValue: nil}} + newValue(orig).CopyTo(dest) + assert.Nil(t, dest.getOrig().Value.(*otlpcommon.AnyValue_ArrayValue).ArrayValue) + + // Test copy empty value. + orig = &otlpcommon.AnyValue{} + newValue(orig).CopyTo(dest) + assert.Nil(t, dest.getOrig().Value) + + av := NewValueEmpty() + destVal := otlpcommon.AnyValue{Value: &otlpcommon.AnyValue_IntValue{}} + av.CopyTo(newValue(&destVal)) + assert.EqualValues(t, nil, destVal.Value) +} + +func TestSliceWithNilValues(t *testing.T) { + origWithNil := []otlpcommon.AnyValue{ + {}, + {Value: &otlpcommon.AnyValue_StringValue{StringValue: "test_value"}}, + } + sm := newSlice(&origWithNil) + + val := sm.At(0) + assert.EqualValues(t, ValueTypeEmpty, val.Type()) + assert.EqualValues(t, "", val.Str()) + + val = sm.At(1) + assert.EqualValues(t, ValueTypeStr, val.Type()) + assert.EqualValues(t, "test_value", val.Str()) + + sm.AppendEmpty().SetStr("other_value") + val = sm.At(2) + assert.EqualValues(t, ValueTypeStr, val.Type()) + assert.EqualValues(t, "other_value", val.Str()) +} + +func TestValueAsString(t *testing.T) { + tests := []struct { + name string + input Value + expected string + }{ + { + name: "string", + input: NewValueStr("string value"), + expected: "string value", + }, + { + name: "int64", + input: NewValueInt(42), + expected: "42", + }, + { + name: "float64", + input: NewValueDouble(1.61803399), + expected: "1.61803399", + }, + { + name: "small float64", + input: NewValueDouble(.000000009), + expected: "9e-9", + }, + { + name: "bad float64", + input: NewValueDouble(math.Inf(1)), + expected: "json: unsupported value: +Inf", + }, + { + name: "boolean", + input: NewValueBool(true), + expected: "true", + }, + { + name: "empty_map", + input: NewValueMap(), + expected: "{}", + }, + { + name: "simple_map", + input: generateTestValueMap(), + expected: "{\"arrKey\":[\"strOne\",\"strTwo\"],\"boolKey\":false,\"floatKey\":18.6,\"intKey\":7,\"mapKey\":{\"keyOne\":\"valOne\",\"keyTwo\":\"valTwo\"},\"nullKey\":null,\"strKey\":\"strVal\"}", + }, + { + name: "empty_array", + input: NewValueSlice(), + expected: "[]", + }, + { + name: "simple_array", + input: generateTestValueSlice(), + expected: "[\"strVal\",7,18.6,false,null]", + }, + { + name: "empty", + input: NewValueEmpty(), + expected: "", + }, + { + name: "bytes", + input: generateTestValueBytes(), + expected: base64.StdEncoding.EncodeToString([]byte("String bytes")), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + actual := test.input.AsString() + assert.Equal(t, test.expected, actual) + }) + } +} + +func TestValueAsRaw(t *testing.T) { + tests := []struct { + name string + input Value + expected any + }{ + { + name: "string", + input: NewValueStr("value"), + expected: "value", + }, + { + name: "int", + input: NewValueInt(11), + expected: int64(11), + }, + { + name: "double", + input: NewValueDouble(1.2), + expected: 1.2, + }, + { + name: "bytes", + input: generateTestValueBytes(), + expected: []byte("String bytes"), + }, + { + name: "empty", + input: NewValueEmpty(), + expected: nil, + }, + { + name: "slice", + input: generateTestValueSlice(), + expected: []any{"strVal", int64(7), 18.6, false, nil}, + }, + { + name: "map", + input: generateTestValueMap(), + expected: map[string]any{ + "mapKey": map[string]any{"keyOne": "valOne", "keyTwo": "valTwo"}, + "nullKey": nil, + "strKey": "strVal", + "arrKey": []any{"strOne", "strTwo"}, + "boolKey": false, + "floatKey": 18.6, + "intKey": int64(7), + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + actual := test.input.AsRaw() + assert.Equal(t, test.expected, actual) + }) + } +} + +func TestNewValueFromRaw(t *testing.T) { + tests := []struct { + name string + input any + expected Value + }{ + { + name: "nil", + input: nil, + expected: NewValueEmpty(), + }, + { + name: "string", + input: "text", + expected: NewValueStr("text"), + }, + { + name: "int", + input: 123, + expected: NewValueInt(int64(123)), + }, + { + name: "int8", + input: int8(12), + expected: NewValueInt(int64(12)), + }, + { + name: "int16", + input: int16(23), + expected: NewValueInt(int64(23)), + }, + { + name: "int32", + input: int32(34), + expected: NewValueInt(int64(34)), + }, + { + name: "int64", + input: int64(45), + expected: NewValueInt(45), + }, + { + name: "uint", + input: uint(56), + expected: NewValueInt(int64(56)), + }, + { + name: "uint8", + input: uint8(67), + expected: NewValueInt(int64(67)), + }, + { + name: "uint16", + input: uint16(78), + expected: NewValueInt(int64(78)), + }, + { + name: "uint32", + input: uint32(89), + expected: NewValueInt(int64(89)), + }, + { + name: "uint64", + input: uint64(90), + expected: NewValueInt(int64(90)), + }, + { + name: "float32", + input: float32(1.234), + expected: NewValueDouble(float64(float32(1.234))), + }, + { + name: "float64", + input: float64(2.345), + expected: NewValueDouble(float64(2.345)), + }, + { + name: "bool", + input: true, + expected: NewValueBool(true), + }, + { + name: "bytes", + input: []byte{1, 2, 3}, + expected: func() Value { + m := NewValueBytes() + m.Bytes().FromRaw([]byte{1, 2, 3}) + return m + }(), + }, + { + name: "map", + input: map[string]any{ + "k": "v", + }, + expected: func() Value { + m := NewValueMap() + assert.NoError(t, m.Map().FromRaw(map[string]any{"k": "v"})) + return m + }(), + }, + { + name: "empty map", + input: map[string]any{}, + expected: func() Value { + m := NewValueMap() + assert.NoError(t, m.Map().FromRaw(map[string]any{})) + return m + }(), + }, + { + name: "slice", + input: []any{"v1", "v2"}, + expected: (func() Value { + s := NewValueSlice() + assert.NoError(t, s.Slice().FromRaw([]any{"v1", "v2"})) + return s + })(), + }, + { + name: "empty slice", + input: []any{}, + expected: (func() Value { + s := NewValueSlice() + assert.NoError(t, s.Slice().FromRaw([]any{})) + return s + })(), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual := NewValueEmpty() + assert.NoError(t, actual.FromRaw(tt.input)) + assert.Equal(t, tt.expected, actual) + }) + } +} + +func TestNewValueFromRawInvalid(t *testing.T) { + actual := NewValueEmpty() + assert.EqualError(t, actual.FromRaw(ValueTypeDouble), "") +} + +func generateTestValueMap() Value { + ret := NewValueMap() + attrMap := ret.Map() + attrMap.PutStr("strKey", "strVal") + attrMap.PutInt("intKey", 7) + attrMap.PutDouble("floatKey", 18.6) + attrMap.PutBool("boolKey", false) + attrMap.PutEmpty("nullKey") + + m := attrMap.PutEmptyMap("mapKey") + m.PutStr("keyOne", "valOne") + m.PutStr("keyTwo", "valTwo") + + s := attrMap.PutEmptySlice("arrKey") + s.AppendEmpty().SetStr("strOne") + s.AppendEmpty().SetStr("strTwo") + + return ret +} + +func generateTestValueSlice() Value { + ret := NewValueSlice() + attrArr := ret.Slice() + attrArr.AppendEmpty().SetStr("strVal") + attrArr.AppendEmpty().SetInt(7) + attrArr.AppendEmpty().SetDouble(18.6) + attrArr.AppendEmpty().SetBool(false) + attrArr.AppendEmpty() + return ret +} + +func generateTestValueBytes() Value { + v := NewValueBytes() + v.Bytes().FromRaw([]byte("String bytes")) + return v +}