Switch to using the dep tool and update all the dependencies

This commit is contained in:
Nick Craig-Wood 2017-05-11 15:39:54 +01:00
parent 5135ff73cb
commit 98c2d2c41b
5321 changed files with 4483201 additions and 5922 deletions

227
vendor/google.golang.org/api/iterator/examples_test.go generated vendored Normal file
View file

@ -0,0 +1,227 @@
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package iterator_test
import (
"bytes"
"fmt"
"html/template"
"log"
"math"
"net/http"
"sort"
"strconv"
"golang.org/x/net/context"
"google.golang.org/api/iterator"
)
var (
client *Client
ctx = context.Background()
)
var pageTemplate = template.Must(template.New("").Parse(`
<table>
{{range .Entries}}
<tr><td>{{.}}</td></tr>
{{end}}
</table>
{{with .Next}}
<a href="/entries?pageToken={{.}}">Next Page</a>
{{end}}
`))
// This example demonstrates how to use Pager to support
// pagination on a web site.
func Example_webHandler(w http.ResponseWriter, r *http.Request) {
const pageSize = 25
it := client.Items(ctx)
var items []int
pageToken, err := iterator.NewPager(it, pageSize, r.URL.Query().Get("pageToken")).NextPage(&items)
if err != nil {
http.Error(w, fmt.Sprintf("getting next page: %v", err), http.StatusInternalServerError)
}
data := struct {
Items []int
Next string
}{
items,
pageToken,
}
var buf bytes.Buffer
if err := pageTemplate.Execute(&buf, data); err != nil {
http.Error(w, fmt.Sprintf("executing page template: %v", err), http.StatusInternalServerError)
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
if _, err := buf.WriteTo(w); err != nil {
log.Printf("writing response: %v", err)
}
}
// This example demonstrates how to use a Pager to page through an iterator in a loop.
func Example_pageLoop() {
// Find all primes up to 42, in pages of size 5.
const max = 42
const pageSize = 5
p := iterator.NewPager(Primes(max), pageSize, "" /* start from the beginning */)
for page := 0; ; page++ {
var items []int
pageToken, err := p.NextPage(&items)
if err != nil {
log.Fatalf("Iterator paging failed: %v", err)
}
fmt.Printf("Page %d: %v\n", page, items)
if pageToken == "" {
break
}
}
// Output:
// Page 0: [2 3 5 7 11]
// Page 1: [13 17 19 23 29]
// Page 2: [31 37 41]
}
// The example demonstrates how to use a Pager to request a page from a given token.
func Example_pageToken() {
const pageSize = 5
const pageToken = "1337"
p := iterator.NewPager(Primes(0), pageSize, pageToken)
var items []int
nextPage, err := p.NextPage(&items)
if err != nil {
log.Fatalf("Iterator paging failed: %v", err)
}
fmt.Printf("Primes: %v\nToken: %q\n", items, nextPage)
// Output:
// Primes: [1361 1367 1373 1381 1399]
// Token: "1400"
}
// This example demonstrates how to get exactly the items in the buffer, without
// triggering an extra RPC.
func Example_serverPages() {
// The iterator returned by Primes has a default page size of 20, which means
// it will return all the primes in the range [2, 21).
it := Primes(0)
var items []int
for {
item, err := it.Next()
if err != nil && err != iterator.Done {
log.Fatal(err)
}
if err == iterator.Done {
break
}
items = append(items, item)
if it.PageInfo().Remaining() == 0 {
break
}
}
fmt.Println(items)
// Output:
// [2 3 5 7 11 13 17 19]
}
// Primes returns a iterator which returns a sequence of prime numbers.
// If non-zero, max specifies the maximum number which could possibly be
// returned.
func Primes(max int) *SieveIterator {
it := &SieveIterator{pos: 2, max: max}
it.pageInfo, it.nextFunc = iterator.NewPageInfo(
it.fetch,
func() int { return len(it.items) },
func() interface{} { b := it.items; it.items = nil; return b })
return it
}
// SieveIterator is an iterator that returns primes using the sieve of
// Eratosthenes. It is a demonstration of how an iterator might work.
// Internally, it uses "page size" as the number of ints to consider,
// and "page token" as the first number to consider (defaults to 2).
type SieveIterator struct {
pageInfo *iterator.PageInfo
nextFunc func() error
max int // The largest number to consider.
p []int // Primes in the range [2, pos).
pos int // Next number to consider when generating p.
items []int
}
// PageInfo returns a PageInfo, which supports pagination.
func (it *SieveIterator) PageInfo() *iterator.PageInfo { return it.pageInfo }
func (it *SieveIterator) fetch(pageSize int, pageToken string) (string, error) {
start := 2
if pageToken != "" {
s, err := strconv.Atoi(pageToken)
if err != nil || s < 2 {
return "", fmt.Errorf("invalid token %q", pageToken)
}
start = s
}
if pageSize == 0 {
pageSize = 20 // Default page size.
}
// Make sure sufficient primes have been calculated.
it.calc(start + pageSize)
// Find the subslice of primes which match this page.
// Note that PageInfo requires that fetch does not remove any existing items,
// so we cannot assume that items is empty at this call.
items := it.p[sort.SearchInts(it.p, start):]
items = items[:sort.SearchInts(items, start+pageSize)]
it.items = append(it.items, items...)
if it.max > 0 && start+pageSize > it.max {
return "", nil // No more possible numbers to return.
}
return strconv.Itoa(start + pageSize), nil
}
// calc populates p with all primes up to, but not including, max.
func (it *SieveIterator) calc(max int) {
if it.max > 0 && max > it.max+1 { // it.max is an inclusive bounds, max is exclusive.
max = it.max + 1
}
outer:
for x := it.pos; x < max; x++ {
sqrt := int(math.Sqrt(float64(x)))
for _, p := range it.p {
switch {
case x%p == 0:
// Not a prime.
continue outer
case p > sqrt:
// Only need to check up to sqrt.
break
}
}
it.p = append(it.p, x)
}
it.pos = max
}
func (it *SieveIterator) Next() (int, error) {
if err := it.nextFunc(); err != nil {
return 0, err
}
item := it.items[0]
it.items = it.items[1:]
return item, nil
}

231
vendor/google.golang.org/api/iterator/iterator.go generated vendored Normal file
View file

@ -0,0 +1,231 @@
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package iterator provides support for standard Google API iterators.
// See https://github.com/GoogleCloudPlatform/gcloud-golang/wiki/Iterator-Guidelines.
package iterator
import (
"errors"
"fmt"
"reflect"
)
// Done is returned by an iterator's Next method when the iteration is
// complete; when there are no more items to return.
var Done = errors.New("no more items in iterator")
// We don't support mixed calls to Next and NextPage because they play
// with the paging state in incompatible ways.
var errMixed = errors.New("iterator: Next and NextPage called on same iterator")
// PageInfo contains information about an iterator's paging state.
type PageInfo struct {
// Token is the token used to retrieve the next page of items from the
// API. You may set Token immediately after creating an iterator to
// begin iteration at a particular point. If Token is the empty string,
// the iterator will begin with the first eligible item.
//
// The result of setting Token after the first call to Next is undefined.
//
// After the underlying API method is called to retrieve a page of items,
// Token is set to the next-page token in the response.
Token string
// MaxSize is the maximum number of items returned by a call to the API.
// Set MaxSize as a hint to optimize the buffering behavior of the iterator.
// If zero, the page size is determined by the underlying service.
//
// Use Pager to retrieve a page of a specific, exact size.
MaxSize int
// The error state of the iterator. Manipulated by PageInfo.next and Pager.
// This is a latch: it starts as nil, and once set should never change.
err error
// If true, no more calls to fetch should be made. Set to true when fetch
// returns an empty page token. The iterator is Done when this is true AND
// the buffer is empty.
atEnd bool
// Function that fetches a page from the underlying service. It should pass
// the pageSize and pageToken arguments to the service, fill the buffer
// with the results from the call, and return the next-page token returned
// by the service. The function must not remove any existing items from the
// buffer. If the underlying RPC takes an int32 page size, pageSize should
// be silently truncated.
fetch func(pageSize int, pageToken string) (nextPageToken string, err error)
// Function that clears the iterator's buffer, returning any currently buffered items.
bufLen func() int
// Function that returns the buffer, after setting the buffer variable to nil.
takeBuf func() interface{}
// Set to true on first call to PageInfo.next or Pager.NextPage. Used to check
// for calls to both Next and NextPage with the same iterator.
nextCalled, nextPageCalled bool
}
// NewPageInfo exposes internals for iterator implementations.
// It is not a stable interface.
var NewPageInfo = newPageInfo
// If an iterator can support paging, its iterator-creating method should call
// this (via the NewPageInfo variable above).
//
// The fetch, bufLen and takeBuf arguments provide access to the
// iterator's internal slice of buffered items. They behave as described in
// PageInfo, above.
//
// The return value is the PageInfo.next method bound to the returned PageInfo value.
// (Returning it avoids exporting PageInfo.next.)
func newPageInfo(fetch func(int, string) (string, error), bufLen func() int, takeBuf func() interface{}) (*PageInfo, func() error) {
pi := &PageInfo{
fetch: fetch,
bufLen: bufLen,
takeBuf: takeBuf,
}
return pi, pi.next
}
// Remaining returns the number of items available before the iterator makes another API call.
func (pi *PageInfo) Remaining() int { return pi.bufLen() }
// next provides support for an iterator's Next function. An iterator's Next
// should return the error returned by next if non-nil; else it can assume
// there is at least one item in its buffer, and it should return that item and
// remove it from the buffer.
func (pi *PageInfo) next() error {
pi.nextCalled = true
if pi.err != nil { // Once we get an error, always return it.
// TODO(jba): fix so users can retry on transient errors? Probably not worth it.
return pi.err
}
if pi.nextPageCalled {
pi.err = errMixed
return pi.err
}
// Loop until we get some items or reach the end.
for pi.bufLen() == 0 && !pi.atEnd {
if err := pi.fill(pi.MaxSize); err != nil {
pi.err = err
return pi.err
}
if pi.Token == "" {
pi.atEnd = true
}
}
// Either the buffer is non-empty or pi.atEnd is true (or both).
if pi.bufLen() == 0 {
// The buffer is empty and pi.atEnd is true, i.e. the service has no
// more items.
pi.err = Done
}
return pi.err
}
// Call the service to fill the buffer, using size and pi.Token. Set pi.Token to the
// next-page token returned by the call.
// If fill returns a non-nil error, the buffer will be empty.
func (pi *PageInfo) fill(size int) error {
tok, err := pi.fetch(size, pi.Token)
if err != nil {
pi.takeBuf() // clear the buffer
return err
}
pi.Token = tok
return nil
}
// Pageable is implemented by iterators that support paging.
type Pageable interface {
// PageInfo returns paging information associated with the iterator.
PageInfo() *PageInfo
}
// Pager supports retrieving iterator items a page at a time.
type Pager struct {
pageInfo *PageInfo
pageSize int
}
// NewPager returns a pager that uses iter. Calls to its NextPage method will
// obtain exactly pageSize items, unless fewer remain. The pageToken argument
// indicates where to start the iteration. Pass the empty string to start at
// the beginning, or pass a token retrieved from a call to Pager.NextPage.
//
// If you use an iterator with a Pager, you must not call Next on the iterator.
func NewPager(iter Pageable, pageSize int, pageToken string) *Pager {
p := &Pager{
pageInfo: iter.PageInfo(),
pageSize: pageSize,
}
p.pageInfo.Token = pageToken
if pageSize <= 0 {
p.pageInfo.err = errors.New("iterator: page size must be positive")
}
return p
}
// NextPage retrieves a sequence of items from the iterator and appends them
// to slicep, which must be a pointer to a slice of the iterator's item type.
// Exactly p.pageSize items will be appended, unless fewer remain.
//
// The first return value is the page token to use for the next page of items.
// If empty, there are no more pages. Aside from checking for the end of the
// iteration, the returned page token is only needed if the iteration is to be
// resumed a later time, in another context (possibly another process).
//
// The second return value is non-nil if an error occurred. It will never be
// the special iterator sentinel value Done. To recognize the end of the
// iteration, compare nextPageToken to the empty string.
//
// It is possible for NextPage to return a single zero-length page along with
// an empty page token when there are no more items in the iteration.
func (p *Pager) NextPage(slicep interface{}) (nextPageToken string, err error) {
p.pageInfo.nextPageCalled = true
if p.pageInfo.err != nil {
return "", p.pageInfo.err
}
if p.pageInfo.nextCalled {
p.pageInfo.err = errMixed
return "", p.pageInfo.err
}
if p.pageInfo.bufLen() > 0 {
return "", errors.New("must call NextPage with an empty buffer")
}
// The buffer must be empty here, so takeBuf is a no-op. We call it just to get
// the buffer's type.
wantSliceType := reflect.PtrTo(reflect.ValueOf(p.pageInfo.takeBuf()).Type())
if slicep == nil {
return "", errors.New("nil passed to Pager.NextPage")
}
vslicep := reflect.ValueOf(slicep)
if vslicep.Type() != wantSliceType {
return "", fmt.Errorf("slicep should be of type %s, got %T", wantSliceType, slicep)
}
for p.pageInfo.bufLen() < p.pageSize {
if err := p.pageInfo.fill(p.pageSize - p.pageInfo.bufLen()); err != nil {
p.pageInfo.err = err
return "", p.pageInfo.err
}
if p.pageInfo.Token == "" {
break
}
}
e := vslicep.Elem()
e.Set(reflect.AppendSlice(e, reflect.ValueOf(p.pageInfo.takeBuf())))
return p.pageInfo.Token, nil
}

270
vendor/google.golang.org/api/iterator/iterator_test.go generated vendored Normal file
View file

@ -0,0 +1,270 @@
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package iterator_test
import (
"encoding/json"
"math"
"reflect"
"testing"
"golang.org/x/net/context"
"google.golang.org/api/iterator"
itest "google.golang.org/api/iterator/testing"
)
// Service represents the implementation of a Google API's List method.
// We want to test against a large range of possible valid behaviors.
// All the behaviors this can generate are valid under the spec for
// Google API paging.
type service struct {
// End of the sequence. end-1 is the last value returned.
end int
// Maximum number of items to return in one RPC. Also the default page size.
// If zero, max is unlimited.
max int
// If true, return two empty pages before each RPC that returns items, and
// two zero pages at the end. E.g. if end = 5, max = 2 and the pageSize
// parameter to List is zero, then the number of items returned in
// successive RPCS is:
// 0 0 2 0 0 2 0 0 1 0 0
// Note that this implies that the RPC returning the last items will have a
// non-empty page token.
zeroes bool
}
// List simulates an API List RPC. It returns integers in the range [0, s.end).
func (s *service) List(pageSize int, pageToken string) ([]int, string, error) {
max := s.max
if max == 0 {
max = math.MaxInt64
}
// Never give back any more than s.max.
if pageSize <= 0 || pageSize > max {
pageSize = max
}
state := &listState{}
if pageToken != "" {
if err := json.Unmarshal([]byte(pageToken), state); err != nil {
return nil, "", err
}
}
ints := state.advance(pageSize, s.end, s.zeroes)
if state.Start == s.end && (!s.zeroes || state.NumZeroes == 2) {
pageToken = ""
} else {
bytes, err := json.Marshal(state)
if err != nil {
return nil, "", err
}
pageToken = string(bytes)
}
return ints, pageToken, nil
}
type listState struct {
Start int // where to start this page
NumZeroes int // number of consecutive empty pages before this
}
func (s *listState) advance(pageSize, end int, zeroes bool) []int {
var page []int
if zeroes && s.NumZeroes != 2 {
// Return a zero page.
} else {
for i := s.Start; i < end && len(page) < pageSize; i++ {
page = append(page, i)
}
}
s.Start += len(page)
if len(page) == 0 {
s.NumZeroes++
} else {
s.NumZeroes = 0
}
return page
}
func TestServiceList(t *testing.T) {
for _, test := range []struct {
svc service
pageSize int
want [][]int
}{
{service{end: 0}, 0, [][]int{nil}},
{service{end: 5}, 0, [][]int{{0, 1, 2, 3, 4}}},
{service{end: 5}, 8, [][]int{{0, 1, 2, 3, 4}}},
{service{end: 5}, 2, [][]int{{0, 1}, {2, 3}, {4}}},
{service{end: 5, max: 2}, 0, [][]int{{0, 1}, {2, 3}, {4}}},
{service{end: 5, max: 2}, 1, [][]int{{0}, {1}, {2}, {3}, {4}}},
{service{end: 5, max: 2}, 10, [][]int{{0, 1}, {2, 3}, {4}}},
{service{end: 5, zeroes: true}, 0, [][]int{nil, nil, {0, 1, 2, 3, 4}, nil, nil}},
{service{end: 5, max: 3, zeroes: true}, 0, [][]int{nil, nil, {0, 1, 2}, nil, nil, {3, 4}, nil, nil}},
} {
var got [][]int
token := ""
for {
items, nextToken, err := test.svc.List(test.pageSize, token)
if err != nil {
t.Fatalf("%v, %d: %v", test.svc, test.pageSize, err)
}
got = append(got, items)
if nextToken == "" {
break
}
token = nextToken
}
if !reflect.DeepEqual(got, test.want) {
t.Errorf("%v, %d: got %v, want %v", test.svc, test.pageSize, got, test.want)
}
}
}
type Client struct{ s *service }
// ItemIterator is a sample implementation of a standard iterator.
type ItemIterator struct {
pageInfo *iterator.PageInfo
nextFunc func() error
s *service
items []int
}
// PageInfo returns a PageInfo, which supports pagination.
func (it *ItemIterator) PageInfo() *iterator.PageInfo { return it.pageInfo }
// Items is a sample implementation of an iterator-creating method.
func (c *Client) Items(ctx context.Context) *ItemIterator {
it := &ItemIterator{s: c.s}
it.pageInfo, it.nextFunc = iterator.NewPageInfo(
it.fetch,
func() int { return len(it.items) },
func() interface{} { b := it.items; it.items = nil; return b })
return it
}
func (it *ItemIterator) fetch(pageSize int, pageToken string) (string, error) {
items, tok, err := it.s.List(pageSize, pageToken)
it.items = append(it.items, items...)
return tok, err
}
func (it *ItemIterator) Next() (int, error) {
if err := it.nextFunc(); err != nil {
return 0, err
}
item := it.items[0]
it.items = it.items[1:]
return item, nil
}
func TestNext(t *testing.T) {
// Test the iterator's Next method with a variety of different service behaviors.
// This is primarily a test of PageInfo.next.
for _, svc := range []service{
{end: 0},
{end: 5},
{end: 5, max: 1},
{end: 5, max: 2},
{end: 5, zeroes: true},
{end: 5, max: 2, zeroes: true},
} {
client := &Client{&svc}
msg, ok := itest.TestIterator(
seq(0, svc.end),
func() interface{} { return client.Items(ctx) },
func(it interface{}) (interface{}, error) { return it.(*ItemIterator).Next() })
if !ok {
t.Errorf("%+v: %s", svc, msg)
}
}
}
// TODO(jba): test setting PageInfo.MaxSize
// TODO(jba): test setting PageInfo.Token
// Verify that, for an iterator that uses PageInfo.next to implement its Next
// method, using Next and NextPage together result in an error.
func TestNextWithNextPage(t *testing.T) {
client := &Client{&service{end: 11}}
var items []int
// Calling Next before NextPage.
it := client.Items(ctx)
it.Next()
_, err := iterator.NewPager(it, 1, "").NextPage(&items)
if err == nil {
t.Error("NextPage after Next: got nil, want error")
}
_, err = it.Next()
if err == nil {
t.Error("Next after NextPage: got nil, want error")
}
// Next between two calls to NextPage.
it = client.Items(ctx)
p := iterator.NewPager(it, 1, "")
p.NextPage(&items)
_, err = it.Next()
if err == nil {
t.Error("Next after NextPage: got nil, want error")
}
_, err = p.NextPage(&items)
if err == nil {
t.Error("second NextPage after Next: got nil, want error")
}
}
// Verify that we turn various potential reflection panics into errors.
func TestNextPageReflectionErrors(t *testing.T) {
client := &Client{&service{end: 1}}
p := iterator.NewPager(client.Items(ctx), 1, "")
// Passing the nil interface value.
_, err := p.NextPage(nil)
if err == nil {
t.Error("nil: got nil, want error")
}
// Passing a non-slice.
_, err = p.NextPage(17)
if err == nil {
t.Error("non-slice: got nil, want error")
}
// Passing a slice of the wrong type.
var bools []bool
_, err = p.NextPage(&bools)
if err == nil {
t.Error("wrong type: got nil, want error")
}
// Using a slice of the right type, but not passing a pointer to it.
var ints []int
_, err = p.NextPage(ints)
if err == nil {
t.Error("not a pointer: got nil, want error")
}
}
// seq returns a slice containing the values in [from, to).
func seq(from, to int) []int {
var r []int
for i := from; i < to; i++ {
r = append(r, i)
}
return r
}

View file

@ -0,0 +1,187 @@
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package testing provides support functions for testing iterators conforming
// to the standard pattern.
// See package google.golang.org/api/iterator and
// https://github.com/GoogleCloudPlatform/gcloud-golang/wiki/Iterator-Guidelines.
package testing
import (
"fmt"
"reflect"
"google.golang.org/api/iterator"
)
// TestIterator tests the Next method of a standard iterator. It assumes that
// the underlying sequence to be iterated over already exists.
//
// The want argument should be a slice that contains the elements of this
// sequence. It may be an empty slice, but it must not be the nil interface
// value. The elements must be comparable with reflect.DeepEqual.
//
// The create function should create and return a new iterator.
// It will typically look like
// func() interface{} { return client.Items(ctx) }
//
// The next function takes the return value of create and should return the
// result of calling Next on the iterator. It can usually be defined as
// func(it interface{}) (interface{}, error) { return it.(*ItemIterator).Next() }
//
// TestIterator checks that the iterator returns all the elements of want
// in order, followed by (zero, done). It also confirms that subsequent calls
// to next also return (zero, done).
//
// If the iterator implements the method
// PageInfo() *iterator.PageInfo
// then exact pagination with iterator.Pager is also tested. Pagination testing
// will be more informative if the want slice contains at least three elements.
//
// On success, TestIterator returns ("", true). On failure, it returns a
// suitable error message and false.
func TestIterator(want interface{}, create func() interface{}, next func(interface{}) (interface{}, error)) (string, bool) {
vWant := reflect.ValueOf(want)
if vWant.Kind() != reflect.Slice {
return "'want' must be a slice", false
}
it := create()
msg, ok := testNext(vWant, it, next)
if !ok {
return msg, ok
}
if _, ok := it.(iterator.Pageable); !ok || vWant.Len() == 0 {
return "", true
}
return testPaging(vWant, create, next)
}
// Check that the iterator returns vWant, the desired sequence.
func testNext(vWant reflect.Value, it interface{}, next func(interface{}) (interface{}, error)) (string, bool) {
for i := 0; i < vWant.Len(); i++ {
got, err := next(it)
if err != nil {
return fmt.Sprintf("#%d: got %v, expected an item", i, err), false
}
w := vWant.Index(i).Interface()
if !reflect.DeepEqual(got, w) {
return fmt.Sprintf("#%d: got %+v, want %+v", i, got, w), false
}
}
// We now should see (<zero value of item type>, done), no matter how many
// additional calls we make.
zero := reflect.Zero(vWant.Type().Elem()).Interface()
for i := 0; i < 3; i++ {
got, err := next(it)
if err != iterator.Done {
return fmt.Sprintf("at end: got error %v, want iterator.Done", err), false
}
// Since err == iterator.Done, got should be zero.
if got != zero {
return fmt.Sprintf("got %+v with done, want zero %T", got, zero), false
}
}
return "", true
}
// Test the iterator's behavior when used with iterator.Pager.
func testPaging(vWant reflect.Value, create func() interface{}, next func(interface{}) (interface{}, error)) (string, bool) {
// Test page sizes that are smaller, equal to, and greater than the length
// of the expected sequence.
for _, pageSize := range []int{1, 2, vWant.Len(), vWant.Len() + 10} {
wantPages := wantedPages(vWant, pageSize)
// Test the Pager in two ways.
// First, by creating a single Pager and calling NextPage in a loop,
// ignoring the page token except for detecting the end of the
// iteration.
it := create().(iterator.Pageable)
pager := iterator.NewPager(it, pageSize, "")
msg, ok := testPager(fmt.Sprintf("ignore page token, pageSize %d", pageSize),
vWant.Type(), wantPages,
func(_ string, pagep interface{}) (string, error) {
return pager.NextPage(pagep)
})
if !ok {
return msg, false
}
// Second, by creating a new Pager for each page, passing in the page
// token from the previous page, as would be done in a web handler.
it = create().(iterator.Pageable)
msg, ok = testPager(fmt.Sprintf("use page token, pageSize %d", pageSize),
vWant.Type(), wantPages,
func(pageToken string, pagep interface{}) (string, error) {
return iterator.NewPager(it, pageSize, pageToken).NextPage(pagep)
})
if !ok {
return msg, false
}
}
return "", true
}
// Create the pages we expect to see.
func wantedPages(vWant reflect.Value, pageSize int) []interface{} {
var pages []interface{}
for i, j := 0, pageSize; i < vWant.Len(); i, j = j, j+pageSize {
if j > vWant.Len() {
j = vWant.Len()
}
pages = append(pages, vWant.Slice(i, j).Interface())
}
return pages
}
func testPager(prefix string, sliceType reflect.Type, wantPages []interface{},
nextPage func(pageToken string, pagep interface{}) (string, error)) (string, bool) {
tok := ""
var err error
for i := 0; i < len(wantPages)+1; i++ {
vpagep := reflect.New(sliceType)
tok, err = nextPage(tok, vpagep.Interface())
if err != nil {
return fmt.Sprintf("%s, page #%d: got error %v", prefix, i, err), false
}
if i == len(wantPages) {
// Allow one empty page at the end.
if vpagep.Elem().Len() != 0 || tok != "" {
return fmt.Sprintf("%s: did not get one empty page at end", prefix), false
}
break
}
if msg, ok := compareSlices(vpagep.Elem(), reflect.ValueOf(wantPages[i])); !ok {
return fmt.Sprintf("%s, page #%d:\n%s", prefix, i, msg), false
}
if tok == "" {
if i != len(wantPages)-1 {
return fmt.Sprintf("%s, page #%d: got empty page token", prefix, i), false
}
break
}
}
return "", true
}
// Compare two slices element-by-element. If they are equal, return ("", true).
// Otherwise, return a description of the difference and false.
func compareSlices(vgot, vwant reflect.Value) (string, bool) {
if got, want := vgot.Len(), vwant.Len(); got != want {
return fmt.Sprintf("got %d items, want %d", got, want), false
}
for i := 0; i < vgot.Len(); i++ {
if got, want := vgot.Index(i).Interface(), vwant.Index(i).Interface(); !reflect.DeepEqual(got, want) {
return fmt.Sprintf("got[%d] = %+v\nwant = %+v", i, got, want), false
}
}
return "", true
}

View file

@ -0,0 +1,39 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package testing
import (
"reflect"
"testing"
)
func TestCompareSlices(t *testing.T) {
for _, test := range []struct {
a, b []int
wantEqual bool
}{
{nil, nil, true},
{nil, []int{}, true},
{[]int{1, 2}, []int{1, 2}, true},
{[]int{1}, []int{1, 2}, false},
{[]int{1, 2}, []int{1}, false},
{[]int{1, 2}, []int{1, 3}, false},
} {
_, got := compareSlices(reflect.ValueOf(test.a), reflect.ValueOf(test.b))
if got != test.wantEqual {
t.Errorf("%v, %v: got %t, want %t", test.a, test.b, got, test.wantEqual)
}
}
}