blob: 3018693c247453ff4a5f9a47646946a9718a9595 [file] [log] [blame] [edit]
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pprof
import (
"context"
"fmt"
"reflect"
"slices"
"strings"
"testing"
)
func labelsSorted(ctx context.Context) []label {
ls := []label{}
ForLabels(ctx, func(key, value string) bool {
ls = append(ls, label{key, value})
return true
})
slices.SortFunc(ls, func(a, b label) int { return strings.Compare(a.key, b.key) })
return ls
}
func TestContextLabels(t *testing.T) {
// Background context starts with no labels.
ctx := context.Background()
labels := labelsSorted(ctx)
if len(labels) != 0 {
t.Errorf("labels on background context: want [], got %v ", labels)
}
// Add a single label.
ctx = WithLabels(ctx, Labels("key", "value"))
// Retrieve it with Label.
v, ok := Label(ctx, "key")
if !ok || v != "value" {
t.Errorf(`Label(ctx, "key"): got %v, %v; want "value", ok`, v, ok)
}
gotLabels := labelsSorted(ctx)
wantLabels := []label{{"key", "value"}}
if !reflect.DeepEqual(gotLabels, wantLabels) {
t.Errorf("(sorted) labels on context: got %v, want %v", gotLabels, wantLabels)
}
// Add a label with a different key.
ctx = WithLabels(ctx, Labels("key2", "value2"))
v, ok = Label(ctx, "key2")
if !ok || v != "value2" {
t.Errorf(`Label(ctx, "key2"): got %v, %v; want "value2", ok`, v, ok)
}
gotLabels = labelsSorted(ctx)
wantLabels = []label{{"key", "value"}, {"key2", "value2"}}
if !reflect.DeepEqual(gotLabels, wantLabels) {
t.Errorf("(sorted) labels on context: got %v, want %v", gotLabels, wantLabels)
}
// Add label with first key to test label replacement.
ctx = WithLabels(ctx, Labels("key", "value3"))
v, ok = Label(ctx, "key")
if !ok || v != "value3" {
t.Errorf(`Label(ctx, "key3"): got %v, %v; want "value3", ok`, v, ok)
}
gotLabels = labelsSorted(ctx)
wantLabels = []label{{"key", "value3"}, {"key2", "value2"}}
if !reflect.DeepEqual(gotLabels, wantLabels) {
t.Errorf("(sorted) labels on context: got %v, want %v", gotLabels, wantLabels)
}
// Labels called with two labels with the same key should pick the second.
ctx = WithLabels(ctx, Labels("key4", "value4a", "key4", "value4b"))
v, ok = Label(ctx, "key4")
if !ok || v != "value4b" {
t.Errorf(`Label(ctx, "key4"): got %v, %v; want "value4b", ok`, v, ok)
}
gotLabels = labelsSorted(ctx)
wantLabels = []label{{"key", "value3"}, {"key2", "value2"}, {"key4", "value4b"}}
if !reflect.DeepEqual(gotLabels, wantLabels) {
t.Errorf("(sorted) labels on context: got %v, want %v", gotLabels, wantLabels)
}
}
func TestLabelMapStringer(t *testing.T) {
for _, tbl := range []struct {
m labelMap
expected string
}{
{
m: labelMap{
// empty map
},
expected: "{}",
}, {
m: labelMap{
Labels("foo", "bar"),
},
expected: `{"foo":"bar"}`,
}, {
m: labelMap{
Labels(
"foo", "bar",
"key1", "value1",
"key2", "value2",
"key3", "value3",
"key4WithNewline", "\nvalue4",
),
},
expected: `{"foo":"bar", "key1":"value1", "key2":"value2", "key3":"value3", "key4WithNewline":"\nvalue4"}`,
},
} {
if got := tbl.m.String(); tbl.expected != got {
t.Errorf("%#v.String() = %q; want %q", tbl.m, got, tbl.expected)
}
}
}
func BenchmarkLabels(b *testing.B) {
b.Run("set-one", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
Do(context.Background(), Labels("key", "value"), func(context.Context) {})
}
})
b.Run("merge-one", func(b *testing.B) {
ctx := WithLabels(context.Background(), Labels("key1", "val1"))
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
Do(ctx, Labels("key2", "value2"), func(context.Context) {})
}
})
b.Run("overwrite-one", func(b *testing.B) {
ctx := WithLabels(context.Background(), Labels("key", "val"))
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
Do(ctx, Labels("key", "value"), func(context.Context) {})
}
})
for _, scenario := range []string{"ordered", "unordered"} {
var labels []string
for i := 0; i < 10; i++ {
labels = append(labels, fmt.Sprintf("key%03d", i), fmt.Sprintf("value%03d", i))
}
if scenario == "unordered" {
labels[0], labels[len(labels)-1] = labels[len(labels)-1], labels[0]
}
b.Run(scenario, func(b *testing.B) {
b.Run("set-many", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
Do(context.Background(), Labels(labels...), func(context.Context) {})
}
})
b.Run("merge-many", func(b *testing.B) {
ctx := WithLabels(context.Background(), Labels(labels[:len(labels)/2]...))
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
Do(ctx, Labels(labels[len(labels)/2:]...), func(context.Context) {})
}
})
b.Run("overwrite-many", func(b *testing.B) {
ctx := WithLabels(context.Background(), Labels(labels...))
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
Do(ctx, Labels(labels...), func(context.Context) {})
}
})
})
}
// TODO: hit slow path in Labels
}