...

Source file src/google.golang.org/api/internal/gensupport/buffer_test.go

Documentation: google.golang.org/api/internal/gensupport

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

View as plain text