You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

317 rivejä
8.4 KiB

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