github.com/grafana/pyroscope@v1.18.0/pkg/model/stacktraces_test.go (about) 1 package model 2 3 import ( 4 "testing" 5 6 "github.com/stretchr/testify/require" 7 8 ingestv1 "github.com/grafana/pyroscope/api/gen/proto/go/ingester/v1" 9 ) 10 11 func TestStackTraceMerger(t *testing.T) { 12 for _, tc := range []struct { 13 name string 14 in []*ingestv1.MergeProfilesStacktracesResult 15 maxNodes int64 16 expected *Tree 17 }{ 18 { 19 name: "empty", 20 in: []*ingestv1.MergeProfilesStacktracesResult{}, 21 expected: newTree(nil), 22 }, 23 { 24 name: "single", 25 in: []*ingestv1.MergeProfilesStacktracesResult{ 26 { 27 Stacktraces: []*ingestv1.StacktraceSample{ 28 { 29 FunctionIds: []int32{1, 0}, 30 Value: 1, 31 }, 32 { 33 FunctionIds: []int32{2, 1, 0}, 34 Value: 3, 35 }, 36 }, 37 FunctionNames: []string{"my", "other", "stack"}, 38 }, 39 }, 40 expected: newTree([]stacktraces{ 41 {locations: []string{"other", "my"}, value: 1}, 42 {locations: []string{"stack", "other", "my"}, value: 3}, 43 }), 44 }, 45 { 46 name: "multiple", 47 in: []*ingestv1.MergeProfilesStacktracesResult{ 48 { 49 Stacktraces: []*ingestv1.StacktraceSample{ 50 { 51 FunctionIds: []int32{1, 0}, 52 Value: 1, 53 }, 54 { 55 FunctionIds: []int32{2, 1, 0}, 56 Value: 3, 57 }, 58 { 59 FunctionIds: []int32{3}, 60 Value: 4, 61 }, 62 }, 63 FunctionNames: []string{"my", "other", "stack", "foo"}, 64 }, 65 { 66 Stacktraces: []*ingestv1.StacktraceSample{ 67 { 68 FunctionIds: []int32{1, 0}, 69 Value: 1, 70 }, 71 { 72 FunctionIds: []int32{2, 1, 0}, 73 Value: 3, 74 }, 75 { 76 FunctionIds: []int32{3}, 77 Value: 5, 78 }, 79 }, 80 FunctionNames: []string{"my", "other", "stack", "bar"}, 81 }, 82 }, 83 expected: newTree([]stacktraces{ 84 {locations: []string{"bar"}, value: 5}, 85 {locations: []string{"foo"}, value: 4}, 86 {locations: []string{"other", "my"}, value: 2}, 87 {locations: []string{"stack", "other", "my"}, value: 6}, 88 }), 89 }, 90 { 91 name: "multiple with truncation", 92 maxNodes: 3, 93 in: []*ingestv1.MergeProfilesStacktracesResult{ 94 { 95 Stacktraces: []*ingestv1.StacktraceSample{ 96 { 97 FunctionIds: []int32{1, 0}, 98 Value: 1, 99 }, 100 { 101 FunctionIds: []int32{2, 1, 0}, 102 Value: 3, 103 }, 104 { 105 FunctionIds: []int32{3}, 106 Value: 4, 107 }, 108 }, 109 FunctionNames: []string{"my", "qux", "stack", "foo"}, 110 }, 111 { 112 Stacktraces: []*ingestv1.StacktraceSample{ 113 { 114 FunctionIds: []int32{1, 0}, 115 Value: 1, 116 }, 117 { 118 FunctionIds: []int32{2, 1, 0}, 119 Value: 3, 120 }, 121 { 122 FunctionIds: []int32{3}, 123 Value: 5, 124 }, 125 }, 126 FunctionNames: []string{"my", "qux", "stack", "bar"}, 127 }, 128 }, 129 expected: newTree([]stacktraces{ 130 {locations: []string{"other"}, value: 9}, 131 {locations: []string{"qux", "my"}, value: 2}, 132 {locations: []string{"stack", "qux", "my"}, value: 6}, 133 }), 134 }, 135 } { 136 tc := tc 137 t.Run(tc.name, func(t *testing.T) { 138 m := NewStackTraceMerger() 139 for _, x := range tc.in { 140 m.MergeStackTraces(x.Stacktraces, x.FunctionNames) 141 } 142 yn := m.TreeBytes(tc.maxNodes) 143 actual, err := UnmarshalTree(yn) 144 require.NoError(t, err) 145 require.Equal(t, tc.expected.String(), actual.String()) 146 }) 147 } 148 }