1// Copyright 2015 Google LLC 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package gensupport 6 7import ( 8 "bytes" 9 "io" 10 "io/ioutil" 11 "reflect" 12 "testing" 13 "testing/iotest" 14 15 "google.golang.org/api/googleapi" 16) 17 18// getChunkAsString reads a chunk from mb, but does not call Next. 19func getChunkAsString(t *testing.T, mb *MediaBuffer) (string, error) { 20 chunk, _, size, err := mb.Chunk() 21 22 buf, e := ioutil.ReadAll(chunk) 23 if e != nil { 24 t.Fatalf("Failed reading chunk: %v", e) 25 } 26 if size != len(buf) { 27 t.Fatalf("reported chunk size doesn't match actual chunk size: got: %v; want: %v", size, len(buf)) 28 } 29 return string(buf), err 30} 31 32func TestChunking(t *testing.T) { 33 type testCase struct { 34 data string // the data to read from the Reader 35 finalErr error // error to return after data has been read 36 chunkSize int 37 wantChunks []string 38 } 39 40 for _, singleByteReads := range []bool{true, false} { 41 for _, tc := range []testCase{ 42 { 43 data: "abcdefg", 44 finalErr: nil, 45 chunkSize: 3, 46 wantChunks: []string{"abc", "def", "g"}, 47 }, 48 { 49 data: "abcdefg", 50 finalErr: nil, 51 chunkSize: 1, 52 wantChunks: []string{"a", "b", "c", "d", "e", "f", "g"}, 53 }, 54 { 55 data: "abcdefg", 56 finalErr: nil, 57 chunkSize: 7, 58 wantChunks: []string{"abcdefg"}, 59 }, 60 { 61 data: "abcdefg", 62 finalErr: nil, 63 chunkSize: 8, 64 wantChunks: []string{"abcdefg"}, 65 }, 66 { 67 data: "abcdefg", 68 finalErr: io.ErrUnexpectedEOF, 69 chunkSize: 3, 70 wantChunks: []string{"abc", "def", "g"}, 71 }, 72 { 73 data: "abcdefg", 74 finalErr: io.ErrUnexpectedEOF, 75 chunkSize: 8, 76 wantChunks: []string{"abcdefg"}, 77 }, 78 } { 79 var r io.Reader = &errReader{buf: []byte(tc.data), err: tc.finalErr} 80 81 if singleByteReads { 82 r = iotest.OneByteReader(r) 83 } 84 85 mb := NewMediaBuffer(r, tc.chunkSize) 86 var gotErr error 87 got := []string{} 88 for { 89 chunk, err := getChunkAsString(t, mb) 90 if len(chunk) != 0 { 91 got = append(got, string(chunk)) 92 } 93 if err != nil { 94 gotErr = err 95 break 96 } 97 mb.Next() 98 } 99 100 if !reflect.DeepEqual(got, tc.wantChunks) { 101 t.Errorf("Failed reading buffer: got: %v; want:%v", got, tc.wantChunks) 102 } 103 104 expectedErr := tc.finalErr 105 if expectedErr == nil { 106 expectedErr = io.EOF 107 } 108 if gotErr != expectedErr { 109 t.Errorf("Reading buffer error: got: %v; want: %v", gotErr, expectedErr) 110 } 111 } 112 } 113} 114 115func TestChunkCanBeReused(t *testing.T) { 116 er := &errReader{buf: []byte("abcdefg")} 117 mb := NewMediaBuffer(er, 3) 118 119 // expectChunk reads a chunk and checks that it got what was wanted. 120 expectChunk := func(want string, wantErr error) { 121 got, err := getChunkAsString(t, mb) 122 if err != wantErr { 123 t.Errorf("error reading buffer: got: %v; want: %v", err, wantErr) 124 } 125 if !reflect.DeepEqual(got, want) { 126 t.Errorf("Failed reading buffer: got: %q; want:%q", got, want) 127 } 128 } 129 expectChunk("abc", nil) 130 // On second call, should get same chunk again. 131 expectChunk("abc", nil) 132 mb.Next() 133 expectChunk("def", nil) 134 expectChunk("def", nil) 135 mb.Next() 136 expectChunk("g", io.EOF) 137 expectChunk("g", io.EOF) 138 mb.Next() 139 expectChunk("", io.EOF) 140} 141 142func TestPos(t *testing.T) { 143 er := &errReader{buf: []byte("abcdefg")} 144 mb := NewMediaBuffer(er, 3) 145 146 expectChunkAtOffset := func(want int64, wantErr error) { 147 _, off, _, err := mb.Chunk() 148 if err != wantErr { 149 t.Errorf("error reading buffer: got: %v; want: %v", err, wantErr) 150 } 151 if got := off; got != want { 152 t.Errorf("resumable buffer Pos: got: %v; want: %v", got, want) 153 } 154 } 155 156 // We expect the first chunk to be at offset 0. 157 expectChunkAtOffset(0, nil) 158 // Fetching the same chunk should return the same offset. 159 expectChunkAtOffset(0, nil) 160 161 // Calling Next multiple times should only cause off to advance by 3, since off is not advanced until 162 // the chunk is actually read. 163 mb.Next() 164 mb.Next() 165 expectChunkAtOffset(3, nil) 166 167 mb.Next() 168 169 // Load the final 1-byte chunk. 170 expectChunkAtOffset(6, io.EOF) 171 172 // Next will advance 1 byte. But there are no more chunks, so off will not increase beyond 7. 173 mb.Next() 174 expectChunkAtOffset(7, io.EOF) 175 mb.Next() 176 expectChunkAtOffset(7, io.EOF) 177} 178 179// bytes.Reader implements both Reader and ReaderAt. The following types 180// implement various combinations of Reader, ReaderAt and ContentTyper, by 181// wrapping bytes.Reader. All implement at least ReaderAt, so they can be 182// passed to ReaderAtToReader. The following table summarizes which types 183// implement which interfaces: 184// 185// ReaderAt Reader ContentTyper 186// reader x x 187// typerReader x x x 188// readerAt x 189// typerReaderAt x x 190 191// reader implements Reader, in addition to ReaderAt. 192type reader struct { 193 r *bytes.Reader 194} 195 196func (r *reader) ReadAt(b []byte, off int64) (n int, err error) { 197 return r.r.ReadAt(b, off) 198} 199 200func (r *reader) Read(b []byte) (n int, err error) { 201 return r.r.Read(b) 202} 203 204// typerReader implements Reader and ContentTyper, in addition to ReaderAt. 205type typerReader struct { 206 r *bytes.Reader 207} 208 209func (tr *typerReader) ReadAt(b []byte, off int64) (n int, err error) { 210 return tr.r.ReadAt(b, off) 211} 212 213func (tr *typerReader) Read(b []byte) (n int, err error) { 214 return tr.r.Read(b) 215} 216 217func (tr *typerReader) ContentType() string { 218 return "ctype" 219} 220 221// readerAt implements only ReaderAt. 222type readerAt struct { 223 r *bytes.Reader 224} 225 226func (ra *readerAt) ReadAt(b []byte, off int64) (n int, err error) { 227 return ra.r.ReadAt(b, off) 228} 229 230// typerReaderAt implements ContentTyper, in addition to ReaderAt. 231type typerReaderAt struct { 232 r *bytes.Reader 233} 234 235func (tra *typerReaderAt) ReadAt(b []byte, off int64) (n int, err error) { 236 return tra.r.ReadAt(b, off) 237} 238 239func (tra *typerReaderAt) ContentType() string { 240 return "ctype" 241} 242 243func TestAdapter(t *testing.T) { 244 data := "abc" 245 246 checkConversion := func(to io.Reader, wantTyper bool) { 247 if _, ok := to.(googleapi.ContentTyper); ok != wantTyper { 248 t.Errorf("reader implements typer? got: %v; want: %v", ok, wantTyper) 249 } 250 if typer, ok := to.(googleapi.ContentTyper); ok && typer.ContentType() != "ctype" { 251 t.Errorf("content type: got: %s; want: ctype", typer.ContentType()) 252 } 253 buf, err := ioutil.ReadAll(to) 254 if err != nil { 255 t.Errorf("error reading data: %v", err) 256 return 257 } 258 if !bytes.Equal(buf, []byte(data)) { 259 t.Errorf("failed reading data: got: %s; want: %s", buf, data) 260 } 261 } 262 263 type testCase struct { 264 from io.ReaderAt 265 wantTyper bool 266 } 267 for _, tc := range []testCase{ 268 { 269 from: &reader{bytes.NewReader([]byte(data))}, 270 wantTyper: false, 271 }, 272 { 273 // Reader and ContentTyper 274 from: &typerReader{bytes.NewReader([]byte(data))}, 275 wantTyper: true, 276 }, 277 { 278 // ReaderAt 279 from: &readerAt{bytes.NewReader([]byte(data))}, 280 wantTyper: false, 281 }, 282 { 283 // ReaderAt and ContentTyper 284 from: &typerReaderAt{bytes.NewReader([]byte(data))}, 285 wantTyper: true, 286 }, 287 } { 288 to := ReaderAtToReader(tc.from, int64(len(data))) 289 checkConversion(to, tc.wantTyper) 290 // tc.from is a ReaderAt, and should be treated like one, even 291 // if it also implements Reader. Specifically, it can be 292 // reused and read from the beginning. 293 to = ReaderAtToReader(tc.from, int64(len(data))) 294 checkConversion(to, tc.wantTyper) 295 } 296} 297