upload_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. package s3manager_test
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "net/http"
  8. "net/http/httptest"
  9. "reflect"
  10. "sort"
  11. "strings"
  12. "sync"
  13. "testing"
  14. "github.com/aws/aws-sdk-go/aws"
  15. "github.com/aws/aws-sdk-go/aws/awserr"
  16. "github.com/aws/aws-sdk-go/aws/awsutil"
  17. "github.com/aws/aws-sdk-go/aws/request"
  18. "github.com/aws/aws-sdk-go/awstesting/unit"
  19. "github.com/aws/aws-sdk-go/service/s3"
  20. "github.com/aws/aws-sdk-go/service/s3/s3manager"
  21. "github.com/stretchr/testify/assert"
  22. )
  23. var emptyList = []string{}
  24. func val(i interface{}, s string) interface{} {
  25. v, err := awsutil.ValuesAtPath(i, s)
  26. if err != nil || len(v) == 0 {
  27. return nil
  28. }
  29. if _, ok := v[0].(io.Reader); ok {
  30. return v[0]
  31. }
  32. if rv := reflect.ValueOf(v[0]); rv.Kind() == reflect.Ptr {
  33. return rv.Elem().Interface()
  34. }
  35. return v[0]
  36. }
  37. func contains(src []string, s string) bool {
  38. for _, v := range src {
  39. if s == v {
  40. return true
  41. }
  42. }
  43. return false
  44. }
  45. func loggingSvc(ignoreOps []string) (*s3.S3, *[]string, *[]interface{}) {
  46. var m sync.Mutex
  47. partNum := 0
  48. names := []string{}
  49. params := []interface{}{}
  50. svc := s3.New(unit.Session)
  51. svc.Handlers.Unmarshal.Clear()
  52. svc.Handlers.UnmarshalMeta.Clear()
  53. svc.Handlers.UnmarshalError.Clear()
  54. svc.Handlers.Send.Clear()
  55. svc.Handlers.Send.PushBack(func(r *request.Request) {
  56. m.Lock()
  57. defer m.Unlock()
  58. if !contains(ignoreOps, r.Operation.Name) {
  59. names = append(names, r.Operation.Name)
  60. params = append(params, r.Params)
  61. }
  62. r.HTTPResponse = &http.Response{
  63. StatusCode: 200,
  64. Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
  65. }
  66. switch data := r.Data.(type) {
  67. case *s3.CreateMultipartUploadOutput:
  68. data.UploadId = aws.String("UPLOAD-ID")
  69. case *s3.UploadPartOutput:
  70. partNum++
  71. data.ETag = aws.String(fmt.Sprintf("ETAG%d", partNum))
  72. case *s3.CompleteMultipartUploadOutput:
  73. data.Location = aws.String("https://location")
  74. data.VersionId = aws.String("VERSION-ID")
  75. case *s3.PutObjectOutput:
  76. data.VersionId = aws.String("VERSION-ID")
  77. }
  78. })
  79. return svc, &names, &params
  80. }
  81. func buflen(i interface{}) int {
  82. r := i.(io.Reader)
  83. b, _ := ioutil.ReadAll(r)
  84. return len(b)
  85. }
  86. func TestUploadOrderMulti(t *testing.T) {
  87. s, ops, args := loggingSvc(emptyList)
  88. u := s3manager.NewUploaderWithClient(s)
  89. resp, err := u.Upload(&s3manager.UploadInput{
  90. Bucket: aws.String("Bucket"),
  91. Key: aws.String("Key"),
  92. Body: bytes.NewReader(buf12MB),
  93. ServerSideEncryption: aws.String("aws:kms"),
  94. SSEKMSKeyId: aws.String("KmsId"),
  95. ContentType: aws.String("content/type"),
  96. })
  97. assert.NoError(t, err)
  98. assert.Equal(t, []string{"CreateMultipartUpload", "UploadPart", "UploadPart", "UploadPart", "CompleteMultipartUpload"}, *ops)
  99. assert.Equal(t, "https://location", resp.Location)
  100. assert.Equal(t, "UPLOAD-ID", resp.UploadID)
  101. assert.Equal(t, aws.String("VERSION-ID"), resp.VersionID)
  102. // Validate input values
  103. // UploadPart
  104. assert.Equal(t, "UPLOAD-ID", val((*args)[1], "UploadId"))
  105. assert.Equal(t, "UPLOAD-ID", val((*args)[2], "UploadId"))
  106. assert.Equal(t, "UPLOAD-ID", val((*args)[3], "UploadId"))
  107. // CompleteMultipartUpload
  108. assert.Equal(t, "UPLOAD-ID", val((*args)[4], "UploadId"))
  109. assert.Equal(t, int64(1), val((*args)[4], "MultipartUpload.Parts[0].PartNumber"))
  110. assert.Equal(t, int64(2), val((*args)[4], "MultipartUpload.Parts[1].PartNumber"))
  111. assert.Equal(t, int64(3), val((*args)[4], "MultipartUpload.Parts[2].PartNumber"))
  112. assert.Regexp(t, `^ETAG\d+$`, val((*args)[4], "MultipartUpload.Parts[0].ETag"))
  113. assert.Regexp(t, `^ETAG\d+$`, val((*args)[4], "MultipartUpload.Parts[1].ETag"))
  114. assert.Regexp(t, `^ETAG\d+$`, val((*args)[4], "MultipartUpload.Parts[2].ETag"))
  115. // Custom headers
  116. assert.Equal(t, "aws:kms", val((*args)[0], "ServerSideEncryption"))
  117. assert.Equal(t, "KmsId", val((*args)[0], "SSEKMSKeyId"))
  118. assert.Equal(t, "content/type", val((*args)[0], "ContentType"))
  119. }
  120. func TestUploadOrderMultiDifferentPartSize(t *testing.T) {
  121. s, ops, args := loggingSvc(emptyList)
  122. mgr := s3manager.NewUploaderWithClient(s, func(u *s3manager.Uploader) {
  123. u.PartSize = 1024 * 1024 * 7
  124. u.Concurrency = 1
  125. })
  126. _, err := mgr.Upload(&s3manager.UploadInput{
  127. Bucket: aws.String("Bucket"),
  128. Key: aws.String("Key"),
  129. Body: bytes.NewReader(buf12MB),
  130. })
  131. assert.NoError(t, err)
  132. assert.Equal(t, []string{"CreateMultipartUpload", "UploadPart", "UploadPart", "CompleteMultipartUpload"}, *ops)
  133. // Part lengths
  134. assert.Equal(t, 1024*1024*7, buflen(val((*args)[1], "Body")))
  135. assert.Equal(t, 1024*1024*5, buflen(val((*args)[2], "Body")))
  136. }
  137. func TestUploadIncreasePartSize(t *testing.T) {
  138. s, ops, args := loggingSvc(emptyList)
  139. mgr := s3manager.NewUploaderWithClient(s, func(u *s3manager.Uploader) {
  140. u.Concurrency = 1
  141. u.MaxUploadParts = 2
  142. })
  143. _, err := mgr.Upload(&s3manager.UploadInput{
  144. Bucket: aws.String("Bucket"),
  145. Key: aws.String("Key"),
  146. Body: bytes.NewReader(buf12MB),
  147. })
  148. assert.NoError(t, err)
  149. assert.Equal(t, int64(s3manager.DefaultDownloadPartSize), mgr.PartSize)
  150. assert.Equal(t, []string{"CreateMultipartUpload", "UploadPart", "UploadPart", "CompleteMultipartUpload"}, *ops)
  151. // Part lengths
  152. assert.Equal(t, (1024*1024*6)+1, buflen(val((*args)[1], "Body")))
  153. assert.Equal(t, (1024*1024*6)-1, buflen(val((*args)[2], "Body")))
  154. }
  155. func TestUploadFailIfPartSizeTooSmall(t *testing.T) {
  156. mgr := s3manager.NewUploader(unit.Session, func(u *s3manager.Uploader) {
  157. u.PartSize = 5
  158. })
  159. resp, err := mgr.Upload(&s3manager.UploadInput{
  160. Bucket: aws.String("Bucket"),
  161. Key: aws.String("Key"),
  162. Body: bytes.NewReader(buf12MB),
  163. })
  164. assert.Nil(t, resp)
  165. assert.NotNil(t, err)
  166. aerr := err.(awserr.Error)
  167. assert.Equal(t, "ConfigError", aerr.Code())
  168. assert.Contains(t, aerr.Message(), "part size must be at least")
  169. }
  170. func TestUploadOrderSingle(t *testing.T) {
  171. s, ops, args := loggingSvc(emptyList)
  172. mgr := s3manager.NewUploaderWithClient(s)
  173. resp, err := mgr.Upload(&s3manager.UploadInput{
  174. Bucket: aws.String("Bucket"),
  175. Key: aws.String("Key"),
  176. Body: bytes.NewReader(buf2MB),
  177. ServerSideEncryption: aws.String("aws:kms"),
  178. SSEKMSKeyId: aws.String("KmsId"),
  179. ContentType: aws.String("content/type"),
  180. })
  181. assert.NoError(t, err)
  182. assert.Equal(t, []string{"PutObject"}, *ops)
  183. assert.NotEqual(t, "", resp.Location)
  184. assert.Equal(t, aws.String("VERSION-ID"), resp.VersionID)
  185. assert.Equal(t, "", resp.UploadID)
  186. assert.Equal(t, "aws:kms", val((*args)[0], "ServerSideEncryption"))
  187. assert.Equal(t, "KmsId", val((*args)[0], "SSEKMSKeyId"))
  188. assert.Equal(t, "content/type", val((*args)[0], "ContentType"))
  189. }
  190. func TestUploadOrderSingleFailure(t *testing.T) {
  191. s, ops, _ := loggingSvc(emptyList)
  192. s.Handlers.Send.PushBack(func(r *request.Request) {
  193. r.HTTPResponse.StatusCode = 400
  194. })
  195. mgr := s3manager.NewUploaderWithClient(s)
  196. resp, err := mgr.Upload(&s3manager.UploadInput{
  197. Bucket: aws.String("Bucket"),
  198. Key: aws.String("Key"),
  199. Body: bytes.NewReader(buf2MB),
  200. })
  201. assert.Error(t, err)
  202. assert.Equal(t, []string{"PutObject"}, *ops)
  203. assert.Nil(t, resp)
  204. }
  205. func TestUploadOrderZero(t *testing.T) {
  206. s, ops, args := loggingSvc(emptyList)
  207. mgr := s3manager.NewUploaderWithClient(s)
  208. resp, err := mgr.Upload(&s3manager.UploadInput{
  209. Bucket: aws.String("Bucket"),
  210. Key: aws.String("Key"),
  211. Body: bytes.NewReader(make([]byte, 0)),
  212. })
  213. assert.NoError(t, err)
  214. assert.Equal(t, []string{"PutObject"}, *ops)
  215. assert.NotEqual(t, "", resp.Location)
  216. assert.Equal(t, "", resp.UploadID)
  217. assert.Equal(t, 0, buflen(val((*args)[0], "Body")))
  218. }
  219. func TestUploadOrderMultiFailure(t *testing.T) {
  220. s, ops, _ := loggingSvc(emptyList)
  221. s.Handlers.Send.PushBack(func(r *request.Request) {
  222. switch t := r.Data.(type) {
  223. case *s3.UploadPartOutput:
  224. if *t.ETag == "ETAG2" {
  225. r.HTTPResponse.StatusCode = 400
  226. }
  227. }
  228. })
  229. mgr := s3manager.NewUploaderWithClient(s, func(u *s3manager.Uploader) {
  230. u.Concurrency = 1
  231. })
  232. _, err := mgr.Upload(&s3manager.UploadInput{
  233. Bucket: aws.String("Bucket"),
  234. Key: aws.String("Key"),
  235. Body: bytes.NewReader(buf12MB),
  236. })
  237. assert.Error(t, err)
  238. assert.Equal(t, []string{"CreateMultipartUpload", "UploadPart", "UploadPart", "AbortMultipartUpload"}, *ops)
  239. }
  240. func TestUploadOrderMultiFailureOnComplete(t *testing.T) {
  241. s, ops, _ := loggingSvc(emptyList)
  242. s.Handlers.Send.PushBack(func(r *request.Request) {
  243. switch r.Data.(type) {
  244. case *s3.CompleteMultipartUploadOutput:
  245. r.HTTPResponse.StatusCode = 400
  246. }
  247. })
  248. mgr := s3manager.NewUploaderWithClient(s, func(u *s3manager.Uploader) {
  249. u.Concurrency = 1
  250. })
  251. _, err := mgr.Upload(&s3manager.UploadInput{
  252. Bucket: aws.String("Bucket"),
  253. Key: aws.String("Key"),
  254. Body: bytes.NewReader(buf12MB),
  255. })
  256. assert.Error(t, err)
  257. assert.Equal(t, []string{"CreateMultipartUpload", "UploadPart", "UploadPart",
  258. "UploadPart", "CompleteMultipartUpload", "AbortMultipartUpload"}, *ops)
  259. }
  260. func TestUploadOrderMultiFailureOnCreate(t *testing.T) {
  261. s, ops, _ := loggingSvc(emptyList)
  262. s.Handlers.Send.PushBack(func(r *request.Request) {
  263. switch r.Data.(type) {
  264. case *s3.CreateMultipartUploadOutput:
  265. r.HTTPResponse.StatusCode = 400
  266. }
  267. })
  268. mgr := s3manager.NewUploaderWithClient(s)
  269. _, err := mgr.Upload(&s3manager.UploadInput{
  270. Bucket: aws.String("Bucket"),
  271. Key: aws.String("Key"),
  272. Body: bytes.NewReader(make([]byte, 1024*1024*12)),
  273. })
  274. assert.Error(t, err)
  275. assert.Equal(t, []string{"CreateMultipartUpload"}, *ops)
  276. }
  277. func TestUploadOrderMultiFailureLeaveParts(t *testing.T) {
  278. s, ops, _ := loggingSvc(emptyList)
  279. s.Handlers.Send.PushBack(func(r *request.Request) {
  280. switch data := r.Data.(type) {
  281. case *s3.UploadPartOutput:
  282. if *data.ETag == "ETAG2" {
  283. r.HTTPResponse.StatusCode = 400
  284. }
  285. }
  286. })
  287. mgr := s3manager.NewUploaderWithClient(s, func(u *s3manager.Uploader) {
  288. u.Concurrency = 1
  289. u.LeavePartsOnError = true
  290. })
  291. _, err := mgr.Upload(&s3manager.UploadInput{
  292. Bucket: aws.String("Bucket"),
  293. Key: aws.String("Key"),
  294. Body: bytes.NewReader(make([]byte, 1024*1024*12)),
  295. })
  296. assert.Error(t, err)
  297. assert.Equal(t, []string{"CreateMultipartUpload", "UploadPart", "UploadPart"}, *ops)
  298. }
  299. type failreader struct {
  300. times int
  301. failCount int
  302. }
  303. func (f *failreader) Read(b []byte) (int, error) {
  304. f.failCount++
  305. if f.failCount >= f.times {
  306. return 0, fmt.Errorf("random failure")
  307. }
  308. return len(b), nil
  309. }
  310. func TestUploadOrderReadFail1(t *testing.T) {
  311. s, ops, _ := loggingSvc(emptyList)
  312. mgr := s3manager.NewUploaderWithClient(s)
  313. _, err := mgr.Upload(&s3manager.UploadInput{
  314. Bucket: aws.String("Bucket"),
  315. Key: aws.String("Key"),
  316. Body: &failreader{times: 1},
  317. })
  318. assert.Equal(t, "ReadRequestBody", err.(awserr.Error).Code())
  319. assert.EqualError(t, err.(awserr.Error).OrigErr(), "random failure")
  320. assert.Equal(t, []string{}, *ops)
  321. }
  322. func TestUploadOrderReadFail2(t *testing.T) {
  323. s, ops, _ := loggingSvc([]string{"UploadPart"})
  324. mgr := s3manager.NewUploaderWithClient(s, func(u *s3manager.Uploader) {
  325. u.Concurrency = 1
  326. })
  327. _, err := mgr.Upload(&s3manager.UploadInput{
  328. Bucket: aws.String("Bucket"),
  329. Key: aws.String("Key"),
  330. Body: &failreader{times: 2},
  331. })
  332. assert.Equal(t, "MultipartUpload", err.(awserr.Error).Code())
  333. assert.Equal(t, "ReadRequestBody", err.(awserr.Error).OrigErr().(awserr.Error).Code())
  334. assert.Contains(t, err.(awserr.Error).OrigErr().Error(), "random failure")
  335. assert.Equal(t, []string{"CreateMultipartUpload", "AbortMultipartUpload"}, *ops)
  336. }
  337. type sizedReader struct {
  338. size int
  339. cur int
  340. err error
  341. }
  342. func (s *sizedReader) Read(p []byte) (n int, err error) {
  343. if s.cur >= s.size {
  344. if s.err == nil {
  345. s.err = io.EOF
  346. }
  347. return 0, s.err
  348. }
  349. n = len(p)
  350. s.cur += len(p)
  351. if s.cur > s.size {
  352. n -= s.cur - s.size
  353. }
  354. return
  355. }
  356. func TestUploadOrderMultiBufferedReader(t *testing.T) {
  357. s, ops, args := loggingSvc(emptyList)
  358. mgr := s3manager.NewUploaderWithClient(s)
  359. _, err := mgr.Upload(&s3manager.UploadInput{
  360. Bucket: aws.String("Bucket"),
  361. Key: aws.String("Key"),
  362. Body: &sizedReader{size: 1024 * 1024 * 12},
  363. })
  364. assert.NoError(t, err)
  365. assert.Equal(t, []string{"CreateMultipartUpload", "UploadPart", "UploadPart", "UploadPart", "CompleteMultipartUpload"}, *ops)
  366. // Part lengths
  367. parts := []int{
  368. buflen(val((*args)[1], "Body")),
  369. buflen(val((*args)[2], "Body")),
  370. buflen(val((*args)[3], "Body")),
  371. }
  372. sort.Ints(parts)
  373. assert.Equal(t, []int{1024 * 1024 * 2, 1024 * 1024 * 5, 1024 * 1024 * 5}, parts)
  374. }
  375. func TestUploadOrderMultiBufferedReaderUnexpectedEOF(t *testing.T) {
  376. s, ops, args := loggingSvc(emptyList)
  377. mgr := s3manager.NewUploaderWithClient(s)
  378. _, err := mgr.Upload(&s3manager.UploadInput{
  379. Bucket: aws.String("Bucket"),
  380. Key: aws.String("Key"),
  381. Body: &sizedReader{size: 1024 * 1024 * 12, err: io.ErrUnexpectedEOF},
  382. })
  383. assert.NoError(t, err)
  384. assert.Equal(t, []string{"CreateMultipartUpload", "UploadPart", "UploadPart", "UploadPart", "CompleteMultipartUpload"}, *ops)
  385. // Part lengths
  386. parts := []int{
  387. buflen(val((*args)[1], "Body")),
  388. buflen(val((*args)[2], "Body")),
  389. buflen(val((*args)[3], "Body")),
  390. }
  391. sort.Ints(parts)
  392. assert.Equal(t, []int{1024 * 1024 * 2, 1024 * 1024 * 5, 1024 * 1024 * 5}, parts)
  393. }
  394. // TestUploadOrderMultiBufferedReaderEOF tests the edge case where the
  395. // file size is the same as part size, which means nextReader will
  396. // return io.EOF rather than io.ErrUnexpectedEOF
  397. func TestUploadOrderMultiBufferedReaderEOF(t *testing.T) {
  398. s, ops, args := loggingSvc(emptyList)
  399. mgr := s3manager.NewUploaderWithClient(s)
  400. _, err := mgr.Upload(&s3manager.UploadInput{
  401. Bucket: aws.String("Bucket"),
  402. Key: aws.String("Key"),
  403. Body: &sizedReader{size: 1024 * 1024 * 10, err: io.EOF},
  404. })
  405. assert.NoError(t, err)
  406. assert.Equal(t, []string{"CreateMultipartUpload", "UploadPart", "UploadPart", "CompleteMultipartUpload"}, *ops)
  407. // Part lengths
  408. parts := []int{
  409. buflen(val((*args)[1], "Body")),
  410. buflen(val((*args)[2], "Body")),
  411. }
  412. sort.Ints(parts)
  413. assert.Equal(t, []int{1024 * 1024 * 5, 1024 * 1024 * 5}, parts)
  414. }
  415. func TestUploadOrderMultiBufferedReaderExceedTotalParts(t *testing.T) {
  416. s, ops, _ := loggingSvc([]string{"UploadPart"})
  417. mgr := s3manager.NewUploaderWithClient(s, func(u *s3manager.Uploader) {
  418. u.Concurrency = 1
  419. u.MaxUploadParts = 2
  420. })
  421. resp, err := mgr.Upload(&s3manager.UploadInput{
  422. Bucket: aws.String("Bucket"),
  423. Key: aws.String("Key"),
  424. Body: &sizedReader{size: 1024 * 1024 * 12},
  425. })
  426. assert.Error(t, err)
  427. assert.Nil(t, resp)
  428. assert.Equal(t, []string{"CreateMultipartUpload", "AbortMultipartUpload"}, *ops)
  429. aerr := err.(awserr.Error)
  430. assert.Equal(t, "MultipartUpload", aerr.Code())
  431. assert.Equal(t, "TotalPartsExceeded", aerr.OrigErr().(awserr.Error).Code())
  432. assert.Contains(t, aerr.Error(), "configured MaxUploadParts (2)")
  433. }
  434. func TestUploadOrderSingleBufferedReader(t *testing.T) {
  435. s, ops, _ := loggingSvc(emptyList)
  436. mgr := s3manager.NewUploaderWithClient(s)
  437. resp, err := mgr.Upload(&s3manager.UploadInput{
  438. Bucket: aws.String("Bucket"),
  439. Key: aws.String("Key"),
  440. Body: &sizedReader{size: 1024 * 1024 * 2},
  441. })
  442. assert.NoError(t, err)
  443. assert.Equal(t, []string{"PutObject"}, *ops)
  444. assert.NotEqual(t, "", resp.Location)
  445. assert.Equal(t, "", resp.UploadID)
  446. }
  447. func TestUploadZeroLenObject(t *testing.T) {
  448. requestMade := false
  449. server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  450. requestMade = true
  451. w.WriteHeader(http.StatusOK)
  452. }))
  453. mgr := s3manager.NewUploaderWithClient(s3.New(unit.Session, &aws.Config{
  454. Endpoint: aws.String(server.URL),
  455. }))
  456. resp, err := mgr.Upload(&s3manager.UploadInput{
  457. Bucket: aws.String("Bucket"),
  458. Key: aws.String("Key"),
  459. Body: strings.NewReader(""),
  460. })
  461. assert.NoError(t, err)
  462. assert.True(t, requestMade)
  463. assert.NotEqual(t, "", resp.Location)
  464. assert.Equal(t, "", resp.UploadID)
  465. }
  466. func TestUploadInputS3PutObjectInputPairity(t *testing.T) {
  467. matchings := compareStructType(reflect.TypeOf(s3.PutObjectInput{}),
  468. reflect.TypeOf(s3manager.UploadInput{}))
  469. aOnly := []string{}
  470. bOnly := []string{}
  471. for k, c := range matchings {
  472. if c == 1 && k != "ContentLength" {
  473. aOnly = append(aOnly, k)
  474. } else if c == 2 {
  475. bOnly = append(bOnly, k)
  476. }
  477. }
  478. assert.Empty(t, aOnly, "s3.PutObjectInput")
  479. assert.Empty(t, bOnly, "s3Manager.UploadInput")
  480. }
  481. func compareStructType(a, b reflect.Type) map[string]int {
  482. if a.Kind() != reflect.Struct || b.Kind() != reflect.Struct {
  483. panic(fmt.Sprintf("types must both be structs, got %v and %v", a.Kind(), b.Kind()))
  484. }
  485. aFields := enumFields(a)
  486. bFields := enumFields(b)
  487. matchings := map[string]int{}
  488. for i := 0; i < len(aFields) || i < len(bFields); i++ {
  489. if i < len(aFields) {
  490. c := matchings[aFields[i].Name]
  491. matchings[aFields[i].Name] = c + 1
  492. }
  493. if i < len(bFields) {
  494. c := matchings[bFields[i].Name]
  495. matchings[bFields[i].Name] = c + 2
  496. }
  497. }
  498. return matchings
  499. }
  500. func enumFields(v reflect.Type) []reflect.StructField {
  501. fields := []reflect.StructField{}
  502. for i := 0; i < v.NumField(); i++ {
  503. field := v.Field(i)
  504. // Ignoreing anon fields
  505. if field.PkgPath != "" {
  506. // Ignore unexported fields
  507. continue
  508. }
  509. fields = append(fields, field)
  510. }
  511. return fields
  512. }