Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 

271 рядки
7.3 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 iterator_test
  15. import (
  16. "context"
  17. "encoding/json"
  18. "math"
  19. "reflect"
  20. "testing"
  21. "google.golang.org/api/iterator"
  22. itest "google.golang.org/api/iterator/testing"
  23. )
  24. // Service represents the implementation of a Google API's List method.
  25. // We want to test against a large range of possible valid behaviors.
  26. // All the behaviors this can generate are valid under the spec for
  27. // Google API paging.
  28. type service struct {
  29. // End of the sequence. end-1 is the last value returned.
  30. end int
  31. // Maximum number of items to return in one RPC. Also the default page size.
  32. // If zero, max is unlimited.
  33. max int
  34. // If true, return two empty pages before each RPC that returns items, and
  35. // two zero pages at the end. E.g. if end = 5, max = 2 and the pageSize
  36. // parameter to List is zero, then the number of items returned in
  37. // successive RPCS is:
  38. // 0 0 2 0 0 2 0 0 1 0 0
  39. // Note that this implies that the RPC returning the last items will have a
  40. // non-empty page token.
  41. zeroes bool
  42. }
  43. // List simulates an API List RPC. It returns integers in the range [0, s.end).
  44. func (s *service) List(pageSize int, pageToken string) ([]int, string, error) {
  45. max := s.max
  46. if max == 0 {
  47. max = math.MaxInt64
  48. }
  49. // Never give back any more than s.max.
  50. if pageSize <= 0 || pageSize > max {
  51. pageSize = max
  52. }
  53. state := &listState{}
  54. if pageToken != "" {
  55. if err := json.Unmarshal([]byte(pageToken), state); err != nil {
  56. return nil, "", err
  57. }
  58. }
  59. ints := state.advance(pageSize, s.end, s.zeroes)
  60. if state.Start == s.end && (!s.zeroes || state.NumZeroes == 2) {
  61. pageToken = ""
  62. } else {
  63. bytes, err := json.Marshal(state)
  64. if err != nil {
  65. return nil, "", err
  66. }
  67. pageToken = string(bytes)
  68. }
  69. return ints, pageToken, nil
  70. }
  71. type listState struct {
  72. Start int // where to start this page
  73. NumZeroes int // number of consecutive empty pages before this
  74. }
  75. func (s *listState) advance(pageSize, end int, zeroes bool) []int {
  76. var page []int
  77. if zeroes && s.NumZeroes != 2 {
  78. // Return a zero page.
  79. } else {
  80. for i := s.Start; i < end && len(page) < pageSize; i++ {
  81. page = append(page, i)
  82. }
  83. }
  84. s.Start += len(page)
  85. if len(page) == 0 {
  86. s.NumZeroes++
  87. } else {
  88. s.NumZeroes = 0
  89. }
  90. return page
  91. }
  92. func TestServiceList(t *testing.T) {
  93. for _, test := range []struct {
  94. svc service
  95. pageSize int
  96. want [][]int
  97. }{
  98. {service{end: 0}, 0, [][]int{nil}},
  99. {service{end: 5}, 0, [][]int{{0, 1, 2, 3, 4}}},
  100. {service{end: 5}, 8, [][]int{{0, 1, 2, 3, 4}}},
  101. {service{end: 5}, 2, [][]int{{0, 1}, {2, 3}, {4}}},
  102. {service{end: 5, max: 2}, 0, [][]int{{0, 1}, {2, 3}, {4}}},
  103. {service{end: 5, max: 2}, 1, [][]int{{0}, {1}, {2}, {3}, {4}}},
  104. {service{end: 5, max: 2}, 10, [][]int{{0, 1}, {2, 3}, {4}}},
  105. {service{end: 5, zeroes: true}, 0, [][]int{nil, nil, {0, 1, 2, 3, 4}, nil, nil}},
  106. {service{end: 5, max: 3, zeroes: true}, 0, [][]int{nil, nil, {0, 1, 2}, nil, nil, {3, 4}, nil, nil}},
  107. } {
  108. var got [][]int
  109. token := ""
  110. for {
  111. items, nextToken, err := test.svc.List(test.pageSize, token)
  112. if err != nil {
  113. t.Fatalf("%v, %d: %v", test.svc, test.pageSize, err)
  114. }
  115. got = append(got, items)
  116. if nextToken == "" {
  117. break
  118. }
  119. token = nextToken
  120. }
  121. if !reflect.DeepEqual(got, test.want) {
  122. t.Errorf("%v, %d: got %v, want %v", test.svc, test.pageSize, got, test.want)
  123. }
  124. }
  125. }
  126. type Client struct{ s *service }
  127. // ItemIterator is a sample implementation of a standard iterator.
  128. type ItemIterator struct {
  129. pageInfo *iterator.PageInfo
  130. nextFunc func() error
  131. s *service
  132. items []int
  133. }
  134. // PageInfo returns a PageInfo, which supports pagination.
  135. func (it *ItemIterator) PageInfo() *iterator.PageInfo { return it.pageInfo }
  136. // Items is a sample implementation of an iterator-creating method.
  137. func (c *Client) Items(ctx context.Context) *ItemIterator {
  138. it := &ItemIterator{s: c.s}
  139. it.pageInfo, it.nextFunc = iterator.NewPageInfo(
  140. it.fetch,
  141. func() int { return len(it.items) },
  142. func() interface{} { b := it.items; it.items = nil; return b })
  143. return it
  144. }
  145. func (it *ItemIterator) fetch(pageSize int, pageToken string) (string, error) {
  146. items, tok, err := it.s.List(pageSize, pageToken)
  147. it.items = append(it.items, items...)
  148. return tok, err
  149. }
  150. func (it *ItemIterator) Next() (int, error) {
  151. if err := it.nextFunc(); err != nil {
  152. return 0, err
  153. }
  154. item := it.items[0]
  155. it.items = it.items[1:]
  156. return item, nil
  157. }
  158. func TestNext(t *testing.T) {
  159. // Test the iterator's Next method with a variety of different service behaviors.
  160. // This is primarily a test of PageInfo.next.
  161. for _, svc := range []service{
  162. {end: 0},
  163. {end: 5},
  164. {end: 5, max: 1},
  165. {end: 5, max: 2},
  166. {end: 5, zeroes: true},
  167. {end: 5, max: 2, zeroes: true},
  168. } {
  169. client := &Client{&svc}
  170. msg, ok := itest.TestIterator(
  171. seq(0, svc.end),
  172. func() interface{} { return client.Items(ctx) },
  173. func(it interface{}) (interface{}, error) { return it.(*ItemIterator).Next() })
  174. if !ok {
  175. t.Errorf("%+v: %s", svc, msg)
  176. }
  177. }
  178. }
  179. // TODO(jba): test setting PageInfo.MaxSize
  180. // TODO(jba): test setting PageInfo.Token
  181. // Verify that, for an iterator that uses PageInfo.next to implement its Next
  182. // method, using Next and NextPage together result in an error.
  183. func TestNextWithNextPage(t *testing.T) {
  184. client := &Client{&service{end: 11}}
  185. var items []int
  186. // Calling Next before NextPage.
  187. it := client.Items(ctx)
  188. it.Next()
  189. _, err := iterator.NewPager(it, 1, "").NextPage(&items)
  190. if err == nil {
  191. t.Error("NextPage after Next: got nil, want error")
  192. }
  193. _, err = it.Next()
  194. if err == nil {
  195. t.Error("Next after NextPage: got nil, want error")
  196. }
  197. // Next between two calls to NextPage.
  198. it = client.Items(ctx)
  199. p := iterator.NewPager(it, 1, "")
  200. p.NextPage(&items)
  201. _, err = it.Next()
  202. if err == nil {
  203. t.Error("Next after NextPage: got nil, want error")
  204. }
  205. _, err = p.NextPage(&items)
  206. if err == nil {
  207. t.Error("second NextPage after Next: got nil, want error")
  208. }
  209. }
  210. // Verify that we turn various potential reflection panics into errors.
  211. func TestNextPageReflectionErrors(t *testing.T) {
  212. client := &Client{&service{end: 1}}
  213. p := iterator.NewPager(client.Items(ctx), 1, "")
  214. // Passing the nil interface value.
  215. _, err := p.NextPage(nil)
  216. if err == nil {
  217. t.Error("nil: got nil, want error")
  218. }
  219. // Passing a non-slice.
  220. _, err = p.NextPage(17)
  221. if err == nil {
  222. t.Error("non-slice: got nil, want error")
  223. }
  224. // Passing a slice of the wrong type.
  225. var bools []bool
  226. _, err = p.NextPage(&bools)
  227. if err == nil {
  228. t.Error("wrong type: got nil, want error")
  229. }
  230. // Using a slice of the right type, but not passing a pointer to it.
  231. var ints []int
  232. _, err = p.NextPage(ints)
  233. if err == nil {
  234. t.Error("not a pointer: got nil, want error")
  235. }
  236. }
  237. // seq returns a slice containing the values in [from, to).
  238. func seq(from, to int) []int {
  239. var r []int
  240. for i := from; i < to; i++ {
  241. r = append(r, i)
  242. }
  243. return r
  244. }