123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
- // Copyright 2015 Google Inc. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package gensupport
- import (
- "bytes"
- "io"
- "io/ioutil"
- "reflect"
- "testing"
- "testing/iotest"
- "google.golang.org/api/googleapi"
- )
- // getChunkAsString reads a chunk from rb, but does not call Next.
- func getChunkAsString(t *testing.T, rb *ResumableBuffer) (string, error) {
- chunk, _, size, err := rb.Chunk()
- buf, e := ioutil.ReadAll(chunk)
- if e != nil {
- t.Fatalf("Failed reading chunk: %v", e)
- }
- if size != len(buf) {
- t.Fatalf("reported chunk size doesn't match actual chunk size: got: %v; want: %v", size, len(buf))
- }
- return string(buf), err
- }
- func TestChunking(t *testing.T) {
- type testCase struct {
- data string // the data to read from the Reader
- finalErr error // error to return after data has been read
- chunkSize int
- wantChunks []string
- }
- for _, singleByteReads := range []bool{true, false} {
- for _, tc := range []testCase{
- {
- data: "abcdefg",
- finalErr: nil,
- chunkSize: 3,
- wantChunks: []string{"abc", "def", "g"},
- },
- {
- data: "abcdefg",
- finalErr: nil,
- chunkSize: 1,
- wantChunks: []string{"a", "b", "c", "d", "e", "f", "g"},
- },
- {
- data: "abcdefg",
- finalErr: nil,
- chunkSize: 7,
- wantChunks: []string{"abcdefg"},
- },
- {
- data: "abcdefg",
- finalErr: nil,
- chunkSize: 8,
- wantChunks: []string{"abcdefg"},
- },
- {
- data: "abcdefg",
- finalErr: io.ErrUnexpectedEOF,
- chunkSize: 3,
- wantChunks: []string{"abc", "def", "g"},
- },
- {
- data: "abcdefg",
- finalErr: io.ErrUnexpectedEOF,
- chunkSize: 8,
- wantChunks: []string{"abcdefg"},
- },
- } {
- var r io.Reader = &errReader{buf: []byte(tc.data), err: tc.finalErr}
- if singleByteReads {
- r = iotest.OneByteReader(r)
- }
- rb := NewResumableBuffer(r, tc.chunkSize)
- var gotErr error
- got := []string{}
- for {
- chunk, err := getChunkAsString(t, rb)
- if len(chunk) != 0 {
- got = append(got, string(chunk))
- }
- if err != nil {
- gotErr = err
- break
- }
- rb.Next()
- }
- if !reflect.DeepEqual(got, tc.wantChunks) {
- t.Errorf("Failed reading buffer: got: %v; want:%v", got, tc.wantChunks)
- }
- expectedErr := tc.finalErr
- if expectedErr == nil {
- expectedErr = io.EOF
- }
- if gotErr != expectedErr {
- t.Errorf("Reading buffer error: got: %v; want: %v", gotErr, expectedErr)
- }
- }
- }
- }
- func TestChunkCanBeReused(t *testing.T) {
- er := &errReader{buf: []byte("abcdefg")}
- rb := NewResumableBuffer(er, 3)
- // expectChunk reads a chunk and checks that it got what was wanted.
- expectChunk := func(want string, wantErr error) {
- got, err := getChunkAsString(t, rb)
- if err != wantErr {
- t.Errorf("error reading buffer: got: %v; want: %v", err, wantErr)
- }
- if !reflect.DeepEqual(got, want) {
- t.Errorf("Failed reading buffer: got: %q; want:%q", got, want)
- }
- }
- expectChunk("abc", nil)
- // On second call, should get same chunk again.
- expectChunk("abc", nil)
- rb.Next()
- expectChunk("def", nil)
- expectChunk("def", nil)
- rb.Next()
- expectChunk("g", io.EOF)
- expectChunk("g", io.EOF)
- rb.Next()
- expectChunk("", io.EOF)
- }
- func TestPos(t *testing.T) {
- er := &errReader{buf: []byte("abcdefg")}
- rb := NewResumableBuffer(er, 3)
- expectChunkAtOffset := func(want int64, wantErr error) {
- _, off, _, err := rb.Chunk()
- if err != wantErr {
- t.Errorf("error reading buffer: got: %v; want: %v", err, wantErr)
- }
- if got := off; got != want {
- t.Errorf("resumable buffer Pos: got: %v; want: %v", got, want)
- }
- }
- // We expect the first chunk to be at offset 0.
- expectChunkAtOffset(0, nil)
- // Fetching the same chunk should return the same offset.
- expectChunkAtOffset(0, nil)
- // Calling Next multiple times should only cause off to advance by 3, since off is not advanced until
- // the chunk is actually read.
- rb.Next()
- rb.Next()
- expectChunkAtOffset(3, nil)
- rb.Next()
- // Load the final 1-byte chunk.
- expectChunkAtOffset(6, io.EOF)
- // Next will advance 1 byte. But there are no more chunks, so off will not increase beyond 7.
- rb.Next()
- expectChunkAtOffset(7, io.EOF)
- rb.Next()
- expectChunkAtOffset(7, io.EOF)
- }
- // bytes.Reader implements both Reader and ReaderAt. The following types
- // implement various combinations of Reader, ReaderAt and ContentTyper, by
- // wrapping bytes.Reader. All implement at least ReaderAt, so they can be
- // passed to ReaderAtToReader. The following table summarizes which types
- // implement which interfaces:
- //
- // ReaderAt Reader ContentTyper
- // reader x x
- // typerReader x x x
- // readerAt x
- // typerReaderAt x x
- // reader implements Reader, in addition to ReaderAt.
- type reader struct {
- r *bytes.Reader
- }
- func (r *reader) ReadAt(b []byte, off int64) (n int, err error) {
- return r.r.ReadAt(b, off)
- }
- func (r *reader) Read(b []byte) (n int, err error) {
- return r.r.Read(b)
- }
- // typerReader implements Reader and ContentTyper, in addition to ReaderAt.
- type typerReader struct {
- r *bytes.Reader
- }
- func (tr *typerReader) ReadAt(b []byte, off int64) (n int, err error) {
- return tr.r.ReadAt(b, off)
- }
- func (tr *typerReader) Read(b []byte) (n int, err error) {
- return tr.r.Read(b)
- }
- func (tr *typerReader) ContentType() string {
- return "ctype"
- }
- // readerAt implements only ReaderAt.
- type readerAt struct {
- r *bytes.Reader
- }
- func (ra *readerAt) ReadAt(b []byte, off int64) (n int, err error) {
- return ra.r.ReadAt(b, off)
- }
- // typerReaderAt implements ContentTyper, in addition to ReaderAt.
- type typerReaderAt struct {
- r *bytes.Reader
- }
- func (tra *typerReaderAt) ReadAt(b []byte, off int64) (n int, err error) {
- return tra.r.ReadAt(b, off)
- }
- func (tra *typerReaderAt) ContentType() string {
- return "ctype"
- }
- func TestAdapter(t *testing.T) {
- data := "abc"
- checkConversion := func(to io.Reader, wantTyper bool) {
- if _, ok := to.(googleapi.ContentTyper); ok != wantTyper {
- t.Errorf("reader implements typer? got: %v; want: %v", ok, wantTyper)
- }
- if typer, ok := to.(googleapi.ContentTyper); ok && typer.ContentType() != "ctype" {
- t.Errorf("content type: got: %s; want: ctype", typer.ContentType())
- }
- buf, err := ioutil.ReadAll(to)
- if err != nil {
- t.Errorf("error reading data: %v", err)
- return
- }
- if !bytes.Equal(buf, []byte(data)) {
- t.Errorf("failed reading data: got: %s; want: %s", buf, data)
- }
- }
- type testCase struct {
- from io.ReaderAt
- wantTyper bool
- }
- for _, tc := range []testCase{
- {
- from: &reader{bytes.NewReader([]byte(data))},
- wantTyper: false,
- },
- {
- // Reader and ContentTyper
- from: &typerReader{bytes.NewReader([]byte(data))},
- wantTyper: true,
- },
- {
- // ReaderAt
- from: &readerAt{bytes.NewReader([]byte(data))},
- wantTyper: false,
- },
- {
- // ReaderAt and ContentTyper
- from: &typerReaderAt{bytes.NewReader([]byte(data))},
- wantTyper: true,
- },
- } {
- to := ReaderAtToReader(tc.from, int64(len(data)))
- checkConversion(to, tc.wantTyper)
- // tc.from is a ReaderAt, and should be treated like one, even
- // if it also implements Reader. Specifically, it can be
- // reused and read from the beginning.
- to = ReaderAtToReader(tc.from, int64(len(data)))
- checkConversion(to, tc.wantTyper)
- }
- }
|