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

View as plain text