storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/object-api-multipart_test.go (about)

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