You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

363 lines
8.9 KiB

  1. // Copyright 2015 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 bigquery
  15. import (
  16. "context"
  17. "errors"
  18. "fmt"
  19. "testing"
  20. "cloud.google.com/go/internal/testutil"
  21. "google.golang.org/api/iterator"
  22. )
  23. type fetchResponse struct {
  24. result *fetchPageResult // The result to return.
  25. err error // The error to return.
  26. }
  27. // pageFetcherStub services fetch requests by returning data from an in-memory list of values.
  28. type pageFetcherStub struct {
  29. fetchResponses map[string]fetchResponse
  30. err error
  31. }
  32. func (pf *pageFetcherStub) fetchPage(ctx context.Context, _ *Table, _ Schema, _ uint64, _ int64, pageToken string) (*fetchPageResult, error) {
  33. call, ok := pf.fetchResponses[pageToken]
  34. if !ok {
  35. pf.err = fmt.Errorf("Unexpected page token: %q", pageToken)
  36. }
  37. return call.result, call.err
  38. }
  39. func TestIterator(t *testing.T) {
  40. var (
  41. iiSchema = Schema{
  42. {Type: IntegerFieldType},
  43. {Type: IntegerFieldType},
  44. }
  45. siSchema = Schema{
  46. {Type: StringFieldType},
  47. {Type: IntegerFieldType},
  48. }
  49. )
  50. fetchFailure := errors.New("fetch failure")
  51. testCases := []struct {
  52. desc string
  53. pageToken string
  54. fetchResponses map[string]fetchResponse
  55. want [][]Value
  56. wantErr error
  57. wantSchema Schema
  58. wantTotalRows uint64
  59. }{
  60. {
  61. desc: "Iteration over single empty page",
  62. fetchResponses: map[string]fetchResponse{
  63. "": {
  64. result: &fetchPageResult{
  65. pageToken: "",
  66. rows: [][]Value{},
  67. schema: Schema{},
  68. },
  69. },
  70. },
  71. want: [][]Value{},
  72. wantSchema: Schema{},
  73. },
  74. {
  75. desc: "Iteration over single page",
  76. fetchResponses: map[string]fetchResponse{
  77. "": {
  78. result: &fetchPageResult{
  79. pageToken: "",
  80. rows: [][]Value{{1, 2}, {11, 12}},
  81. schema: iiSchema,
  82. totalRows: 4,
  83. },
  84. },
  85. },
  86. want: [][]Value{{1, 2}, {11, 12}},
  87. wantSchema: iiSchema,
  88. wantTotalRows: 4,
  89. },
  90. {
  91. desc: "Iteration over single page with different schema",
  92. fetchResponses: map[string]fetchResponse{
  93. "": {
  94. result: &fetchPageResult{
  95. pageToken: "",
  96. rows: [][]Value{{"1", 2}, {"11", 12}},
  97. schema: siSchema,
  98. },
  99. },
  100. },
  101. want: [][]Value{{"1", 2}, {"11", 12}},
  102. wantSchema: siSchema,
  103. },
  104. {
  105. desc: "Iteration over two pages",
  106. fetchResponses: map[string]fetchResponse{
  107. "": {
  108. result: &fetchPageResult{
  109. pageToken: "a",
  110. rows: [][]Value{{1, 2}, {11, 12}},
  111. schema: iiSchema,
  112. totalRows: 4,
  113. },
  114. },
  115. "a": {
  116. result: &fetchPageResult{
  117. pageToken: "",
  118. rows: [][]Value{{101, 102}, {111, 112}},
  119. schema: iiSchema,
  120. totalRows: 4,
  121. },
  122. },
  123. },
  124. want: [][]Value{{1, 2}, {11, 12}, {101, 102}, {111, 112}},
  125. wantSchema: iiSchema,
  126. wantTotalRows: 4,
  127. },
  128. {
  129. desc: "Server response includes empty page",
  130. fetchResponses: map[string]fetchResponse{
  131. "": {
  132. result: &fetchPageResult{
  133. pageToken: "a",
  134. rows: [][]Value{{1, 2}, {11, 12}},
  135. schema: iiSchema,
  136. },
  137. },
  138. "a": {
  139. result: &fetchPageResult{
  140. pageToken: "b",
  141. rows: [][]Value{},
  142. schema: iiSchema,
  143. },
  144. },
  145. "b": {
  146. result: &fetchPageResult{
  147. pageToken: "",
  148. rows: [][]Value{{101, 102}, {111, 112}},
  149. schema: iiSchema,
  150. },
  151. },
  152. },
  153. want: [][]Value{{1, 2}, {11, 12}, {101, 102}, {111, 112}},
  154. wantSchema: iiSchema,
  155. },
  156. {
  157. desc: "Fetch error",
  158. fetchResponses: map[string]fetchResponse{
  159. "": {
  160. result: &fetchPageResult{
  161. pageToken: "a",
  162. rows: [][]Value{{1, 2}, {11, 12}},
  163. schema: iiSchema,
  164. },
  165. },
  166. "a": {
  167. // We returns some data from this fetch, but also an error.
  168. // So the end result should include only data from the previous fetch.
  169. err: fetchFailure,
  170. result: &fetchPageResult{
  171. pageToken: "b",
  172. rows: [][]Value{{101, 102}, {111, 112}},
  173. schema: iiSchema,
  174. },
  175. },
  176. },
  177. want: [][]Value{{1, 2}, {11, 12}},
  178. wantErr: fetchFailure,
  179. wantSchema: iiSchema,
  180. },
  181. {
  182. desc: "Skip over an entire page",
  183. pageToken: "a",
  184. fetchResponses: map[string]fetchResponse{
  185. "": {
  186. result: &fetchPageResult{
  187. pageToken: "a",
  188. rows: [][]Value{{1, 2}, {11, 12}},
  189. schema: iiSchema,
  190. },
  191. },
  192. "a": {
  193. result: &fetchPageResult{
  194. pageToken: "",
  195. rows: [][]Value{{101, 102}, {111, 112}},
  196. schema: iiSchema,
  197. },
  198. },
  199. },
  200. want: [][]Value{{101, 102}, {111, 112}},
  201. wantSchema: iiSchema,
  202. },
  203. {
  204. desc: "Skip beyond all data",
  205. pageToken: "b",
  206. fetchResponses: map[string]fetchResponse{
  207. "": {
  208. result: &fetchPageResult{
  209. pageToken: "a",
  210. rows: [][]Value{{1, 2}, {11, 12}},
  211. schema: iiSchema,
  212. },
  213. },
  214. "a": {
  215. result: &fetchPageResult{
  216. pageToken: "b",
  217. rows: [][]Value{{101, 102}, {111, 112}},
  218. schema: iiSchema,
  219. },
  220. },
  221. "b": {
  222. result: &fetchPageResult{},
  223. },
  224. },
  225. // In this test case, Next will return false on its first call,
  226. // so we won't even attempt to call Get.
  227. want: [][]Value{},
  228. wantSchema: Schema{},
  229. },
  230. }
  231. for _, tc := range testCases {
  232. pf := &pageFetcherStub{
  233. fetchResponses: tc.fetchResponses,
  234. }
  235. it := newRowIterator(context.Background(), nil, pf.fetchPage)
  236. it.PageInfo().Token = tc.pageToken
  237. values, schema, totalRows, err := consumeRowIterator(it)
  238. if err != tc.wantErr {
  239. t.Fatalf("%s: got %v, want %v", tc.desc, err, tc.wantErr)
  240. }
  241. if (len(values) != 0 || len(tc.want) != 0) && !testutil.Equal(values, tc.want) {
  242. t.Errorf("%s: values:\ngot: %v\nwant:%v", tc.desc, values, tc.want)
  243. }
  244. if (len(schema) != 0 || len(tc.wantSchema) != 0) && !testutil.Equal(schema, tc.wantSchema) {
  245. t.Errorf("%s: iterator.Schema:\ngot: %v\nwant: %v", tc.desc, schema, tc.wantSchema)
  246. }
  247. if totalRows != tc.wantTotalRows {
  248. t.Errorf("%s: totalRows: got %d, want %d", tc.desc, totalRows, tc.wantTotalRows)
  249. }
  250. }
  251. }
  252. // consumeRowIterator reads the schema and all values from a RowIterator and returns them.
  253. func consumeRowIterator(it *RowIterator) ([][]Value, Schema, uint64, error) {
  254. var (
  255. got [][]Value
  256. schema Schema
  257. totalRows uint64
  258. )
  259. for {
  260. var vls []Value
  261. err := it.Next(&vls)
  262. if err == iterator.Done {
  263. return got, schema, totalRows, nil
  264. }
  265. if err != nil {
  266. return got, schema, totalRows, err
  267. }
  268. got = append(got, vls)
  269. schema = it.Schema
  270. totalRows = it.TotalRows
  271. }
  272. }
  273. func TestNextDuringErrorState(t *testing.T) {
  274. pf := &pageFetcherStub{
  275. fetchResponses: map[string]fetchResponse{
  276. "": {err: errors.New("bang")},
  277. },
  278. }
  279. it := newRowIterator(context.Background(), nil, pf.fetchPage)
  280. var vals []Value
  281. if err := it.Next(&vals); err == nil {
  282. t.Errorf("Expected error after calling Next")
  283. }
  284. if err := it.Next(&vals); err == nil {
  285. t.Errorf("Expected error calling Next again when iterator has a non-nil error.")
  286. }
  287. }
  288. func TestNextAfterFinished(t *testing.T) {
  289. testCases := []struct {
  290. fetchResponses map[string]fetchResponse
  291. want [][]Value
  292. }{
  293. {
  294. fetchResponses: map[string]fetchResponse{
  295. "": {
  296. result: &fetchPageResult{
  297. pageToken: "",
  298. rows: [][]Value{{1, 2}, {11, 12}},
  299. },
  300. },
  301. },
  302. want: [][]Value{{1, 2}, {11, 12}},
  303. },
  304. {
  305. fetchResponses: map[string]fetchResponse{
  306. "": {
  307. result: &fetchPageResult{
  308. pageToken: "",
  309. rows: [][]Value{},
  310. },
  311. },
  312. },
  313. want: [][]Value{},
  314. },
  315. }
  316. for _, tc := range testCases {
  317. pf := &pageFetcherStub{
  318. fetchResponses: tc.fetchResponses,
  319. }
  320. it := newRowIterator(context.Background(), nil, pf.fetchPage)
  321. values, _, _, err := consumeRowIterator(it)
  322. if err != nil {
  323. t.Fatal(err)
  324. }
  325. if (len(values) != 0 || len(tc.want) != 0) && !testutil.Equal(values, tc.want) {
  326. t.Errorf("values: got:\n%v\nwant:\n%v", values, tc.want)
  327. }
  328. // Try calling Get again.
  329. var vals []Value
  330. if err := it.Next(&vals); err != iterator.Done {
  331. t.Errorf("Expected Done calling Next when there are no more values")
  332. }
  333. }
  334. }
  335. func TestIteratorNextTypes(t *testing.T) {
  336. it := newRowIterator(context.Background(), nil, nil)
  337. for _, v := range []interface{}{3, "s", []int{}, &[]int{},
  338. map[string]Value{}, &map[string]interface{}{},
  339. struct{}{},
  340. } {
  341. if err := it.Next(v); err == nil {
  342. t.Errorf("%v: want error, got nil", v)
  343. }
  344. }
  345. }