暫無描述

interactive_test.go 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  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. package driver
  15. import (
  16. "fmt"
  17. "io"
  18. "math/rand"
  19. "strings"
  20. "testing"
  21. "github.com/google/pprof/internal/plugin"
  22. "github.com/google/pprof/internal/report"
  23. "github.com/google/pprof/profile"
  24. )
  25. func TestShell(t *testing.T) {
  26. p := &profile.Profile{}
  27. generateReportWrapper = checkValue
  28. defer func() { generateReportWrapper = generateReport }()
  29. // Use test commands and variables to exercise interactive processing
  30. var savedCommands commands
  31. savedCommands, pprofCommands = pprofCommands, testCommands
  32. defer func() { pprofCommands = savedCommands }()
  33. savedVariables := pprofVariables
  34. defer func() { pprofVariables = savedVariables }()
  35. // Random interleave of independent scripts
  36. pprofVariables = testVariables(savedVariables)
  37. o := setDefaults(nil)
  38. o.UI = newUI(t, interleave(script, 0))
  39. if err := interactive(p, o); err != nil {
  40. t.Error("first attempt:", err)
  41. }
  42. // Random interleave of independent scripts
  43. pprofVariables = testVariables(savedVariables)
  44. o.UI = newUI(t, interleave(script, 1))
  45. if err := interactive(p, o); err != nil {
  46. t.Error("second attempt:", err)
  47. }
  48. // Random interleave of independent scripts with shortcuts
  49. pprofVariables = testVariables(savedVariables)
  50. var scScript []string
  51. pprofShortcuts, scScript = makeShortcuts(interleave(script, 2), 1)
  52. o.UI = newUI(t, scScript)
  53. if err := interactive(p, o); err != nil {
  54. t.Error("first shortcut attempt:", err)
  55. }
  56. // Random interleave of independent scripts with shortcuts
  57. pprofVariables = testVariables(savedVariables)
  58. pprofShortcuts, scScript = makeShortcuts(interleave(script, 1), 2)
  59. o.UI = newUI(t, scScript)
  60. if err := interactive(p, o); err != nil {
  61. t.Error("second shortcut attempt:", err)
  62. }
  63. // Verify propagation of IO errors
  64. pprofVariables = testVariables(savedVariables)
  65. o.UI = newUI(t, []string{"**error**"})
  66. if err := interactive(p, o); err == nil {
  67. t.Error("expected IO error, got nil")
  68. }
  69. }
  70. var testCommands = commands{
  71. "check": &command{report.Raw, nil, true, "", ""},
  72. }
  73. func testVariables(base variables) variables {
  74. v := base.makeCopy()
  75. v["b"] = &variable{boolKind, "f", "", ""}
  76. v["bb"] = &variable{boolKind, "f", "", ""}
  77. v["i"] = &variable{intKind, "0", "", ""}
  78. v["ii"] = &variable{intKind, "0", "", ""}
  79. v["f"] = &variable{floatKind, "0", "", ""}
  80. v["ff"] = &variable{floatKind, "0", "", ""}
  81. v["s"] = &variable{stringKind, "", "", ""}
  82. v["ss"] = &variable{stringKind, "", "", ""}
  83. v["ta"] = &variable{boolKind, "f", "radio", ""}
  84. v["tb"] = &variable{boolKind, "f", "radio", ""}
  85. v["tc"] = &variable{boolKind, "t", "radio", ""}
  86. return v
  87. }
  88. // script contains sequences of commands to be executed for testing. Commands
  89. // are split by semicolon and interleaved randomly, so they must be
  90. // independent from each other.
  91. var script = []string{
  92. "bb=true;bb=false;check bb=false;bb=yes;check bb=true",
  93. "b=1;check b=true;b=n;check b=false",
  94. "i=-1;i=-2;check i=-2;i=999999;check i=999999",
  95. "check ii=0;ii=-1;check ii=-1;ii=100;check ii=100",
  96. "f=-1;f=-2.5;check f=-2.5;f=0.0001;check f=0.0001",
  97. "check ff=0;ff=-1.01;check ff=-1.01;ff=100;check ff=100",
  98. "s=one;s=two;check s=two",
  99. "ss=tree;check ss=tree;ss=;check ss;ss=forest;check ss=forest",
  100. "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",
  101. }
  102. func makeShortcuts(input []string, seed int) (shortcuts, []string) {
  103. rand.Seed(int64(seed))
  104. s := shortcuts{}
  105. var output, chunk []string
  106. for _, l := range input {
  107. chunk = append(chunk, l)
  108. switch rand.Intn(3) {
  109. case 0:
  110. // Create a macro for commands in 'chunk'.
  111. macro := fmt.Sprintf("alias%d", len(s))
  112. s[macro] = chunk
  113. output = append(output, macro)
  114. chunk = nil
  115. case 1:
  116. // Append commands in 'chunk' by themselves.
  117. output = append(output, chunk...)
  118. chunk = nil
  119. case 2:
  120. // Accumulate commands into 'chunk'
  121. }
  122. }
  123. output = append(output, chunk...)
  124. return s, output
  125. }
  126. func newUI(t *testing.T, input []string) plugin.UI {
  127. return &testUI{
  128. t: t,
  129. input: input,
  130. }
  131. }
  132. type testUI struct {
  133. t *testing.T
  134. input []string
  135. index int
  136. }
  137. func (ui *testUI) ReadLine(_ string) (string, error) {
  138. if ui.index >= len(ui.input) {
  139. return "", io.EOF
  140. }
  141. input := ui.input[ui.index]
  142. if input == "**error**" {
  143. return "", fmt.Errorf("Error: %s", input)
  144. }
  145. ui.index++
  146. return input, nil
  147. }
  148. func (ui *testUI) Print(args ...interface{}) {
  149. }
  150. func (ui *testUI) PrintErr(args ...interface{}) {
  151. output := fmt.Sprint(args)
  152. if output != "" {
  153. ui.t.Error(output)
  154. }
  155. }
  156. func (ui *testUI) IsTerminal() bool {
  157. return false
  158. }
  159. func (ui *testUI) SetAutoComplete(func(string) string) {
  160. }
  161. func checkValue(p *profile.Profile, cmd []string, vars variables, o *plugin.Options) error {
  162. if len(cmd) != 2 {
  163. return fmt.Errorf("expected len(cmd)==2, got %v", cmd)
  164. }
  165. input := cmd[1]
  166. args := strings.SplitN(input, "=", 2)
  167. if len(args) == 0 {
  168. return fmt.Errorf("unexpected empty input")
  169. }
  170. name, value := args[0], ""
  171. if len(args) == 2 {
  172. value = args[1]
  173. }
  174. gotv := vars[name]
  175. if gotv == nil {
  176. return fmt.Errorf("Could not find variable named %s", name)
  177. }
  178. if got := gotv.stringValue(); got != value {
  179. return fmt.Errorf("Variable %s, want %s, got %s", name, value, got)
  180. }
  181. return nil
  182. }
  183. func interleave(input []string, seed int) []string {
  184. var inputs [][]string
  185. for _, s := range input {
  186. inputs = append(inputs, strings.Split(s, ";"))
  187. }
  188. rand.Seed(int64(seed))
  189. var output []string
  190. for len(inputs) > 0 {
  191. next := rand.Intn(len(inputs))
  192. output = append(output, inputs[next][0])
  193. if tail := inputs[next][1:]; len(tail) > 0 {
  194. inputs[next] = tail
  195. } else {
  196. inputs = append(inputs[:next], inputs[next+1:]...)
  197. }
  198. }
  199. return output
  200. }
  201. func TestInteractiveCommands(t *testing.T) {
  202. type interactiveTestcase struct {
  203. input string
  204. want map[string]string
  205. }
  206. testcases := []interactiveTestcase{
  207. {
  208. "top 10 --cum focus1 -ignore focus2",
  209. map[string]string{
  210. "functions": "true",
  211. "nodecount": "10",
  212. "cum": "true",
  213. "focus": "focus1|focus2",
  214. "ignore": "ignore",
  215. },
  216. },
  217. {
  218. "top10 --cum focus1 -ignore focus2",
  219. map[string]string{
  220. "functions": "true",
  221. "nodecount": "10",
  222. "cum": "true",
  223. "focus": "focus1|focus2",
  224. "ignore": "ignore",
  225. },
  226. },
  227. {
  228. "dot",
  229. map[string]string{
  230. "functions": "true",
  231. "nodecount": "80",
  232. "cum": "false",
  233. },
  234. },
  235. {
  236. "tags -ignore1 -ignore2 focus1 >out",
  237. map[string]string{
  238. "functions": "true",
  239. "nodecount": "80",
  240. "cum": "false",
  241. "output": "out",
  242. "tagfocus": "focus1",
  243. "tagignore": "ignore1|ignore2",
  244. },
  245. },
  246. {
  247. "weblist find -test",
  248. map[string]string{
  249. "functions": "false",
  250. "addressnoinlines": "true",
  251. "nodecount": "0",
  252. "cum": "false",
  253. "flat": "true",
  254. "ignore": "test",
  255. },
  256. },
  257. {
  258. "callgrind fun -ignore >out",
  259. map[string]string{
  260. "functions": "false",
  261. "addresses": "true",
  262. "nodecount": "0",
  263. "cum": "false",
  264. "flat": "true",
  265. "output": "out",
  266. },
  267. },
  268. {
  269. "999",
  270. nil, // Error
  271. },
  272. }
  273. for _, tc := range testcases {
  274. cmd, vars, err := parseCommandLine(strings.Fields(tc.input))
  275. if tc.want == nil && err != nil {
  276. // Error expected
  277. continue
  278. }
  279. if err != nil {
  280. t.Errorf("failed on %q: %v", tc.input, err)
  281. continue
  282. }
  283. vars = applyCommandOverrides(cmd, vars)
  284. for n, want := range tc.want {
  285. if got := vars[n].stringValue(); got != want {
  286. t.Errorf("failed on %q, cmd=%q, %s got %s, want %s", tc.input, cmd, n, got, want)
  287. }
  288. }
  289. }
  290. }