github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/object-api-multipart_test.go (about)

     1  // Copyright (c) 2015-2021 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package cmd
    19  
    20  import (
    21  	"bytes"
    22  	"context"
    23  	"fmt"
    24  	"math/rand"
    25  	"reflect"
    26  	"runtime"
    27  	"strings"
    28  	"testing"
    29  
    30  	"github.com/dustin/go-humanize"
    31  	"github.com/minio/minio/internal/config/storageclass"
    32  	"github.com/minio/minio/internal/hash"
    33  	"github.com/minio/minio/internal/ioutil"
    34  )
    35  
    36  // Wrapper for calling NewMultipartUpload tests for both Erasure multiple disks and single node setup.
    37  func TestObjectNewMultipartUpload(t *testing.T) {
    38  	if runtime.GOOS == globalWindowsOSName {
    39  		t.Skip()
    40  	}
    41  	ExecObjectLayerTest(t, testObjectNewMultipartUpload)
    42  }
    43  
    44  // Tests validate creation of new multipart upload instance.
    45  func testObjectNewMultipartUpload(obj ObjectLayer, instanceType string, t TestErrHandler) {
    46  	bucket := "minio-bucket"
    47  	object := "minio-object"
    48  	opts := ObjectOptions{}
    49  	_, err := obj.NewMultipartUpload(context.Background(), "--", object, opts)
    50  	if err == nil {
    51  		t.Fatalf("%s: Expected to fail since bucket name is invalid.", instanceType)
    52  	}
    53  
    54  	errMsg := "Bucket not found: minio-bucket"
    55  	// operation expected to fail since the bucket on which NewMultipartUpload is being initiated doesn't exist.
    56  	_, err = obj.NewMultipartUpload(context.Background(), bucket, object, opts)
    57  	if err == nil {
    58  		t.Fatalf("%s: Expected to fail since the NewMultipartUpload is initialized on a non-existent bucket.", instanceType)
    59  	}
    60  	if errMsg != err.Error() {
    61  		t.Errorf("%s, Expected to fail with Error \"%s\", but instead found \"%s\".", instanceType, errMsg, err.Error())
    62  	}
    63  
    64  	// Create bucket before initiating NewMultipartUpload.
    65  	err = obj.MakeBucket(context.Background(), bucket, MakeBucketOptions{})
    66  	if err != nil {
    67  		// failed to create newbucket, abort.
    68  		t.Fatalf("%s : %s", instanceType, err.Error())
    69  	}
    70  
    71  	res, err := obj.NewMultipartUpload(context.Background(), bucket, "\\", opts)
    72  	if err != nil {
    73  		t.Fatalf("%s : %s", instanceType, err.Error())
    74  	}
    75  
    76  	err = obj.AbortMultipartUpload(context.Background(), bucket, "\\", res.UploadID, opts)
    77  	if err != nil {
    78  		switch err.(type) {
    79  		case InvalidUploadID:
    80  			t.Fatalf("%s: New Multipart upload failed to create uuid file.", instanceType)
    81  		default:
    82  			t.Fatalf(err.Error())
    83  		}
    84  	}
    85  }
    86  
    87  // Wrapper for calling AbortMultipartUpload tests for both Erasure multiple disks and single node setup.
    88  func TestObjectAbortMultipartUpload(t *testing.T) {
    89  	ExecObjectLayerTest(t, testObjectAbortMultipartUpload)
    90  }
    91  
    92  // Tests validate creation of abort multipart upload instance.
    93  func testObjectAbortMultipartUpload(obj ObjectLayer, instanceType string, t TestErrHandler) {
    94  	bucket := "minio-bucket"
    95  	object := "minio-object"
    96  	opts := ObjectOptions{}
    97  	// Create bucket before initiating NewMultipartUpload.
    98  	err := obj.MakeBucket(context.Background(), bucket, MakeBucketOptions{})
    99  	if err != nil {
   100  		// failed to create newbucket, abort.
   101  		t.Fatalf("%s : %s", instanceType, err.Error())
   102  	}
   103  
   104  	res, err := obj.NewMultipartUpload(context.Background(), bucket, object, opts)
   105  	if err != nil {
   106  		t.Fatalf("%s : %s", instanceType, err.Error())
   107  	}
   108  	uploadID := res.UploadID
   109  
   110  	abortTestCases := []struct {
   111  		bucketName      string
   112  		objName         string
   113  		uploadID        string
   114  		expectedErrType error
   115  	}{
   116  		{"--", object, uploadID, BucketNameInvalid{}},
   117  		{"foo", object, uploadID, BucketNotFound{}},
   118  		{bucket, object, "foo-foo", InvalidUploadID{}},
   119  		{bucket, object, uploadID, nil},
   120  	}
   121  
   122  	if runtime.GOOS != globalWindowsOSName {
   123  		abortTestCases = append(abortTestCases, struct {
   124  			bucketName      string
   125  			objName         string
   126  			uploadID        string
   127  			expectedErrType error
   128  		}{bucket, "\\", uploadID, InvalidUploadID{}})
   129  	}
   130  
   131  	// Iterating over creatPartCases to generate multipart chunks.
   132  	for i, testCase := range abortTestCases {
   133  		err = obj.AbortMultipartUpload(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, opts)
   134  		if testCase.expectedErrType == nil && err != nil {
   135  			t.Errorf("Test %d, unexpected err is received: %v, expected:%v\n", i+1, err, testCase.expectedErrType)
   136  		}
   137  		if testCase.expectedErrType != nil && !isSameType(err, testCase.expectedErrType) {
   138  			t.Errorf("Test %d, unexpected err is received: %v, expected:%v\n", i+1, err, testCase.expectedErrType)
   139  		}
   140  	}
   141  }
   142  
   143  // Wrapper for calling isUploadIDExists tests for both Erasure multiple disks and single node setup.
   144  func TestObjectAPIIsUploadIDExists(t *testing.T) {
   145  	ExecObjectLayerTest(t, testObjectAPIIsUploadIDExists)
   146  }
   147  
   148  // Tests validates the validator for existence of uploadID.
   149  func testObjectAPIIsUploadIDExists(obj ObjectLayer, instanceType string, t TestErrHandler) {
   150  	bucket := "minio-bucket"
   151  	object := "minio-object"
   152  
   153  	// Create bucket before initiating NewMultipartUpload.
   154  	err := obj.MakeBucket(context.Background(), bucket, MakeBucketOptions{})
   155  	if err != nil {
   156  		// Failed to create newbucket, abort.
   157  		t.Fatalf("%s : %s", instanceType, err.Error())
   158  	}
   159  
   160  	_, err = obj.NewMultipartUpload(context.Background(), bucket, object, ObjectOptions{})
   161  	if err != nil {
   162  		t.Fatalf("%s : %s", instanceType, err.Error())
   163  	}
   164  
   165  	opts := ObjectOptions{}
   166  	err = obj.AbortMultipartUpload(context.Background(), bucket, object, "abc", opts)
   167  	switch err.(type) {
   168  	case InvalidUploadID:
   169  	default:
   170  		t.Fatalf("%s: Expected uploadIDPath to exist.", instanceType)
   171  	}
   172  }
   173  
   174  // Wrapper for calling PutObjectPart tests for both Erasure multiple disks and single node setup.
   175  func TestObjectAPIPutObjectPart(t *testing.T) {
   176  	ExecExtendedObjectLayerTest(t, testObjectAPIPutObjectPart)
   177  }
   178  
   179  // Tests validate correctness of PutObjectPart.
   180  func testObjectAPIPutObjectPart(obj ObjectLayer, instanceType string, t TestErrHandler) {
   181  	// Generating cases for which the PutObjectPart fails.
   182  	bucket := "minio-bucket"
   183  	object := "minio-object"
   184  	opts := ObjectOptions{}
   185  	// Create bucket before initiating NewMultipartUpload.
   186  	err := obj.MakeBucket(context.Background(), bucket, MakeBucketOptions{})
   187  	if err != nil {
   188  		// Failed to create newbucket, abort.
   189  		t.Fatalf("%s : %s", instanceType, err.Error())
   190  	}
   191  	// Initiate Multipart Upload on the above created bucket.
   192  	res, err := obj.NewMultipartUpload(context.Background(), bucket, object, opts)
   193  	if err != nil {
   194  		// Failed to create NewMultipartUpload, abort.
   195  		t.Fatalf("%s : %s", instanceType, err.Error())
   196  	}
   197  
   198  	err = obj.MakeBucket(context.Background(), "abc", MakeBucketOptions{})
   199  	if err != nil {
   200  		// Failed to create newbucket, abort.
   201  		t.Fatalf("%s : %s", instanceType, err.Error())
   202  	}
   203  
   204  	resN, err := obj.NewMultipartUpload(context.Background(), "abc", "def", opts)
   205  	if err != nil {
   206  		// Failed to create NewMultipartUpload, abort.
   207  		t.Fatalf("%s : %s", instanceType, err.Error())
   208  	}
   209  
   210  	uploadID := res.UploadID
   211  	// Creating a dummy bucket for tests.
   212  	err = obj.MakeBucket(context.Background(), "unused-bucket", MakeBucketOptions{})
   213  	if err != nil {
   214  		// Failed to create newbucket, abort.
   215  		t.Fatalf("%s : %s", instanceType, err.Error())
   216  	}
   217  
   218  	obj.DeleteBucket(context.Background(), "abc", DeleteBucketOptions{})
   219  
   220  	// Collection of non-exhaustive PutObjectPart test cases, valid errors
   221  	// and success responses.
   222  	testCases := []struct {
   223  		bucketName      string
   224  		objName         string
   225  		uploadID        string
   226  		PartID          int
   227  		inputReaderData string
   228  		inputMd5        string
   229  		inputSHA256     string
   230  		inputDataSize   int64
   231  		// flag indicating whether the test should pass.
   232  		shouldPass bool
   233  		// expected error output.
   234  		expectedMd5   string
   235  		expectedError error
   236  	}{
   237  		// Test case  1-4.
   238  		// Cases with invalid bucket name.
   239  		{bucketName: ".test", objName: "obj", PartID: 1, expectedError: fmt.Errorf("%s", "Bucket name invalid: .test")},
   240  		{bucketName: "------", objName: "obj", PartID: 1, expectedError: fmt.Errorf("%s", "Bucket name invalid: ------")},
   241  		{
   242  			bucketName: "$this-is-not-valid-too", objName: "obj", PartID: 1,
   243  			expectedError: fmt.Errorf("%s", "Bucket name invalid: $this-is-not-valid-too"),
   244  		},
   245  		{bucketName: "a", objName: "obj", PartID: 1, expectedError: fmt.Errorf("%s", "Bucket name invalid: a")},
   246  		// Test case - 5.
   247  		// Case with invalid object names.
   248  		{bucketName: bucket, PartID: 1, expectedError: fmt.Errorf("%s", "Object name invalid: minio-bucket/")},
   249  		// Test case - 6.
   250  		// Valid object and bucket names but non-existent bucket.
   251  		{bucketName: "abc", objName: "def", uploadID: resN.UploadID, PartID: 1, expectedError: fmt.Errorf("%s", "Bucket not found: abc")},
   252  		// Test Case - 7.
   253  		// Existing bucket, but using a bucket on which NewMultipartUpload is not Initiated.
   254  		{bucketName: "unused-bucket", objName: "def", uploadID: "xyz", PartID: 1, expectedError: fmt.Errorf("%s", "Invalid upload id xyz")},
   255  		// Test Case - 8.
   256  		// Existing bucket, object name different from which NewMultipartUpload is constructed from.
   257  		// Expecting "Invalid upload id".
   258  		{bucketName: bucket, objName: "def", uploadID: "xyz", PartID: 1, expectedError: fmt.Errorf("%s", "Invalid upload id xyz")},
   259  		// Test Case - 9.
   260  		// Existing bucket, bucket and object name are the ones from which NewMultipartUpload is constructed from.
   261  		// But the uploadID is invalid.
   262  		// Expecting "Invalid upload id".
   263  		{bucketName: bucket, objName: object, uploadID: "xyz", PartID: 1, expectedError: fmt.Errorf("%s", "Invalid upload id xyz")},
   264  		// Test Case - 10.
   265  		// Case with valid UploadID, existing bucket name.
   266  		// But using the bucket name from which NewMultipartUpload is not constructed from.
   267  		{bucketName: "unused-bucket", objName: object, uploadID: uploadID, PartID: 1, expectedError: fmt.Errorf("%s", "Invalid upload id "+uploadID)},
   268  		// Test Case - 11.
   269  		// Case with valid UploadID, existing bucket name.
   270  		// But using the object name from which NewMultipartUpload is not constructed from.
   271  		{bucketName: bucket, objName: "none-object", uploadID: uploadID, PartID: 1, expectedError: fmt.Errorf("%s", "Invalid upload id "+uploadID)},
   272  		// Test case - 12.
   273  		// Input to replicate Md5 mismatch.
   274  		{
   275  			bucketName: bucket, objName: object, uploadID: uploadID, PartID: 1, inputMd5: "d41d8cd98f00b204e9800998ecf8427f",
   276  			expectedError: hash.BadDigest{ExpectedMD5: "d41d8cd98f00b204e9800998ecf8427f", CalculatedMD5: "d41d8cd98f00b204e9800998ecf8427e"},
   277  		},
   278  		// Test case - 13.
   279  		// When incorrect sha256 is provided.
   280  		{
   281  			bucketName: bucket, objName: object, uploadID: uploadID, PartID: 1, inputSHA256: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b854",
   282  			expectedError: hash.SHA256Mismatch{
   283  				ExpectedSHA256:   "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b854",
   284  				CalculatedSHA256: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
   285  			},
   286  		},
   287  		// Test case - 14.
   288  		// Input with size more than the size of actual data inside the reader.
   289  		{
   290  			bucketName: bucket, objName: object, uploadID: uploadID, PartID: 1, inputReaderData: "abcd", inputMd5: "e2fc714c4727ee9395f324cd2e7f3335", inputDataSize: int64(len("abcd") + 1),
   291  			expectedError: hash.BadDigest{ExpectedMD5: "e2fc714c4727ee9395f324cd2e7f3335", CalculatedMD5: "e2fc714c4727ee9395f324cd2e7f331f"},
   292  		},
   293  		// Test case - 15.
   294  		// Input with size less than the size of actual data inside the reader.
   295  		{
   296  			bucketName: bucket, objName: object, uploadID: uploadID, PartID: 1, inputReaderData: "abcd", inputMd5: "900150983cd24fb0d6963f7d28e17f73", inputDataSize: int64(len("abcd") - 1),
   297  			expectedError: ioutil.ErrOverread,
   298  		},
   299  
   300  		// Test case - 16-19.
   301  		// Validating for success cases.
   302  		{bucketName: bucket, objName: object, uploadID: uploadID, PartID: 1, inputReaderData: "abcd", inputMd5: "e2fc714c4727ee9395f324cd2e7f331f", inputSHA256: "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589", inputDataSize: int64(len("abcd")), shouldPass: true},
   303  		{bucketName: bucket, objName: object, uploadID: uploadID, PartID: 2, inputReaderData: "efgh", inputMd5: "1f7690ebdd9b4caf8fab49ca1757bf27", inputSHA256: "e5e088a0b66163a0a26a5e053d2a4496dc16ab6e0e3dd1adf2d16aa84a078c9d", inputDataSize: int64(len("efgh")), shouldPass: true},
   304  		{bucketName: bucket, objName: object, uploadID: uploadID, PartID: 3, inputReaderData: "ijkl", inputMd5: "09a0877d04abf8759f99adec02baf579", inputSHA256: "005c19658919186b85618c5870463eec8d9b8c1a9d00208a5352891ba5bbe086", inputDataSize: int64(len("abcd")), shouldPass: true},
   305  		{bucketName: bucket, objName: object, uploadID: uploadID, PartID: 4, inputReaderData: "mnop", inputMd5: "e132e96a5ddad6da8b07bba6f6131fef", inputSHA256: "f1afc31479522d6cff1ed068f93998f05a8cd3b22f5c37d7f307084f62d1d270", inputDataSize: int64(len("abcd")), shouldPass: true},
   306  	}
   307  
   308  	// Validate all the test cases.
   309  	for i, testCase := range testCases {
   310  		actualInfo, actualErr := obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetPutObjReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.inputDataSize, testCase.inputMd5, testCase.inputSHA256), opts)
   311  		// All are test cases above are expected to fail.
   312  		if actualErr != nil && testCase.shouldPass {
   313  			t.Errorf("Test %d: %s: Expected to pass, but failed with: <ERROR> %s.", i+1, instanceType, actualErr.Error())
   314  		}
   315  		if actualErr == nil && !testCase.shouldPass {
   316  			t.Errorf("Test %d: %s: Expected to fail with <ERROR> \"%s\", but passed instead.", i+1, instanceType, testCase.expectedError.Error())
   317  		}
   318  		// Failed as expected, but does it fail for the expected reason.
   319  		if actualErr != nil && !testCase.shouldPass {
   320  			if testCase.expectedError.Error() != actualErr.Error() {
   321  				t.Errorf("Test %d: %s: Expected to fail with error \"%s\", but instead failed with error \"%s\" instead.", i+1, instanceType, testCase.expectedError.Error(), actualErr.Error())
   322  			}
   323  		}
   324  		// Test passes as expected, but the output values are verified for correctness here.
   325  		if actualErr == nil && testCase.shouldPass {
   326  			// Asserting whether the md5 output is correct.
   327  			if testCase.inputMd5 != actualInfo.ETag {
   328  				t.Errorf("Test %d: %s: Calculated Md5 different from the actual one %s.", i+1, instanceType, actualInfo.ETag)
   329  			}
   330  		}
   331  	}
   332  }
   333  
   334  // Wrapper for calling TestListMultipartUploads tests for both Erasure multiple disks and single node setup.
   335  func TestListMultipartUploads(t *testing.T) {
   336  	ExecExtendedObjectLayerTest(t, testListMultipartUploads)
   337  }
   338  
   339  // testListMultipartUploads - Tests validate listing of multipart uploads.
   340  func testListMultipartUploads(obj ObjectLayer, instanceType string, t TestErrHandler) {
   341  	bucketNames := []string{"minio-bucket", "minio-2-bucket", "minio-3-bucket"}
   342  	objectNames := []string{"minio-object-1.txt", "minio-object.txt", "neymar-1.jpeg", "neymar.jpeg", "parrot-1.png", "parrot.png"}
   343  	uploadIDs := []string{}
   344  	opts := ObjectOptions{}
   345  	// bucketnames[0].
   346  	// objectNames[0].
   347  	// uploadIds [0].
   348  	// Create bucket before initiating NewMultipartUpload.
   349  	err := obj.MakeBucket(context.Background(), bucketNames[0], MakeBucketOptions{})
   350  	if err != nil {
   351  		// Failed to create newbucket, abort.
   352  		t.Fatalf("%s : %s", instanceType, err.Error())
   353  	}
   354  	// Initiate Multipart Upload on the above created bucket.
   355  	res, err := obj.NewMultipartUpload(context.Background(), bucketNames[0], objectNames[0], opts)
   356  	if err != nil {
   357  		// Failed to create NewMultipartUpload, abort.
   358  		t.Fatalf("%s : %s", instanceType, err.Error())
   359  	}
   360  
   361  	uploadIDs = append(uploadIDs, res.UploadID)
   362  
   363  	// bucketnames[1].
   364  	// objectNames[0].
   365  	// uploadIds [1-3].
   366  	// Bucket to test for multiple upload Id's for a given object.
   367  	err = obj.MakeBucket(context.Background(), bucketNames[1], MakeBucketOptions{})
   368  	if err != nil {
   369  		// Failed to create newbucket, abort.
   370  		t.Fatalf("%s : %s", instanceType, err.Error())
   371  	}
   372  	for i := 0; i < 3; i++ {
   373  		// Initiate Multipart Upload on bucketNames[1] for the same object 3 times.
   374  		//  Used to test the listing for the case of multiple uploadID's for a given object.
   375  		res, err = obj.NewMultipartUpload(context.Background(), bucketNames[1], objectNames[0], opts)
   376  		if err != nil {
   377  			// Failed to create NewMultipartUpload, abort.
   378  			t.Fatalf("%s : %s", instanceType, err.Error())
   379  		}
   380  
   381  		uploadIDs = append(uploadIDs, res.UploadID)
   382  	}
   383  
   384  	// Bucket to test for multiple objects, each with unique UUID.
   385  	// bucketnames[2].
   386  	// objectNames[0-2].
   387  	// uploadIds [4-9].
   388  	err = obj.MakeBucket(context.Background(), bucketNames[2], MakeBucketOptions{})
   389  	if err != nil {
   390  		// Failed to create newbucket, abort.
   391  		t.Fatalf("%s : %s", instanceType, err.Error())
   392  	}
   393  	// Initiate Multipart Upload on bucketNames[2].
   394  	//  Used to test the listing for the case of multiple objects for a given bucket.
   395  	for i := 0; i < 6; i++ {
   396  		res, err = obj.NewMultipartUpload(context.Background(), bucketNames[2], objectNames[i], opts)
   397  		if err != nil {
   398  			// Failed to create NewMultipartUpload, abort.
   399  			t.Fatalf("%s : %s", instanceType, err.Error())
   400  		}
   401  		// uploadIds [4-9].
   402  		uploadIDs = append(uploadIDs, res.UploadID)
   403  	}
   404  	// Create multipart parts.
   405  	// Need parts to be uploaded before MultipartLists can be called and tested.
   406  	createPartCases := []struct {
   407  		bucketName      string
   408  		objName         string
   409  		uploadID        string
   410  		PartID          int
   411  		inputReaderData string
   412  		inputMd5        string
   413  		inputDataSize   int64
   414  		expectedMd5     string
   415  	}{
   416  		// Case 1-4.
   417  		// Creating sequence of parts for same uploadID.
   418  		// Used to ensure that the ListMultipartResult produces one output for the four parts uploaded below for the given upload ID.
   419  		{bucketNames[0], objectNames[0], uploadIDs[0], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"},
   420  		{bucketNames[0], objectNames[0], uploadIDs[0], 2, "efgh", "1f7690ebdd9b4caf8fab49ca1757bf27", int64(len("efgh")), "1f7690ebdd9b4caf8fab49ca1757bf27"},
   421  		{bucketNames[0], objectNames[0], uploadIDs[0], 3, "ijkl", "09a0877d04abf8759f99adec02baf579", int64(len("abcd")), "09a0877d04abf8759f99adec02baf579"},
   422  		{bucketNames[0], objectNames[0], uploadIDs[0], 4, "mnop", "e132e96a5ddad6da8b07bba6f6131fef", int64(len("abcd")), "e132e96a5ddad6da8b07bba6f6131fef"},
   423  		// Cases 5-7.
   424  		// Create parts with 3 uploadID's for the same object.
   425  		// Testing for listing of all the uploadID's for given object.
   426  		// Insertion with 3 different uploadID's are done for same bucket and object.
   427  		{bucketNames[1], objectNames[0], uploadIDs[1], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"},
   428  		{bucketNames[1], objectNames[0], uploadIDs[2], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"},
   429  		{bucketNames[1], objectNames[0], uploadIDs[3], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"},
   430  		// Case 8-13.
   431  		// Generating parts for different objects.
   432  		{bucketNames[2], objectNames[0], uploadIDs[4], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"},
   433  		{bucketNames[2], objectNames[1], uploadIDs[5], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"},
   434  		{bucketNames[2], objectNames[2], uploadIDs[6], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"},
   435  		{bucketNames[2], objectNames[3], uploadIDs[7], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"},
   436  		{bucketNames[2], objectNames[4], uploadIDs[8], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"},
   437  		{bucketNames[2], objectNames[5], uploadIDs[9], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"},
   438  	}
   439  	sha256sum := ""
   440  	// Iterating over creatPartCases to generate multipart chunks.
   441  	for _, testCase := range createPartCases {
   442  		_, err := obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetPutObjReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.inputDataSize, testCase.inputMd5, sha256sum), opts)
   443  		if err != nil {
   444  			t.Fatalf("%s : %s", instanceType, err.Error())
   445  		}
   446  
   447  	}
   448  
   449  	// Expected Results set for asserting ListObjectMultipart test.
   450  	listMultipartResults := []ListMultipartsInfo{
   451  		// listMultipartResults - 1.
   452  		// Used to check that the result produces only one output for the 4 parts uploaded in cases 1-4 of createPartCases above.
   453  		// ListMultipartUploads doesn't list the parts.
   454  		{
   455  			MaxUploads: 100,
   456  			Uploads: []MultipartInfo{
   457  				{
   458  					Object:   objectNames[0],
   459  					UploadID: uploadIDs[0],
   460  				},
   461  			},
   462  		},
   463  		// listMultipartResults - 2.
   464  		// Used to check that the result produces if keyMarker is set to the only available object.
   465  		// `KeyMarker` is set.
   466  		// ListMultipartUploads doesn't list the parts.
   467  		{
   468  			MaxUploads: 100,
   469  			KeyMarker:  "minio-object-1.txt",
   470  		},
   471  		// listMultipartResults - 3.
   472  		// `KeyMarker` is set, no MultipartInfo expected.
   473  		// ListMultipartUploads doesn't list the parts.
   474  		// `Maxupload` value is asserted.
   475  		{
   476  			MaxUploads: 100,
   477  			KeyMarker:  "orange",
   478  		},
   479  		// listMultipartResults - 4.
   480  		// `KeyMarker` is set, no MultipartInfo expected.
   481  		// Maxupload value is asserted.
   482  		{
   483  			MaxUploads: 1,
   484  			KeyMarker:  "orange",
   485  		},
   486  		// listMultipartResults - 5.
   487  		// `KeyMarker` is set. It contains part of the objectname as `KeyPrefix`.
   488  		// Expecting the result to contain one MultipartInfo entry and Istruncated to be false.
   489  		{
   490  			MaxUploads:  10,
   491  			KeyMarker:   "min",
   492  			IsTruncated: false,
   493  			Uploads: []MultipartInfo{
   494  				{
   495  					Object:   objectNames[0],
   496  					UploadID: uploadIDs[0],
   497  				},
   498  			},
   499  		},
   500  		// listMultipartResults - 6.
   501  		// `KeyMarker` is set. It contains part of the objectname as `KeyPrefix`.
   502  		// `MaxUploads` is set equal to the number of meta data entries in the result, the result contains only one entry.
   503  		// Expecting the result to contain one MultipartInfo entry and IsTruncated to be false.
   504  		{
   505  			MaxUploads:  1,
   506  			KeyMarker:   "min",
   507  			IsTruncated: false,
   508  			Uploads: []MultipartInfo{
   509  				{
   510  					Object:   objectNames[0],
   511  					UploadID: uploadIDs[0],
   512  				},
   513  			},
   514  		},
   515  		// listMultipartResults - 7.
   516  		// `KeyMarker` is set. It contains part of the objectname as `KeyPrefix`.
   517  		// Testing for the case with `MaxUploads` set to 0.
   518  		// Expecting the result to contain no MultipartInfo entry since `MaxUploads` is set to 0.
   519  		// Expecting `IsTruncated` to be true.
   520  		{
   521  			MaxUploads:  0,
   522  			KeyMarker:   "min",
   523  			IsTruncated: true,
   524  		},
   525  		// listMultipartResults - 8.
   526  		// `KeyMarker` is set. It contains part of the objectname as KeyPrefix.
   527  		// Testing for the case with `MaxUploads` set to 0.
   528  		// Expecting the result to contain no MultipartInfo entry since `MaxUploads` is set to 0.
   529  		// Expecting `isTruncated` to be true.
   530  		{
   531  			MaxUploads:  0,
   532  			KeyMarker:   "min",
   533  			IsTruncated: true,
   534  		},
   535  		// listMultipartResults - 9.
   536  		// `KeyMarker` is set. It contains part of the objectname as KeyPrefix.
   537  		// `KeyMarker` is set equal to the object name in the result.
   538  		// Expecting the result to contain one MultipartInfo entry and IsTruncated to be false.
   539  		{
   540  			MaxUploads:  2,
   541  			KeyMarker:   "minio-object",
   542  			IsTruncated: false,
   543  			Uploads: []MultipartInfo{
   544  				{
   545  					Object:   objectNames[0],
   546  					UploadID: uploadIDs[0],
   547  				},
   548  			},
   549  		},
   550  		// listMultipartResults - 10.
   551  		// Prefix is set. It is set equal to the object name.
   552  		// MaxUploads is set more than number of meta data entries in the result.
   553  		// Expecting the result to contain one MultipartInfo entry and IsTruncated to be false.
   554  		{
   555  			MaxUploads:  2,
   556  			Prefix:      "minio-object-1.txt",
   557  			IsTruncated: false,
   558  			Uploads: []MultipartInfo{
   559  				{
   560  					Object:   objectNames[0],
   561  					UploadID: uploadIDs[0],
   562  				},
   563  			},
   564  		},
   565  		// listMultipartResults - 11.
   566  		// Setting `Prefix` to contain the object name as its prefix.
   567  		// MaxUploads is set more than number of meta data entries in the result.
   568  		// Expecting the result to contain one MultipartInfo entry and IsTruncated to be false.
   569  		{
   570  			MaxUploads:  2,
   571  			Prefix:      "min",
   572  			IsTruncated: false,
   573  			Uploads: []MultipartInfo{
   574  				{
   575  					Object:   objectNames[0],
   576  					UploadID: uploadIDs[0],
   577  				},
   578  			},
   579  		},
   580  		// listMultipartResults - 12.
   581  		// Setting `Prefix` to contain the object name as its prefix.
   582  		// MaxUploads is set equal to number of meta data entries in the result.
   583  		// Expecting the result to contain one MultipartInfo entry and IsTruncated to be false.
   584  		{
   585  			MaxUploads:  1,
   586  			Prefix:      "min",
   587  			IsTruncated: false,
   588  			Uploads: []MultipartInfo{
   589  				{
   590  					Object:   objectNames[0],
   591  					UploadID: uploadIDs[0],
   592  				},
   593  			},
   594  		},
   595  		// listMultipartResults - 13.
   596  		// `Prefix` is set. It doesn't contain object name as its preifx.
   597  		// MaxUploads is set more than number of meta data entries in the result.
   598  		// Expecting no `Uploads` metadata.
   599  		{
   600  			MaxUploads:  2,
   601  			Prefix:      "orange",
   602  			IsTruncated: false,
   603  		},
   604  		// listMultipartResults - 14.
   605  		// `Prefix` is set. It doesn't contain object name as its preifx.
   606  		// MaxUploads is set more than number of meta data entries in the result.
   607  		// Expecting the result to contain 0 uploads and isTruncated to false.
   608  		{
   609  			MaxUploads:  2,
   610  			Prefix:      "Asia",
   611  			IsTruncated: false,
   612  		},
   613  		// listMultipartResults - 15.
   614  		// Setting `Delimiter`.
   615  		// MaxUploads is set more than number of meta data entries in the result.
   616  		// Expecting the result to contain one MultipartInfo entry and IsTruncated to be false.
   617  		{
   618  			MaxUploads:  2,
   619  			Delimiter:   SlashSeparator,
   620  			Prefix:      "",
   621  			IsTruncated: false,
   622  			Uploads: []MultipartInfo{
   623  				{
   624  					Object:   objectNames[0],
   625  					UploadID: uploadIDs[0],
   626  				},
   627  			},
   628  		},
   629  		// listMultipartResults - 16.
   630  		// Testing for listing of 3 uploadID's for a given object.
   631  		// Will be used to list on bucketNames[1].
   632  		{
   633  			MaxUploads: 100,
   634  			Uploads: []MultipartInfo{
   635  				{
   636  					Object:   objectNames[0],
   637  					UploadID: uploadIDs[1],
   638  				},
   639  				{
   640  					Object:   objectNames[0],
   641  					UploadID: uploadIDs[2],
   642  				},
   643  				{
   644  					Object:   objectNames[0],
   645  					UploadID: uploadIDs[3],
   646  				},
   647  			},
   648  		},
   649  		// listMultipartResults - 17.
   650  		// Testing for listing of 3 uploadID's (uploadIDs[1-3]) for a given object with uploadID Marker set.
   651  		// uploadIDs[1] is set as UploadMarker, Expecting it to be skipped in the result.
   652  		// uploadIDs[2] and uploadIDs[3] are expected to be in the result.
   653  		// Istruncted is expected to be false.
   654  		// Will be used to list on bucketNames[1].
   655  		{
   656  			MaxUploads:     100,
   657  			KeyMarker:      "minio-object-1.txt",
   658  			UploadIDMarker: uploadIDs[1],
   659  			IsTruncated:    false,
   660  			Uploads: []MultipartInfo{
   661  				{
   662  					Object:   objectNames[0],
   663  					UploadID: uploadIDs[2],
   664  				},
   665  				{
   666  					Object:   objectNames[0],
   667  					UploadID: uploadIDs[3],
   668  				},
   669  			},
   670  		},
   671  		// listMultipartResults - 18.
   672  		// Testing for listing of 3 uploadID's (uploadIDs[1-3])  for a given object with uploadID Marker set.
   673  		// uploadIDs[2] is set as UploadMarker, Expecting it to be skipped in the result.
   674  		// Only uploadIDs[3] are expected to be in the result.
   675  		// Istruncted is expected to be false.
   676  		// Will be used to list on bucketNames[1].
   677  		{
   678  			MaxUploads:     100,
   679  			KeyMarker:      "minio-object-1.txt",
   680  			UploadIDMarker: uploadIDs[2],
   681  			IsTruncated:    false,
   682  			Uploads: []MultipartInfo{
   683  				{
   684  					Object:   objectNames[0],
   685  					UploadID: uploadIDs[3],
   686  				},
   687  			},
   688  		},
   689  		// listMultipartResults - 19.
   690  		// Testing for listing of 3 uploadID's for a given object, setting maxKeys to be 2.
   691  		// There are 3 MultipartInfo in the result (uploadIDs[1-3]), it should be truncated to 2.
   692  		// Since there is only single object for bucketNames[1], the NextKeyMarker is set to its name.
   693  		// The last entry in the result, uploadIDs[2], that is should be set as NextUploadIDMarker.
   694  		// Will be used to list on bucketNames[1].
   695  		{
   696  			MaxUploads:         2,
   697  			IsTruncated:        true,
   698  			NextKeyMarker:      objectNames[0],
   699  			NextUploadIDMarker: uploadIDs[2],
   700  			Uploads: []MultipartInfo{
   701  				{
   702  					Object:   objectNames[0],
   703  					UploadID: uploadIDs[1],
   704  				},
   705  				{
   706  					Object:   objectNames[0],
   707  					UploadID: uploadIDs[2],
   708  				},
   709  			},
   710  		},
   711  		// listMultipartResults - 20.
   712  		// Testing for listing of 3 uploadID's for a given object, setting maxKeys to be 1.
   713  		// There are 3 MultipartInfo in the result (uploadIDs[1-3]), it should be truncated to 1.
   714  		// The last entry in the result, uploadIDs[1], that is should be set as NextUploadIDMarker.
   715  		// Will be used to list on bucketNames[1].
   716  		{
   717  			MaxUploads:         1,
   718  			IsTruncated:        true,
   719  			NextKeyMarker:      objectNames[0],
   720  			NextUploadIDMarker: uploadIDs[1],
   721  			Uploads: []MultipartInfo{
   722  				{
   723  					Object:   objectNames[0],
   724  					UploadID: uploadIDs[1],
   725  				},
   726  			},
   727  		},
   728  		// listMultipartResults - 21.
   729  		// Testing for listing of 3 uploadID's for a given object, setting maxKeys to be 3.
   730  		// There are 3 MultipartInfo in the result (uploadIDs[1-3]), hence no truncation is expected.
   731  		// Since all the MultipartInfo is listed, expecting no values for NextUploadIDMarker and NextKeyMarker.
   732  		// Will be used to list on bucketNames[1].
   733  		{
   734  			MaxUploads:  3,
   735  			IsTruncated: false,
   736  			Uploads: []MultipartInfo{
   737  				{
   738  					Object:   objectNames[0],
   739  					UploadID: uploadIDs[1],
   740  				},
   741  				{
   742  					Object:   objectNames[0],
   743  					UploadID: uploadIDs[2],
   744  				},
   745  				{
   746  					Object:   objectNames[0],
   747  					UploadID: uploadIDs[3],
   748  				},
   749  			},
   750  		},
   751  		// listMultipartResults - 22.
   752  		// Testing for listing of 3 uploadID's for a given object, setting `prefix` to be "min".
   753  		// Will be used to list on bucketNames[1].
   754  		{
   755  			MaxUploads:  10,
   756  			IsTruncated: false,
   757  			Prefix:      "min",
   758  			Uploads: []MultipartInfo{
   759  				{
   760  					Object:   objectNames[0],
   761  					UploadID: uploadIDs[1],
   762  				},
   763  				{
   764  					Object:   objectNames[0],
   765  					UploadID: uploadIDs[2],
   766  				},
   767  				{
   768  					Object:   objectNames[0],
   769  					UploadID: uploadIDs[3],
   770  				},
   771  			},
   772  		},
   773  		// listMultipartResults - 23.
   774  		// Testing for listing of 3 uploadID's for a given object
   775  		// setting `prefix` to be "orange".
   776  		// Will be used to list on bucketNames[1].
   777  		{
   778  			MaxUploads:  10,
   779  			IsTruncated: false,
   780  			Prefix:      "orange",
   781  		},
   782  		// listMultipartResults - 24.
   783  		// Testing for listing of 3 uploadID's for a given object.
   784  		// setting `prefix` to be "Asia".
   785  		// Will be used to list on bucketNames[1].
   786  		{
   787  			MaxUploads:  10,
   788  			IsTruncated: false,
   789  			Prefix:      "Asia",
   790  		},
   791  		// listMultipartResults - 25.
   792  		// Testing for listing of 3 uploadID's for a given object.
   793  		// setting `prefix` and uploadIDMarker.
   794  		// Will be used to list on bucketNames[1].
   795  		{
   796  			MaxUploads:     10,
   797  			KeyMarker:      "minio-object-1.txt",
   798  			IsTruncated:    false,
   799  			Prefix:         "min",
   800  			UploadIDMarker: uploadIDs[1],
   801  			Uploads: []MultipartInfo{
   802  				{
   803  					Object:   objectNames[0],
   804  					UploadID: uploadIDs[2],
   805  				},
   806  				{
   807  					Object:   objectNames[0],
   808  					UploadID: uploadIDs[3],
   809  				},
   810  			},
   811  		},
   812  
   813  		// Operations on bucket 2.
   814  		// listMultipartResults - 26.
   815  		// checking listing everything.
   816  		{
   817  			MaxUploads:  100,
   818  			IsTruncated: false,
   819  
   820  			Uploads: []MultipartInfo{
   821  				{
   822  					Object:   objectNames[0],
   823  					UploadID: uploadIDs[4],
   824  				},
   825  				{
   826  					Object:   objectNames[1],
   827  					UploadID: uploadIDs[5],
   828  				},
   829  				{
   830  					Object:   objectNames[2],
   831  					UploadID: uploadIDs[6],
   832  				},
   833  				{
   834  					Object:   objectNames[3],
   835  					UploadID: uploadIDs[7],
   836  				},
   837  				{
   838  					Object:   objectNames[4],
   839  					UploadID: uploadIDs[8],
   840  				},
   841  				{
   842  					Object:   objectNames[5],
   843  					UploadID: uploadIDs[9],
   844  				},
   845  			},
   846  		},
   847  		// listMultipartResults - 27.
   848  		//  listing with `prefix` "min".
   849  		{
   850  			MaxUploads:  100,
   851  			IsTruncated: false,
   852  			Prefix:      "min",
   853  			Uploads: []MultipartInfo{
   854  				{
   855  					Object:   objectNames[0],
   856  					UploadID: uploadIDs[4],
   857  				},
   858  				{
   859  					Object:   objectNames[1],
   860  					UploadID: uploadIDs[5],
   861  				},
   862  			},
   863  		},
   864  		// listMultipartResults - 28.
   865  		//  listing with `prefix` "ney".
   866  		{
   867  			MaxUploads:  100,
   868  			IsTruncated: false,
   869  			Prefix:      "ney",
   870  			Uploads: []MultipartInfo{
   871  				{
   872  					Object:   objectNames[2],
   873  					UploadID: uploadIDs[6],
   874  				},
   875  				{
   876  					Object:   objectNames[3],
   877  					UploadID: uploadIDs[7],
   878  				},
   879  			},
   880  		},
   881  		// listMultipartResults - 29.
   882  		//  listing with `prefix` "parrot".
   883  		{
   884  			MaxUploads:  100,
   885  			IsTruncated: false,
   886  			Prefix:      "parrot",
   887  			Uploads: []MultipartInfo{
   888  				{
   889  					Object:   objectNames[4],
   890  					UploadID: uploadIDs[8],
   891  				},
   892  				{
   893  					Object:   objectNames[5],
   894  					UploadID: uploadIDs[9],
   895  				},
   896  			},
   897  		},
   898  		// listMultipartResults - 30.
   899  		//  listing with `prefix` "neymar.jpeg".
   900  		// prefix set to object name.
   901  		{
   902  			MaxUploads:  100,
   903  			IsTruncated: false,
   904  			Prefix:      "neymar.jpeg",
   905  			Uploads: []MultipartInfo{
   906  				{
   907  					Object:   objectNames[3],
   908  					UploadID: uploadIDs[7],
   909  				},
   910  			},
   911  		},
   912  
   913  		// listMultipartResults - 31.
   914  		// checking listing with marker set to 3.
   915  		// `NextUploadIDMarker` is expected to be set on last uploadID in the result.
   916  		// `NextKeyMarker` is expected to be set on the last object key in the list.
   917  		{
   918  			MaxUploads:         3,
   919  			IsTruncated:        true,
   920  			NextUploadIDMarker: uploadIDs[6],
   921  			NextKeyMarker:      objectNames[2],
   922  			Uploads: []MultipartInfo{
   923  				{
   924  					Object:   objectNames[0],
   925  					UploadID: uploadIDs[4],
   926  				},
   927  				{
   928  					Object:   objectNames[1],
   929  					UploadID: uploadIDs[5],
   930  				},
   931  				{
   932  					Object:   objectNames[2],
   933  					UploadID: uploadIDs[6],
   934  				},
   935  			},
   936  		},
   937  		// listMultipartResults - 32.
   938  		// checking listing with marker set to no of objects in the list.
   939  		// `NextUploadIDMarker` is expected to be empty since all results are listed.
   940  		// `NextKeyMarker` is expected to be empty since all results are listed.
   941  		{
   942  			MaxUploads:  6,
   943  			IsTruncated: false,
   944  			Uploads: []MultipartInfo{
   945  				{
   946  					Object:   objectNames[0],
   947  					UploadID: uploadIDs[4],
   948  				},
   949  				{
   950  					Object:   objectNames[1],
   951  					UploadID: uploadIDs[5],
   952  				},
   953  				{
   954  					Object:   objectNames[2],
   955  					UploadID: uploadIDs[6],
   956  				},
   957  				{
   958  					Object:   objectNames[3],
   959  					UploadID: uploadIDs[7],
   960  				},
   961  				{
   962  					Object:   objectNames[4],
   963  					UploadID: uploadIDs[8],
   964  				},
   965  				{
   966  					Object:   objectNames[5],
   967  					UploadID: uploadIDs[9],
   968  				},
   969  			},
   970  		},
   971  		// listMultipartResults - 33.
   972  		// checking listing with `UploadIDMarker` set.
   973  		{
   974  			MaxUploads:     10,
   975  			IsTruncated:    false,
   976  			UploadIDMarker: uploadIDs[6],
   977  			Uploads: []MultipartInfo{
   978  				{
   979  					Object:   objectNames[3],
   980  					UploadID: uploadIDs[7],
   981  				},
   982  				{
   983  					Object:   objectNames[4],
   984  					UploadID: uploadIDs[8],
   985  				},
   986  				{
   987  					Object:   objectNames[5],
   988  					UploadID: uploadIDs[9],
   989  				},
   990  			},
   991  		},
   992  		// listMultipartResults - 34.
   993  		// checking listing with `KeyMarker` set.
   994  		{
   995  			MaxUploads:  10,
   996  			IsTruncated: false,
   997  			KeyMarker:   objectNames[3],
   998  			Uploads: []MultipartInfo{
   999  				{
  1000  					Object:   objectNames[4],
  1001  					UploadID: uploadIDs[8],
  1002  				},
  1003  				{
  1004  					Object:   objectNames[5],
  1005  					UploadID: uploadIDs[9],
  1006  				},
  1007  			},
  1008  		},
  1009  		// listMultipartResults - 35.
  1010  		// Checking listing with `Prefix` and `KeyMarker`.
  1011  		// No upload MultipartInfo in the result expected since KeyMarker is set to last Key in the result.
  1012  		{
  1013  			MaxUploads:  10,
  1014  			IsTruncated: false,
  1015  			Prefix:      "minio-object",
  1016  			KeyMarker:   objectNames[1],
  1017  		},
  1018  		// listMultipartResults - 36.
  1019  		// checking listing with `Prefix` and `UploadIDMarker` set.
  1020  		{
  1021  			MaxUploads:     10,
  1022  			IsTruncated:    false,
  1023  			Prefix:         "minio",
  1024  			UploadIDMarker: uploadIDs[4],
  1025  			Uploads: []MultipartInfo{
  1026  				{
  1027  					Object:   objectNames[1],
  1028  					UploadID: uploadIDs[5],
  1029  				},
  1030  			},
  1031  		},
  1032  		// listMultipartResults - 37.
  1033  		// Checking listing with `KeyMarker` and `UploadIDMarker` set.
  1034  		{
  1035  			MaxUploads:     10,
  1036  			IsTruncated:    false,
  1037  			KeyMarker:      "minio-object.txt",
  1038  			UploadIDMarker: uploadIDs[5],
  1039  		},
  1040  	}
  1041  
  1042  	// Collection of non-exhaustive ListMultipartUploads test cases, valid errors
  1043  	// and success responses.
  1044  	testCases := []struct {
  1045  		// Inputs to ListMultipartUploads.
  1046  		bucket         string
  1047  		prefix         string
  1048  		keyMarker      string
  1049  		uploadIDMarker string
  1050  		delimiter      string
  1051  		maxUploads     int
  1052  		// Expected output of ListMultipartUploads.
  1053  		expectedResult ListMultipartsInfo
  1054  		expectedErr    error
  1055  		// Flag indicating whether the test is expected to pass or not.
  1056  		shouldPass bool
  1057  	}{
  1058  		// Test cases with invalid bucket names ( Test number 1-4 ).
  1059  		{".test", "", "", "", "", 0, ListMultipartsInfo{}, BucketNameInvalid{Bucket: ".test"}, false},
  1060  		{"Test", "", "", "", "", 0, ListMultipartsInfo{}, BucketNameInvalid{Bucket: "Test"}, false},
  1061  		{"---", "", "", "", "", 0, ListMultipartsInfo{}, BucketNameInvalid{Bucket: "---"}, false},
  1062  		{"ad", "", "", "", "", 0, ListMultipartsInfo{}, BucketNameInvalid{Bucket: "ad"}, false},
  1063  		// Valid bucket names, but they do not exist (Test number 5-7).
  1064  		{"volatile-bucket-1", "", "", "", "", 0, ListMultipartsInfo{}, BucketNotFound{Bucket: "volatile-bucket-1"}, false},
  1065  		{"volatile-bucket-2", "", "", "", "", 0, ListMultipartsInfo{}, BucketNotFound{Bucket: "volatile-bucket-2"}, false},
  1066  		{"volatile-bucket-3", "", "", "", "", 0, ListMultipartsInfo{}, BucketNotFound{Bucket: "volatile-bucket-3"}, false},
  1067  		// Valid, existing bucket, delimiter not supported, returns empty values (Test number 8-9).
  1068  		{bucketNames[0], "", "", "", "*", 0, ListMultipartsInfo{Delimiter: "*"}, nil, true},
  1069  		{bucketNames[0], "", "", "", "-", 0, ListMultipartsInfo{Delimiter: "-"}, nil, true},
  1070  		// If marker is *after* the last possible object from the prefix it should return an empty list.
  1071  		{
  1072  			bucketNames[0], "Asia", "europe-object", "", "", 0,
  1073  			ListMultipartsInfo{KeyMarker: "europe-object", Prefix: "Asia", IsTruncated: false},
  1074  			nil, true,
  1075  		},
  1076  		// Setting an invalid combination of uploadIDMarker and Marker (Test number 11-12).
  1077  		{
  1078  			bucketNames[0], "asia", "asia/europe/", "abc", "", 0,
  1079  			ListMultipartsInfo{},
  1080  			fmt.Errorf("Invalid combination of uploadID marker '%s' and marker '%s'", "abc", "asia/europe/"), false,
  1081  		},
  1082  		{
  1083  			// Contains a base64 padding character
  1084  			bucketNames[0], "asia", "asia/europe", "abc=", "", 0,
  1085  			ListMultipartsInfo{},
  1086  			fmt.Errorf("Malformed upload id %s", "abc="), false,
  1087  		},
  1088  
  1089  		// Setting up valid case of ListMultiPartUploads.
  1090  		// Test case with multiple parts for a single uploadID (Test number 13).
  1091  		{bucketNames[0], "", "", "", "", 100, listMultipartResults[0], nil, true},
  1092  		// Test with a KeyMarker (Test number 14-17).
  1093  		{bucketNames[0], "", "minio-object-1.txt", "", "", 100, listMultipartResults[1], nil, true},
  1094  		{bucketNames[0], "", "orange", "", "", 100, listMultipartResults[2], nil, true},
  1095  		{bucketNames[0], "", "orange", "", "", 1, listMultipartResults[3], nil, true},
  1096  		{bucketNames[0], "", "min", "", "", 10, listMultipartResults[4], nil, true},
  1097  		// Test case with keyMarker set equal to number of parts in the result. (Test number 18).
  1098  		{bucketNames[0], "", "min", "", "", 1, listMultipartResults[5], nil, true},
  1099  		// Test case with keyMarker set to 0. (Test number 19).
  1100  		{bucketNames[0], "", "min", "", "", 0, listMultipartResults[6], nil, true},
  1101  		// Test case with keyMarker less than 0. (Test number 20).
  1102  		// {bucketNames[0], "", "min", "", "", -1, listMultipartResults[7], nil, true},
  1103  		// The result contains only one entry. The  KeyPrefix is set to the object name in the result.
  1104  		// Expecting the result to skip the KeyPrefix entry in the result (Test number 21).
  1105  		{bucketNames[0], "", "minio-object", "", "", 2, listMultipartResults[8], nil, true},
  1106  		// Test case containing prefix values.
  1107  		// Setting prefix to be equal to object name.(Test number 22).
  1108  		{bucketNames[0], "minio-object-1.txt", "", "", "", 2, listMultipartResults[9], nil, true},
  1109  		// Setting `prefix` to contain the object name as its prefix (Test number 23).
  1110  		{bucketNames[0], "min", "", "", "", 2, listMultipartResults[10], nil, true},
  1111  		// Setting `prefix` to contain the object name as its prefix (Test number 24).
  1112  		{bucketNames[0], "min", "", "", "", 1, listMultipartResults[11], nil, true},
  1113  		// Setting `prefix` to not to contain the object name as its prefix (Test number 25-26).
  1114  		{bucketNames[0], "orange", "", "", "", 2, listMultipartResults[12], nil, true},
  1115  		{bucketNames[0], "Asia", "", "", "", 2, listMultipartResults[13], nil, true},
  1116  		// setting delimiter (Test number 27).
  1117  		{bucketNames[0], "", "", "", SlashSeparator, 2, listMultipartResults[14], nil, true},
  1118  		// Test case with multiple uploadID listing for given object (Test number 28).
  1119  		{bucketNames[1], "", "", "", "", 100, listMultipartResults[15], nil, true},
  1120  		// Test case with multiple uploadID listing for given object, but uploadID marker set.
  1121  		// Testing whether the marker entry is skipped (Test number 29-30).
  1122  		{bucketNames[1], "", "minio-object-1.txt", uploadIDs[1], "", 100, listMultipartResults[16], nil, true},
  1123  		{bucketNames[1], "", "minio-object-1.txt", uploadIDs[2], "", 100, listMultipartResults[17], nil, true},
  1124  		// Test cases with multiple uploadID listing for a given object (Test number 31-32).
  1125  		// MaxKeys set to values lesser than the number of entries in the MultipartInfo.
  1126  		// IsTruncated is expected to be true.
  1127  		{bucketNames[1], "", "", "", "", 2, listMultipartResults[18], nil, true},
  1128  		{bucketNames[1], "", "", "", "", 1, listMultipartResults[19], nil, true},
  1129  		// MaxKeys set to the value which is equal to no of entries in the MultipartInfo (Test number 33).
  1130  		// In case of bucketNames[1], there are 3 entries.
  1131  		// Since all available entries are listed, IsTruncated is expected to be false
  1132  		// and NextMarkers are expected to empty.
  1133  		{bucketNames[1], "", "", "", "", 3, listMultipartResults[20], nil, true},
  1134  		// Adding  prefix (Test number 34-36).
  1135  		{bucketNames[1], "min", "", "", "", 10, listMultipartResults[21], nil, true},
  1136  		{bucketNames[1], "orange", "", "", "", 10, listMultipartResults[22], nil, true},
  1137  		{bucketNames[1], "Asia", "", "", "", 10, listMultipartResults[23], nil, true},
  1138  		// Test case with `Prefix` and `UploadIDMarker` (Test number 37).
  1139  		{bucketNames[1], "min", "minio-object-1.txt", uploadIDs[1], "", 10, listMultipartResults[24], nil, true},
  1140  		// Test case for bucket with multiple objects in it.
  1141  		// Bucket used : `bucketNames[2]`.
  1142  		// Objects used: `objectNames[1-5]`.
  1143  		// UploadId's used: uploadIds[4-8].
  1144  		// (Test number 39).
  1145  		{bucketNames[2], "", "", "", "", 100, listMultipartResults[25], nil, true},
  1146  		// Test cases with prefixes.
  1147  		// Testing listing with prefix set to "min" (Test number 40)	.
  1148  		{bucketNames[2], "min", "", "", "", 100, listMultipartResults[26], nil, true},
  1149  		// Testing listing with prefix set to "ney" (Test number 41).
  1150  		{bucketNames[2], "ney", "", "", "", 100, listMultipartResults[27], nil, true},
  1151  		// Testing listing with prefix set to "par" (Test number 42).
  1152  		{bucketNames[2], "parrot", "", "", "", 100, listMultipartResults[28], nil, true},
  1153  		// Testing listing with prefix set to object name "neymar.jpeg" (Test number 43).
  1154  		{bucketNames[2], "neymar.jpeg", "", "", "", 100, listMultipartResults[29], nil, true},
  1155  		// Testing listing with `MaxUploads` set to 3 (Test number 44).
  1156  		{bucketNames[2], "", "", "", "", 3, listMultipartResults[30], nil, true},
  1157  		// In case of bucketNames[2], there are 6 entries (Test number 45).
  1158  		// Since all available entries are listed, IsTruncated is expected to be false
  1159  		// and NextMarkers are expected to empty.
  1160  		{bucketNames[2], "", "", "", "", 6, listMultipartResults[31], nil, true},
  1161  		// Test case with `KeyMarker` (Test number 47).
  1162  		{bucketNames[2], "", objectNames[3], "", "", 10, listMultipartResults[33], nil, true},
  1163  		// Test case with `prefix` and `KeyMarker` (Test number 48).
  1164  		{bucketNames[2], "minio-object", objectNames[1], "", "", 10, listMultipartResults[34], nil, true},
  1165  	}
  1166  
  1167  	for i, testCase := range testCases {
  1168  		// fmt.Println(i+1, testCase) // uncomment to peek into the test cases.
  1169  		actualResult, actualErr := obj.ListMultipartUploads(context.Background(), testCase.bucket, testCase.prefix, testCase.keyMarker, testCase.uploadIDMarker, testCase.delimiter, testCase.maxUploads)
  1170  		if actualErr != nil && testCase.shouldPass {
  1171  			t.Errorf("Test %d: %s: Expected to pass, but failed with: <ERROR> %s", i+1, instanceType, actualErr.Error())
  1172  		}
  1173  		if actualErr == nil && !testCase.shouldPass {
  1174  			t.Errorf("Test %d: %s: Expected to fail with <ERROR> \"%s\", but passed instead", i+1, instanceType, testCase.expectedErr.Error())
  1175  		}
  1176  		// Failed as expected, but does it fail for the expected reason.
  1177  		if actualErr != nil && !testCase.shouldPass {
  1178  			if !strings.Contains(actualErr.Error(), testCase.expectedErr.Error()) {
  1179  				t.Errorf("Test %d: %s: Expected to fail with error \"%s\", but instead failed with error \"%s\" instead", i+1, instanceType, testCase.expectedErr.Error(), actualErr.Error())
  1180  			}
  1181  		}
  1182  		// Passes as expected, but asserting the results.
  1183  		if actualErr == nil && testCase.shouldPass {
  1184  			expectedResult := testCase.expectedResult
  1185  			// Asserting the MaxUploads.
  1186  			if actualResult.MaxUploads != expectedResult.MaxUploads {
  1187  				t.Errorf("Test %d: %s: Expected the MaxUploads to be %d, but instead found it to be %d", i+1, instanceType, expectedResult.MaxUploads, actualResult.MaxUploads)
  1188  			}
  1189  			// Asserting Prefix.
  1190  			if actualResult.Prefix != expectedResult.Prefix {
  1191  				t.Errorf("Test %d: %s: Expected Prefix to be \"%s\", but instead found it to be \"%s\"", i+1, instanceType, expectedResult.Prefix, actualResult.Prefix)
  1192  			}
  1193  			// Asserting Delimiter.
  1194  			if actualResult.Delimiter != expectedResult.Delimiter {
  1195  				t.Errorf("Test %d: %s: Expected Delimiter to be \"%s\", but instead found it to be \"%s\"", i+1, instanceType, expectedResult.Delimiter, actualResult.Delimiter)
  1196  			}
  1197  			// Asserting the keyMarker.
  1198  			if actualResult.KeyMarker != expectedResult.KeyMarker {
  1199  				t.Errorf("Test %d: %s: Expected keyMarker to be \"%s\", but instead found it to be \"%s\"", i+1, instanceType, expectedResult.KeyMarker, actualResult.KeyMarker)
  1200  			}
  1201  		}
  1202  	}
  1203  }
  1204  
  1205  // Wrapper for calling TestListObjectPartsDiskNotFound tests for both Erasure multiple disks and single node setup.
  1206  func TestListObjectPartsDiskNotFound(t *testing.T) {
  1207  	ExecObjectLayerDiskAlteredTest(t, testListObjectPartsDiskNotFound)
  1208  }
  1209  
  1210  // testListObjectParts - Tests validate listing of object parts when disks go offline.
  1211  func testListObjectPartsDiskNotFound(obj ObjectLayer, instanceType string, disks []string, t *testing.T) {
  1212  	bucketNames := []string{"minio-bucket", "minio-2-bucket"}
  1213  	objectNames := []string{"minio-object-1.txt"}
  1214  	uploadIDs := []string{}
  1215  
  1216  	globalStorageClass.Update(storageclass.Config{
  1217  		RRS: storageclass.StorageClass{
  1218  			Parity: 2,
  1219  		},
  1220  		Standard: storageclass.StorageClass{
  1221  			Parity: 4,
  1222  		},
  1223  	})
  1224  
  1225  	// bucketnames[0].
  1226  	// objectNames[0].
  1227  	// uploadIds [0].
  1228  	// Create bucket before initiating NewMultipartUpload.
  1229  	err := obj.MakeBucket(context.Background(), bucketNames[0], MakeBucketOptions{})
  1230  	if err != nil {
  1231  		// Failed to create newbucket, abort.
  1232  		t.Fatalf("%s : %s", instanceType, err.Error())
  1233  	}
  1234  	opts := ObjectOptions{}
  1235  	// Initiate Multipart Upload on the above created bucket.
  1236  	res, err := obj.NewMultipartUpload(context.Background(), bucketNames[0], objectNames[0], opts)
  1237  	if err != nil {
  1238  		// Failed to create NewMultipartUpload, abort.
  1239  		t.Fatalf("%s : %s", instanceType, err.Error())
  1240  	}
  1241  
  1242  	z := obj.(*erasureServerPools)
  1243  	er := z.serverPools[0].sets[0]
  1244  
  1245  	erasureDisks := er.getDisks()
  1246  	ridx := rand.Intn(len(erasureDisks))
  1247  
  1248  	z.serverPools[0].erasureDisksMu.Lock()
  1249  	er.getDisks = func() []StorageAPI {
  1250  		erasureDisks[ridx] = newNaughtyDisk(erasureDisks[ridx], nil, errFaultyDisk)
  1251  		return erasureDisks
  1252  	}
  1253  	z.serverPools[0].erasureDisksMu.Unlock()
  1254  
  1255  	uploadIDs = append(uploadIDs, res.UploadID)
  1256  
  1257  	// Create multipart parts.
  1258  	// Need parts to be uploaded before MultipartLists can be called and tested.
  1259  	createPartCases := []struct {
  1260  		bucketName      string
  1261  		objName         string
  1262  		uploadID        string
  1263  		PartID          int
  1264  		inputReaderData string
  1265  		inputMd5        string
  1266  		inputDataSize   int64
  1267  		expectedMd5     string
  1268  	}{
  1269  		// Case 1-4.
  1270  		// Creating sequence of parts for same uploadID.
  1271  		// Used to ensure that the ListMultipartResult produces one output for the four parts uploaded below for the given upload ID.
  1272  		{bucketNames[0], objectNames[0], uploadIDs[0], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"},
  1273  		{bucketNames[0], objectNames[0], uploadIDs[0], 2, "efgh", "1f7690ebdd9b4caf8fab49ca1757bf27", int64(len("efgh")), "1f7690ebdd9b4caf8fab49ca1757bf27"},
  1274  		{bucketNames[0], objectNames[0], uploadIDs[0], 3, "ijkl", "09a0877d04abf8759f99adec02baf579", int64(len("abcd")), "09a0877d04abf8759f99adec02baf579"},
  1275  		{bucketNames[0], objectNames[0], uploadIDs[0], 4, "mnop", "e132e96a5ddad6da8b07bba6f6131fef", int64(len("abcd")), "e132e96a5ddad6da8b07bba6f6131fef"},
  1276  	}
  1277  	sha256sum := ""
  1278  	// Iterating over creatPartCases to generate multipart chunks.
  1279  	for _, testCase := range createPartCases {
  1280  		_, err := obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetPutObjReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.inputDataSize, testCase.inputMd5, sha256sum), opts)
  1281  		if err != nil {
  1282  			t.Fatalf("%s : %s", instanceType, err.Error())
  1283  		}
  1284  	}
  1285  
  1286  	partInfos := []ListPartsInfo{
  1287  		// partinfos - 0.
  1288  		{
  1289  			Bucket:   bucketNames[0],
  1290  			Object:   objectNames[0],
  1291  			MaxParts: 10,
  1292  			UploadID: uploadIDs[0],
  1293  			Parts: []PartInfo{
  1294  				{
  1295  					PartNumber: 1,
  1296  					Size:       4,
  1297  					ETag:       "e2fc714c4727ee9395f324cd2e7f331f",
  1298  				},
  1299  				{
  1300  					PartNumber: 2,
  1301  					Size:       4,
  1302  					ETag:       "1f7690ebdd9b4caf8fab49ca1757bf27",
  1303  				},
  1304  				{
  1305  					PartNumber: 3,
  1306  					Size:       4,
  1307  					ETag:       "09a0877d04abf8759f99adec02baf579",
  1308  				},
  1309  				{
  1310  					PartNumber: 4,
  1311  					Size:       4,
  1312  					ETag:       "e132e96a5ddad6da8b07bba6f6131fef",
  1313  				},
  1314  			},
  1315  		},
  1316  		// partinfos - 1.
  1317  		{
  1318  			Bucket:               bucketNames[0],
  1319  			Object:               objectNames[0],
  1320  			MaxParts:             3,
  1321  			NextPartNumberMarker: 3,
  1322  			IsTruncated:          true,
  1323  			UploadID:             uploadIDs[0],
  1324  			Parts: []PartInfo{
  1325  				{
  1326  					PartNumber: 1,
  1327  					Size:       4,
  1328  					ETag:       "e2fc714c4727ee9395f324cd2e7f331f",
  1329  				},
  1330  				{
  1331  					PartNumber: 2,
  1332  					Size:       4,
  1333  					ETag:       "1f7690ebdd9b4caf8fab49ca1757bf27",
  1334  				},
  1335  				{
  1336  					PartNumber: 3,
  1337  					Size:       4,
  1338  					ETag:       "09a0877d04abf8759f99adec02baf579",
  1339  				},
  1340  			},
  1341  		},
  1342  		// partinfos - 2.
  1343  		{
  1344  			Bucket:           bucketNames[0],
  1345  			Object:           objectNames[0],
  1346  			MaxParts:         2,
  1347  			IsTruncated:      false,
  1348  			UploadID:         uploadIDs[0],
  1349  			PartNumberMarker: 3,
  1350  			Parts: []PartInfo{
  1351  				{
  1352  					PartNumber: 4,
  1353  					Size:       4,
  1354  					ETag:       "e132e96a5ddad6da8b07bba6f6131fef",
  1355  				},
  1356  			},
  1357  		},
  1358  	}
  1359  
  1360  	// Collection of non-exhaustive ListObjectParts test cases, valid errors
  1361  	// and success responses.
  1362  	testCases := []struct {
  1363  		bucket           string
  1364  		object           string
  1365  		uploadID         string
  1366  		partNumberMarker int
  1367  		maxParts         int
  1368  		// Expected output of ListPartsInfo.
  1369  		expectedResult ListPartsInfo
  1370  		expectedErr    error
  1371  		// Flag indicating whether the test is expected to pass or not.
  1372  		shouldPass bool
  1373  	}{
  1374  		// Test cases with invalid bucket names (Test number 1-4).
  1375  		{".test", "", "", 0, 0, ListPartsInfo{}, BucketNameInvalid{Bucket: ".test"}, false},
  1376  		{"Test", "", "", 0, 0, ListPartsInfo{}, BucketNameInvalid{Bucket: "Test"}, false},
  1377  		{"---", "", "", 0, 0, ListPartsInfo{}, BucketNameInvalid{Bucket: "---"}, false},
  1378  		{"ad", "", "", 0, 0, ListPartsInfo{}, BucketNameInvalid{Bucket: "ad"}, false},
  1379  		// Test cases for listing uploadID with single part.
  1380  		// Valid bucket names, but they do not exist (Test number 5-7).
  1381  		{"volatile-bucket-1", "test1", "", 0, 0, ListPartsInfo{}, BucketNotFound{Bucket: "volatile-bucket-1"}, false},
  1382  		{"volatile-bucket-2", "test1", "", 0, 0, ListPartsInfo{}, BucketNotFound{Bucket: "volatile-bucket-2"}, false},
  1383  		{"volatile-bucket-3", "test1", "", 0, 0, ListPartsInfo{}, BucketNotFound{Bucket: "volatile-bucket-3"}, false},
  1384  		// Test case for Asserting for invalid objectName (Test number 8).
  1385  		{bucketNames[0], "", "", 0, 0, ListPartsInfo{}, ObjectNameInvalid{Bucket: bucketNames[0]}, false},
  1386  		// Asserting for Invalid UploadID (Test number 9).
  1387  		{bucketNames[0], objectNames[0], "abc", 0, 0, ListPartsInfo{}, InvalidUploadID{UploadID: "abc"}, false},
  1388  		// Test case for uploadID with multiple parts (Test number 12).
  1389  		{bucketNames[0], objectNames[0], uploadIDs[0], 0, 10, partInfos[0], nil, true},
  1390  		// Test case with maxParts set to less than number of parts (Test number 13).
  1391  		{bucketNames[0], objectNames[0], uploadIDs[0], 0, 3, partInfos[1], nil, true},
  1392  		// Test case with partNumberMarker set (Test number 14)-.
  1393  		{bucketNames[0], objectNames[0], uploadIDs[0], 3, 2, partInfos[2], nil, true},
  1394  	}
  1395  
  1396  	for i, testCase := range testCases {
  1397  		actualResult, actualErr := obj.ListObjectParts(context.Background(), testCase.bucket, testCase.object, testCase.uploadID, testCase.partNumberMarker, testCase.maxParts, ObjectOptions{})
  1398  		if actualErr != nil && testCase.shouldPass {
  1399  			t.Errorf("Test %d: %s: Expected to pass, but failed with: <ERROR> %s", i+1, instanceType, actualErr.Error())
  1400  		}
  1401  		if actualErr == nil && !testCase.shouldPass {
  1402  			t.Errorf("Test %d: %s: Expected to fail with <ERROR> \"%s\", but passed instead", i+1, instanceType, testCase.expectedErr.Error())
  1403  		}
  1404  		// Failed as expected, but does it fail for the expected reason.
  1405  		if actualErr != nil && !testCase.shouldPass {
  1406  			if !strings.Contains(actualErr.Error(), testCase.expectedErr.Error()) {
  1407  				t.Errorf("Test %d: %s: Expected to fail with error \"%s\", but instead failed with error \"%s\" instead", i+1, instanceType, testCase.expectedErr, actualErr)
  1408  			}
  1409  		}
  1410  		// Passes as expected, but asserting the results.
  1411  		if actualErr == nil && testCase.shouldPass {
  1412  			expectedResult := testCase.expectedResult
  1413  			// Asserting the MaxParts.
  1414  			if actualResult.MaxParts != expectedResult.MaxParts {
  1415  				t.Errorf("Test %d: %s: Expected the MaxParts to be %d, but instead found it to be %d", i+1, instanceType, expectedResult.MaxParts, actualResult.MaxParts)
  1416  			}
  1417  			// Asserting Object Name.
  1418  			if actualResult.Object != expectedResult.Object {
  1419  				t.Errorf("Test %d: %s: Expected Object name to be \"%s\", but instead found it to be \"%s\"", i+1, instanceType, expectedResult.Object, actualResult.Object)
  1420  			}
  1421  			// Asserting UploadID.
  1422  			if actualResult.UploadID != expectedResult.UploadID {
  1423  				t.Errorf("Test %d: %s: Expected UploadID to be \"%s\", but instead found it to be \"%s\"", i+1, instanceType, expectedResult.UploadID, actualResult.UploadID)
  1424  			}
  1425  			// Asserting NextPartNumberMarker.
  1426  			if actualResult.NextPartNumberMarker != expectedResult.NextPartNumberMarker {
  1427  				t.Errorf("Test %d: %s: Expected NextPartNumberMarker to be \"%d\", but instead found it to be \"%d\"", i+1, instanceType, expectedResult.NextPartNumberMarker, actualResult.NextPartNumberMarker)
  1428  			}
  1429  			// Asserting PartNumberMarker.
  1430  			if actualResult.PartNumberMarker != expectedResult.PartNumberMarker {
  1431  				t.Errorf("Test %d: %s: Expected PartNumberMarker to be \"%d\", but instead found it to be \"%d\"", i+1, instanceType, expectedResult.PartNumberMarker, actualResult.PartNumberMarker)
  1432  			}
  1433  			// Asserting the BucketName.
  1434  			if actualResult.Bucket != expectedResult.Bucket {
  1435  				t.Errorf("Test %d: %s: Expected Bucket to be \"%s\", but instead found it to be \"%s\"", i+1, instanceType, expectedResult.Bucket, actualResult.Bucket)
  1436  			}
  1437  			// Asserting IsTruncated.
  1438  			if actualResult.IsTruncated != testCase.expectedResult.IsTruncated {
  1439  				t.Errorf("Test %d: %s: Expected IsTruncated to be \"%v\", but found it to \"%v\"", i+1, instanceType, expectedResult.IsTruncated, actualResult.IsTruncated)
  1440  			}
  1441  			// Asserting the number of Parts.
  1442  			if len(expectedResult.Parts) != len(actualResult.Parts) {
  1443  				t.Errorf("Test %d: %s: Expected the result to contain info of %d Parts, but found %d instead", i+1, instanceType, len(expectedResult.Parts), len(actualResult.Parts))
  1444  			} else {
  1445  				// Iterating over the partInfos and asserting the fields.
  1446  				for j, actualMetaData := range actualResult.Parts {
  1447  					//  Asserting the PartNumber in the PartInfo.
  1448  					if actualMetaData.PartNumber != expectedResult.Parts[j].PartNumber {
  1449  						t.Errorf("Test %d: %s: Part %d: Expected PartNumber to be \"%d\", but instead found \"%d\"", i+1, instanceType, j+1, expectedResult.Parts[j].PartNumber, actualMetaData.PartNumber)
  1450  					}
  1451  					//  Asserting the Size in the PartInfo.
  1452  					if actualMetaData.Size != expectedResult.Parts[j].Size {
  1453  						t.Errorf("Test %d: %s: Part %d: Expected Part Size to be \"%d\", but instead found \"%d\"", i+1, instanceType, j+1, expectedResult.Parts[j].Size, actualMetaData.Size)
  1454  					}
  1455  					//  Asserting the ETag in the PartInfo.
  1456  					if actualMetaData.ETag != expectedResult.Parts[j].ETag {
  1457  						t.Errorf("Test %d: %s: Part %d: Expected Etag to be \"%s\", but instead found \"%s\"", i+1, instanceType, j+1, expectedResult.Parts[j].ETag, actualMetaData.ETag)
  1458  					}
  1459  				}
  1460  			}
  1461  		}
  1462  	}
  1463  }
  1464  
  1465  // Wrapper for calling TestListObjectParts tests for both Erasure multiple disks and single node setup.
  1466  func TestListObjectParts(t *testing.T) {
  1467  	ExecObjectLayerTest(t, testListObjectParts)
  1468  }
  1469  
  1470  // testListObjectParts - test validate listing of object parts.
  1471  func testListObjectParts(obj ObjectLayer, instanceType string, t TestErrHandler) {
  1472  	bucketNames := []string{"minio-bucket", "minio-2-bucket"}
  1473  	objectNames := []string{"minio-object-1.txt"}
  1474  	uploadIDs := []string{}
  1475  	opts := ObjectOptions{}
  1476  	// bucketnames[0].
  1477  	// objectNames[0].
  1478  	// uploadIds [0].
  1479  	// Create bucket before initiating NewMultipartUpload.
  1480  	err := obj.MakeBucket(context.Background(), bucketNames[0], MakeBucketOptions{})
  1481  	if err != nil {
  1482  		// Failed to create newbucket, abort.
  1483  		t.Fatalf("%s : %s", instanceType, err.Error())
  1484  	}
  1485  	// Initiate Multipart Upload on the above created bucket.
  1486  	res, err := obj.NewMultipartUpload(context.Background(), bucketNames[0], objectNames[0], opts)
  1487  	if err != nil {
  1488  		// Failed to create NewMultipartUpload, abort.
  1489  		t.Fatalf("%s : %s", instanceType, err.Error())
  1490  	}
  1491  
  1492  	uploadIDs = append(uploadIDs, res.UploadID)
  1493  
  1494  	// Create multipart parts.
  1495  	// Need parts to be uploaded before MultipartLists can be called and tested.
  1496  	createPartCases := []struct {
  1497  		bucketName      string
  1498  		objName         string
  1499  		uploadID        string
  1500  		PartID          int
  1501  		inputReaderData string
  1502  		inputMd5        string
  1503  		inputDataSize   int64
  1504  		expectedMd5     string
  1505  	}{
  1506  		// Case 1-4.
  1507  		// Creating sequence of parts for same uploadID.
  1508  		// Used to ensure that the ListMultipartResult produces one output for the four parts uploaded below for the given upload ID.
  1509  		{bucketNames[0], objectNames[0], uploadIDs[0], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"},
  1510  		{bucketNames[0], objectNames[0], uploadIDs[0], 2, "efgh", "1f7690ebdd9b4caf8fab49ca1757bf27", int64(len("efgh")), "1f7690ebdd9b4caf8fab49ca1757bf27"},
  1511  		{bucketNames[0], objectNames[0], uploadIDs[0], 3, "ijkl", "09a0877d04abf8759f99adec02baf579", int64(len("abcd")), "09a0877d04abf8759f99adec02baf579"},
  1512  		{bucketNames[0], objectNames[0], uploadIDs[0], 4, "mnop", "e132e96a5ddad6da8b07bba6f6131fef", int64(len("abcd")), "e132e96a5ddad6da8b07bba6f6131fef"},
  1513  	}
  1514  	sha256sum := ""
  1515  	// Iterating over creatPartCases to generate multipart chunks.
  1516  	for _, testCase := range createPartCases {
  1517  		_, err := obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetPutObjReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.inputDataSize, testCase.inputMd5, sha256sum), opts)
  1518  		if err != nil {
  1519  			t.Fatalf("%s : %s", instanceType, err.Error())
  1520  		}
  1521  	}
  1522  
  1523  	partInfos := []ListPartsInfo{
  1524  		// partinfos - 0.
  1525  		{
  1526  			Bucket:   bucketNames[0],
  1527  			Object:   objectNames[0],
  1528  			MaxParts: 10,
  1529  			UploadID: uploadIDs[0],
  1530  			Parts: []PartInfo{
  1531  				{
  1532  					PartNumber: 1,
  1533  					Size:       4,
  1534  					ETag:       "e2fc714c4727ee9395f324cd2e7f331f",
  1535  				},
  1536  				{
  1537  					PartNumber: 2,
  1538  					Size:       4,
  1539  					ETag:       "1f7690ebdd9b4caf8fab49ca1757bf27",
  1540  				},
  1541  				{
  1542  					PartNumber: 3,
  1543  					Size:       4,
  1544  					ETag:       "09a0877d04abf8759f99adec02baf579",
  1545  				},
  1546  				{
  1547  					PartNumber: 4,
  1548  					Size:       4,
  1549  					ETag:       "e132e96a5ddad6da8b07bba6f6131fef",
  1550  				},
  1551  			},
  1552  		},
  1553  		// partinfos - 1.
  1554  		{
  1555  			Bucket:               bucketNames[0],
  1556  			Object:               objectNames[0],
  1557  			MaxParts:             3,
  1558  			NextPartNumberMarker: 3,
  1559  			IsTruncated:          true,
  1560  			UploadID:             uploadIDs[0],
  1561  			Parts: []PartInfo{
  1562  				{
  1563  					PartNumber: 1,
  1564  					Size:       4,
  1565  					ETag:       "e2fc714c4727ee9395f324cd2e7f331f",
  1566  				},
  1567  				{
  1568  					PartNumber: 2,
  1569  					Size:       4,
  1570  					ETag:       "1f7690ebdd9b4caf8fab49ca1757bf27",
  1571  				},
  1572  				{
  1573  					PartNumber: 3,
  1574  					Size:       4,
  1575  					ETag:       "09a0877d04abf8759f99adec02baf579",
  1576  				},
  1577  			},
  1578  		},
  1579  		// partinfos - 2.
  1580  		{
  1581  			Bucket:           bucketNames[0],
  1582  			Object:           objectNames[0],
  1583  			MaxParts:         2,
  1584  			IsTruncated:      false,
  1585  			UploadID:         uploadIDs[0],
  1586  			PartNumberMarker: 3,
  1587  			Parts: []PartInfo{
  1588  				{
  1589  					PartNumber: 4,
  1590  					Size:       4,
  1591  					ETag:       "e132e96a5ddad6da8b07bba6f6131fef",
  1592  				},
  1593  			},
  1594  		},
  1595  	}
  1596  
  1597  	// Collection of non-exhaustive ListObjectParts test cases, valid errors
  1598  	// and success responses.
  1599  	testCases := []struct {
  1600  		bucket           string
  1601  		object           string
  1602  		uploadID         string
  1603  		partNumberMarker int
  1604  		maxParts         int
  1605  		// Expected output of ListPartsInfo.
  1606  		expectedResult ListPartsInfo
  1607  		expectedErr    error
  1608  		// Flag indicating whether the test is expected to pass or not.
  1609  		shouldPass bool
  1610  	}{
  1611  		// Test cases with invalid bucket names (Test number 1-4).
  1612  		{".test", "", "", 0, 0, ListPartsInfo{}, BucketNameInvalid{Bucket: ".test"}, false},
  1613  		{"Test", "", "", 0, 0, ListPartsInfo{}, BucketNameInvalid{Bucket: "Test"}, false},
  1614  		{"---", "", "", 0, 0, ListPartsInfo{}, BucketNameInvalid{Bucket: "---"}, false},
  1615  		{"ad", "", "", 0, 0, ListPartsInfo{}, BucketNameInvalid{Bucket: "ad"}, false},
  1616  		// Test cases for listing uploadID with single part.
  1617  		// Valid bucket names, but they do not exist (Test number 5-7).
  1618  		{"volatile-bucket-1", "test1", "", 0, 0, ListPartsInfo{}, BucketNotFound{Bucket: "volatile-bucket-1"}, false},
  1619  		{"volatile-bucket-2", "test1", "", 0, 0, ListPartsInfo{}, BucketNotFound{Bucket: "volatile-bucket-2"}, false},
  1620  		{"volatile-bucket-3", "test1", "", 0, 0, ListPartsInfo{}, BucketNotFound{Bucket: "volatile-bucket-3"}, false},
  1621  		// Test case for Asserting for invalid objectName (Test number 8).
  1622  		{bucketNames[0], "", "", 0, 0, ListPartsInfo{}, ObjectNameInvalid{Bucket: bucketNames[0]}, false},
  1623  		// Asserting for Invalid UploadID (Test number 9).
  1624  		{bucketNames[0], objectNames[0], "abc", 0, 0, ListPartsInfo{}, InvalidUploadID{UploadID: "abc"}, false},
  1625  		// Test case for uploadID with multiple parts (Test number 12).
  1626  		{bucketNames[0], objectNames[0], uploadIDs[0], 0, 10, partInfos[0], nil, true},
  1627  		// Test case with maxParts set to less than number of parts (Test number 13).
  1628  		{bucketNames[0], objectNames[0], uploadIDs[0], 0, 3, partInfos[1], nil, true},
  1629  		// Test case with partNumberMarker set (Test number 14)-.
  1630  		{bucketNames[0], objectNames[0], uploadIDs[0], 3, 2, partInfos[2], nil, true},
  1631  	}
  1632  
  1633  	for i, testCase := range testCases {
  1634  		actualResult, actualErr := obj.ListObjectParts(context.Background(), testCase.bucket, testCase.object, testCase.uploadID, testCase.partNumberMarker, testCase.maxParts, opts)
  1635  		if actualErr != nil && testCase.shouldPass {
  1636  			t.Errorf("Test %d: %s: Expected to pass, but failed with: <ERROR> %s", i+1, instanceType, actualErr.Error())
  1637  		}
  1638  		if actualErr == nil && !testCase.shouldPass {
  1639  			t.Errorf("Test %d: %s: Expected to fail with <ERROR> \"%s\", but passed instead", i+1, instanceType, testCase.expectedErr.Error())
  1640  		}
  1641  		// Failed as expected, but does it fail for the expected reason.
  1642  		if actualErr != nil && !testCase.shouldPass {
  1643  			if !strings.Contains(actualErr.Error(), testCase.expectedErr.Error()) {
  1644  				t.Errorf("Test %d: %s: Expected to fail with error \"%s\", but instead failed with error \"%s\" instead", i+1, instanceType, testCase.expectedErr.Error(), actualErr.Error())
  1645  			}
  1646  		}
  1647  		// Passes as expected, but asserting the results.
  1648  		if actualErr == nil && testCase.shouldPass {
  1649  			expectedResult := testCase.expectedResult
  1650  			// Asserting the MaxParts.
  1651  			if actualResult.MaxParts != expectedResult.MaxParts {
  1652  				t.Errorf("Test %d: %s: Expected the MaxParts to be %d, but instead found it to be %d", i+1, instanceType, expectedResult.MaxParts, actualResult.MaxParts)
  1653  			}
  1654  			// Asserting Object Name.
  1655  			if actualResult.Object != expectedResult.Object {
  1656  				t.Errorf("Test %d: %s: Expected Object name to be \"%s\", but instead found it to be \"%s\"", i+1, instanceType, expectedResult.Object, actualResult.Object)
  1657  			}
  1658  			// Asserting UploadID.
  1659  			if actualResult.UploadID != expectedResult.UploadID {
  1660  				t.Errorf("Test %d: %s: Expected UploadID to be \"%s\", but instead found it to be \"%s\"", i+1, instanceType, expectedResult.UploadID, actualResult.UploadID)
  1661  			}
  1662  			// Asserting PartNumberMarker.
  1663  			if actualResult.PartNumberMarker != expectedResult.PartNumberMarker {
  1664  				t.Errorf("Test %d: %s: Expected PartNumberMarker to be \"%d\", but instead found it to be \"%d\"", i+1, instanceType, expectedResult.PartNumberMarker, actualResult.PartNumberMarker)
  1665  			}
  1666  			// Asserting the BucketName.
  1667  			if actualResult.Bucket != expectedResult.Bucket {
  1668  				t.Errorf("Test %d: %s: Expected Bucket to be \"%s\", but instead found it to be \"%s\"", i+1, instanceType, expectedResult.Bucket, actualResult.Bucket)
  1669  			}
  1670  
  1671  			// Asserting IsTruncated.
  1672  			if actualResult.IsTruncated != testCase.expectedResult.IsTruncated {
  1673  				t.Errorf("Test %d: %s: Expected IsTruncated to be \"%v\", but found it to \"%v\"", i+1, instanceType, expectedResult.IsTruncated, actualResult.IsTruncated)
  1674  				continue
  1675  			}
  1676  			// Asserting NextPartNumberMarker.
  1677  			if actualResult.NextPartNumberMarker != expectedResult.NextPartNumberMarker {
  1678  				t.Errorf("Test %d: %s: Expected NextPartNumberMarker to be \"%d\", but instead found it to be \"%d\"", i+1, instanceType, expectedResult.NextPartNumberMarker, actualResult.NextPartNumberMarker)
  1679  				continue
  1680  			}
  1681  			// Asserting the number of Parts.
  1682  			if len(expectedResult.Parts) != len(actualResult.Parts) {
  1683  				t.Errorf("Test %d: %s: Expected the result to contain info of %d Parts, but found %d instead", i+1, instanceType, len(expectedResult.Parts), len(actualResult.Parts))
  1684  				continue
  1685  			}
  1686  			// Iterating over the partInfos and asserting the fields.
  1687  			for j, actualMetaData := range actualResult.Parts {
  1688  				//  Asserting the PartNumber in the PartInfo.
  1689  				if actualMetaData.PartNumber != expectedResult.Parts[j].PartNumber {
  1690  					t.Errorf("Test %d: %s: Part %d: Expected PartNumber to be \"%d\", but instead found \"%d\"", i+1, instanceType, j+1, expectedResult.Parts[j].PartNumber, actualMetaData.PartNumber)
  1691  				}
  1692  				//  Asserting the Size in the PartInfo.
  1693  				if actualMetaData.Size != expectedResult.Parts[j].Size {
  1694  					t.Errorf("Test %d: %s: Part %d: Expected Part Size to be \"%d\", but instead found \"%d\"", i+1, instanceType, j+1, expectedResult.Parts[j].Size, actualMetaData.Size)
  1695  				}
  1696  				//  Asserting the ETag in the PartInfo.
  1697  				if actualMetaData.ETag != expectedResult.Parts[j].ETag {
  1698  					t.Errorf("Test %d: %s: Part %d: Expected Etag to be \"%s\", but instead found \"%s\"", i+1, instanceType, j+1, expectedResult.Parts[j].ETag, actualMetaData.ETag)
  1699  				}
  1700  			}
  1701  		}
  1702  	}
  1703  }
  1704  
  1705  // Test for validating complete Multipart upload.
  1706  func TestObjectCompleteMultipartUpload(t *testing.T) {
  1707  	ExecExtendedObjectLayerTest(t, testObjectCompleteMultipartUpload)
  1708  }
  1709  
  1710  // Tests validate CompleteMultipart functionality.
  1711  func testObjectCompleteMultipartUpload(obj ObjectLayer, instanceType string, t TestErrHandler) {
  1712  	var err error
  1713  	bucketNames := []string{"minio-bucket", "minio-2-bucket"}
  1714  	objectNames := []string{"minio-object-1.txt"}
  1715  	uploadIDs := []string{}
  1716  
  1717  	// bucketnames[0].
  1718  	// objectNames[0].
  1719  	// uploadIds [0].
  1720  	// Create bucket before initiating NewMultipartUpload.
  1721  	err = obj.MakeBucket(context.Background(), bucketNames[0], MakeBucketOptions{})
  1722  	if err != nil {
  1723  		// Failed to create newbucket, abort.
  1724  		t.Fatalf("%s : %s", instanceType, err)
  1725  	}
  1726  	// Initiate Multipart Upload on the above created bucket.
  1727  	res, err := obj.NewMultipartUpload(context.Background(), bucketNames[0], objectNames[0], ObjectOptions{UserDefined: map[string]string{"X-Amz-Meta-Id": "id"}})
  1728  	if err != nil {
  1729  		// Failed to create NewMultipartUpload, abort.
  1730  		t.Fatalf("%s : %s", instanceType, err)
  1731  	}
  1732  
  1733  	uploadIDs = append(uploadIDs, res.UploadID)
  1734  	// Parts with size greater than 5 MiB.
  1735  	// Generating a 6MiB byte array.
  1736  	validPart := bytes.Repeat([]byte("abcdef"), 1*humanize.MiByte)
  1737  	validPartMD5 := getMD5Hash(validPart)
  1738  	// Create multipart parts.
  1739  	// Need parts to be uploaded before CompleteMultiPartUpload can be called tested.
  1740  	parts := []struct {
  1741  		bucketName      string
  1742  		objName         string
  1743  		uploadID        string
  1744  		PartID          int
  1745  		inputReaderData string
  1746  		inputMd5        string
  1747  		inputDataSize   int64
  1748  	}{
  1749  		// Case 1-4.
  1750  		// Creating sequence of parts for same uploadID.
  1751  		{bucketNames[0], objectNames[0], uploadIDs[0], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd"))},
  1752  		{bucketNames[0], objectNames[0], uploadIDs[0], 2, "efgh", "1f7690ebdd9b4caf8fab49ca1757bf27", int64(len("efgh"))},
  1753  		{bucketNames[0], objectNames[0], uploadIDs[0], 3, "ijkl", "09a0877d04abf8759f99adec02baf579", int64(len("abcd"))},
  1754  		{bucketNames[0], objectNames[0], uploadIDs[0], 4, "mnop", "e132e96a5ddad6da8b07bba6f6131fef", int64(len("abcd"))},
  1755  		// Part with size larger than 5Mb.
  1756  		{bucketNames[0], objectNames[0], uploadIDs[0], 5, string(validPart), validPartMD5, int64(len(validPart))},
  1757  		{bucketNames[0], objectNames[0], uploadIDs[0], 6, string(validPart), validPartMD5, int64(len(validPart))},
  1758  		{bucketNames[0], objectNames[0], uploadIDs[0], 7, string(validPart), validPartMD5, int64(len(validPart))},
  1759  	}
  1760  	sha256sum := ""
  1761  	var opts ObjectOptions
  1762  	// Iterating over creatPartCases to generate multipart chunks.
  1763  	for _, part := range parts {
  1764  		_, err = obj.PutObjectPart(context.Background(), part.bucketName, part.objName, part.uploadID, part.PartID, mustGetPutObjReader(t, bytes.NewBufferString(part.inputReaderData), part.inputDataSize, part.inputMd5, sha256sum), opts)
  1765  		if err != nil {
  1766  			t.Fatalf("%s : %s", instanceType, err)
  1767  		}
  1768  	}
  1769  	// Parts to be sent as input for CompleteMultipartUpload.
  1770  	inputParts := []struct {
  1771  		parts []CompletePart
  1772  	}{
  1773  		// inputParts - 0.
  1774  		// Case for replicating ETag mismatch.
  1775  		{
  1776  			[]CompletePart{
  1777  				{ETag: "abcd", PartNumber: 1},
  1778  			},
  1779  		},
  1780  		// inputParts - 1.
  1781  		// should error out with part too small.
  1782  		{
  1783  			[]CompletePart{
  1784  				{ETag: "e2fc714c4727ee9395f324cd2e7f331f", PartNumber: 1},
  1785  				{ETag: "1f7690ebdd9b4caf8fab49ca1757bf27", PartNumber: 2},
  1786  			},
  1787  		},
  1788  		// inputParts - 2.
  1789  		// Case with invalid Part number.
  1790  		{
  1791  			[]CompletePart{
  1792  				{ETag: "e2fc714c4727ee9395f324cd2e7f331f", PartNumber: 10},
  1793  			},
  1794  		},
  1795  		// inputParts - 3.
  1796  		// Case with valid part.
  1797  		// Part size greater than 5MB.
  1798  		{
  1799  			[]CompletePart{
  1800  				{ETag: fmt.Sprintf("\"\"\"\"\"%s\"\"\"", validPartMD5), PartNumber: 5},
  1801  			},
  1802  		},
  1803  		// inputParts - 4.
  1804  		// Used to verify that the other remaining parts are deleted after
  1805  		// a successful call to CompleteMultipartUpload.
  1806  		{
  1807  			[]CompletePart{
  1808  				{ETag: validPartMD5, PartNumber: 6},
  1809  			},
  1810  		},
  1811  	}
  1812  	s3MD5 := getCompleteMultipartMD5(inputParts[3].parts)
  1813  
  1814  	// Test cases with sample input values for CompleteMultipartUpload.
  1815  	testCases := []struct {
  1816  		bucket   string
  1817  		object   string
  1818  		uploadID string
  1819  		parts    []CompletePart
  1820  		// Expected output of CompleteMultipartUpload.
  1821  		expectedS3MD5 string
  1822  		expectedErr   error
  1823  		// Flag indicating whether the test is expected to pass or not.
  1824  		shouldPass bool
  1825  	}{
  1826  		// Test cases with invalid bucket names (Test number 1-4).
  1827  		{".test", "", "", []CompletePart{}, "", BucketNameInvalid{Bucket: ".test"}, false},
  1828  		{"Test", "", "", []CompletePart{}, "", BucketNameInvalid{Bucket: "Test"}, false},
  1829  		{"---", "", "", []CompletePart{}, "", BucketNameInvalid{Bucket: "---"}, false},
  1830  		{"ad", "", "", []CompletePart{}, "", BucketNameInvalid{Bucket: "ad"}, false},
  1831  		// Test cases for listing uploadID with single part.
  1832  		// Valid bucket names, but they do not exist (Test number 5-7).
  1833  		{"volatile-bucket-1", "test1", "", []CompletePart{}, "", BucketNotFound{Bucket: "volatile-bucket-1"}, false},
  1834  		{"volatile-bucket-2", "test1", "", []CompletePart{}, "", BucketNotFound{Bucket: "volatile-bucket-2"}, false},
  1835  		{"volatile-bucket-3", "test1", "", []CompletePart{}, "", BucketNotFound{Bucket: "volatile-bucket-3"}, false},
  1836  		// Test case for Asserting for invalid objectName (Test number 8).
  1837  		{bucketNames[0], "", "", []CompletePart{}, "", ObjectNameInvalid{Bucket: bucketNames[0]}, false},
  1838  		// Asserting for Invalid UploadID (Test number 9).
  1839  		{bucketNames[0], objectNames[0], "abc", []CompletePart{}, "", InvalidUploadID{UploadID: "abc"}, false},
  1840  		// Test case with invalid Part Etag (Test number 10-11).
  1841  		{bucketNames[0], objectNames[0], uploadIDs[0], []CompletePart{{ETag: "abc"}}, "", InvalidPart{}, false},
  1842  		{bucketNames[0], objectNames[0], uploadIDs[0], []CompletePart{{ETag: "abcz"}}, "", InvalidPart{}, false},
  1843  		// Part number 0 doesn't exist, expecting InvalidPart error (Test number 12).
  1844  		{bucketNames[0], objectNames[0], uploadIDs[0], []CompletePart{{ETag: "abcd", PartNumber: 0}}, "", InvalidPart{}, false},
  1845  		// // Upload and PartNumber exists, But a deliberate ETag mismatch is introduced (Test number 13).
  1846  		{bucketNames[0], objectNames[0], uploadIDs[0], inputParts[0].parts, "", InvalidPart{}, false},
  1847  		// Test case with non existent object name (Test number 14).
  1848  		{bucketNames[0], "my-object", uploadIDs[0], []CompletePart{{ETag: "abcd", PartNumber: 1}}, "", InvalidUploadID{UploadID: uploadIDs[0]}, false},
  1849  		// Testing for Part being too small (Test number 15).
  1850  		{bucketNames[0], objectNames[0], uploadIDs[0], inputParts[1].parts, "", PartTooSmall{PartNumber: 1}, false},
  1851  		// TestCase with invalid Part Number (Test number 16).
  1852  		// Should error with Invalid Part .
  1853  		{bucketNames[0], objectNames[0], uploadIDs[0], inputParts[2].parts, "", InvalidPart{}, false},
  1854  		// Test case with unsorted parts (Test number 17).
  1855  		{bucketNames[0], objectNames[0], uploadIDs[0], inputParts[3].parts, s3MD5, nil, true},
  1856  		// The other parts will be flushed after a successful CompletePart (Test number 18).
  1857  		// the case above successfully completes CompleteMultipartUpload, the remaining Parts will be flushed.
  1858  		// Expecting to fail with Invalid UploadID.
  1859  		{bucketNames[0], objectNames[0], uploadIDs[0], inputParts[4].parts, "", InvalidUploadID{UploadID: uploadIDs[0]}, false},
  1860  	}
  1861  
  1862  	for _, testCase := range testCases {
  1863  		testCase := testCase
  1864  		t.(*testing.T).Run("", func(t *testing.T) {
  1865  			opts = ObjectOptions{}
  1866  			actualResult, actualErr := obj.CompleteMultipartUpload(context.Background(), testCase.bucket, testCase.object, testCase.uploadID, testCase.parts, ObjectOptions{})
  1867  			if actualErr != nil && testCase.shouldPass {
  1868  				t.Errorf("%s: Expected to pass, but failed with: <ERROR> %s", instanceType, actualErr)
  1869  			}
  1870  			if actualErr == nil && !testCase.shouldPass {
  1871  				t.Errorf("%s: Expected to fail with <ERROR> \"%s\", but passed instead", instanceType, testCase.expectedErr)
  1872  			}
  1873  			// Failed as expected, but does it fail for the expected reason.
  1874  			if actualErr != nil && !testCase.shouldPass {
  1875  				if reflect.TypeOf(actualErr) != reflect.TypeOf(testCase.expectedErr) {
  1876  					t.Errorf("%s: Expected to fail with error \"%s\", but instead failed with error \"%s\"", instanceType, testCase.expectedErr, actualErr)
  1877  				}
  1878  			}
  1879  			// Passes as expected, but asserting the results.
  1880  			if actualErr == nil && testCase.shouldPass {
  1881  				// Asserting IsTruncated.
  1882  				if actualResult.ETag != testCase.expectedS3MD5 {
  1883  					t.Errorf("%s: Expected the result to be \"%v\", but found it to \"%v\"", instanceType, testCase.expectedS3MD5, actualResult)
  1884  				}
  1885  			}
  1886  		})
  1887  	}
  1888  }
  1889  
  1890  // Benchmarks for ObjectLayer.PutObjectPart().
  1891  // The intent is to benchmark PutObjectPart for various sizes ranging from few bytes to 100MB.
  1892  // Also each of these Benchmarks are run both Erasure and FS backends.
  1893  
  1894  // BenchmarkPutObjectPart5MbFS - Benchmark FS.PutObjectPart() for object size of 5MB.
  1895  func BenchmarkPutObjectPart5MbFS(b *testing.B) {
  1896  	benchmarkPutObjectPart(b, "FS", 5*humanize.MiByte)
  1897  }
  1898  
  1899  // BenchmarkPutObjectPart5MbErasure - Benchmark Erasure.PutObjectPart() for object size of 5MB.
  1900  func BenchmarkPutObjectPart5MbErasure(b *testing.B) {
  1901  	benchmarkPutObjectPart(b, "Erasure", 5*humanize.MiByte)
  1902  }
  1903  
  1904  // BenchmarkPutObjectPart10MbFS - Benchmark FS.PutObjectPart() for object size of 10MB.
  1905  func BenchmarkPutObjectPart10MbFS(b *testing.B) {
  1906  	benchmarkPutObjectPart(b, "FS", 10*humanize.MiByte)
  1907  }
  1908  
  1909  // BenchmarkPutObjectPart10MbErasure - Benchmark Erasure.PutObjectPart() for object size of 10MB.
  1910  func BenchmarkPutObjectPart10MbErasure(b *testing.B) {
  1911  	benchmarkPutObjectPart(b, "Erasure", 10*humanize.MiByte)
  1912  }
  1913  
  1914  // BenchmarkPutObjectPart25MbFS - Benchmark FS.PutObjectPart() for object size of 25MB.
  1915  func BenchmarkPutObjectPart25MbFS(b *testing.B) {
  1916  	benchmarkPutObjectPart(b, "FS", 25*humanize.MiByte)
  1917  }
  1918  
  1919  // BenchmarkPutObjectPart25MbErasure - Benchmark Erasure.PutObjectPart() for object size of 25MB.
  1920  func BenchmarkPutObjectPart25MbErasure(b *testing.B) {
  1921  	benchmarkPutObjectPart(b, "Erasure", 25*humanize.MiByte)
  1922  }
  1923  
  1924  // BenchmarkPutObjectPart50MbFS - Benchmark FS.PutObjectPart() for object size of 50MB.
  1925  func BenchmarkPutObjectPart50MbFS(b *testing.B) {
  1926  	benchmarkPutObjectPart(b, "FS", 50*humanize.MiByte)
  1927  }
  1928  
  1929  // BenchmarkPutObjectPart50MbErasure - Benchmark Erasure.PutObjectPart() for object size of 50MB.
  1930  func BenchmarkPutObjectPart50MbErasure(b *testing.B) {
  1931  	benchmarkPutObjectPart(b, "Erasure", 50*humanize.MiByte)
  1932  }