Kaynağa Gözat

Clarify error messages in interactive for groups (#294)

Margaret Nolan 7 yıl önce
ebeveyn
işleme
532a375ba6

+ 23
- 0
internal/driver/interactive.go Dosyayı Görüntüle

@@ -42,6 +42,9 @@ func interactive(p *profile.Profile, o *plugin.Options) error {
42 42
 	interactiveMode = true
43 43
 	shortcuts := profileShortcuts(p)
44 44
 
45
+	// Get all groups in pprofVariables to allow for clearer error messages.
46
+	groups := groupOptions(pprofVariables)
47
+
45 48
 	greetings(p, o.UI)
46 49
 	for {
47 50
 		input, err := o.UI.ReadLine("(pprof) ")
@@ -87,6 +90,9 @@ func interactive(p *profile.Profile, o *plugin.Options) error {
87 90
 						o.UI.PrintErr(err)
88 91
 					}
89 92
 					continue
93
+				} else if okValues := groups[name]; okValues != nil {
94
+					o.UI.PrintErr(fmt.Errorf("Unrecognized value for %s: %q. Use one of %s", name, value, strings.Join(okValues, ", ")))
95
+					continue
90 96
 				}
91 97
 			}
92 98
 
@@ -118,6 +124,23 @@ func interactive(p *profile.Profile, o *plugin.Options) error {
118 124
 	}
119 125
 }
120 126
 
127
+// groupOptions returns a map containing all non-empty groups
128
+// mapped to an array of the option names in that group in
129
+// sorted order.
130
+func groupOptions(vars variables) map[string][]string {
131
+	groups := make(map[string][]string)
132
+	for name, option := range vars {
133
+		group := option.group
134
+		if group != "" {
135
+			groups[group] = append(groups[group], name)
136
+		}
137
+	}
138
+	for _, names := range groups {
139
+		sort.Strings(names)
140
+	}
141
+	return groups
142
+}
143
+
121 144
 var generateReportWrapper = generateReport // For testing purposes.
122 145
 
123 146
 // greetings prints a brief welcome and some overall profile

+ 19
- 39
internal/driver/interactive_test.go Dosyayı Görüntüle

@@ -16,12 +16,12 @@ package driver
16 16
 
17 17
 import (
18 18
 	"fmt"
19
-	"io"
20 19
 	"math/rand"
21 20
 	"strings"
22 21
 	"testing"
23 22
 
24 23
 	"github.com/google/pprof/internal/plugin"
24
+	"github.com/google/pprof/internal/proftest"
25 25
 	"github.com/google/pprof/internal/report"
26 26
 	"github.com/google/pprof/profile"
27 27
 )
@@ -70,6 +70,21 @@ func TestShell(t *testing.T) {
70 70
 		t.Error("second shortcut attempt:", err)
71 71
 	}
72 72
 
73
+	// Group with invalid value
74
+	pprofVariables = testVariables(savedVariables)
75
+	ui := &proftest.TestUI{
76
+		T:       t,
77
+		Input:   []string{"cumulative=this"},
78
+		AllowRx: `Unrecognized value for cumulative: "this". Use one of cum, flat`,
79
+	}
80
+	o.UI = ui
81
+	if err := interactive(p, o); err != nil {
82
+		t.Error("invalid group value:", err)
83
+	}
84
+	// Confirm error message written out once.
85
+	if ui.NumAllowRxMatches != 1 {
86
+		t.Errorf("want error message to be printed 1 time, got %v", ui.NumAllowRxMatches)
87
+	}
73 88
 	// Verify propagation of IO errors
74 89
 	pprofVariables = testVariables(savedVariables)
75 90
 	o.UI = newUI(t, []string{"**error**"})
@@ -144,45 +159,10 @@ func makeShortcuts(input []string, seed int) (shortcuts, []string) {
144 159
 }
145 160
 
146 161
 func newUI(t *testing.T, input []string) plugin.UI {
147
-	return &testUI{
148
-		t:     t,
149
-		input: input,
150
-	}
151
-}
152
-
153
-type testUI struct {
154
-	t     *testing.T
155
-	input []string
156
-	index int
157
-}
158
-
159
-func (ui *testUI) ReadLine(_ string) (string, error) {
160
-	if ui.index >= len(ui.input) {
161
-		return "", io.EOF
162
+	return &proftest.TestUI{
163
+		T:     t,
164
+		Input: input,
162 165
 	}
163
-	input := ui.input[ui.index]
164
-	if input == "**error**" {
165
-		return "", fmt.Errorf("Error: %s", input)
166
-	}
167
-	ui.index++
168
-	return input, nil
169
-}
170
-
171
-func (ui *testUI) Print(args ...interface{}) {
172
-}
173
-
174
-func (ui *testUI) PrintErr(args ...interface{}) {
175
-	output := fmt.Sprint(args)
176
-	if output != "" {
177
-		ui.t.Error(output)
178
-	}
179
-}
180
-
181
-func (ui *testUI) IsTerminal() bool {
182
-	return false
183
-}
184
-
185
-func (ui *testUI) SetAutoComplete(func(string) string) {
186 166
 }
187 167
 
188 168
 func checkValue(p *profile.Profile, cmd []string, vars variables, o *plugin.Options) error {

+ 12
- 1
internal/proftest/proftest.go Dosyayı Görüntüle

@@ -19,6 +19,7 @@ package proftest
19 19
 import (
20 20
 	"encoding/json"
21 21
 	"fmt"
22
+	"io"
22 23
 	"io/ioutil"
23 24
 	"os"
24 25
 	"os/exec"
@@ -80,11 +81,21 @@ type TestUI struct {
80 81
 	Ignore            int
81 82
 	AllowRx           string
82 83
 	NumAllowRxMatches int
84
+	Input             []string
85
+	index             int
83 86
 }
84 87
 
85 88
 // ReadLine returns no input, as no input is expected during testing.
86 89
 func (ui *TestUI) ReadLine(_ string) (string, error) {
87
-	return "", fmt.Errorf("no input")
90
+	if ui.index >= len(ui.Input) {
91
+		return "", io.EOF
92
+	}
93
+	input := ui.Input[ui.index]
94
+	ui.index++
95
+	if input == "**error**" {
96
+		return "", fmt.Errorf("Error: %s", input)
97
+	}
98
+	return input, nil
88 99
 }
89 100
 
90 101
 // Print messages are discarded by the test UI.