blobstore_test.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. // Copyright 2011 Google Inc. All rights reserved.
  2. // Use of this source code is governed by the Apache 2.0
  3. // license that can be found in the LICENSE file.
  4. package blobstore
  5. import (
  6. "io"
  7. "os"
  8. "strconv"
  9. "strings"
  10. "testing"
  11. "google.golang.org/appengine"
  12. "google.golang.org/appengine/internal/aetesting"
  13. pb "google.golang.org/appengine/internal/blobstore"
  14. )
  15. const rbs = readBufferSize
  16. func min(x, y int) int {
  17. if x < y {
  18. return x
  19. }
  20. return y
  21. }
  22. func fakeFetchData(req *pb.FetchDataRequest, res *pb.FetchDataResponse) error {
  23. i0 := int(*req.StartIndex)
  24. i1 := int(*req.EndIndex + 1) // Blobstore's end-indices are inclusive; Go's are exclusive.
  25. bk := *req.BlobKey
  26. if i := strings.Index(bk, "."); i != -1 {
  27. // Strip everything past the ".".
  28. bk = bk[:i]
  29. }
  30. switch bk {
  31. case "a14p":
  32. const s = "abcdefghijklmnop"
  33. i0 := min(len(s), i0)
  34. i1 := min(len(s), i1)
  35. res.Data = []byte(s[i0:i1])
  36. case "longBlob":
  37. res.Data = make([]byte, i1-i0)
  38. for i := range res.Data {
  39. res.Data[i] = 'A' + uint8(i0/rbs)
  40. i0++
  41. }
  42. }
  43. return nil
  44. }
  45. // step is one step of a readerTest.
  46. // It consists of a Reader method to call, the method arguments
  47. // (lenp, offset, whence) and the expected results.
  48. type step struct {
  49. method string
  50. lenp int
  51. offset int64
  52. whence int
  53. want string
  54. wantErr error
  55. }
  56. var readerTest = []struct {
  57. blobKey string
  58. step []step
  59. }{
  60. {"noSuchBlobKey", []step{
  61. {"Read", 8, 0, 0, "", io.EOF},
  62. }},
  63. {"a14p.0", []step{
  64. // Test basic reads.
  65. {"Read", 1, 0, 0, "a", nil},
  66. {"Read", 3, 0, 0, "bcd", nil},
  67. {"Read", 1, 0, 0, "e", nil},
  68. {"Read", 2, 0, 0, "fg", nil},
  69. // Test Seek.
  70. {"Seek", 0, 2, os.SEEK_SET, "2", nil},
  71. {"Read", 5, 0, 0, "cdefg", nil},
  72. {"Seek", 0, 2, os.SEEK_CUR, "9", nil},
  73. {"Read", 1, 0, 0, "j", nil},
  74. // Test reads up to and past EOF.
  75. {"Read", 5, 0, 0, "klmno", nil},
  76. {"Read", 5, 0, 0, "p", nil},
  77. {"Read", 5, 0, 0, "", io.EOF},
  78. // Test ReadAt.
  79. {"ReadAt", 4, 0, 0, "abcd", nil},
  80. {"ReadAt", 4, 3, 0, "defg", nil},
  81. {"ReadAt", 4, 12, 0, "mnop", nil},
  82. {"ReadAt", 4, 13, 0, "nop", io.EOF},
  83. {"ReadAt", 4, 99, 0, "", io.EOF},
  84. }},
  85. {"a14p.1", []step{
  86. // Test Seek before any reads.
  87. {"Seek", 0, 2, os.SEEK_SET, "2", nil},
  88. {"Read", 1, 0, 0, "c", nil},
  89. // Test that ReadAt doesn't affect the Read offset.
  90. {"ReadAt", 3, 9, 0, "jkl", nil},
  91. {"Read", 3, 0, 0, "def", nil},
  92. }},
  93. {"a14p.2", []step{
  94. // Test ReadAt before any reads or seeks.
  95. {"ReadAt", 2, 14, 0, "op", nil},
  96. }},
  97. {"longBlob.0", []step{
  98. // Test basic read.
  99. {"Read", 1, 0, 0, "A", nil},
  100. // Test that Read returns early when the buffer is exhausted.
  101. {"Seek", 0, rbs - 2, os.SEEK_SET, strconv.Itoa(rbs - 2), nil},
  102. {"Read", 5, 0, 0, "AA", nil},
  103. {"Read", 3, 0, 0, "BBB", nil},
  104. // Test that what we just read is still in the buffer.
  105. {"Seek", 0, rbs - 2, os.SEEK_SET, strconv.Itoa(rbs - 2), nil},
  106. {"Read", 5, 0, 0, "AABBB", nil},
  107. // Test ReadAt.
  108. {"ReadAt", 3, rbs - 4, 0, "AAA", nil},
  109. {"ReadAt", 6, rbs - 4, 0, "AAAABB", nil},
  110. {"ReadAt", 8, rbs - 4, 0, "AAAABBBB", nil},
  111. {"ReadAt", 5, rbs - 4, 0, "AAAAB", nil},
  112. {"ReadAt", 2, rbs - 4, 0, "AA", nil},
  113. // Test seeking backwards from the Read offset.
  114. {"Seek", 0, 2*rbs - 8, os.SEEK_SET, strconv.Itoa(2*rbs - 8), nil},
  115. {"Read", 1, 0, 0, "B", nil},
  116. {"Read", 1, 0, 0, "B", nil},
  117. {"Read", 1, 0, 0, "B", nil},
  118. {"Read", 1, 0, 0, "B", nil},
  119. {"Read", 8, 0, 0, "BBBBCCCC", nil},
  120. }},
  121. {"longBlob.1", []step{
  122. // Test ReadAt with a slice larger than the buffer size.
  123. {"LargeReadAt", 2*rbs - 2, 0, 0, strconv.Itoa(2*rbs - 2), nil},
  124. {"LargeReadAt", 2*rbs - 1, 0, 0, strconv.Itoa(2*rbs - 1), nil},
  125. {"LargeReadAt", 2*rbs + 0, 0, 0, strconv.Itoa(2*rbs + 0), nil},
  126. {"LargeReadAt", 2*rbs + 1, 0, 0, strconv.Itoa(2*rbs + 1), nil},
  127. {"LargeReadAt", 2*rbs + 2, 0, 0, strconv.Itoa(2*rbs + 2), nil},
  128. {"LargeReadAt", 2*rbs - 2, 1, 0, strconv.Itoa(2*rbs - 2), nil},
  129. {"LargeReadAt", 2*rbs - 1, 1, 0, strconv.Itoa(2*rbs - 1), nil},
  130. {"LargeReadAt", 2*rbs + 0, 1, 0, strconv.Itoa(2*rbs + 0), nil},
  131. {"LargeReadAt", 2*rbs + 1, 1, 0, strconv.Itoa(2*rbs + 1), nil},
  132. {"LargeReadAt", 2*rbs + 2, 1, 0, strconv.Itoa(2*rbs + 2), nil},
  133. }},
  134. }
  135. func TestReader(t *testing.T) {
  136. for _, rt := range readerTest {
  137. c := aetesting.FakeSingleContext(t, "blobstore", "FetchData", fakeFetchData)
  138. r := NewReader(c, appengine.BlobKey(rt.blobKey))
  139. for i, step := range rt.step {
  140. var (
  141. got string
  142. gotErr error
  143. n int
  144. offset int64
  145. )
  146. switch step.method {
  147. case "LargeReadAt":
  148. p := make([]byte, step.lenp)
  149. n, gotErr = r.ReadAt(p, step.offset)
  150. got = strconv.Itoa(n)
  151. case "Read":
  152. p := make([]byte, step.lenp)
  153. n, gotErr = r.Read(p)
  154. got = string(p[:n])
  155. case "ReadAt":
  156. p := make([]byte, step.lenp)
  157. n, gotErr = r.ReadAt(p, step.offset)
  158. got = string(p[:n])
  159. case "Seek":
  160. offset, gotErr = r.Seek(step.offset, step.whence)
  161. got = strconv.FormatInt(offset, 10)
  162. default:
  163. t.Fatalf("unknown method: %s", step.method)
  164. }
  165. if gotErr != step.wantErr {
  166. t.Fatalf("%s step %d: got error %v want %v", rt.blobKey, i, gotErr, step.wantErr)
  167. }
  168. if got != step.want {
  169. t.Fatalf("%s step %d: got %q want %q", rt.blobKey, i, got, step.want)
  170. }
  171. }
  172. }
  173. }