github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/structs/flamebearer/flamebearer_test.go (about) 1 package flamebearer 2 3 import ( 4 . "github.com/onsi/ginkgo/v2" 5 . "github.com/onsi/gomega" 6 7 "github.com/pyroscope-io/pyroscope/pkg/storage/metadata" 8 "github.com/pyroscope-io/pyroscope/pkg/storage/segment" 9 "github.com/pyroscope-io/pyroscope/pkg/storage/tree" 10 ) 11 12 var ( 13 startTime = int64(1635508310) 14 durationDelta = int64(10) 15 samples = []uint64{1} 16 watermarks = map[int]int64{1: 1} 17 maxNodes = 1024 18 spyName = "spy-name" 19 sampleRate = uint32(10) 20 units = metadata.Units("units") 21 ) 22 23 var _ = Describe("FlamebearerProfile", func() { 24 Context("single", func() { 25 It("sets all attributes correctly", func() { 26 // taken from tree package tests 27 tree := tree.New() 28 tree.Insert([]byte("a;b"), uint64(1)) 29 tree.Insert([]byte("a;c"), uint64(2)) 30 31 timeline := &segment.Timeline{ 32 StartTime: startTime, 33 Samples: samples, 34 DurationDeltaNormalized: durationDelta, 35 Watermarks: watermarks, 36 } 37 38 p := NewProfile(ProfileConfig{ 39 Name: "name", 40 Tree: tree, 41 MaxNodes: maxNodes, 42 Timeline: timeline, 43 Metadata: metadata.Metadata{ 44 SpyName: spyName, 45 SampleRate: sampleRate, 46 Units: units, 47 }, 48 }) 49 50 // Flamebearer 51 Expect(p.Flamebearer.Names).To(Equal([]string{"total", "a", "c", "b"})) 52 Expect(p.Flamebearer.Levels).To(Equal([][]int{ 53 {0, 3, 0, 0}, 54 {0, 3, 0, 1}, 55 {0, 1, 1, 3, 0, 2, 2, 2}, 56 })) 57 Expect(p.Flamebearer.NumTicks).To(Equal(3)) 58 Expect(p.Flamebearer.MaxSelf).To(Equal(2)) 59 60 // Metadata 61 Expect(p.Metadata.Name).To(Equal("name")) 62 Expect(p.Metadata.Format).To(Equal("single")) 63 Expect(p.Metadata.SpyName).To(Equal(spyName)) 64 Expect(p.Metadata.SampleRate).To(Equal(sampleRate)) 65 Expect(p.Metadata.Units).To(Equal(units)) 66 67 // Timeline 68 Expect(p.Timeline.StartTime).To(Equal(startTime)) 69 Expect(p.Timeline.Samples).To(Equal(samples)) 70 Expect(p.Timeline.DurationDelta).To(Equal(durationDelta)) 71 Expect(p.Timeline.Watermarks).To(Equal(watermarks)) 72 73 // Ticks 74 Expect(p.LeftTicks).To(BeZero()) 75 Expect(p.RightTicks).To(BeZero()) 76 77 // Validate 78 Expect(p.Validate()).To(BeNil()) 79 }) 80 }) 81 }) 82 83 var _ = Describe("Diff", func() { 84 var treeA *tree.Tree 85 var treeB *tree.Tree 86 87 BeforeEach(func() { 88 treeA = tree.New() 89 treeA.Insert([]byte("a;b"), uint64(1)) 90 treeA.Insert([]byte("a;c"), uint64(2)) 91 treeB = tree.New() 92 treeB.Insert([]byte("a;b"), uint64(4)) 93 treeB.Insert([]byte("a;c"), uint64(8)) 94 }) 95 96 Context("sampleRate does not match", func() { 97 When("they are both set", func() { 98 It("returns an error", func() { 99 left := ProfileConfig{ 100 Name: "name", 101 Metadata: metadata.Metadata{SampleRate: 100}, 102 Tree: treeA, 103 MaxNodes: maxNodes, 104 } 105 right := ProfileConfig{ 106 Name: "name", 107 Metadata: metadata.Metadata{SampleRate: 101}, 108 Tree: treeB, 109 MaxNodes: maxNodes, 110 } 111 _, err := NewCombinedProfile(left, right) 112 Expect(err).To(MatchError("left sample rate (100) does not match right sample rate (101)")) 113 }) 114 }) 115 116 When("one of them is empty", func() { 117 It("does not return an error", func() { 118 left := ProfileConfig{ 119 Name: "name", 120 Metadata: metadata.Metadata{SampleRate: 0}, 121 Tree: treeA, 122 MaxNodes: maxNodes, 123 } 124 right := ProfileConfig{ 125 Name: "name", 126 Metadata: metadata.Metadata{SampleRate: 101}, 127 Tree: treeB, 128 MaxNodes: maxNodes, 129 } 130 131 _, err := NewCombinedProfile(left, right) 132 Expect(err).ToNot(HaveOccurred()) 133 134 _, err = NewCombinedProfile(right, left) 135 Expect(err).ToNot(HaveOccurred()) 136 }) 137 }) 138 }) 139 140 Context("units does not match", func() { 141 When("they are both set", func() { 142 It("returns an error", func() { 143 left := ProfileConfig{ 144 Name: "name", 145 Metadata: metadata.Metadata{Units: "unitA", SampleRate: sampleRate}, 146 Tree: treeA, 147 MaxNodes: maxNodes, 148 } 149 right := ProfileConfig{ 150 Name: "name", 151 Metadata: metadata.Metadata{Units: "unitB", SampleRate: sampleRate}, 152 Tree: treeB, 153 MaxNodes: maxNodes, 154 } 155 _, err := NewCombinedProfile(left, right) 156 Expect(err).To(MatchError("left units (unitA) does not match right units (unitB)")) 157 }) 158 }) 159 160 When("one of them is empty", func() { 161 It("does not return an error", func() { 162 left := ProfileConfig{ 163 Name: "name", 164 Metadata: metadata.Metadata{SampleRate: sampleRate}, 165 Tree: treeA, 166 MaxNodes: maxNodes, 167 } 168 right := ProfileConfig{ 169 Name: "name", 170 Metadata: metadata.Metadata{SampleRate: sampleRate, Units: "unitB"}, 171 Tree: treeB, 172 MaxNodes: maxNodes, 173 } 174 175 _, err := NewCombinedProfile(left, right) 176 Expect(err).ToNot(HaveOccurred()) 177 178 _, err = NewCombinedProfile(right, left) 179 Expect(err).ToNot(HaveOccurred()) 180 }) 181 }) 182 }) 183 184 Context("diff", func() { 185 It("sets all attributes correctly", func() { 186 // taken from tree package tests 187 treeA := tree.New() 188 treeA.Insert([]byte("a;b"), uint64(1)) 189 treeA.Insert([]byte("a;c"), uint64(2)) 190 treeB := tree.New() 191 treeB.Insert([]byte("a;b"), uint64(4)) 192 treeB.Insert([]byte("a;c"), uint64(8)) 193 194 left := ProfileConfig{ 195 Name: "name", 196 Metadata: metadata.Metadata{SampleRate: sampleRate, Units: units}, 197 Tree: treeA, 198 MaxNodes: maxNodes, 199 } 200 right := ProfileConfig{ 201 Name: "name", 202 Metadata: metadata.Metadata{SampleRate: sampleRate, Units: units}, 203 Tree: treeB, 204 MaxNodes: maxNodes, 205 } 206 207 p, err := NewCombinedProfile(left, right) 208 Expect(err).ToNot(HaveOccurred()) 209 210 // Flamebearer 211 Expect(p.Flamebearer.Names).To(Equal([]string{"total", "a", "c", "b"})) 212 Expect(p.Flamebearer.Levels).To(Equal([][]int{ 213 {0, 3, 0, 0, 12, 0, 0}, 214 {0, 3, 0, 0, 12, 0, 1}, 215 {0, 1, 1, 0, 4, 4, 3, 0, 2, 2, 0, 8, 8, 2}, 216 })) 217 Expect(p.Flamebearer.NumTicks).To(Equal(15)) 218 Expect(p.Flamebearer.MaxSelf).To(Equal(8)) 219 220 // Metadata 221 Expect(p.Metadata.Name).To(Equal("name")) 222 Expect(p.Metadata.Format).To(Equal("double")) 223 Expect(p.Metadata.SpyName).To(Equal("")) 224 Expect(p.Metadata.SampleRate).To(Equal(sampleRate)) 225 Expect(p.Metadata.Units).To(Equal(units)) 226 227 // Timeline 228 Expect(p.Timeline).To(BeNil()) 229 230 // Ticks 231 Expect(p.LeftTicks).To(Equal(uint64(3))) 232 Expect(p.RightTicks).To(Equal(uint64(12))) 233 234 // Validate 235 Expect(p.Validate()).To(BeNil()) 236 }) 237 }) 238 }) 239 240 var _ = Describe("Checking profile validation", func() { 241 When("the version is invalid", func() { 242 var fb FlamebearerProfile 243 BeforeEach(func() { 244 fb.Version = 2 245 }) 246 247 Context("and we validate the profile", func() { 248 It("returns an error", func() { 249 Expect(fb.Validate()).To(MatchError("unsupported version 2")) 250 }) 251 }) 252 }) 253 254 When("the format is unsupported", func() { 255 var fb FlamebearerProfile 256 257 Context("and we validate the profile", func() { 258 It("returns an error", func() { 259 Expect(fb.Validate()).To(MatchError("unsupported format ")) 260 }) 261 }) 262 }) 263 264 When("there are no names", func() { 265 var fb FlamebearerProfile 266 BeforeEach(func() { 267 fb.Metadata.Format = "single" 268 }) 269 270 Context("and we validate the profile", func() { 271 It("returns an error", func() { 272 Expect(fb.Validate()).To(MatchError("a profile must have at least one symbol name")) 273 }) 274 }) 275 }) 276 277 When("there are no levels", func() { 278 var fb FlamebearerProfile 279 BeforeEach(func() { 280 fb.Metadata.Format = "single" 281 fb.Flamebearer.Names = []string{"name"} 282 }) 283 284 Context("and we validate the profile", func() { 285 It("returns an error", func() { 286 Expect(fb.Validate()).To(MatchError("a profile must have at least one profiling level")) 287 }) 288 }) 289 }) 290 291 When("the level size is invalid for the profile format", func() { 292 Context("and we validate a single profile", func() { 293 var fb FlamebearerProfile 294 BeforeEach(func() { 295 fb.Metadata.Format = "single" 296 fb.Flamebearer.Names = []string{"name"} 297 fb.Flamebearer.Levels = [][]int{{0, 0, 0, 0, 0, 0, 0}} 298 }) 299 300 It("returns an error", func() { 301 Expect(fb.Validate()).To(MatchError("a profile level should have a multiple of 4 values, but there's a level with 7 values")) 302 }) 303 }) 304 305 Context("and we validate a double profile", func() { 306 var fb FlamebearerProfile 307 BeforeEach(func() { 308 fb.Metadata.Format = "double" 309 fb.Flamebearer.Names = []string{"name"} 310 fb.Flamebearer.Levels = [][]int{{0, 0, 0, 0}} 311 }) 312 313 It("returns an error", func() { 314 Expect(fb.Validate()).To(MatchError("a profile level should have a multiple of 7 values, but there's a level with 4 values")) 315 }) 316 }) 317 }) 318 319 When("the name index is invalid", func() { 320 Context("and we validate a single profile", func() { 321 var fb FlamebearerProfile 322 BeforeEach(func() { 323 fb.Metadata.Format = "single" 324 fb.Flamebearer.Names = []string{"name"} 325 fb.Flamebearer.Levels = [][]int{{0, 0, 0, 1}} 326 }) 327 328 It("returns an error", func() { 329 Expect(fb.Validate()).To(MatchError("invalid name index 1, it should be smaller than 1")) 330 }) 331 }) 332 333 Context("and we validate a double profile", func() { 334 var fb FlamebearerProfile 335 BeforeEach(func() { 336 fb.Metadata.Format = "double" 337 fb.Flamebearer.Names = []string{"name"} 338 fb.Flamebearer.Levels = [][]int{{0, 0, 0, 0, 0, 0, 1}} 339 }) 340 341 It("returns an error", func() { 342 Expect(fb.Validate()).To(MatchError("invalid name index 1, it should be smaller than 1")) 343 }) 344 }) 345 }) 346 347 When("everything is valid", func() { 348 Context("and we validate a single profile", func() { 349 var fb FlamebearerProfile 350 BeforeEach(func() { 351 fb.Metadata.Format = "single" 352 fb.Flamebearer.Names = []string{"name"} 353 fb.Flamebearer.Levels = [][]int{{0, 0, 0, 0}} 354 }) 355 356 It("returns no error", func() { 357 Expect(fb.Validate()).To(BeNil()) 358 }) 359 }) 360 361 Context("and we validate a double profile", func() { 362 var fb FlamebearerProfile 363 BeforeEach(func() { 364 fb.Metadata.Format = "double" 365 fb.Flamebearer.Names = []string{"name"} 366 fb.Flamebearer.Levels = [][]int{{0, 0, 0, 0, 0, 0, 0}} 367 }) 368 369 It("returns an error", func() { 370 Expect(fb.Validate()).To(BeNil()) 371 }) 372 }) 373 }) 374 })