123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- // 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"
- "io"
- "math/rand"
- "strings"
- "testing"
-
- "github.com/google/pprof/internal/plugin"
- "github.com/google/pprof/internal/report"
- "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 }()
-
- savedVariables := pprofVariables
- defer func() { pprofVariables = savedVariables }()
-
- // Random interleave of independent scripts
- pprofVariables = testVariables(savedVariables)
- o := setDefaults(nil)
- o.UI = newUI(t, interleave(script, 0))
- if err := interactive(p, o); err != nil {
- t.Error("first attempt:", err)
- }
- // Random interleave of independent scripts
- pprofVariables = testVariables(savedVariables)
- o.UI = newUI(t, interleave(script, 1))
- if err := interactive(p, o); err != nil {
- t.Error("second attempt:", err)
- }
-
- // Random interleave of independent scripts with shortcuts
- pprofVariables = testVariables(savedVariables)
- var scScript []string
- pprofShortcuts, scScript = makeShortcuts(interleave(script, 2), 1)
- o.UI = newUI(t, scScript)
- if err := interactive(p, o); err != nil {
- t.Error("first shortcut attempt:", err)
- }
-
- // Random interleave of independent scripts with shortcuts
- pprofVariables = testVariables(savedVariables)
- pprofShortcuts, scScript = makeShortcuts(interleave(script, 1), 2)
- o.UI = newUI(t, scScript)
- if err := interactive(p, o); err != nil {
- t.Error("second shortcut attempt:", err)
- }
-
- // Verify propagation of IO errors
- pprofVariables = testVariables(savedVariables)
- o.UI = newUI(t, []string{"**error**"})
- if err := interactive(p, o); err == nil {
- t.Error("expected IO error, got nil")
- }
-
- }
-
- var testCommands = commands{
- "check": &command{report.Raw, nil, true, "", ""},
- }
-
- func testVariables(base variables) variables {
- v := base.makeCopy()
-
- v["b"] = &variable{boolKind, "f", "", ""}
- v["bb"] = &variable{boolKind, "f", "", ""}
- v["i"] = &variable{intKind, "0", "", ""}
- v["ii"] = &variable{intKind, "0", "", ""}
- v["f"] = &variable{floatKind, "0", "", ""}
- v["ff"] = &variable{floatKind, "0", "", ""}
- v["s"] = &variable{stringKind, "", "", ""}
- v["ss"] = &variable{stringKind, "", "", ""}
-
- v["ta"] = &variable{boolKind, "f", "radio", ""}
- v["tb"] = &variable{boolKind, "f", "radio", ""}
- v["tc"] = &variable{boolKind, "t", "radio", ""}
-
- return v
- }
-
- // 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{
- "bb=true;bb=false;check bb=false;bb=yes;check bb=true",
- "b=1;check b=true;b=n;check b=false",
- "i=-1;i=-2;check i=-2;i=999999;check i=999999",
- "check ii=0;ii=-1;check ii=-1;ii=100;check ii=100",
- "f=-1;f=-2.5;check f=-2.5;f=0.0001;check f=0.0001",
- "check ff=0;ff=-1.01;check ff=-1.01;ff=100;check ff=100",
- "s=one;s=two;check s=two",
- "ss=tree;check ss=tree;ss=;check ss;ss=forest;check ss=forest",
- "ta=true;check ta=true;check tb=false;check tc=false;tb=1;check tb=true;check ta=false;check tc=false;tc=yes;check tb=false;check ta=false;check tc=true",
- }
-
- 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 newUI(t *testing.T, input []string) plugin.UI {
- return &testUI{
- t: t,
- input: input,
- }
- }
-
- type testUI struct {
- t *testing.T
- input []string
- index int
- }
-
- func (ui *testUI) ReadLine(_ string) (string, error) {
- if ui.index >= len(ui.input) {
- return "", io.EOF
- }
- input := ui.input[ui.index]
- if input == "**error**" {
- return "", fmt.Errorf("Error: %s", input)
- }
- ui.index++
- return input, nil
- }
-
- func (ui *testUI) Print(args ...interface{}) {
- }
-
- func (ui *testUI) PrintErr(args ...interface{}) {
- output := fmt.Sprint(args)
- if output != "" {
- ui.t.Error(output)
- }
- }
-
- func (ui *testUI) IsTerminal() bool {
- return false
- }
-
- func (ui *testUI) SetAutoComplete(func(string) string) {
- }
-
- func checkValue(p *profile.Profile, cmd []string, vars variables, 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]
- }
-
- gotv := vars[name]
- if gotv == nil {
- return fmt.Errorf("Could not find variable named %s", name)
- }
-
- if got := gotv.stringValue(); 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{
- "functions": "true",
- "nodecount": "10",
- "cum": "true",
- "focus": "focus1|focus2",
- "ignore": "ignore",
- },
- },
- {
- "top10 --cum focus1 -ignore focus2",
- map[string]string{
- "functions": "true",
- "nodecount": "10",
- "cum": "true",
- "focus": "focus1|focus2",
- "ignore": "ignore",
- },
- },
- {
- "dot",
- map[string]string{
- "functions": "true",
- "nodecount": "80",
- "cum": "false",
- },
- },
- {
- "tags -ignore1 -ignore2 focus1 >out",
- map[string]string{
- "functions": "true",
- "nodecount": "80",
- "cum": "false",
- "output": "out",
- "tagfocus": "focus1",
- "tagignore": "ignore1|ignore2",
- },
- },
- {
- "weblist find -test",
- map[string]string{
- "functions": "false",
- "addressnoinlines": "true",
- "nodecount": "0",
- "cum": "false",
- "flat": "true",
- "ignore": "test",
- },
- },
- {
- "callgrind fun -ignore >out",
- map[string]string{
- "functions": "false",
- "addresses": "true",
- "nodecount": "0",
- "cum": "false",
- "flat": "true",
- "output": "out",
- },
- },
- {
- "999",
- nil, // Error
- },
- }
-
- for _, tc := range testcases {
- cmd, vars, 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
- }
- vars = applyCommandOverrides(cmd, vars)
-
- for n, want := range tc.want {
- if got := vars[n].stringValue(); got != want {
- t.Errorf("failed on %q, cmd=%q, %s got %s, want %s", tc.input, cmd, n, got, want)
- }
- }
- }
- }
|