...

Source file src/github.com/google/pprof/internal/driver/interactive_test.go

Documentation: github.com/google/pprof/internal/driver

     1  // Copyright 2014 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package driver
    16  
    17  import (
    18  	"fmt"
    19  	"math/rand"
    20  	"strings"
    21  	"testing"
    22  
    23  	"github.com/google/pprof/internal/plugin"
    24  	"github.com/google/pprof/internal/proftest"
    25  	"github.com/google/pprof/internal/report"
    26  	"github.com/google/pprof/internal/transport"
    27  	"github.com/google/pprof/profile"
    28  )
    29  
    30  func TestShell(t *testing.T) {
    31  	p := &profile.Profile{}
    32  	generateReportWrapper = checkValue
    33  	defer func() { generateReportWrapper = generateReport }()
    34  
    35  	// Use test commands and variables to exercise interactive processing
    36  	var savedCommands commands
    37  	savedCommands, pprofCommands = pprofCommands, testCommands
    38  	defer func() { pprofCommands = savedCommands }()
    39  
    40  	savedConfig := currentConfig()
    41  	defer setCurrentConfig(savedConfig)
    42  
    43  	shortcuts1, scScript1 := makeShortcuts(interleave(script, 2), 1)
    44  	shortcuts2, scScript2 := makeShortcuts(interleave(script, 1), 2)
    45  
    46  	var testcases = []struct {
    47  		name              string
    48  		input             []string
    49  		shortcuts         shortcuts
    50  		allowRx           string
    51  		numAllowRxMatches int
    52  		propagateError    bool
    53  	}{
    54  		{"Random interleave of independent scripts 1", interleave(script, 0), pprofShortcuts, "", 0, false},
    55  		{"Random interleave of independent scripts 2", interleave(script, 1), pprofShortcuts, "", 0, false},
    56  		{"Random interleave of independent scripts with shortcuts 1", scScript1, shortcuts1, "", 0, false},
    57  		{"Random interleave of independent scripts with shortcuts 2", scScript2, shortcuts2, "", 0, false},
    58  		{"Group with invalid value", []string{"sort=this"}, pprofShortcuts, `invalid "sort" value`, 1, false},
    59  		{"No special value provided for the option", []string{"sample_index"}, pprofShortcuts, `please specify a value, e.g. sample_index=<val>`, 1, false},
    60  		{"No string value provided for the option", []string{"focus"}, pprofShortcuts, `please specify a value, e.g. focus=<val>`, 1, false},
    61  		{"No float value provided for the option", []string{"divide_by"}, pprofShortcuts, `please specify a value, e.g. divide_by=<val>`, 1, false},
    62  		{"Helpful input format reminder", []string{"sample_index 0"}, pprofShortcuts, `did you mean: sample_index=0`, 1, false},
    63  		{"Verify propagation of IO errors", []string{"**error**"}, pprofShortcuts, "", 0, true},
    64  	}
    65  
    66  	o := setDefaults(&plugin.Options{HTTPTransport: transport.New(nil)})
    67  	for _, tc := range testcases {
    68  		t.Run(tc.name, func(t *testing.T) {
    69  			setCurrentConfig(savedConfig)
    70  			pprofShortcuts = tc.shortcuts
    71  			ui := &proftest.TestUI{
    72  				T:       t,
    73  				Input:   tc.input,
    74  				AllowRx: tc.allowRx,
    75  			}
    76  			o.UI = ui
    77  
    78  			err := interactive(p, o)
    79  			if (tc.propagateError && err == nil) || (!tc.propagateError && err != nil) {
    80  				t.Errorf("%s: %v", tc.name, err)
    81  			}
    82  
    83  			// Confirm error message written out once.
    84  			if tc.numAllowRxMatches != ui.NumAllowRxMatches {
    85  				t.Errorf("want error message to be printed %d time(s), got %d",
    86  					tc.numAllowRxMatches, ui.NumAllowRxMatches)
    87  			}
    88  		})
    89  	}
    90  }
    91  
    92  var testCommands = commands{
    93  	"check": &command{report.Raw, nil, nil, true, "", ""},
    94  }
    95  
    96  // script contains sequences of commands to be executed for testing. Commands
    97  // are split by semicolon and interleaved randomly, so they must be
    98  // independent from each other.
    99  var script = []string{
   100  	"call_tree=true;call_tree=false;check call_tree=false;call_tree=yes;check call_tree=true",
   101  	"mean=1;check mean=true;mean=n;check mean=false",
   102  	"nodecount=-1;nodecount=-2;check nodecount=-2;nodecount=999999;check nodecount=999999",
   103  	"nodefraction=-1;nodefraction=-2.5;check nodefraction=-2.5;nodefraction=0.0001;check nodefraction=0.0001",
   104  	"focus=one;focus=two;check focus=two",
   105  	"flat=true;check sort=flat;cum=1;check sort=cum",
   106  }
   107  
   108  func makeShortcuts(input []string, seed int64) (shortcuts, []string) {
   109  	rand := rand.New(rand.NewSource(seed))
   110  
   111  	s := shortcuts{}
   112  	var output, chunk []string
   113  	for _, l := range input {
   114  		chunk = append(chunk, l)
   115  		switch rand.Intn(3) {
   116  		case 0:
   117  			// Create a macro for commands in 'chunk'.
   118  			macro := fmt.Sprintf("alias%d", len(s))
   119  			s[macro] = chunk
   120  			output = append(output, macro)
   121  			chunk = nil
   122  		case 1:
   123  			// Append commands in 'chunk' by themselves.
   124  			output = append(output, chunk...)
   125  			chunk = nil
   126  		case 2:
   127  			// Accumulate commands into 'chunk'
   128  		}
   129  	}
   130  	output = append(output, chunk...)
   131  	return s, output
   132  }
   133  
   134  func checkValue(p *profile.Profile, cmd []string, cfg config, o *plugin.Options) error {
   135  	if len(cmd) != 2 {
   136  		return fmt.Errorf("expected len(cmd)==2, got %v", cmd)
   137  	}
   138  
   139  	input := cmd[1]
   140  	args := strings.SplitN(input, "=", 2)
   141  	if len(args) == 0 {
   142  		return fmt.Errorf("unexpected empty input")
   143  	}
   144  	name, value := args[0], ""
   145  	if len(args) == 2 {
   146  		value = args[1]
   147  	}
   148  
   149  	f, ok := configFieldMap[name]
   150  	if !ok {
   151  		return fmt.Errorf("Could not find variable named %s", name)
   152  	}
   153  
   154  	if got := cfg.get(f); got != value {
   155  		return fmt.Errorf("Variable %s, want %s, got %s", name, value, got)
   156  	}
   157  	return nil
   158  }
   159  
   160  func interleave(input []string, seed int64) []string {
   161  	var inputs [][]string
   162  	for _, s := range input {
   163  		inputs = append(inputs, strings.Split(s, ";"))
   164  	}
   165  	rand := rand.New(rand.NewSource(seed))
   166  	var output []string
   167  	for len(inputs) > 0 {
   168  		next := rand.Intn(len(inputs))
   169  		output = append(output, inputs[next][0])
   170  		if tail := inputs[next][1:]; len(tail) > 0 {
   171  			inputs[next] = tail
   172  		} else {
   173  			inputs = append(inputs[:next], inputs[next+1:]...)
   174  		}
   175  	}
   176  	return output
   177  }
   178  
   179  func TestInteractiveCommands(t *testing.T) {
   180  	type interactiveTestcase struct {
   181  		input string
   182  		want  map[string]string
   183  	}
   184  
   185  	testcases := []interactiveTestcase{
   186  		{
   187  			"top 10 --cum focus1 -ignore focus2",
   188  			map[string]string{
   189  				"granularity": "functions",
   190  				"nodecount":   "10",
   191  				"sort":        "cum",
   192  				"focus":       "focus1|focus2",
   193  				"ignore":      "ignore",
   194  			},
   195  		},
   196  		{
   197  			"top10 --cum focus1 -ignore focus2",
   198  			map[string]string{
   199  				"granularity": "functions",
   200  				"nodecount":   "10",
   201  				"sort":        "cum",
   202  				"focus":       "focus1|focus2",
   203  				"ignore":      "ignore",
   204  			},
   205  		},
   206  		{
   207  			"dot",
   208  			map[string]string{
   209  				"granularity": "functions",
   210  				"nodecount":   "80",
   211  				"sort":        "flat",
   212  			},
   213  		},
   214  		{
   215  			"tags   -ignore1 -ignore2 focus1 >out",
   216  			map[string]string{
   217  				"granularity": "functions",
   218  				"nodecount":   "80",
   219  				"sort":        "flat",
   220  				"output":      "out",
   221  				"tagfocus":    "focus1",
   222  				"tagignore":   "ignore1|ignore2",
   223  			},
   224  		},
   225  		{
   226  			"weblist  find -test",
   227  			map[string]string{
   228  				"granularity": "addresses",
   229  				"noinlines":   "false",
   230  				"nodecount":   "0",
   231  				"sort":        "flat",
   232  				"ignore":      "test",
   233  			},
   234  		},
   235  		{
   236  			"callgrind   fun -ignore  >out",
   237  			map[string]string{
   238  				"granularity": "addresses",
   239  				"nodecount":   "0",
   240  				"sort":        "flat",
   241  				"output":      "out",
   242  			},
   243  		},
   244  		{
   245  			"999",
   246  			nil, // Error
   247  		},
   248  	}
   249  
   250  	for _, tc := range testcases {
   251  		cmd, cfg, err := parseCommandLine(strings.Fields(tc.input))
   252  		if tc.want == nil && err != nil {
   253  			// Error expected
   254  			continue
   255  		}
   256  		if err != nil {
   257  			t.Errorf("failed on %q: %v", tc.input, err)
   258  			continue
   259  		}
   260  
   261  		// Get report output format
   262  		c := pprofCommands[cmd[0]]
   263  		if c == nil {
   264  			t.Fatalf("unexpected nil command")
   265  		}
   266  		cfg = applyCommandOverrides(cmd[0], c.format, cfg)
   267  
   268  		for n, want := range tc.want {
   269  			if got := cfg.get(configFieldMap[n]); got != want {
   270  				t.Errorf("failed on %q, cmd=%q, %s got %s, want %s", tc.input, cmd, n, got, want)
   271  			}
   272  		}
   273  	}
   274  }
   275  

View as plain text