Source file src/runtime/pprof/label_test.go

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package pprof
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"reflect"
    11  	"slices"
    12  	"strings"
    13  	"testing"
    14  )
    15  
    16  func labelsSorted(ctx context.Context) []label {
    17  	ls := []label{}
    18  	ForLabels(ctx, func(key, value string) bool {
    19  		ls = append(ls, label{key, value})
    20  		return true
    21  	})
    22  	slices.SortFunc(ls, func(a, b label) int { return strings.Compare(a.key, b.key) })
    23  	return ls
    24  }
    25  
    26  func TestContextLabels(t *testing.T) {
    27  	// Background context starts with no labels.
    28  	ctx := context.Background()
    29  	labels := labelsSorted(ctx)
    30  	if len(labels) != 0 {
    31  		t.Errorf("labels on background context: want [], got %v ", labels)
    32  	}
    33  
    34  	// Add a single label.
    35  	ctx = WithLabels(ctx, Labels("key", "value"))
    36  	// Retrieve it with Label.
    37  	v, ok := Label(ctx, "key")
    38  	if !ok || v != "value" {
    39  		t.Errorf(`Label(ctx, "key"): got %v, %v; want "value", ok`, v, ok)
    40  	}
    41  	gotLabels := labelsSorted(ctx)
    42  	wantLabels := []label{{"key", "value"}}
    43  	if !reflect.DeepEqual(gotLabels, wantLabels) {
    44  		t.Errorf("(sorted) labels on context: got %v, want %v", gotLabels, wantLabels)
    45  	}
    46  
    47  	// Add a label with a different key.
    48  	ctx = WithLabels(ctx, Labels("key2", "value2"))
    49  	v, ok = Label(ctx, "key2")
    50  	if !ok || v != "value2" {
    51  		t.Errorf(`Label(ctx, "key2"): got %v, %v; want "value2", ok`, v, ok)
    52  	}
    53  	gotLabels = labelsSorted(ctx)
    54  	wantLabels = []label{{"key", "value"}, {"key2", "value2"}}
    55  	if !reflect.DeepEqual(gotLabels, wantLabels) {
    56  		t.Errorf("(sorted) labels on context: got %v, want %v", gotLabels, wantLabels)
    57  	}
    58  
    59  	// Add label with first key to test label replacement.
    60  	ctx = WithLabels(ctx, Labels("key", "value3"))
    61  	v, ok = Label(ctx, "key")
    62  	if !ok || v != "value3" {
    63  		t.Errorf(`Label(ctx, "key3"): got %v, %v; want "value3", ok`, v, ok)
    64  	}
    65  	gotLabels = labelsSorted(ctx)
    66  	wantLabels = []label{{"key", "value3"}, {"key2", "value2"}}
    67  	if !reflect.DeepEqual(gotLabels, wantLabels) {
    68  		t.Errorf("(sorted) labels on context: got %v, want %v", gotLabels, wantLabels)
    69  	}
    70  
    71  	// Labels called with two labels with the same key should pick the second.
    72  	ctx = WithLabels(ctx, Labels("key4", "value4a", "key4", "value4b"))
    73  	v, ok = Label(ctx, "key4")
    74  	if !ok || v != "value4b" {
    75  		t.Errorf(`Label(ctx, "key4"): got %v, %v; want "value4b", ok`, v, ok)
    76  	}
    77  	gotLabels = labelsSorted(ctx)
    78  	wantLabels = []label{{"key", "value3"}, {"key2", "value2"}, {"key4", "value4b"}}
    79  	if !reflect.DeepEqual(gotLabels, wantLabels) {
    80  		t.Errorf("(sorted) labels on context: got %v, want %v", gotLabels, wantLabels)
    81  	}
    82  }
    83  
    84  func TestLabelMapStringer(t *testing.T) {
    85  	for _, tbl := range []struct {
    86  		m        labelMap
    87  		expected string
    88  	}{
    89  		{
    90  			m: labelMap{
    91  				// empty map
    92  			},
    93  			expected: "{}",
    94  		}, {
    95  			m: labelMap{
    96  				"foo": "bar",
    97  			},
    98  			expected: `{"foo":"bar"}`,
    99  		}, {
   100  			m: labelMap{
   101  				"foo":             "bar",
   102  				"key1":            "value1",
   103  				"key2":            "value2",
   104  				"key3":            "value3",
   105  				"key4WithNewline": "\nvalue4",
   106  			},
   107  			expected: `{"foo":"bar", "key1":"value1", "key2":"value2", "key3":"value3", "key4WithNewline":"\nvalue4"}`,
   108  		},
   109  	} {
   110  		if got := tbl.m.String(); tbl.expected != got {
   111  			t.Errorf("%#v.String() = %q; want %q", tbl.m, got, tbl.expected)
   112  		}
   113  	}
   114  }
   115  
   116  func BenchmarkLabels(b *testing.B) {
   117  	b.Run("set-one", func(b *testing.B) {
   118  		b.ReportAllocs()
   119  		b.ResetTimer()
   120  		for i := 0; i < b.N; i++ {
   121  			Do(context.Background(), Labels("key", "value"), func(context.Context) {})
   122  		}
   123  	})
   124  
   125  	b.Run("merge-one", func(b *testing.B) {
   126  		ctx := WithLabels(context.Background(), Labels("key1", "val1"))
   127  
   128  		b.ReportAllocs()
   129  		b.ResetTimer()
   130  		for i := 0; i < b.N; i++ {
   131  			Do(ctx, Labels("key2", "value2"), func(context.Context) {})
   132  		}
   133  	})
   134  
   135  	b.Run("overwrite-one", func(b *testing.B) {
   136  		ctx := WithLabels(context.Background(), Labels("key", "val"))
   137  
   138  		b.ReportAllocs()
   139  		b.ResetTimer()
   140  		for i := 0; i < b.N; i++ {
   141  			Do(ctx, Labels("key", "value"), func(context.Context) {})
   142  		}
   143  	})
   144  
   145  	for _, scenario := range []string{"ordered", "unordered"} {
   146  		var labels []string
   147  		for i := 0; i < 10; i++ {
   148  			labels = append(labels, fmt.Sprintf("key%03d", i), fmt.Sprintf("value%03d", i))
   149  		}
   150  		if scenario == "unordered" {
   151  			labels[0], labels[len(labels)-1] = labels[len(labels)-1], labels[0]
   152  		}
   153  
   154  		b.Run(scenario, func(b *testing.B) {
   155  			b.Run("set-many", func(b *testing.B) {
   156  				b.ReportAllocs()
   157  				b.ResetTimer()
   158  				for i := 0; i < b.N; i++ {
   159  					Do(context.Background(), Labels(labels...), func(context.Context) {})
   160  				}
   161  			})
   162  
   163  			b.Run("merge-many", func(b *testing.B) {
   164  				ctx := WithLabels(context.Background(), Labels(labels[:len(labels)/2]...))
   165  
   166  				b.ResetTimer()
   167  				b.ReportAllocs()
   168  				for i := 0; i < b.N; i++ {
   169  					Do(ctx, Labels(labels[len(labels)/2:]...), func(context.Context) {})
   170  				}
   171  			})
   172  
   173  			b.Run("overwrite-many", func(b *testing.B) {
   174  				ctx := WithLabels(context.Background(), Labels(labels...))
   175  
   176  				b.ReportAllocs()
   177  				b.ResetTimer()
   178  				for i := 0; i < b.N; i++ {
   179  					Do(ctx, Labels(labels...), func(context.Context) {})
   180  				}
   181  			})
   182  		})
   183  	}
   184  
   185  	// TODO: hit slow path in Labels
   186  }
   187  

View as plain text