1*4947cdc7SCole Faust// Copyright 2019 The Bazel Authors. All rights reserved. 2*4947cdc7SCole Faust// Use of this source code is governed by a BSD-style 3*4947cdc7SCole Faust// license that can be found in the LICENSE file. 4*4947cdc7SCole Faust 5*4947cdc7SCole Faustpackage starlark_test 6*4947cdc7SCole Faust 7*4947cdc7SCole Faustimport ( 8*4947cdc7SCole Faust "bytes" 9*4947cdc7SCole Faust "fmt" 10*4947cdc7SCole Faust "io/ioutil" 11*4947cdc7SCole Faust "os" 12*4947cdc7SCole Faust "os/exec" 13*4947cdc7SCole Faust "strings" 14*4947cdc7SCole Faust "testing" 15*4947cdc7SCole Faust 16*4947cdc7SCole Faust "go.starlark.net/starlark" 17*4947cdc7SCole Faust) 18*4947cdc7SCole Faust 19*4947cdc7SCole Faust// TestProfile is a simple integration test that the profiler 20*4947cdc7SCole Faust// emits minimally plausible pprof-compatible output. 21*4947cdc7SCole Faustfunc TestProfile(t *testing.T) { 22*4947cdc7SCole Faust prof, err := ioutil.TempFile("", "profile_test") 23*4947cdc7SCole Faust if err != nil { 24*4947cdc7SCole Faust t.Fatal(err) 25*4947cdc7SCole Faust } 26*4947cdc7SCole Faust defer prof.Close() 27*4947cdc7SCole Faust defer os.Remove(prof.Name()) 28*4947cdc7SCole Faust if err := starlark.StartProfile(prof); err != nil { 29*4947cdc7SCole Faust t.Fatal(err) 30*4947cdc7SCole Faust } 31*4947cdc7SCole Faust 32*4947cdc7SCole Faust const src = ` 33*4947cdc7SCole Faustdef fibonacci(n): 34*4947cdc7SCole Faust res = list(range(n)) 35*4947cdc7SCole Faust for i in res[2:]: 36*4947cdc7SCole Faust res[i] = res[i-2] + res[i-1] 37*4947cdc7SCole Faust return res 38*4947cdc7SCole Faust 39*4947cdc7SCole Faustfibonacci(100000) 40*4947cdc7SCole Faust` 41*4947cdc7SCole Faust 42*4947cdc7SCole Faust thread := new(starlark.Thread) 43*4947cdc7SCole Faust if _, err := starlark.ExecFile(thread, "foo.star", src, nil); err != nil { 44*4947cdc7SCole Faust _ = starlark.StopProfile() 45*4947cdc7SCole Faust t.Fatal(err) 46*4947cdc7SCole Faust } 47*4947cdc7SCole Faust if err := starlark.StopProfile(); err != nil { 48*4947cdc7SCole Faust t.Fatal(err) 49*4947cdc7SCole Faust } 50*4947cdc7SCole Faust prof.Sync() 51*4947cdc7SCole Faust cmd := exec.Command("go", "tool", "pprof", "-top", prof.Name()) 52*4947cdc7SCole Faust cmd.Stderr = new(bytes.Buffer) 53*4947cdc7SCole Faust cmd.Stdout = new(bytes.Buffer) 54*4947cdc7SCole Faust if err := cmd.Run(); err != nil { 55*4947cdc7SCole Faust t.Fatalf("pprof failed: %v; output=<<%s>>", err, cmd.Stderr) 56*4947cdc7SCole Faust } 57*4947cdc7SCole Faust 58*4947cdc7SCole Faust // Typical output (may vary by go release): 59*4947cdc7SCole Faust // 60*4947cdc7SCole Faust // Type: wall 61*4947cdc7SCole Faust // Time: Apr 4, 2019 at 11:10am (EDT) 62*4947cdc7SCole Faust // Duration: 251.62ms, Total samples = 250ms (99.36%) 63*4947cdc7SCole Faust // Showing nodes accounting for 250ms, 100% of 250ms total 64*4947cdc7SCole Faust // flat flat% sum% cum cum% 65*4947cdc7SCole Faust // 320ms 100% 100% 320ms 100% fibonacci 66*4947cdc7SCole Faust // 0 0% 100% 320ms 100% foo.star 67*4947cdc7SCole Faust // 68*4947cdc7SCole Faust // We'll assert a few key substrings are present. 69*4947cdc7SCole Faust got := fmt.Sprint(cmd.Stdout) 70*4947cdc7SCole Faust for _, want := range []string{ 71*4947cdc7SCole Faust "flat%", 72*4947cdc7SCole Faust "fibonacci", 73*4947cdc7SCole Faust "foo.star", 74*4947cdc7SCole Faust } { 75*4947cdc7SCole Faust if !strings.Contains(got, want) { 76*4947cdc7SCole Faust t.Errorf("output did not contain %q", want) 77*4947cdc7SCole Faust } 78*4947cdc7SCole Faust } 79*4947cdc7SCole Faust if t.Failed() { 80*4947cdc7SCole Faust t.Logf("stderr=%v", cmd.Stderr) 81*4947cdc7SCole Faust t.Logf("stdout=%v", cmd.Stdout) 82*4947cdc7SCole Faust } 83*4947cdc7SCole Faust} 84