暂无描述

interactive_test.go 8.1KB

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