package report import ( "fmt" "reflect" "testing" "github.com/google/pprof/profile" ) // makeTestStacks generates a StackSet from a supplied list of samples. func makeTestStacks(samples ...*profile.Sample) StackSet { prof := makeTestProfile(samples...) rpt := NewDefault(prof, Options{OutputFormat: Tree, CallTree: true}) return rpt.Stacks() } func TestStacks(t *testing.T) { // See report_test.go for the functions available to use in tests. main, foo, bar, tee := testL[0], testL[1], testL[2], testL[3] // stack holds an expected stack value found in StackSet. type stack struct { value int64 names []string } makeStack := func(value int64, names ...string) stack { return stack{value, names} } for _, c := range []struct { name string stacks StackSet expect []stack }{ { "simple", makeTestStacks( testSample(100, bar, foo, main), testSample(200, tee, foo, main), ), []stack{ makeStack(100, "0:root", "1:main", "2:foo", "3:bar"), makeStack(200, "0:root", "1:main", "2:foo", "4:tee"), }, }, { "recursion", makeTestStacks( testSample(100, bar, foo, foo, foo, main), testSample(200, bar, foo, foo, main), ), []stack{ // Note: Recursive calls to foo have different source indices. makeStack(100, "0:root", "1:main", "2:foo", "2:foo", "2:foo", "3:bar"), makeStack(200, "0:root", "1:main", "2:foo", "2:foo", "3:bar"), }, }, } { t.Run(c.name, func(t *testing.T) { var got []stack for _, s := range c.stacks.Stacks { stk := stack{ value: s.Value, names: make([]string, len(s.Sources)), } for i, src := range s.Sources { stk.names[i] = fmt.Sprint(src, ":", c.stacks.Sources[src].FullName) } got = append(got, stk) } if !reflect.DeepEqual(c.expect, got) { t.Errorf("expecting source %+v, got %+v", c.expect, got) } }) } } func TestStackSources(t *testing.T) { // See report_test.go for the functions available to use in tests. main, foo, bar, tee, inl := testL[0], testL[1], testL[2], testL[3], testL[5] type srcInfo struct { name string self int64 inlined bool } source := func(stacks StackSet, name string) srcInfo { src := findSource(stacks, name) return srcInfo{src.FullName, src.Self, src.Inlined} } for _, c := range []struct { name string stacks StackSet srcs []srcInfo }{ { "empty", makeTestStacks(), []srcInfo{}, }, { "two-leaves", makeTestStacks( testSample(100, bar, foo, main), testSample(200, tee, bar, foo, main), testSample(1000, tee, main), ), []srcInfo{ {"main", 0, false}, {"bar", 100, false}, {"foo", 0, false}, {"tee", 1200, false}, }, }, { "inlined", makeTestStacks( testSample(100, inl), testSample(200, inl), ), []srcInfo{ // inl has bar->tee {"tee", 300, true}, }, }, { "recursion", makeTestStacks( testSample(100, foo, foo, foo, main), testSample(100, foo, foo, main), ), []srcInfo{ {"main", 0, false}, {"foo", 200, false}, }, }, { "flat", makeTestStacks( testSample(100, main), testSample(100, foo), testSample(100, bar), testSample(100, tee), ), []srcInfo{ {"main", 100, false}, {"bar", 100, false}, {"foo", 100, false}, {"tee", 100, false}, }, }, } { t.Run(c.name, func(t *testing.T) { for _, expect := range c.srcs { got := source(c.stacks, expect.name) if !reflect.DeepEqual(expect, got) { t.Errorf("expecting source %+v, got %+v", expect, got) } } }) } } func findSource(stacks StackSet, name string) StackSource { for _, src := range stacks.Sources { if src.FullName == name { return src } } return StackSource{} }