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  				Labels("foo", "bar"),
    97  			},
    98  			expected: `{"foo":"bar"}`,
    99  		}, {
   100  			m: labelMap{
   101  				Labels(
   102  					"foo", "bar",
   103  					"key1", "value1",
   104  					"key2", "value2",
   105  					"key3", "value3",
   106  					"key4WithNewline", "\nvalue4",
   107  				),
   108  			},
   109  			expected: `{"foo":"bar", "key1":"value1", "key2":"value2", "key3":"value3", "key4WithNewline":"\nvalue4"}`,
   110  		},
   111  	} {
   112  		if got := tbl.m.String(); tbl.expected != got {
   113  			t.Errorf("%#v.String() = %q; want %q", tbl.m, got, tbl.expected)
   114  		}
   115  	}
   116  }
   117  
   118  func BenchmarkLabels(b *testing.B) {
   119  	b.Run("set-one", func(b *testing.B) {
   120  		b.ReportAllocs()
   121  		b.ResetTimer()
   122  		for i := 0; i < b.N; i++ {
   123  			Do(context.Background(), Labels("key", "value"), func(context.Context) {})
   124  		}
   125  	})
   126  
   127  	b.Run("merge-one", func(b *testing.B) {
   128  		ctx := WithLabels(context.Background(), Labels("key1", "val1"))
   129  
   130  		b.ReportAllocs()
   131  		b.ResetTimer()
   132  		for i := 0; i < b.N; i++ {
   133  			Do(ctx, Labels("key2", "value2"), func(context.Context) {})
   134  		}
   135  	})
   136  
   137  	b.Run("overwrite-one", func(b *testing.B) {
   138  		ctx := WithLabels(context.Background(), Labels("key", "val"))
   139  
   140  		b.ReportAllocs()
   141  		b.ResetTimer()
   142  		for i := 0; i < b.N; i++ {
   143  			Do(ctx, Labels("key", "value"), func(context.Context) {})
   144  		}
   145  	})
   146  
   147  	for _, scenario := range []string{"ordered", "unordered"} {
   148  		var labels []string
   149  		for i := 0; i < 10; i++ {
   150  			labels = append(labels, fmt.Sprintf("key%03d", i), fmt.Sprintf("value%03d", i))
   151  		}
   152  		if scenario == "unordered" {
   153  			labels[0], labels[len(labels)-1] = labels[len(labels)-1], labels[0]
   154  		}
   155  
   156  		b.Run(scenario, func(b *testing.B) {
   157  			b.Run("set-many", func(b *testing.B) {
   158  				b.ReportAllocs()
   159  				b.ResetTimer()
   160  				for i := 0; i < b.N; i++ {
   161  					Do(context.Background(), Labels(labels...), func(context.Context) {})
   162  				}
   163  			})
   164  
   165  			b.Run("merge-many", func(b *testing.B) {
   166  				ctx := WithLabels(context.Background(), Labels(labels[:len(labels)/2]...))
   167  
   168  				b.ResetTimer()
   169  				b.ReportAllocs()
   170  				for i := 0; i < b.N; i++ {
   171  					Do(ctx, Labels(labels[len(labels)/2:]...), func(context.Context) {})
   172  				}
   173  			})
   174  
   175  			b.Run("overwrite-many", func(b *testing.B) {
   176  				ctx := WithLabels(context.Background(), Labels(labels...))
   177  
   178  				b.ReportAllocs()
   179  				b.ResetTimer()
   180  				for i := 0; i < b.N; i++ {
   181  					Do(ctx, Labels(labels...), func(context.Context) {})
   182  				}
   183  			})
   184  		})
   185  	}
   186  
   187  	// TODO: hit slow path in Labels
   188  }
   189  

View as plain text