builder_test.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. // Copyright 2017 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package strings_test
  5. import (
  6. "bytes"
  7. "runtime"
  8. . "strings"
  9. "testing"
  10. "unicode/utf8"
  11. )
  12. func check(t *testing.T, b *Builder, want string) {
  13. t.Helper()
  14. got := b.String()
  15. if got != want {
  16. t.Errorf("String: got %#q; want %#q", got, want)
  17. return
  18. }
  19. if n := b.Len(); n != len(got) {
  20. t.Errorf("Len: got %d; but len(String()) is %d", n, len(got))
  21. }
  22. if n := b.Cap(); n < len(got) {
  23. t.Errorf("Cap: got %d; but len(String()) is %d", n, len(got))
  24. }
  25. }
  26. func TestBuilder(t *testing.T) {
  27. var b Builder
  28. check(t, &b, "")
  29. n, err := b.WriteString("hello")
  30. if err != nil || n != 5 {
  31. t.Errorf("WriteString: got %d,%s; want 5,nil", n, err)
  32. }
  33. check(t, &b, "hello")
  34. if err = b.WriteByte(' '); err != nil {
  35. t.Errorf("WriteByte: %s", err)
  36. }
  37. check(t, &b, "hello ")
  38. n, err = b.WriteString("world")
  39. if err != nil || n != 5 {
  40. t.Errorf("WriteString: got %d,%s; want 5,nil", n, err)
  41. }
  42. check(t, &b, "hello world")
  43. }
  44. func TestBuilderString(t *testing.T) {
  45. var b Builder
  46. b.WriteString("alpha")
  47. check(t, &b, "alpha")
  48. s1 := b.String()
  49. b.WriteString("beta")
  50. check(t, &b, "alphabeta")
  51. s2 := b.String()
  52. b.WriteString("gamma")
  53. check(t, &b, "alphabetagamma")
  54. s3 := b.String()
  55. // Check that subsequent operations didn't change the returned strings.
  56. if want := "alpha"; s1 != want {
  57. t.Errorf("first String result is now %q; want %q", s1, want)
  58. }
  59. if want := "alphabeta"; s2 != want {
  60. t.Errorf("second String result is now %q; want %q", s2, want)
  61. }
  62. if want := "alphabetagamma"; s3 != want {
  63. t.Errorf("third String result is now %q; want %q", s3, want)
  64. }
  65. }
  66. func TestBuilderReset(t *testing.T) {
  67. var b Builder
  68. check(t, &b, "")
  69. b.WriteString("aaa")
  70. s := b.String()
  71. check(t, &b, "aaa")
  72. b.Reset()
  73. check(t, &b, "")
  74. // Ensure that writing after Reset doesn't alter
  75. // previously returned strings.
  76. b.WriteString("bbb")
  77. check(t, &b, "bbb")
  78. if want := "aaa"; s != want {
  79. t.Errorf("previous String result changed after Reset: got %q; want %q", s, want)
  80. }
  81. }
  82. func TestBuilderGrow(t *testing.T) {
  83. if runtime.Compiler == "gccgo" {
  84. t.Skip("skip for gccgo until escape analysis improves")
  85. }
  86. for _, growLen := range []int{0, 100, 1000, 10000, 100000} {
  87. p := bytes.Repeat([]byte{'a'}, growLen)
  88. allocs := testing.AllocsPerRun(100, func() {
  89. var b Builder
  90. b.Grow(growLen) // should be only alloc, when growLen > 0
  91. if b.Cap() < growLen {
  92. t.Fatalf("growLen=%d: Cap() is lower than growLen", growLen)
  93. }
  94. b.Write(p)
  95. if b.String() != string(p) {
  96. t.Fatalf("growLen=%d: bad data written after Grow", growLen)
  97. }
  98. })
  99. wantAllocs := 1
  100. if growLen == 0 {
  101. wantAllocs = 0
  102. }
  103. if g, w := int(allocs), wantAllocs; g != w {
  104. t.Errorf("growLen=%d: got %d allocs during Write; want %v", growLen, g, w)
  105. }
  106. }
  107. }
  108. func TestBuilderWrite2(t *testing.T) {
  109. const s0 = "hello 世界"
  110. for _, tt := range []struct {
  111. name string
  112. fn func(b *Builder) (int, error)
  113. n int
  114. want string
  115. }{
  116. {
  117. "Write",
  118. func(b *Builder) (int, error) { return b.Write([]byte(s0)) },
  119. len(s0),
  120. s0,
  121. },
  122. {
  123. "WriteRune",
  124. func(b *Builder) (int, error) { return b.WriteRune('a') },
  125. 1,
  126. "a",
  127. },
  128. {
  129. "WriteRuneWide",
  130. func(b *Builder) (int, error) { return b.WriteRune('世') },
  131. 3,
  132. "世",
  133. },
  134. {
  135. "WriteString",
  136. func(b *Builder) (int, error) { return b.WriteString(s0) },
  137. len(s0),
  138. s0,
  139. },
  140. } {
  141. t.Run(tt.name, func(t *testing.T) {
  142. var b Builder
  143. n, err := tt.fn(&b)
  144. if err != nil {
  145. t.Fatalf("first call: got %s", err)
  146. }
  147. if n != tt.n {
  148. t.Errorf("first call: got n=%d; want %d", n, tt.n)
  149. }
  150. check(t, &b, tt.want)
  151. n, err = tt.fn(&b)
  152. if err != nil {
  153. t.Fatalf("second call: got %s", err)
  154. }
  155. if n != tt.n {
  156. t.Errorf("second call: got n=%d; want %d", n, tt.n)
  157. }
  158. check(t, &b, tt.want+tt.want)
  159. })
  160. }
  161. }
  162. func TestBuilderWriteByte(t *testing.T) {
  163. var b Builder
  164. if err := b.WriteByte('a'); err != nil {
  165. t.Error(err)
  166. }
  167. if err := b.WriteByte(0); err != nil {
  168. t.Error(err)
  169. }
  170. check(t, &b, "a\x00")
  171. }
  172. func TestBuilderAllocs(t *testing.T) {
  173. if runtime.Compiler == "gccgo" {
  174. t.Skip("skip for gccgo until escape analysis improves")
  175. }
  176. // Issue 23382; verify that copyCheck doesn't force the
  177. // Builder to escape and be heap allocated.
  178. n := testing.AllocsPerRun(10000, func() {
  179. var b Builder
  180. b.Grow(5)
  181. b.WriteString("abcde")
  182. _ = b.String()
  183. })
  184. if n != 1 {
  185. t.Errorf("Builder allocs = %v; want 1", n)
  186. }
  187. }
  188. func TestBuilderCopyPanic(t *testing.T) {
  189. tests := []struct {
  190. name string
  191. fn func()
  192. wantPanic bool
  193. }{
  194. {
  195. name: "String",
  196. wantPanic: false,
  197. fn: func() {
  198. var a Builder
  199. a.WriteByte('x')
  200. b := a
  201. _ = b.String() // appease vet
  202. },
  203. },
  204. {
  205. name: "Len",
  206. wantPanic: false,
  207. fn: func() {
  208. var a Builder
  209. a.WriteByte('x')
  210. b := a
  211. b.Len()
  212. },
  213. },
  214. {
  215. name: "Cap",
  216. wantPanic: false,
  217. fn: func() {
  218. var a Builder
  219. a.WriteByte('x')
  220. b := a
  221. b.Cap()
  222. },
  223. },
  224. {
  225. name: "Reset",
  226. wantPanic: false,
  227. fn: func() {
  228. var a Builder
  229. a.WriteByte('x')
  230. b := a
  231. b.Reset()
  232. b.WriteByte('y')
  233. },
  234. },
  235. {
  236. name: "Write",
  237. wantPanic: true,
  238. fn: func() {
  239. var a Builder
  240. a.Write([]byte("x"))
  241. b := a
  242. b.Write([]byte("y"))
  243. },
  244. },
  245. {
  246. name: "WriteByte",
  247. wantPanic: true,
  248. fn: func() {
  249. var a Builder
  250. a.WriteByte('x')
  251. b := a
  252. b.WriteByte('y')
  253. },
  254. },
  255. {
  256. name: "WriteString",
  257. wantPanic: true,
  258. fn: func() {
  259. var a Builder
  260. a.WriteString("x")
  261. b := a
  262. b.WriteString("y")
  263. },
  264. },
  265. {
  266. name: "WriteRune",
  267. wantPanic: true,
  268. fn: func() {
  269. var a Builder
  270. a.WriteRune('x')
  271. b := a
  272. b.WriteRune('y')
  273. },
  274. },
  275. {
  276. name: "Grow",
  277. wantPanic: true,
  278. fn: func() {
  279. var a Builder
  280. a.Grow(1)
  281. b := a
  282. b.Grow(2)
  283. },
  284. },
  285. }
  286. for _, tt := range tests {
  287. didPanic := make(chan bool)
  288. go func() {
  289. defer func() { didPanic <- recover() != nil }()
  290. tt.fn()
  291. }()
  292. if got := <-didPanic; got != tt.wantPanic {
  293. t.Errorf("%s: panicked = %v; want %v", tt.name, got, tt.wantPanic)
  294. }
  295. }
  296. }
  297. func TestBuilderWriteInvalidRune(t *testing.T) {
  298. // Invalid runes, including negative ones, should be written as
  299. // utf8.RuneError.
  300. for _, r := range []rune{-1, utf8.MaxRune + 1} {
  301. var b Builder
  302. b.WriteRune(r)
  303. check(t, &b, "\uFFFD")
  304. }
  305. }
  306. var someBytes = []byte("some bytes sdljlk jsklj3lkjlk djlkjw")
  307. var sinkS string
  308. func benchmarkBuilder(b *testing.B, f func(b *testing.B, numWrite int, grow bool)) {
  309. b.Run("1Write_NoGrow", func(b *testing.B) {
  310. b.ReportAllocs()
  311. f(b, 1, false)
  312. })
  313. b.Run("3Write_NoGrow", func(b *testing.B) {
  314. b.ReportAllocs()
  315. f(b, 3, false)
  316. })
  317. b.Run("3Write_Grow", func(b *testing.B) {
  318. b.ReportAllocs()
  319. f(b, 3, true)
  320. })
  321. }
  322. func BenchmarkBuildString_Builder(b *testing.B) {
  323. benchmarkBuilder(b, func(b *testing.B, numWrite int, grow bool) {
  324. for i := 0; i < b.N; i++ {
  325. var buf Builder
  326. if grow {
  327. buf.Grow(len(someBytes) * numWrite)
  328. }
  329. for i := 0; i < numWrite; i++ {
  330. buf.Write(someBytes)
  331. }
  332. sinkS = buf.String()
  333. }
  334. })
  335. }
  336. func BenchmarkBuildString_ByteBuffer(b *testing.B) {
  337. benchmarkBuilder(b, func(b *testing.B, numWrite int, grow bool) {
  338. for i := 0; i < b.N; i++ {
  339. var buf bytes.Buffer
  340. if grow {
  341. buf.Grow(len(someBytes) * numWrite)
  342. }
  343. for i := 0; i < numWrite; i++ {
  344. buf.Write(someBytes)
  345. }
  346. sinkS = buf.String()
  347. }
  348. })
  349. }