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.
 
 
 

419 lines
10 KiB

  1. // Copyright 2016 Google LLC
  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 valuecollector
  15. import (
  16. "fmt"
  17. "testing"
  18. "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug"
  19. "cloud.google.com/go/internal/testutil"
  20. cd "google.golang.org/api/clouddebugger/v2"
  21. )
  22. const (
  23. // Some arbitrary type IDs for the test, for use in debug.Var's TypeID field.
  24. // A TypeID of 0 means the type is unknown, so we start at 1.
  25. int16Type = iota + 1
  26. stringType
  27. structType
  28. pointerType
  29. arrayType
  30. int32Type
  31. debugStringType
  32. mapType
  33. channelType
  34. sliceType
  35. )
  36. func TestValueCollector(t *testing.T) {
  37. // Construct the collector.
  38. c := NewCollector(&Program{}, 26)
  39. // Add some variables of various types, whose values we want the collector to read.
  40. variablesToAdd := []debug.LocalVar{
  41. {Name: "a", Var: debug.Var{TypeID: int16Type, Address: 0x1}},
  42. {Name: "b", Var: debug.Var{TypeID: stringType, Address: 0x2}},
  43. {Name: "c", Var: debug.Var{TypeID: structType, Address: 0x3}},
  44. {Name: "d", Var: debug.Var{TypeID: pointerType, Address: 0x4}},
  45. {Name: "e", Var: debug.Var{TypeID: arrayType, Address: 0x5}},
  46. {Name: "f", Var: debug.Var{TypeID: debugStringType, Address: 0x6}},
  47. {Name: "g", Var: debug.Var{TypeID: mapType, Address: 0x7}},
  48. {Name: "h", Var: debug.Var{TypeID: channelType, Address: 0x8}},
  49. {Name: "i", Var: debug.Var{TypeID: sliceType, Address: 0x9}},
  50. }
  51. expectedResults := []*cd.Variable{
  52. {Name: "a", VarTableIndex: 1},
  53. {Name: "b", VarTableIndex: 2},
  54. {Name: "c", VarTableIndex: 3},
  55. {Name: "d", VarTableIndex: 4},
  56. {Name: "e", VarTableIndex: 5},
  57. {Name: "f", VarTableIndex: 6},
  58. {Name: "g", VarTableIndex: 7},
  59. {Name: "h", VarTableIndex: 8},
  60. {Name: "i", VarTableIndex: 9},
  61. }
  62. for i, v := range variablesToAdd {
  63. added := c.AddVariable(v)
  64. if !testutil.Equal(added, expectedResults[i]) {
  65. t.Errorf("AddVariable: got %+v want %+v", *added, *expectedResults[i])
  66. }
  67. }
  68. // Read the values, compare the output to what we expect.
  69. v := c.ReadValues()
  70. expectedValues := []*cd.Variable{
  71. {},
  72. {Value: "1"},
  73. {Value: `"hello"`},
  74. {
  75. Members: []*cd.Variable{
  76. {Name: "x", VarTableIndex: 1},
  77. {Name: "y", VarTableIndex: 2},
  78. },
  79. },
  80. {
  81. Members: []*cd.Variable{
  82. {VarTableIndex: 1},
  83. },
  84. Value: "0x1",
  85. },
  86. {
  87. Members: []*cd.Variable{
  88. {Name: "[0]", VarTableIndex: 10},
  89. {Name: "[1]", VarTableIndex: 11},
  90. {Name: "[2]", VarTableIndex: 12},
  91. {Name: "[3]", VarTableIndex: 13},
  92. },
  93. Value: "len = 4",
  94. },
  95. {Value: `"world"`},
  96. {
  97. Members: []*cd.Variable{
  98. {Name: "⚫", VarTableIndex: 14},
  99. {Name: "⚫", VarTableIndex: 15},
  100. {Name: "⚫", VarTableIndex: 16},
  101. },
  102. Value: "len = 3",
  103. },
  104. {
  105. Members: []*cd.Variable{
  106. {Name: "[0]", VarTableIndex: 17},
  107. {Name: "[1]", VarTableIndex: 18},
  108. },
  109. Value: "len = 2",
  110. },
  111. {
  112. Members: []*cd.Variable{
  113. {Name: "[0]", VarTableIndex: 19},
  114. {Name: "[1]", VarTableIndex: 20},
  115. },
  116. Value: "len = 2",
  117. },
  118. {Value: "100"},
  119. {Value: "104"},
  120. {Value: "108"},
  121. {Value: "112"},
  122. {
  123. Members: []*cd.Variable{
  124. {Name: "key", VarTableIndex: 21},
  125. {Name: "value", VarTableIndex: 22},
  126. },
  127. },
  128. {
  129. Members: []*cd.Variable{
  130. {Name: "key", VarTableIndex: 23},
  131. {Name: "value", VarTableIndex: 24},
  132. },
  133. },
  134. {
  135. Members: []*cd.Variable{
  136. {Name: "key", VarTableIndex: 25},
  137. {
  138. Name: "value",
  139. Status: &cd.StatusMessage{
  140. Description: &cd.FormatMessage{
  141. Format: "$0",
  142. Parameters: []string{"Not captured"},
  143. },
  144. IsError: true,
  145. RefersTo: "VARIABLE_NAME",
  146. },
  147. },
  148. },
  149. },
  150. {Value: "246"},
  151. {Value: "210"},
  152. {Value: "300"},
  153. {Value: "304"},
  154. {Value: "400"},
  155. {Value: "404"},
  156. {Value: "1400"},
  157. {Value: "1404"},
  158. {Value: "2400"},
  159. }
  160. if !testutil.Equal(v, expectedValues) {
  161. t.Errorf("ReadValues: got %v want %v", v, expectedValues)
  162. // Do element-by-element comparisons, for more useful error messages.
  163. for i := range v {
  164. if i < len(expectedValues) && !testutil.Equal(v[i], expectedValues[i]) {
  165. t.Errorf("element %d: got %+v want %+v", i, *v[i], *expectedValues[i])
  166. }
  167. }
  168. }
  169. }
  170. // Program implements the similarly-named interface in x/debug.
  171. // ValueCollector should only call its Value and MapElement methods.
  172. type Program struct {
  173. debug.Program
  174. }
  175. func (p *Program) Value(v debug.Var) (debug.Value, error) {
  176. // We determine what to return using v.TypeID.
  177. switch v.TypeID {
  178. case int16Type:
  179. // We use the address as the value, so that we're testing whether the right
  180. // address was calculated.
  181. return int16(v.Address), nil
  182. case stringType:
  183. // A string.
  184. return "hello", nil
  185. case structType:
  186. // A struct with two elements.
  187. return debug.Struct{
  188. Fields: []debug.StructField{
  189. {
  190. Name: "x",
  191. Var: debug.Var{TypeID: int16Type, Address: 0x1},
  192. },
  193. {
  194. Name: "y",
  195. Var: debug.Var{TypeID: stringType, Address: 0x2},
  196. },
  197. },
  198. }, nil
  199. case pointerType:
  200. // A pointer to the first variable above.
  201. return debug.Pointer{TypeID: int16Type, Address: 0x1}, nil
  202. case arrayType:
  203. // An array of 4 32-bit-wide elements.
  204. return debug.Array{
  205. ElementTypeID: int32Type,
  206. Address: 0x64,
  207. Length: 4,
  208. StrideBits: 32,
  209. }, nil
  210. case debugStringType:
  211. return debug.String{
  212. Length: 5,
  213. String: "world",
  214. }, nil
  215. case mapType:
  216. return debug.Map{
  217. TypeID: 99,
  218. Address: 0x100,
  219. Length: 3,
  220. }, nil
  221. case channelType:
  222. return debug.Channel{
  223. ElementTypeID: int32Type,
  224. Address: 200,
  225. Buffer: 210,
  226. Length: 2,
  227. Capacity: 10,
  228. Stride: 4,
  229. BufferStart: 9,
  230. }, nil
  231. case sliceType:
  232. // A slice of 2 32-bit-wide elements.
  233. return debug.Slice{
  234. Array: debug.Array{
  235. ElementTypeID: int32Type,
  236. Address: 300,
  237. Length: 2,
  238. StrideBits: 32,
  239. },
  240. Capacity: 50,
  241. }, nil
  242. case int32Type:
  243. // We use the address as the value, so that we're testing whether the right
  244. // address was calculated.
  245. return int32(v.Address), nil
  246. }
  247. return nil, fmt.Errorf("unexpected Value request")
  248. }
  249. func (p *Program) MapElement(m debug.Map, index uint64) (debug.Var, debug.Var, error) {
  250. return debug.Var{TypeID: int16Type, Address: 1000*index + 400},
  251. debug.Var{TypeID: int32Type, Address: 1000*index + 404},
  252. nil
  253. }
  254. func TestLogString(t *testing.T) {
  255. bp := cd.Breakpoint{
  256. Action: "LOG",
  257. LogMessageFormat: "$0 hello, $$7world! $1 $2 $3 $4 $5$6 $7 $8",
  258. EvaluatedExpressions: []*cd.Variable{
  259. {Name: "a", VarTableIndex: 1},
  260. {Name: "b", VarTableIndex: 2},
  261. {Name: "c", VarTableIndex: 3},
  262. {Name: "d", VarTableIndex: 4},
  263. {Name: "e", VarTableIndex: 5},
  264. {Name: "f", VarTableIndex: 6},
  265. {Name: "g", VarTableIndex: 7},
  266. {Name: "h", VarTableIndex: 8},
  267. {Name: "i", VarTableIndex: 9},
  268. },
  269. }
  270. varTable := []*cd.Variable{
  271. {},
  272. {Value: "1"},
  273. {Value: `"hello"`},
  274. {
  275. Members: []*cd.Variable{
  276. {Name: "x", Value: "1"},
  277. {Name: "y", Value: `"hello"`},
  278. {Name: "z", VarTableIndex: 3},
  279. },
  280. },
  281. {
  282. Members: []*cd.Variable{
  283. {VarTableIndex: 1},
  284. },
  285. Value: "0x1",
  286. },
  287. {
  288. Members: []*cd.Variable{
  289. {Name: "[0]", VarTableIndex: 10},
  290. {Name: "[1]", VarTableIndex: 11},
  291. {Name: "[2]", VarTableIndex: 12},
  292. {Name: "[3]", VarTableIndex: 13},
  293. },
  294. Value: "len = 4",
  295. },
  296. {Value: `"world"`},
  297. {
  298. Members: []*cd.Variable{
  299. {Name: "⚫", VarTableIndex: 14},
  300. {Name: "⚫", VarTableIndex: 15},
  301. {Name: "⚫", VarTableIndex: 16},
  302. },
  303. Value: "len = 3",
  304. },
  305. {
  306. Members: []*cd.Variable{
  307. {Name: "[0]", VarTableIndex: 17},
  308. {Name: "[1]", VarTableIndex: 18},
  309. },
  310. Value: "len = 2",
  311. },
  312. {
  313. Members: []*cd.Variable{
  314. {Name: "[0]", VarTableIndex: 19},
  315. {Name: "[1]", VarTableIndex: 20},
  316. },
  317. Value: "len = 2",
  318. },
  319. {Value: "100"},
  320. {Value: "104"},
  321. {Value: "108"},
  322. {Value: "112"},
  323. {
  324. Members: []*cd.Variable{
  325. {Name: "key", VarTableIndex: 21},
  326. {Name: "value", VarTableIndex: 22},
  327. },
  328. },
  329. {
  330. Members: []*cd.Variable{
  331. {Name: "key", VarTableIndex: 23},
  332. {Name: "value", VarTableIndex: 24},
  333. },
  334. },
  335. {
  336. Members: []*cd.Variable{
  337. {Name: "key", VarTableIndex: 25},
  338. {
  339. Name: "value",
  340. Status: &cd.StatusMessage{
  341. Description: &cd.FormatMessage{
  342. Format: "$0",
  343. Parameters: []string{"Not captured"},
  344. },
  345. IsError: true,
  346. RefersTo: "VARIABLE_NAME",
  347. },
  348. },
  349. },
  350. },
  351. {Value: "246"},
  352. {Value: "210"},
  353. {Value: "300"},
  354. {Value: "304"},
  355. {Value: "400"},
  356. {Value: "404"},
  357. {Value: "1400"},
  358. {Value: "1404"},
  359. {Value: "2400"},
  360. }
  361. s := LogString(bp.LogMessageFormat, bp.EvaluatedExpressions, varTable)
  362. expected := `LOGPOINT: 1 hello, $7world! "hello" {x:1, y:"hello", z:...} ` +
  363. `0x1 {100, 104, 108, 112} "world"{400:404, 1400:1404, 2400:(Not captured)} ` +
  364. `{246, 210} {300, 304}`
  365. if s != expected {
  366. t.Errorf("LogString: got %q want %q", s, expected)
  367. }
  368. }
  369. func TestParseToken(t *testing.T) {
  370. for _, c := range []struct {
  371. s string
  372. max int
  373. num int
  374. n int
  375. ok bool
  376. }{
  377. {"", 0, 0, 0, false},
  378. {".", 0, 0, 0, false},
  379. {"0", 0, 0, 1, true},
  380. {"0", 1, 0, 1, true},
  381. {"00", 0, 0, 2, true},
  382. {"1.", 1, 1, 1, true},
  383. {"1.", 0, 0, 0, false},
  384. {"10", 10, 10, 2, true},
  385. {"10..", 10, 10, 2, true},
  386. {"10", 11, 10, 2, true},
  387. {"10..", 11, 10, 2, true},
  388. {"10", 9, 0, 0, false},
  389. {"10..", 9, 0, 0, false},
  390. {" 10", 10, 0, 0, false},
  391. {"010", 10, 10, 3, true},
  392. {"123456789", 123456789, 123456789, 9, true},
  393. {"123456789", 123456788, 0, 0, false},
  394. {"123456789123456789123456789", 999999999, 0, 0, false},
  395. } {
  396. num, n, ok := parseToken(c.s, c.max)
  397. if ok != c.ok {
  398. t.Errorf("parseToken(%q, %d): got ok=%t want ok=%t", c.s, c.max, ok, c.ok)
  399. continue
  400. }
  401. if !ok {
  402. continue
  403. }
  404. if num != c.num || n != c.n {
  405. t.Errorf("parseToken(%q, %d): got %d,%d,%t want %d,%d,%t", c.s, c.max, num, n, ok, c.num, c.n, c.ok)
  406. }
  407. }
  408. }