1
2
3
4
5 package iterator_test
6
7 import (
8 "context"
9 "encoding/json"
10 "math"
11 "reflect"
12 "testing"
13
14 "google.golang.org/api/iterator"
15 itest "google.golang.org/api/iterator/testing"
16 )
17
18
19
20
21
22 type service struct {
23
24 end int
25
26
27 max int
28
29
30
31
32
33
34
35 zeroes bool
36 }
37
38
39 func (s *service) List(pageSize int, pageToken string) ([]int, string, error) {
40 max := s.max
41 if max == 0 {
42 max = math.MaxInt
43 }
44
45 if pageSize <= 0 || pageSize > max {
46 pageSize = max
47 }
48 state := &listState{}
49 if pageToken != "" {
50 if err := json.Unmarshal([]byte(pageToken), state); err != nil {
51 return nil, "", err
52 }
53 }
54 ints := state.advance(pageSize, s.end, s.zeroes)
55 if state.Start == s.end && (!s.zeroes || state.NumZeroes == 2) {
56 pageToken = ""
57 } else {
58 bytes, err := json.Marshal(state)
59 if err != nil {
60 return nil, "", err
61 }
62 pageToken = string(bytes)
63 }
64 return ints, pageToken, nil
65 }
66
67 type listState struct {
68 Start int
69 NumZeroes int
70 }
71
72 func (s *listState) advance(pageSize, end int, zeroes bool) []int {
73 var page []int
74 if zeroes && s.NumZeroes != 2 {
75
76 } else {
77 for i := s.Start; i < end && len(page) < pageSize; i++ {
78 page = append(page, i)
79 }
80 }
81 s.Start += len(page)
82 if len(page) == 0 {
83 s.NumZeroes++
84 } else {
85 s.NumZeroes = 0
86 }
87 return page
88 }
89
90 func TestServiceList(t *testing.T) {
91 for _, test := range []struct {
92 svc service
93 pageSize int
94 want [][]int
95 }{
96 {service{end: 0}, 0, [][]int{nil}},
97 {service{end: 5}, 0, [][]int{{0, 1, 2, 3, 4}}},
98 {service{end: 5}, 8, [][]int{{0, 1, 2, 3, 4}}},
99 {service{end: 5}, 2, [][]int{{0, 1}, {2, 3}, {4}}},
100 {service{end: 5, max: 2}, 0, [][]int{{0, 1}, {2, 3}, {4}}},
101 {service{end: 5, max: 2}, 1, [][]int{{0}, {1}, {2}, {3}, {4}}},
102 {service{end: 5, max: 2}, 10, [][]int{{0, 1}, {2, 3}, {4}}},
103 {service{end: 5, zeroes: true}, 0, [][]int{nil, nil, {0, 1, 2, 3, 4}, nil, nil}},
104 {service{end: 5, max: 3, zeroes: true}, 0, [][]int{nil, nil, {0, 1, 2}, nil, nil, {3, 4}, nil, nil}},
105 } {
106 var got [][]int
107 token := ""
108 for {
109 items, nextToken, err := test.svc.List(test.pageSize, token)
110 if err != nil {
111 t.Fatalf("%v, %d: %v", test.svc, test.pageSize, err)
112 }
113 got = append(got, items)
114 if nextToken == "" {
115 break
116 }
117 token = nextToken
118 }
119 if !reflect.DeepEqual(got, test.want) {
120 t.Errorf("%v, %d: got %v, want %v", test.svc, test.pageSize, got, test.want)
121 }
122 }
123 }
124
125 type Client struct{ s *service }
126
127
128 type ItemIterator struct {
129 pageInfo *iterator.PageInfo
130 nextFunc func() error
131 s *service
132 items []int
133 }
134
135
136 func (it *ItemIterator) PageInfo() *iterator.PageInfo { return it.pageInfo }
137
138
139 func (c *Client) Items(ctx context.Context) *ItemIterator {
140 it := &ItemIterator{s: c.s}
141 it.pageInfo, it.nextFunc = iterator.NewPageInfo(
142 it.fetch,
143 func() int { return len(it.items) },
144 func() interface{} { b := it.items; it.items = nil; return b })
145 return it
146 }
147
148 func (it *ItemIterator) fetch(pageSize int, pageToken string) (string, error) {
149 items, tok, err := it.s.List(pageSize, pageToken)
150 it.items = append(it.items, items...)
151 return tok, err
152 }
153
154 func (it *ItemIterator) Next() (int, error) {
155 if err := it.nextFunc(); err != nil {
156 return 0, err
157 }
158 item := it.items[0]
159 it.items = it.items[1:]
160 return item, nil
161 }
162
163 func TestNext(t *testing.T) {
164
165
166 for _, svc := range []service{
167 {end: 0},
168 {end: 5},
169 {end: 5, max: 1},
170 {end: 5, max: 2},
171 {end: 5, zeroes: true},
172 {end: 5, max: 2, zeroes: true},
173 } {
174 client := &Client{&svc}
175
176 msg, ok := itest.TestIterator(
177 seq(0, svc.end),
178 func() interface{} { return client.Items(ctx) },
179 func(it interface{}) (interface{}, error) { return it.(*ItemIterator).Next() })
180 if !ok {
181 t.Errorf("%+v: %s", svc, msg)
182 }
183 }
184 }
185
186
187
188
189
190
191 func TestNextWithNextPage(t *testing.T) {
192 client := &Client{&service{end: 11}}
193 var items []int
194
195
196 it := client.Items(ctx)
197 it.Next()
198 _, err := iterator.NewPager(it, 1, "").NextPage(&items)
199 if err == nil {
200 t.Error("NextPage after Next: got nil, want error")
201 }
202 _, err = it.Next()
203 if err == nil {
204 t.Error("Next after NextPage: got nil, want error")
205 }
206
207
208 it = client.Items(ctx)
209 p := iterator.NewPager(it, 1, "")
210 p.NextPage(&items)
211 _, err = it.Next()
212 if err == nil {
213 t.Error("Next after NextPage: got nil, want error")
214 }
215 _, err = p.NextPage(&items)
216 if err == nil {
217 t.Error("second NextPage after Next: got nil, want error")
218 }
219 }
220
221
222 func TestNextPageReflectionErrors(t *testing.T) {
223 client := &Client{&service{end: 1}}
224 p := iterator.NewPager(client.Items(ctx), 1, "")
225
226
227 _, err := p.NextPage(nil)
228 if err == nil {
229 t.Error("nil: got nil, want error")
230 }
231
232
233 _, err = p.NextPage(17)
234 if err == nil {
235 t.Error("non-slice: got nil, want error")
236 }
237
238
239 var bools []bool
240 _, err = p.NextPage(&bools)
241 if err == nil {
242 t.Error("wrong type: got nil, want error")
243 }
244
245
246 var ints []int
247 _, err = p.NextPage(ints)
248 if err == nil {
249 t.Error("not a pointer: got nil, want error")
250 }
251 }
252
253
254 func seq(from, to int) []int {
255 var r []int
256 for i := from; i < to; i++ {
257 r = append(r, i)
258 }
259 return r
260 }
261
View as plain text