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  })