Fix profiles JSON unmarshaling logic for byte arrays (#13483)

Add generated test values for primitive slices to ensure we test all
paths.

Signed-off-by: Bogdan Drutu <bogdandrutu@gmail.com>
This commit is contained in:
Bogdan Drutu 2025-07-25 00:02:55 +03:00 committed by GitHub
parent 811b19c99b
commit 467db83ad3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 124 additions and 71 deletions

View File

@ -0,0 +1,25 @@
# Use this changelog template to create an entry for release notes.
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: bug_fix
# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
component: pdata/pprofiles
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Fix profiles JSON unmarshal logic for originalPayload. The bytes have to be base64 encoded.
# One or more tracking issues or pull requests related to the change
issues: [13483]
# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: [user]

View File

@ -189,10 +189,10 @@ var float64Slice = &primitiveSliceStruct{
structName: "Float64Slice",
packageName: "pcommon",
itemType: "float64",
testOrigVal: "1, 2, 3",
testInterfaceOrigVal: []any{1, 2, 3},
testSetVal: "5",
testNewVal: "1, 5, 3",
testOrigVal: "1.1, 2.2, 3.3",
testInterfaceOrigVal: []any{1.1, 2.2, 3.3},
testSetVal: "5.5",
testNewVal: "1.1, 5.5, 3.3",
}
var uInt64Slice = &primitiveSliceStruct{

View File

@ -36,12 +36,14 @@ func CopyOrig{{ .structName }}(dst, src []{{ .itemType }}) []{{ .itemType }} {
return append(dst, src...)
}
func FillTest{{ .structName }}(tv {{ .structName}}) {
func FillTest{{ .structName }}(ms {{ .structName}}) {
*ms.orig = []{{ .itemType }}{ {{ .testOrigVal }} }
}
func GenerateTest{{ .structName }}() {{ .structName }} {
orig := []{{ .itemType }}(nil)
state := StateMutable
var orig []{{ .itemType }} = nil
return {{ .structName }}{&orig, &state}
ms := New{{ .structName }}(&orig, &state)
FillTest{{ .structName }}(ms)
return ms
}

View File

@ -28,12 +28,14 @@ func CopyOrigByteSlice(dst, src []byte) []byte {
return append(dst, src...)
}
func FillTestByteSlice(tv ByteSlice) {
func FillTestByteSlice(ms ByteSlice) {
*ms.orig = []byte{1, 2, 3}
}
func GenerateTestByteSlice() ByteSlice {
orig := []byte(nil)
state := StateMutable
var orig []byte = nil
return ByteSlice{&orig, &state}
ms := NewByteSlice(&orig, &state)
FillTestByteSlice(ms)
return ms
}

View File

@ -28,12 +28,14 @@ func CopyOrigFloat64Slice(dst, src []float64) []float64 {
return append(dst, src...)
}
func FillTestFloat64Slice(tv Float64Slice) {
func FillTestFloat64Slice(ms Float64Slice) {
*ms.orig = []float64{1.1, 2.2, 3.3}
}
func GenerateTestFloat64Slice() Float64Slice {
orig := []float64(nil)
state := StateMutable
var orig []float64 = nil
return Float64Slice{&orig, &state}
ms := NewFloat64Slice(&orig, &state)
FillTestFloat64Slice(ms)
return ms
}

View File

@ -28,12 +28,14 @@ func CopyOrigInt32Slice(dst, src []int32) []int32 {
return append(dst, src...)
}
func FillTestInt32Slice(tv Int32Slice) {
func FillTestInt32Slice(ms Int32Slice) {
*ms.orig = []int32{1, 2, 3}
}
func GenerateTestInt32Slice() Int32Slice {
orig := []int32(nil)
state := StateMutable
var orig []int32 = nil
return Int32Slice{&orig, &state}
ms := NewInt32Slice(&orig, &state)
FillTestInt32Slice(ms)
return ms
}

View File

@ -28,12 +28,14 @@ func CopyOrigInt64Slice(dst, src []int64) []int64 {
return append(dst, src...)
}
func FillTestInt64Slice(tv Int64Slice) {
func FillTestInt64Slice(ms Int64Slice) {
*ms.orig = []int64{1, 2, 3}
}
func GenerateTestInt64Slice() Int64Slice {
orig := []int64(nil)
state := StateMutable
var orig []int64 = nil
return Int64Slice{&orig, &state}
ms := NewInt64Slice(&orig, &state)
FillTestInt64Slice(ms)
return ms
}

View File

@ -28,12 +28,14 @@ func CopyOrigStringSlice(dst, src []string) []string {
return append(dst, src...)
}
func FillTestStringSlice(tv StringSlice) {
func FillTestStringSlice(ms StringSlice) {
*ms.orig = []string{"a", "b", "c"}
}
func GenerateTestStringSlice() StringSlice {
orig := []string(nil)
state := StateMutable
var orig []string = nil
return StringSlice{&orig, &state}
ms := NewStringSlice(&orig, &state)
FillTestStringSlice(ms)
return ms
}

View File

@ -28,12 +28,14 @@ func CopyOrigUInt64Slice(dst, src []uint64) []uint64 {
return append(dst, src...)
}
func FillTestUInt64Slice(tv UInt64Slice) {
func FillTestUInt64Slice(ms UInt64Slice) {
*ms.orig = []uint64{1, 2, 3}
}
func GenerateTestUInt64Slice() UInt64Slice {
orig := []uint64(nil)
state := StateMutable
var orig []uint64 = nil
return UInt64Slice{&orig, &state}
ms := NewUInt64Slice(&orig, &state)
FillTestUInt64Slice(ms)
return ms
}

View File

@ -0,0 +1,23 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
// Code generated by "internal/cmd/pdatagen/main.go". DO NOT EDIT.
// To regenerate this file run "make genpdata".
package internal
import (
"encoding/base64"
jsoniter "github.com/json-iterator/go"
)
func UnmarshalJSONIterByteSlice(ms ByteSlice, iter *jsoniter.Iterator) {
buf := iter.ReadStringAsSlice()
*ms.orig = make([]byte, base64.StdEncoding.DecodedLen(len(buf)))
n, err := base64.StdEncoding.Decode(*ms.orig, buf)
if err != nil {
iter.ReportError("base64.Decode", err.Error())
}
*ms.orig = (*ms.orig)[:n]
}

View File

@ -4,9 +4,6 @@
package internal // import "go.opentelemetry.io/collector/pdata/internal"
import (
"encoding/base64"
"fmt"
jsoniter "github.com/json-iterator/go"
otlpcommon "go.opentelemetry.io/collector/pdata/internal/data/protogen/common/v1"
@ -101,14 +98,8 @@ func UnmarshalJSONIterValue(val Value, iter *jsoniter.Iterator) {
DoubleValue: json.ReadFloat64(iter),
}
case "bytesValue", "bytes_value":
v, err := base64.StdEncoding.DecodeString(iter.ReadString())
if err != nil {
iter.ReportError("bytesValue", fmt.Sprintf("base64 decode:%v", err))
break
}
val.orig.Value = &otlpcommon.AnyValue_BytesValue{
BytesValue: v,
}
val.orig.Value = &otlpcommon.AnyValue_BytesValue{}
UnmarshalJSONIterByteSlice(NewByteSlice(&val.orig.Value.(*otlpcommon.AnyValue_BytesValue).BytesValue, val.state), iter)
case "arrayValue", "array_value":
val.orig.Value = &otlpcommon.AnyValue_ArrayValue{
ArrayValue: readArray(iter),

View File

@ -17,45 +17,45 @@ import (
func TestNewFloat64Slice(t *testing.T) {
ms := NewFloat64Slice()
assert.Equal(t, 0, ms.Len())
ms.FromRaw([]float64{1, 2, 3})
ms.FromRaw([]float64{1.1, 2.2, 3.3})
assert.Equal(t, 3, ms.Len())
assert.Equal(t, []float64{1, 2, 3}, ms.AsRaw())
ms.SetAt(1, float64(5))
assert.Equal(t, []float64{1, 5, 3}, ms.AsRaw())
ms.FromRaw([]float64{3})
assert.Equal(t, []float64{1.1, 2.2, 3.3}, ms.AsRaw())
ms.SetAt(1, float64(5.5))
assert.Equal(t, []float64{1.1, 5.5, 3.3}, ms.AsRaw())
ms.FromRaw([]float64{3.3})
assert.Equal(t, 1, ms.Len())
assert.InDelta(t, float64(3), ms.At(0), 0.01)
assert.InDelta(t, float64(3.3), ms.At(0), 0.01)
cp := NewFloat64Slice()
ms.CopyTo(cp)
ms.SetAt(0, float64(2))
assert.InDelta(t, float64(2), ms.At(0), 0.01)
assert.InDelta(t, float64(3), cp.At(0), 0.01)
ms.SetAt(0, float64(2.2))
assert.InDelta(t, float64(2.2), ms.At(0), 0.01)
assert.InDelta(t, float64(3.3), cp.At(0), 0.01)
ms.CopyTo(cp)
assert.InDelta(t, float64(2), cp.At(0), 0.01)
assert.InDelta(t, float64(2.2), cp.At(0), 0.01)
mv := NewFloat64Slice()
ms.MoveTo(mv)
assert.Equal(t, 0, ms.Len())
assert.Equal(t, 1, mv.Len())
assert.InDelta(t, float64(2), mv.At(0), 0.01)
ms.FromRaw([]float64{1, 2, 3})
assert.InDelta(t, float64(2.2), mv.At(0), 0.01)
ms.FromRaw([]float64{1.1, 2.2, 3.3})
ms.MoveTo(mv)
assert.Equal(t, 3, mv.Len())
assert.InDelta(t, float64(1), mv.At(0), 0.01)
assert.InDelta(t, float64(1.1), mv.At(0), 0.01)
mv.MoveTo(mv)
assert.Equal(t, 3, mv.Len())
assert.InDelta(t, float64(1), mv.At(0), 0.01)
assert.InDelta(t, float64(1.1), mv.At(0), 0.01)
}
func TestFloat64SliceReadOnly(t *testing.T) {
raw := []float64{1, 2, 3}
raw := []float64{1.1, 2.2, 3.3}
state := internal.StateReadOnly
ms := Float64Slice(internal.NewFloat64Slice(&raw, &state))
assert.Equal(t, 3, ms.Len())
assert.InDelta(t, float64(1), ms.At(0), 0.01)
assert.Panics(t, func() { ms.Append(1) })
assert.InDelta(t, float64(1.1), ms.At(0), 0.01)
assert.Panics(t, func() { ms.Append(1.1) })
assert.Panics(t, func() { ms.EnsureCapacity(2) })
assert.Equal(t, raw, ms.AsRaw())
assert.Panics(t, func() { ms.FromRaw(raw) })
@ -71,10 +71,10 @@ func TestFloat64SliceReadOnly(t *testing.T) {
func TestFloat64SliceAppend(t *testing.T) {
ms := NewFloat64Slice()
ms.FromRaw([]float64{1, 2, 3})
ms.Append(5, 5)
ms.FromRaw([]float64{1.1, 2.2, 3.3})
ms.Append(5.5, 5.5)
assert.Equal(t, 5, ms.Len())
assert.InDelta(t, float64(5), ms.At(4), 0.01)
assert.InDelta(t, float64(5.5), ms.At(4), 0.01)
}
func TestFloat64SliceEnsureCapacity(t *testing.T) {
@ -87,7 +87,7 @@ func TestFloat64SliceEnsureCapacity(t *testing.T) {
func TestFloat64SliceAll(t *testing.T) {
ms := NewFloat64Slice()
ms.FromRaw([]float64{1, 2, 3})
ms.FromRaw([]float64{1.1, 2.2, 3.3})
assert.NotEmpty(t, ms.Len())
var c int
@ -107,12 +107,12 @@ func TestFloat64SliceMoveAndAppendTo(t *testing.T) {
assert.Equal(t, ms.Len(), 0)
// Test moving to empty slice
ms.FromRaw([]float64{1, 2, 3})
ms.FromRaw([]float64{1.1, 2.2, 3.3})
ms.MoveAndAppendTo(ms2)
assert.Equal(t, ms2.Len(), 3)
// Test moving to a non empty slice
ms.FromRaw([]float64{1, 2, 3})
ms.FromRaw([]float64{1.1, 2.2, 3.3})
ms.MoveAndAppendTo(ms2)
assert.Equal(t, ms2.Len(), 6)
}
@ -122,18 +122,18 @@ func TestFloat64SliceEqual(t *testing.T) {
ms2 := NewFloat64Slice()
assert.True(t, ms.Equal(ms2))
ms.Append(1, 2, 3)
ms.Append(1.1, 2.2, 3.3)
assert.False(t, ms.Equal(ms2))
ms2.Append(1, 2, 3)
ms2.Append(1.1, 2.2, 3.3)
assert.True(t, ms.Equal(ms2))
}
func BenchmarkFloat64SliceEqual(b *testing.B) {
ms := NewFloat64Slice()
ms.Append(1, 2, 3)
ms.Append(1.1, 2.2, 3.3)
cmp := NewFloat64Slice()
cmp.Append(1, 2, 3)
cmp.Append(1.1, 2.2, 3.3)
b.ResetTimer()
b.ReportAllocs()

View File

@ -187,7 +187,7 @@ func (p Profile) unmarshalJsoniter(iter *jsoniter.Iterator) {
case "originalPayloadFormat", "original_payload_format":
p.orig.OriginalPayloadFormat = iter.ReadString()
case "originalPayload", "original_payload":
p.orig.OriginalPayload = iter.ReadStringAsSlice()
internal.UnmarshalJSONIterByteSlice(internal.NewByteSlice(&p.orig.OriginalPayload, p.state), iter)
default:
iter.Skip()
}