123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- // Copyright 2014 Google Inc. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
-
- package driver
-
- import (
- "fmt"
- "math/rand"
- "strings"
- "testing"
-
- "github.com/google/pprof/pkg/plugin"
- "github.com/google/pprof/pkg/proftest"
- "github.com/google/pprof/pkg/report"
- "github.com/google/pprof/pkg/transport"
- "github.com/google/pprof/profile"
- )
-
- func TestShell(t *testing.T) {
- p := &profile.Profile{}
- generateReportWrapper = checkValue
- defer func() { generateReportWrapper = generateReport }()
-
- // Use test commands and variables to exercise interactive processing
- var savedCommands commands
- savedCommands, pprofCommands = pprofCommands, testCommands
- defer func() { pprofCommands = savedCommands }()
-
- savedConfig := currentConfig()
- defer setCurrentConfig(savedConfig)
-
- shortcuts1, scScript1 := makeShortcuts(interleave(script, 2), 1)
- shortcuts2, scScript2 := makeShortcuts(interleave(script, 1), 2)
-
- var testcases = []struct {
- name string
- input []string
- shortcuts shortcuts
- allowRx string
- numAllowRxMatches int
- propagateError bool
- }{
- {"Random interleave of independent scripts 1", interleave(script, 0), pprofShortcuts, "", 0, false},
- {"Random interleave of independent scripts 2", interleave(script, 1), pprofShortcuts, "", 0, false},
- {"Random interleave of independent scripts with shortcuts 1", scScript1, shortcuts1, "", 0, false},
- {"Random interleave of independent scripts with shortcuts 2", scScript2, shortcuts2, "", 0, false},
- {"Group with invalid value", []string{"sort=this"}, pprofShortcuts, `invalid "sort" value`, 1, false},
- {"No special value provided for the option", []string{"sample_index"}, pprofShortcuts, `please specify a value, e.g. sample_index=<val>`, 1, false},
- {"No string value provided for the option", []string{"focus"}, pprofShortcuts, `please specify a value, e.g. focus=<val>`, 1, false},
- {"No float value provided for the option", []string{"divide_by"}, pprofShortcuts, `please specify a value, e.g. divide_by=<val>`, 1, false},
- {"Helpful input format reminder", []string{"sample_index 0"}, pprofShortcuts, `did you mean: sample_index=0`, 1, false},
- {"Verify propagation of IO errors", []string{"**error**"}, pprofShortcuts, "", 0, true},
- }
-
- o := setDefaults(&plugin.Options{HTTPTransport: transport.New(nil)})
- for _, tc := range testcases {
- t.Run(tc.name, func(t *testing.T) {
- setCurrentConfig(savedConfig)
- pprofShortcuts = tc.shortcuts
- ui := &proftest.TestUI{
- T: t,
- Input: tc.input,
- AllowRx: tc.allowRx,
- }
- o.UI = ui
-
- err := interactive(p, o)
- if (tc.propagateError && err == nil) || (!tc.propagateError && err != nil) {
- t.Errorf("%s: %v", tc.name, err)
- }
-
- // Confirm error message written out once.
- if tc.numAllowRxMatches != ui.NumAllowRxMatches {
- t.Errorf("want error message to be printed %d time(s), got %d",
- tc.numAllowRxMatches, ui.NumAllowRxMatches)
- }
- })
- }
- }
-
- var testCommands = commands{
- "check": &command{report.Raw, nil, nil, true, "", ""},
- }
-
- // script contains sequences of commands to be executed for testing. Commands
- // are split by semicolon and interleaved randomly, so they must be
- // independent from each other.
- var script = []string{
- "call_tree=true;call_tree=false;check call_tree=false;call_tree=yes;check call_tree=true",
- "mean=1;check mean=true;mean=n;check mean=false",
- "nodecount=-1;nodecount=-2;check nodecount=-2;nodecount=999999;check nodecount=999999",
- "nodefraction=-1;nodefraction=-2.5;check nodefraction=-2.5;nodefraction=0.0001;check nodefraction=0.0001",
- "focus=one;focus=two;check focus=two",
- "flat=true;check sort=flat;cum=1;check sort=cum",
- }
-
- func makeShortcuts(input []string, seed int) (shortcuts, []string) {
- rand.Seed(int64(seed))
-
- s := shortcuts{}
- var output, chunk []string
- for _, l := range input {
- chunk = append(chunk, l)
- switch rand.Intn(3) {
- case 0:
- // Create a macro for commands in 'chunk'.
- macro := fmt.Sprintf("alias%d", len(s))
- s[macro] = chunk
- output = append(output, macro)
- chunk = nil
- case 1:
- // Append commands in 'chunk' by themselves.
- output = append(output, chunk...)
- chunk = nil
- case 2:
- // Accumulate commands into 'chunk'
- }
- }
- output = append(output, chunk...)
- return s, output
- }
-
- func checkValue(p *profile.Profile, cmd []string, cfg config, o *plugin.Options) error {
- if len(cmd) != 2 {
- return fmt.Errorf("expected len(cmd)==2, got %v", cmd)
- }
-
- input := cmd[1]
- args := strings.SplitN(input, "=", 2)
- if len(args) == 0 {
- return fmt.Errorf("unexpected empty input")
- }
- name, value := args[0], ""
- if len(args) == 2 {
- value = args[1]
- }
-
- f, ok := configFieldMap[name]
- if !ok {
- return fmt.Errorf("Could not find variable named %s", name)
- }
-
- if got := cfg.get(f); got != value {
- return fmt.Errorf("Variable %s, want %s, got %s", name, value, got)
- }
- return nil
- }
-
- func interleave(input []string, seed int) []string {
- var inputs [][]string
- for _, s := range input {
- inputs = append(inputs, strings.Split(s, ";"))
- }
- rand.Seed(int64(seed))
- var output []string
- for len(inputs) > 0 {
- next := rand.Intn(len(inputs))
- output = append(output, inputs[next][0])
- if tail := inputs[next][1:]; len(tail) > 0 {
- inputs[next] = tail
- } else {
- inputs = append(inputs[:next], inputs[next+1:]...)
- }
- }
- return output
- }
-
- func TestInteractiveCommands(t *testing.T) {
- type interactiveTestcase struct {
- input string
- want map[string]string
- }
-
- testcases := []interactiveTestcase{
- {
- "top 10 --cum focus1 -ignore focus2",
- map[string]string{
- "granularity": "functions",
- "nodecount": "10",
- "sort": "cum",
- "focus": "focus1|focus2",
- "ignore": "ignore",
- },
- },
- {
- "top10 --cum focus1 -ignore focus2",
- map[string]string{
- "granularity": "functions",
- "nodecount": "10",
- "sort": "cum",
- "focus": "focus1|focus2",
- "ignore": "ignore",
- },
- },
- {
- "dot",
- map[string]string{
- "granularity": "functions",
- "nodecount": "80",
- "sort": "flat",
- },
- },
- {
- "tags -ignore1 -ignore2 focus1 >out",
- map[string]string{
- "granularity": "functions",
- "nodecount": "80",
- "sort": "flat",
- "output": "out",
- "tagfocus": "focus1",
- "tagignore": "ignore1|ignore2",
- },
- },
- {
- "weblist find -test",
- map[string]string{
- "granularity": "addresses",
- "noinlines": "true",
- "nodecount": "0",
- "sort": "flat",
- "ignore": "test",
- },
- },
- {
- "callgrind fun -ignore >out",
- map[string]string{
- "granularity": "addresses",
- "nodecount": "0",
- "sort": "flat",
- "output": "out",
- },
- },
- {
- "999",
- nil, // Error
- },
- }
-
- for _, tc := range testcases {
- cmd, cfg, err := parseCommandLine(strings.Fields(tc.input))
- if tc.want == nil && err != nil {
- // Error expected
- continue
- }
- if err != nil {
- t.Errorf("failed on %q: %v", tc.input, err)
- continue
- }
-
- // Get report output format
- c := pprofCommands[cmd[0]]
- if c == nil {
- t.Fatalf("unexpected nil command")
- }
- cfg = applyCommandOverrides(cmd[0], c.format, cfg)
-
- for n, want := range tc.want {
- if got := cfg.get(configFieldMap[n]); got != want {
- t.Errorf("failed on %q, cmd=%q, %s got %s, want %s", tc.input, cmd, n, got, want)
- }
- }
- }
- }
|