...

Source file src/google.golang.org/api/internal/gensupport/media_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  	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  			// Note that ChunkSize = 1 is rounded up to googleapi.MinUploadChunkSize.
    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  			// Note that ChunkSize = 1 is rounded up to googleapi.MinUploadChunkSize.
   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  	// Test that a single chunk results in a getBody function that is non-nil, and
   157  	// that produces the same content as the original body.
   158  
   159  	// Restore the crypto/rand.Reader mocked out below.
   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  			// Note that ChunkSize = 1 is rounded up to googleapi.MinUploadChunkSize.
   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  			// (Because nullReader returns EOF with the last bytes.)
   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  			// Note that ChunkSize = 1 is rounded up to googleapi.MinUploadChunkSize.
   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  // A nullReader simulates reading a fixed number of bytes.
   291  type nullReader struct {
   292  	remain int
   293  }
   294  
   295  // Read doesn't touch buf, but it does reduce the amount of bytes remaining
   296  // by len(buf).
   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