format_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. package format_test
  2. import (
  3. "fmt"
  4. . "github.com/onsi/ginkgo"
  5. . "github.com/onsi/gomega"
  6. . "github.com/onsi/gomega/format"
  7. "github.com/onsi/gomega/types"
  8. "strings"
  9. )
  10. //recursive struct
  11. type StringAlias string
  12. type ByteAlias []byte
  13. type IntAlias int
  14. type AStruct struct {
  15. Exported string
  16. }
  17. type SimpleStruct struct {
  18. Name string
  19. Enumeration int
  20. Veritas bool
  21. Data []byte
  22. secret uint32
  23. }
  24. type ComplexStruct struct {
  25. Strings []string
  26. SimpleThings []*SimpleStruct
  27. DataMaps map[int]ByteAlias
  28. }
  29. type SecretiveStruct struct {
  30. boolValue bool
  31. intValue int
  32. uintValue uint
  33. uintptrValue uintptr
  34. floatValue float32
  35. complexValue complex64
  36. chanValue chan bool
  37. funcValue func()
  38. pointerValue *int
  39. sliceValue []string
  40. byteSliceValue []byte
  41. stringValue string
  42. arrValue [3]int
  43. byteArrValue [3]byte
  44. mapValue map[string]int
  45. structValue AStruct
  46. interfaceValue interface{}
  47. }
  48. type GoStringer struct {
  49. }
  50. func (g GoStringer) GoString() string {
  51. return "go-string"
  52. }
  53. func (g GoStringer) String() string {
  54. return "string"
  55. }
  56. type Stringer struct {
  57. }
  58. func (g Stringer) String() string {
  59. return "string"
  60. }
  61. var _ = Describe("Format", func() {
  62. match := func(typeRepresentation string, valueRepresentation string, args ...interface{}) types.GomegaMatcher {
  63. if len(args) > 0 {
  64. valueRepresentation = fmt.Sprintf(valueRepresentation, args...)
  65. }
  66. return Equal(fmt.Sprintf("%s<%s>: %s", Indent, typeRepresentation, valueRepresentation))
  67. }
  68. matchRegexp := func(typeRepresentation string, valueRepresentation string, args ...interface{}) types.GomegaMatcher {
  69. if len(args) > 0 {
  70. valueRepresentation = fmt.Sprintf(valueRepresentation, args...)
  71. }
  72. return MatchRegexp(fmt.Sprintf("%s<%s>: %s", Indent, typeRepresentation, valueRepresentation))
  73. }
  74. hashMatchingRegexp := func(entries ...string) string {
  75. entriesSwitch := "(" + strings.Join(entries, "|") + ")"
  76. arr := make([]string, len(entries))
  77. for i := range arr {
  78. arr[i] = entriesSwitch
  79. }
  80. return "{" + strings.Join(arr, ", ") + "}"
  81. }
  82. Describe("Message", func() {
  83. Context("with only an actual value", func() {
  84. It("should print out an indented formatted representation of the value and the message", func() {
  85. Ω(Message(3, "to be three.")).Should(Equal("Expected\n <int>: 3\nto be three."))
  86. })
  87. })
  88. Context("with an actual and an expected value", func() {
  89. It("should print out an indented formatted representatino of both values, and the message", func() {
  90. Ω(Message(3, "to equal", 4)).Should(Equal("Expected\n <int>: 3\nto equal\n <int>: 4"))
  91. })
  92. })
  93. })
  94. Describe("IndentString", func() {
  95. It("should indent the string", func() {
  96. Ω(IndentString("foo\n bar\nbaz", 2)).Should(Equal(" foo\n bar\n baz"))
  97. })
  98. })
  99. Describe("Object", func() {
  100. Describe("formatting boolean values", func() {
  101. It("should give the type and format values correctly", func() {
  102. Ω(Object(true, 1)).Should(match("bool", "true"))
  103. Ω(Object(false, 1)).Should(match("bool", "false"))
  104. })
  105. })
  106. Describe("formatting numbers", func() {
  107. It("should give the type and format values correctly", func() {
  108. Ω(Object(int(3), 1)).Should(match("int", "3"))
  109. Ω(Object(int8(3), 1)).Should(match("int8", "3"))
  110. Ω(Object(int16(3), 1)).Should(match("int16", "3"))
  111. Ω(Object(int32(3), 1)).Should(match("int32", "3"))
  112. Ω(Object(int64(3), 1)).Should(match("int64", "3"))
  113. Ω(Object(uint(3), 1)).Should(match("uint", "3"))
  114. Ω(Object(uint8(3), 1)).Should(match("uint8", "3"))
  115. Ω(Object(uint16(3), 1)).Should(match("uint16", "3"))
  116. Ω(Object(uint32(3), 1)).Should(match("uint32", "3"))
  117. Ω(Object(uint64(3), 1)).Should(match("uint64", "3"))
  118. })
  119. It("should handle uintptr differently", func() {
  120. Ω(Object(uintptr(3), 1)).Should(match("uintptr", "0x3"))
  121. })
  122. })
  123. Describe("formatting channels", func() {
  124. It("should give the type and format values correctly", func() {
  125. c := make(chan<- bool, 3)
  126. c <- true
  127. c <- false
  128. Ω(Object(c, 1)).Should(match("chan<- bool | len:2, cap:3", "%v", c))
  129. })
  130. })
  131. Describe("formatting strings", func() {
  132. It("should give the type and format values correctly", func() {
  133. s := "a\nb\nc"
  134. Ω(Object(s, 1)).Should(match("string", `a
  135. b
  136. c`))
  137. })
  138. })
  139. Describe("formatting []byte slices", func() {
  140. It("should present them as strings", func() {
  141. b := []byte("a\nb\nc")
  142. Ω(Object(b, 1)).Should(matchRegexp(`\[\]uint8 \| len:5, cap:\d+`, `a
  143. b
  144. c`))
  145. })
  146. })
  147. Describe("formatting functions", func() {
  148. It("should give the type and format values correctly", func() {
  149. f := func(a string, b []int) ([]byte, error) {
  150. return []byte("abc"), nil
  151. }
  152. Ω(Object(f, 1)).Should(match("func(string, []int) ([]uint8, error)", "%v", f))
  153. })
  154. })
  155. Describe("formatting pointers", func() {
  156. It("should give the type and dereference the value to format it correctly", func() {
  157. a := 3
  158. Ω(Object(&a, 1)).Should(match(fmt.Sprintf("*int | %p", &a), "3"))
  159. })
  160. Context("when there are pointers to pointers...", func() {
  161. It("should recursively deference the pointer until it gets to a value", func() {
  162. a := 3
  163. var b *int
  164. var c **int
  165. var d ***int
  166. b = &a
  167. c = &b
  168. d = &c
  169. Ω(Object(d, 1)).Should(match(fmt.Sprintf("***int | %p", d), "3"))
  170. })
  171. })
  172. Context("when the pointer points to nil", func() {
  173. It("should say nil and not explode", func() {
  174. var a *AStruct
  175. Ω(Object(a, 1)).Should(match("*format_test.AStruct | 0x0", "nil"))
  176. })
  177. })
  178. })
  179. Describe("formatting arrays", func() {
  180. It("should give the type and format values correctly", func() {
  181. w := [3]string{"Jed Bartlet", "Toby Ziegler", "CJ Cregg"}
  182. Ω(Object(w, 1)).Should(match("[3]string", `["Jed Bartlet", "Toby Ziegler", "CJ Cregg"]`))
  183. })
  184. Context("with byte arrays", func() {
  185. It("should give the type and format values correctly", func() {
  186. w := [3]byte{17, 28, 19}
  187. Ω(Object(w, 1)).Should(match("[3]uint8", `[17, 28, 19]`))
  188. })
  189. })
  190. })
  191. Describe("formatting slices", func() {
  192. It("should include the length and capacity in the type information", func() {
  193. s := make([]bool, 3, 4)
  194. Ω(Object(s, 1)).Should(match("[]bool | len:3, cap:4", "[false, false, false]"))
  195. })
  196. Context("when the slice contains long entries", func() {
  197. It("should format the entries with newlines", func() {
  198. w := []string{"Josiah Edward Bartlet", "Toby Ziegler", "CJ Cregg"}
  199. expected := `[
  200. "Josiah Edward Bartlet",
  201. "Toby Ziegler",
  202. "CJ Cregg",
  203. ]`
  204. Ω(Object(w, 1)).Should(match("[]string | len:3, cap:3", expected))
  205. })
  206. })
  207. })
  208. Describe("formatting maps", func() {
  209. It("should include the length in the type information", func() {
  210. m := make(map[int]bool, 5)
  211. m[3] = true
  212. m[4] = false
  213. Ω(Object(m, 1)).Should(matchRegexp(`map\[int\]bool \| len:2`, hashMatchingRegexp("3: true", "4: false")))
  214. })
  215. Context("when the slice contains long entries", func() {
  216. It("should format the entries with newlines", func() {
  217. m := map[string][]byte{}
  218. m["Josiah Edward Bartlet"] = []byte("Martin Sheen")
  219. m["Toby Ziegler"] = []byte("Richard Schiff")
  220. m["CJ Cregg"] = []byte("Allison Janney")
  221. expected := `{
  222. ("Josiah Edward Bartlet": "Martin Sheen"|"Toby Ziegler": "Richard Schiff"|"CJ Cregg": "Allison Janney"),
  223. ("Josiah Edward Bartlet": "Martin Sheen"|"Toby Ziegler": "Richard Schiff"|"CJ Cregg": "Allison Janney"),
  224. ("Josiah Edward Bartlet": "Martin Sheen"|"Toby Ziegler": "Richard Schiff"|"CJ Cregg": "Allison Janney"),
  225. }`
  226. Ω(Object(m, 1)).Should(matchRegexp(`map\[string\]\[\]uint8 \| len:3`, expected))
  227. })
  228. })
  229. })
  230. Describe("formatting structs", func() {
  231. It("should include the struct name and the field names", func() {
  232. s := SimpleStruct{
  233. Name: "Oswald",
  234. Enumeration: 17,
  235. Veritas: true,
  236. Data: []byte("datum"),
  237. secret: 1983,
  238. }
  239. Ω(Object(s, 1)).Should(match("format_test.SimpleStruct", `{Name: "Oswald", Enumeration: 17, Veritas: true, Data: "datum", secret: 1983}`))
  240. })
  241. Context("when the struct contains long entries", func() {
  242. It("should format the entries with new lines", func() {
  243. s := &SimpleStruct{
  244. Name: "Mithrandir Gandalf Greyhame",
  245. Enumeration: 2021,
  246. Veritas: true,
  247. Data: []byte("wizard"),
  248. secret: 3,
  249. }
  250. Ω(Object(s, 1)).Should(match(fmt.Sprintf("*format_test.SimpleStruct | %p", s), `{
  251. Name: "Mithrandir Gandalf Greyhame",
  252. Enumeration: 2021,
  253. Veritas: true,
  254. Data: "wizard",
  255. secret: 3,
  256. }`))
  257. })
  258. })
  259. })
  260. Describe("formatting nil values", func() {
  261. It("should print out nil", func() {
  262. Ω(Object(nil, 1)).Should(match("nil", "nil"))
  263. var typedNil *AStruct
  264. Ω(Object(typedNil, 1)).Should(match("*format_test.AStruct | 0x0", "nil"))
  265. var c chan<- bool
  266. Ω(Object(c, 1)).Should(match("chan<- bool | len:0, cap:0", "nil"))
  267. var s []string
  268. Ω(Object(s, 1)).Should(match("[]string | len:0, cap:0", "nil"))
  269. var m map[string]bool
  270. Ω(Object(m, 1)).Should(match("map[string]bool | len:0", "nil"))
  271. })
  272. })
  273. Describe("formatting aliased types", func() {
  274. It("should print out the correct alias type", func() {
  275. Ω(Object(StringAlias("alias"), 1)).Should(match("format_test.StringAlias", `alias`))
  276. Ω(Object(ByteAlias("alias"), 1)).Should(matchRegexp(`format_test\.ByteAlias \| len:5, cap:\d+`, `alias`))
  277. Ω(Object(IntAlias(3), 1)).Should(match("format_test.IntAlias", "3"))
  278. })
  279. })
  280. Describe("handling nested things", func() {
  281. It("should produce a correctly nested representation", func() {
  282. s := ComplexStruct{
  283. Strings: []string{"lots", "of", "short", "strings"},
  284. SimpleThings: []*SimpleStruct{
  285. {"short", 7, true, []byte("succinct"), 17},
  286. {"something longer", 427, true, []byte("designed to wrap around nicely"), 30},
  287. },
  288. DataMaps: map[int]ByteAlias{
  289. 17: ByteAlias("some substantially longer chunks of data"),
  290. 1138: ByteAlias("that should make things wrap"),
  291. },
  292. }
  293. expected := `{
  294. Strings: \["lots", "of", "short", "strings"\],
  295. SimpleThings: \[
  296. {Name: "short", Enumeration: 7, Veritas: true, Data: "succinct", secret: 17},
  297. {
  298. Name: "something longer",
  299. Enumeration: 427,
  300. Veritas: true,
  301. Data: "designed to wrap around nicely",
  302. secret: 30,
  303. },
  304. \],
  305. DataMaps: {
  306. (17: "some substantially longer chunks of data"|1138: "that should make things wrap"),
  307. (17: "some substantially longer chunks of data"|1138: "that should make things wrap"),
  308. },
  309. }`
  310. Ω(Object(s, 1)).Should(matchRegexp(`format_test\.ComplexStruct`, expected))
  311. })
  312. })
  313. })
  314. Describe("Handling unexported fields in structs", func() {
  315. It("should handle all the various types correctly", func() {
  316. a := int(5)
  317. s := SecretiveStruct{
  318. boolValue: true,
  319. intValue: 3,
  320. uintValue: 4,
  321. uintptrValue: 5,
  322. floatValue: 6.0,
  323. complexValue: complex(5.0, 3.0),
  324. chanValue: make(chan bool, 2),
  325. funcValue: func() {},
  326. pointerValue: &a,
  327. sliceValue: []string{"string", "slice"},
  328. byteSliceValue: []byte("bytes"),
  329. stringValue: "a string",
  330. arrValue: [3]int{11, 12, 13},
  331. byteArrValue: [3]byte{17, 20, 32},
  332. mapValue: map[string]int{"a key": 20, "b key": 30},
  333. structValue: AStruct{"exported"},
  334. interfaceValue: map[string]int{"a key": 17},
  335. }
  336. expected := fmt.Sprintf(`{
  337. boolValue: true,
  338. intValue: 3,
  339. uintValue: 4,
  340. uintptrValue: 0x5,
  341. floatValue: 6,
  342. complexValue: \(5\+3i\),
  343. chanValue: %p,
  344. funcValue: %p,
  345. pointerValue: 5,
  346. sliceValue: \["string", "slice"\],
  347. byteSliceValue: "bytes",
  348. stringValue: "a string",
  349. arrValue: \[11, 12, 13\],
  350. byteArrValue: \[17, 20, 32\],
  351. mapValue: %s,
  352. structValue: {Exported: "exported"},
  353. interfaceValue: {"a key": 17},
  354. }`, s.chanValue, s.funcValue, hashMatchingRegexp(`"a key": 20`, `"b key": 30`))
  355. Ω(Object(s, 1)).Should(matchRegexp(`format_test\.SecretiveStruct`, expected))
  356. })
  357. })
  358. Describe("Handling interfaces", func() {
  359. It("should unpack the interface", func() {
  360. outerHash := map[string]interface{}{}
  361. innerHash := map[string]int{}
  362. innerHash["inner"] = 3
  363. outerHash["integer"] = 2
  364. outerHash["map"] = innerHash
  365. expected := hashMatchingRegexp(`"integer": 2`, `"map": {"inner": 3}`)
  366. Ω(Object(outerHash, 1)).Should(matchRegexp(`map\[string\]interface {} \| len:2`, expected))
  367. })
  368. })
  369. Describe("Handling recursive things", func() {
  370. It("should not go crazy...", func() {
  371. m := map[string]interface{}{}
  372. m["integer"] = 2
  373. m["map"] = m
  374. Ω(Object(m, 1)).Should(ContainSubstring("..."))
  375. })
  376. })
  377. Describe("When instructed to use the Stringer representation", func() {
  378. BeforeEach(func() {
  379. UseStringerRepresentation = true
  380. })
  381. AfterEach(func() {
  382. UseStringerRepresentation = false
  383. })
  384. Context("when passed a GoStringer", func() {
  385. It("should use what GoString() returns", func() {
  386. Ω(Object(GoStringer{}, 1)).Should(ContainSubstring("<format_test.GoStringer>: go-string"))
  387. })
  388. })
  389. Context("when passed a stringer", func() {
  390. It("should use what String() returns", func() {
  391. Ω(Object(Stringer{}, 1)).Should(ContainSubstring("<format_test.Stringer>: string"))
  392. })
  393. })
  394. })
  395. })