1
2
3
4
5 package gensupport
6
7 import (
8 "bytes"
9 cryptorand "crypto/rand"
10 "io"
11 mathrand "math/rand"
12 "net/http"
13 "strings"
14 "testing"
15 "time"
16
17 "google.golang.org/api/googleapi"
18 )
19
20 func TestNewInfoFromMedia(t *testing.T) {
21 const textType = "text/plain; charset=utf-8"
22 for _, test := range []struct {
23 desc string
24 r io.Reader
25 opts []googleapi.MediaOption
26 wantType string
27 wantMedia, wantBuffer, wantSingleChunk bool
28 wantDeadline time.Duration
29 }{
30 {
31 desc: "an empty reader results in a MediaBuffer with a single, empty chunk",
32 r: new(bytes.Buffer),
33 opts: nil,
34 wantType: textType,
35 wantBuffer: true,
36 wantSingleChunk: true,
37 },
38 {
39 desc: "ContentType is observed",
40 r: new(bytes.Buffer),
41 opts: []googleapi.MediaOption{googleapi.ContentType("xyz")},
42 wantType: "xyz",
43 wantBuffer: true,
44 wantSingleChunk: true,
45 },
46 {
47 desc: "ChunkRetryDeadline is observed",
48 r: new(bytes.Buffer),
49 opts: []googleapi.MediaOption{googleapi.ChunkRetryDeadline(time.Second)},
50 wantType: textType,
51 wantBuffer: true,
52 wantSingleChunk: true,
53 wantDeadline: time.Second,
54 },
55 {
56 desc: "chunk size of zero: don't use a MediaBuffer; upload as a single chunk",
57 r: strings.NewReader("12345"),
58 opts: []googleapi.MediaOption{googleapi.ChunkSize(0)},
59 wantType: textType,
60 wantMedia: true,
61 wantSingleChunk: true,
62 },
63 {
64 desc: "chunk size > data size: MediaBuffer with single chunk",
65 r: strings.NewReader("12345"),
66 opts: []googleapi.MediaOption{googleapi.ChunkSize(100)},
67 wantType: textType,
68 wantBuffer: true,
69 wantSingleChunk: true,
70 },
71 {
72 desc: "chunk size == data size: MediaBuffer with single chunk",
73 r: &nullReader{googleapi.MinUploadChunkSize},
74 opts: []googleapi.MediaOption{googleapi.ChunkSize(1)},
75 wantType: "application/octet-stream",
76 wantBuffer: true,
77 wantSingleChunk: true,
78 },
79 {
80 desc: "chunk size < data size: MediaBuffer, not single chunk",
81
82 r: &nullReader{2 * googleapi.MinUploadChunkSize},
83 opts: []googleapi.MediaOption{googleapi.ChunkSize(1)},
84 wantType: "application/octet-stream",
85 wantBuffer: true,
86 wantSingleChunk: false,
87 },
88 } {
89
90 mi := NewInfoFromMedia(test.r, test.opts)
91 if got, want := mi.mType, test.wantType; got != want {
92 t.Errorf("%s: type: got %q, want %q", test.desc, got, want)
93 }
94 if got, want := (mi.media != nil), test.wantMedia; got != want {
95 t.Errorf("%s: media non-nil: got %t, want %t", test.desc, got, want)
96 }
97 if got, want := (mi.buffer != nil), test.wantBuffer; got != want {
98 t.Errorf("%s: buffer non-nil: got %t, want %t", test.desc, got, want)
99 }
100 if got, want := mi.singleChunk, test.wantSingleChunk; got != want {
101 t.Errorf("%s: singleChunk: got %t, want %t", test.desc, got, want)
102 }
103 if got, want := mi.chunkRetryDeadline, test.wantDeadline; got != want {
104 t.Errorf("%s: chunkRetryDeadline: got %v, want %v", test.desc, got, want)
105 }
106 }
107 }
108
109 func TestUploadRequest(t *testing.T) {
110 for _, test := range []struct {
111 desc string
112 r io.Reader
113 chunkSize int
114 wantContentType string
115 wantUploadType string
116 }{
117 {
118 desc: "chunk size of zero: don't use a MediaBuffer; upload as a single chunk",
119 r: strings.NewReader("12345"),
120 chunkSize: 0,
121 wantContentType: "multipart/related;",
122 },
123 {
124 desc: "chunk size > data size: MediaBuffer with single chunk",
125 r: strings.NewReader("12345"),
126 chunkSize: 100,
127 wantContentType: "multipart/related;",
128 },
129 {
130 desc: "chunk size == data size: MediaBuffer with single chunk",
131 r: &nullReader{googleapi.MinUploadChunkSize},
132 chunkSize: 1,
133 wantContentType: "multipart/related;",
134 },
135 {
136 desc: "chunk size < data size: MediaBuffer, not single chunk",
137
138 r: &nullReader{2 * googleapi.MinUploadChunkSize},
139 chunkSize: 1,
140 wantUploadType: "application/octet-stream",
141 },
142 } {
143 mi := NewInfoFromMedia(test.r, []googleapi.MediaOption{googleapi.ChunkSize(test.chunkSize)})
144 h := http.Header{}
145 mi.UploadRequest(h, new(bytes.Buffer))
146 if got, want := h.Get("Content-Type"), test.wantContentType; !strings.HasPrefix(got, want) {
147 t.Errorf("%s: Content-Type: got %q, want prefix %q", test.desc, got, want)
148 }
149 if got, want := h.Get("X-Upload-Content-Type"), test.wantUploadType; got != want {
150 t.Errorf("%s: X-Upload-Content-Type: got %q, want %q", test.desc, got, want)
151 }
152 }
153 }
154
155 func TestUploadRequestGetBody(t *testing.T) {
156
157
158
159
160 defer func(old io.Reader) { cryptorand.Reader = old }(cryptorand.Reader)
161
162 for i, test := range []struct {
163 desc string
164 r io.Reader
165 chunkSize int
166 wantGetBody bool
167 }{
168 {
169 desc: "chunk size of zero: no getBody",
170 r: &nullReader{10},
171 chunkSize: 0,
172 wantGetBody: false,
173 },
174 {
175 desc: "chunk size == data size: 1 chunk, getBody",
176 r: &nullReader{googleapi.MinUploadChunkSize},
177 chunkSize: 1,
178 wantGetBody: true,
179 },
180 {
181 desc: "chunk size < data size: MediaBuffer, >1 chunk, getBody",
182
183 r: &nullReader{2 * googleapi.MinUploadChunkSize},
184 chunkSize: 1,
185 wantGetBody: true,
186 },
187 } {
188 cryptorand.Reader = mathrand.New(mathrand.NewSource(int64(i)))
189
190 mi := NewInfoFromMedia(test.r, []googleapi.MediaOption{googleapi.ChunkSize(test.chunkSize)})
191 r, getBody, _ := mi.UploadRequest(http.Header{}, bytes.NewBuffer([]byte("body")))
192 if got, want := (getBody != nil), test.wantGetBody; got != want {
193 t.Errorf("%s: getBody: got %t, want %t", test.desc, got, want)
194 continue
195 }
196 if getBody == nil {
197 continue
198 }
199 want, err := io.ReadAll(r)
200 if err != nil {
201 t.Fatal(err)
202 }
203 for i := 0; i < 3; i++ {
204 rc, err := getBody()
205 if err != nil {
206 t.Fatal(err)
207 }
208 got, err := io.ReadAll(rc)
209 if err != nil {
210 t.Fatal(err)
211 }
212 if !bytes.Equal(got, want) {
213 t.Errorf("%s, %d:\ngot:\n%s\nwant:\n%s", test.desc, i, string(got), string(want))
214 }
215 }
216 }
217 }
218
219 func TestResumableUpload(t *testing.T) {
220 for _, test := range []struct {
221 desc string
222 r io.Reader
223 chunkSize int
224 wantUploadType string
225 wantResumableUpload bool
226 chunkRetryDeadline time.Duration
227 }{
228 {
229 desc: "chunk size of zero: don't use a MediaBuffer; upload as a single chunk",
230 r: strings.NewReader("12345"),
231 chunkSize: 0,
232 wantUploadType: "multipart",
233 wantResumableUpload: false,
234 },
235 {
236 desc: "chunk size > data size: MediaBuffer with single chunk",
237 r: strings.NewReader("12345"),
238 chunkSize: 100,
239 wantUploadType: "multipart",
240 wantResumableUpload: false,
241 },
242 {
243 desc: "chunk size == data size: MediaBuffer with single chunk",
244
245 r: &nullReader{googleapi.MinUploadChunkSize},
246 chunkSize: googleapi.MinUploadChunkSize,
247 wantUploadType: "multipart",
248 wantResumableUpload: false,
249 },
250 {
251 desc: "chunk size < data size: MediaBuffer, not single chunk",
252
253 r: &nullReader{2 * googleapi.MinUploadChunkSize},
254 chunkSize: 1,
255 wantUploadType: "resumable",
256 wantResumableUpload: true,
257 },
258 {
259 desc: "confirm that ChunkRetryDeadline is carried to ResumableUpload",
260 r: &nullReader{2 * googleapi.MinUploadChunkSize},
261 chunkSize: 1,
262 wantUploadType: "resumable",
263 wantResumableUpload: true,
264 chunkRetryDeadline: 1 * time.Second,
265 },
266 } {
267 opts := []googleapi.MediaOption{googleapi.ChunkSize(test.chunkSize)}
268 if test.chunkRetryDeadline != 0 {
269 opts = append(opts, googleapi.ChunkRetryDeadline(test.chunkRetryDeadline))
270 }
271 mi := NewInfoFromMedia(test.r, opts)
272 if got, want := mi.UploadType(), test.wantUploadType; got != want {
273 t.Errorf("%s: upload type: got %q, want %q", test.desc, got, want)
274 }
275 if got, want := mi.ResumableUpload("") != nil, test.wantResumableUpload; got != want {
276 t.Errorf("%s: resumable upload non-nil: got %t, want %t", test.desc, got, want)
277 }
278 if test.chunkRetryDeadline != 0 {
279 if got := mi.ResumableUpload(""); got != nil {
280 if got.ChunkRetryDeadline != test.chunkRetryDeadline {
281 t.Errorf("%s: ChunkRetryDeadline: got %v, want %v", test.desc, got.ChunkRetryDeadline, test.chunkRetryDeadline)
282 }
283 } else {
284 t.Errorf("%s: test case invalid; resumable upload is nil", test.desc)
285 }
286 }
287 }
288 }
289
290
291 type nullReader struct {
292 remain int
293 }
294
295
296
297 func (r *nullReader) Read(buf []byte) (int, error) {
298 n := len(buf)
299 if r.remain < n {
300 n = r.remain
301 }
302 r.remain -= n
303 var err error
304 if r.remain == 0 {
305 err = io.EOF
306 }
307 return n, err
308 }
309
View as plain text