選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 

188 行
6.7 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 testing provides support functions for testing iterators conforming
  15. // to the standard pattern.
  16. // See package google.golang.org/api/iterator and
  17. // https://github.com/GoogleCloudPlatform/gcloud-golang/wiki/Iterator-Guidelines.
  18. package testing
  19. import (
  20. "fmt"
  21. "reflect"
  22. "google.golang.org/api/iterator"
  23. )
  24. // TestIterator tests the Next method of a standard iterator. It assumes that
  25. // the underlying sequence to be iterated over already exists.
  26. //
  27. // The want argument should be a slice that contains the elements of this
  28. // sequence. It may be an empty slice, but it must not be the nil interface
  29. // value. The elements must be comparable with reflect.DeepEqual.
  30. //
  31. // The create function should create and return a new iterator.
  32. // It will typically look like
  33. // func() interface{} { return client.Items(ctx) }
  34. //
  35. // The next function takes the return value of create and should return the
  36. // result of calling Next on the iterator. It can usually be defined as
  37. // func(it interface{}) (interface{}, error) { return it.(*ItemIterator).Next() }
  38. //
  39. // TestIterator checks that the iterator returns all the elements of want
  40. // in order, followed by (zero, done). It also confirms that subsequent calls
  41. // to next also return (zero, done).
  42. //
  43. // If the iterator implements the method
  44. // PageInfo() *iterator.PageInfo
  45. // then exact pagination with iterator.Pager is also tested. Pagination testing
  46. // will be more informative if the want slice contains at least three elements.
  47. //
  48. // On success, TestIterator returns ("", true). On failure, it returns a
  49. // suitable error message and false.
  50. func TestIterator(want interface{}, create func() interface{}, next func(interface{}) (interface{}, error)) (string, bool) {
  51. vWant := reflect.ValueOf(want)
  52. if vWant.Kind() != reflect.Slice {
  53. return "'want' must be a slice", false
  54. }
  55. it := create()
  56. msg, ok := testNext(vWant, it, next)
  57. if !ok {
  58. return msg, ok
  59. }
  60. if _, ok := it.(iterator.Pageable); !ok || vWant.Len() == 0 {
  61. return "", true
  62. }
  63. return testPaging(vWant, create, next)
  64. }
  65. // Check that the iterator returns vWant, the desired sequence.
  66. func testNext(vWant reflect.Value, it interface{}, next func(interface{}) (interface{}, error)) (string, bool) {
  67. for i := 0; i < vWant.Len(); i++ {
  68. got, err := next(it)
  69. if err != nil {
  70. return fmt.Sprintf("#%d: got %v, expected an item", i, err), false
  71. }
  72. w := vWant.Index(i).Interface()
  73. if !reflect.DeepEqual(got, w) {
  74. return fmt.Sprintf("#%d: got %+v, want %+v", i, got, w), false
  75. }
  76. }
  77. // We now should see (<zero value of item type>, done), no matter how many
  78. // additional calls we make.
  79. zero := reflect.Zero(vWant.Type().Elem()).Interface()
  80. for i := 0; i < 3; i++ {
  81. got, err := next(it)
  82. if err != iterator.Done {
  83. return fmt.Sprintf("at end: got error %v, want iterator.Done", err), false
  84. }
  85. // Since err == iterator.Done, got should be zero.
  86. if got != zero {
  87. return fmt.Sprintf("got %+v with done, want zero %T", got, zero), false
  88. }
  89. }
  90. return "", true
  91. }
  92. // Test the iterator's behavior when used with iterator.Pager.
  93. func testPaging(vWant reflect.Value, create func() interface{}, next func(interface{}) (interface{}, error)) (string, bool) {
  94. // Test page sizes that are smaller, equal to, and greater than the length
  95. // of the expected sequence.
  96. for _, pageSize := range []int{1, 2, vWant.Len(), vWant.Len() + 10} {
  97. wantPages := wantedPages(vWant, pageSize)
  98. // Test the Pager in two ways.
  99. // First, by creating a single Pager and calling NextPage in a loop,
  100. // ignoring the page token except for detecting the end of the
  101. // iteration.
  102. it := create().(iterator.Pageable)
  103. pager := iterator.NewPager(it, pageSize, "")
  104. msg, ok := testPager(fmt.Sprintf("ignore page token, pageSize %d", pageSize),
  105. vWant.Type(), wantPages,
  106. func(_ string, pagep interface{}) (string, error) {
  107. return pager.NextPage(pagep)
  108. })
  109. if !ok {
  110. return msg, false
  111. }
  112. // Second, by creating a new Pager for each page, passing in the page
  113. // token from the previous page, as would be done in a web handler.
  114. it = create().(iterator.Pageable)
  115. msg, ok = testPager(fmt.Sprintf("use page token, pageSize %d", pageSize),
  116. vWant.Type(), wantPages,
  117. func(pageToken string, pagep interface{}) (string, error) {
  118. return iterator.NewPager(it, pageSize, pageToken).NextPage(pagep)
  119. })
  120. if !ok {
  121. return msg, false
  122. }
  123. }
  124. return "", true
  125. }
  126. // Create the pages we expect to see.
  127. func wantedPages(vWant reflect.Value, pageSize int) []interface{} {
  128. var pages []interface{}
  129. for i, j := 0, pageSize; i < vWant.Len(); i, j = j, j+pageSize {
  130. if j > vWant.Len() {
  131. j = vWant.Len()
  132. }
  133. pages = append(pages, vWant.Slice(i, j).Interface())
  134. }
  135. return pages
  136. }
  137. func testPager(prefix string, sliceType reflect.Type, wantPages []interface{},
  138. nextPage func(pageToken string, pagep interface{}) (string, error)) (string, bool) {
  139. tok := ""
  140. var err error
  141. for i := 0; i < len(wantPages)+1; i++ {
  142. vpagep := reflect.New(sliceType)
  143. tok, err = nextPage(tok, vpagep.Interface())
  144. if err != nil {
  145. return fmt.Sprintf("%s, page #%d: got error %v", prefix, i, err), false
  146. }
  147. if i == len(wantPages) {
  148. // Allow one empty page at the end.
  149. if vpagep.Elem().Len() != 0 || tok != "" {
  150. return fmt.Sprintf("%s: did not get one empty page at end", prefix), false
  151. }
  152. break
  153. }
  154. if msg, ok := compareSlices(vpagep.Elem(), reflect.ValueOf(wantPages[i])); !ok {
  155. return fmt.Sprintf("%s, page #%d:\n%s", prefix, i, msg), false
  156. }
  157. if tok == "" {
  158. if i != len(wantPages)-1 {
  159. return fmt.Sprintf("%s, page #%d: got empty page token", prefix, i), false
  160. }
  161. break
  162. }
  163. }
  164. return "", true
  165. }
  166. // Compare two slices element-by-element. If they are equal, return ("", true).
  167. // Otherwise, return a description of the difference and false.
  168. func compareSlices(vgot, vwant reflect.Value) (string, bool) {
  169. if got, want := vgot.Len(), vwant.Len(); got != want {
  170. return fmt.Sprintf("got %d items, want %d", got, want), false
  171. }
  172. for i := 0; i < vgot.Len(); i++ {
  173. if got, want := vgot.Index(i).Interface(), vwant.Index(i).Interface(); !reflect.DeepEqual(got, want) {
  174. return fmt.Sprintf("got[%d] = %+v\nwant = %+v", i, got, want), false
  175. }
  176. }
  177. return "", true
  178. }