1package cortexpb 2 3import ( 4 "encoding/json" 5 stdlibjson "encoding/json" 6 "math" 7 "testing" 8 "unsafe" 9 10 jsoniter "github.com/json-iterator/go" 11 "github.com/prometheus/prometheus/pkg/labels" 12 "github.com/prometheus/prometheus/pkg/textparse" 13 "github.com/stretchr/testify/assert" 14 "github.com/stretchr/testify/require" 15 "github.com/thanos-io/thanos/pkg/testutil" 16) 17 18// This test verifies that jsoninter uses our custom method for marshalling. 19// We do that by using "test sample" recognized by marshal function when in testing mode. 20func TestJsoniterMarshalForSample(t *testing.T) { 21 testMarshalling(t, jsoniter.Marshal, "test sample") 22} 23 24func TestStdlibJsonMarshalForSample(t *testing.T) { 25 testMarshalling(t, stdlibjson.Marshal, "json: error calling MarshalJSON for type cortexpb.Sample: test sample") 26} 27 28func testMarshalling(t *testing.T, marshalFn func(v interface{}) ([]byte, error), expectedError string) { 29 isTesting = true 30 defer func() { isTesting = false }() 31 32 out, err := marshalFn(Sample{Value: 12345, TimestampMs: 98765}) 33 require.NoError(t, err) 34 require.Equal(t, `[98.765,"12345"]`, string(out)) 35 36 _, err = marshalFn(Sample{Value: math.NaN(), TimestampMs: 0}) 37 require.EqualError(t, err, expectedError) 38 39 // If not testing, we get normal output. 40 isTesting = false 41 out, err = marshalFn(Sample{Value: math.NaN(), TimestampMs: 0}) 42 require.NoError(t, err) 43 require.Equal(t, `[0,"NaN"]`, string(out)) 44} 45 46// This test verifies that jsoninter uses our custom method for unmarshalling Sample. 47// As with Marshal, we rely on testing mode and special value that reports error. 48func TestJsoniterUnmarshalForSample(t *testing.T) { 49 testUnmarshalling(t, jsoniter.Unmarshal, "test sample") 50} 51 52func TestStdlibJsonUnmarshalForSample(t *testing.T) { 53 testUnmarshalling(t, json.Unmarshal, "test sample") 54} 55 56func testUnmarshalling(t *testing.T, unmarshalFn func(data []byte, v interface{}) error, expectedError string) { 57 isTesting = true 58 defer func() { isTesting = false }() 59 60 sample := Sample{} 61 62 err := unmarshalFn([]byte(`[98.765,"12345"]`), &sample) 63 require.NoError(t, err) 64 require.Equal(t, Sample{Value: 12345, TimestampMs: 98765}, sample) 65 66 err = unmarshalFn([]byte(`[0.0,"NaN"]`), &sample) 67 require.EqualError(t, err, expectedError) 68 69 isTesting = false 70 err = unmarshalFn([]byte(`[0.0,"NaN"]`), &sample) 71 require.NoError(t, err) 72 require.Equal(t, int64(0), sample.TimestampMs) 73 require.True(t, math.IsNaN(sample.Value)) 74} 75 76func TestMetricMetadataToMetricTypeToMetricType(t *testing.T) { 77 tc := []struct { 78 desc string 79 input MetricMetadata_MetricType 80 expected textparse.MetricType 81 }{ 82 { 83 desc: "with a single-word metric", 84 input: COUNTER, 85 expected: textparse.MetricTypeCounter, 86 }, 87 { 88 desc: "with a two-word metric", 89 input: STATESET, 90 expected: textparse.MetricTypeStateset, 91 }, 92 { 93 desc: "with an unknown metric", 94 input: MetricMetadata_MetricType(100), 95 expected: textparse.MetricTypeUnknown, 96 }, 97 } 98 99 for _, tt := range tc { 100 t.Run(tt.desc, func(t *testing.T) { 101 m := MetricMetadataMetricTypeToMetricType(tt.input) 102 testutil.Equals(t, tt.expected, m) 103 }) 104 } 105} 106 107func TestFromLabelAdaptersToLabels(t *testing.T) { 108 input := []LabelAdapter{{Name: "hello", Value: "world"}} 109 expected := labels.Labels{labels.Label{Name: "hello", Value: "world"}} 110 actual := FromLabelAdaptersToLabels(input) 111 112 assert.Equal(t, expected, actual) 113 114 // All strings must NOT be copied. 115 assert.Equal(t, uintptr(unsafe.Pointer(&input[0].Name)), uintptr(unsafe.Pointer(&actual[0].Name))) 116 assert.Equal(t, uintptr(unsafe.Pointer(&input[0].Value)), uintptr(unsafe.Pointer(&actual[0].Value))) 117} 118 119func TestFromLabelAdaptersToLabelsWithCopy(t *testing.T) { 120 input := []LabelAdapter{{Name: "hello", Value: "world"}} 121 expected := labels.Labels{labels.Label{Name: "hello", Value: "world"}} 122 actual := FromLabelAdaptersToLabelsWithCopy(input) 123 124 assert.Equal(t, expected, actual) 125 126 // All strings must be copied. 127 assert.NotEqual(t, uintptr(unsafe.Pointer(&input[0].Name)), uintptr(unsafe.Pointer(&actual[0].Name))) 128 assert.NotEqual(t, uintptr(unsafe.Pointer(&input[0].Value)), uintptr(unsafe.Pointer(&actual[0].Value))) 129} 130 131func BenchmarkFromLabelAdaptersToLabelsWithCopy(b *testing.B) { 132 input := []LabelAdapter{ 133 {Name: "hello", Value: "world"}, 134 {Name: "some label", Value: "and its value"}, 135 {Name: "long long long long long label name", Value: "perhaps even longer label value, but who's counting anyway?"}} 136 137 for i := 0; i < b.N; i++ { 138 FromLabelAdaptersToLabelsWithCopy(input) 139 } 140} 141