Repository: rclone/rclone
File: fstest/fstests/fstests.go (Go)

Code before:
			obji.WithMetadata(metadata).WithMimeType(mimeType)
		}
		obj, err = f.Put(ctx, in, obji)
		return err
	})
	file.Hashes = uploadHash.Sums()
	if check {
		// Overwrite time with that in metadata if it is already specified
		mtime, ok := metadata["mtime"]
		if ok {
			modTime, err := time.Parse(time.RFC3339Nano, mtime)
			require.NoError(t, err)
			file.ModTime = modTime
		}
		file.Check(t, obj, f.Precision())
		// Re-read the object and check again
		obj = fstest.NewObject(ctx, t, f, file.Path)
		file.Check(t, obj, f.Precision())
	}
	return obj
}

// PutTestContentsOptions is not compatible with the way that the b2 backend handles metadata. This function accepts
// metadata as options to be passed to Put() rather than metadata to be set in the src ObjectInfo
func PutTestContentsOptions(ctx context.Context, t *testing.T, f fs.Fs, file *fstest.Item, contents string, check bool, mimeType string, options ...fs.OpenOption) fs.Object {
	var (
		err        error
		obj        fs.Object
		uploadHash *hash.MultiHasher
	)
	retry(t, "Put", func() error {
		buf := bytes.NewBufferString(contents)
		uploadHash = hash.NewMultiHasher()
		in := io.TeeReader(buf, uploadHash)

		file.Size = int64(buf.Len())
		obji := object.NewStaticObjectInfo(file.Path, file.ModTime, file.Size, true, file.Hashes, nil)
		if mimeType != "" {
			obji.WithMimeType(mimeType)
		}
		obj, err = f.Put(ctx, in, obji, options...)
		return err
	})
	file.Hashes = uploadHash.Sums()
	if check {
		headers := fs.OpenOptionHeaders(options)
		// Overwrite time with that in options if it is already specified
		mtime, ok := headers["src_last_modified_millis"]
		if ok {
			modTime, err := time.Parse(time.RFC3339Nano, mtime)
			require.NoError(t, err)

Review comment (refactor):
I think you can probably add `options ...fs.OpenOption` as the last parameter of PutTestContentsMetadata for a neater fix.

This needs refactoring to take an options object, but that can be another change.

Code after:
				previousMetadata := ci.Metadata
				ci.Metadata = true
				defer func() {
					ci.Metadata = previousMetadata
				}()
			}
			obji.WithMetadata(metadata).WithMimeType(mimeType)
		}
		obj, err = f.Put(ctx, in, obji, options...)
		return err
	})
	file.Hashes = uploadHash.Sums()
	if check {
		// Overwrite time with that in metadata if it is already specified
		mtime, ok := metadata["mtime"]
		if ok {
			modTime, err := time.Parse(time.RFC3339Nano, mtime)
			require.NoError(t, err)
			file.ModTime = modTime
		}
		file.Check(t, obj, f.Precision())
		// Re-read the object and check again
		obj = fstest.NewObject(ctx, t, f, file.Path)
		file.Check(t, obj, f.Precision())
	}
	return obj
}

// PutTestContents puts file with given contents to the remote and checks it but unlike TestPutLarge doesn't remove
func PutTestContents(ctx context.Context, t *testing.T, f fs.Fs, file *fstest.Item, contents string, check bool) fs.Object {
	return PutTestContentsMetadata(ctx, t, f, file, false, contents, check, "", nil)
}

// testPut puts file with random contents to the remote
func testPut(ctx context.Context, t *testing.T, f fs.Fs, file *fstest.Item) (string, fs.Object) {
	return testPutMimeType(ctx, t, f, file, "", nil)
}

// testPutMimeType puts file with random contents to the remote and the mime type given
func testPutMimeType(ctx context.Context, t *testing.T, f fs.Fs, file *fstest.Item, mimeType string, metadata fs.Metadata) (string, fs.Object) {
	contents := random.String(100)
	// We just generated new contents, but file may contain hashes generated by a previous operation
	if len(file.Hashes) > 0 {
		file.Hashes = make(map[hash.Type]string)
	}
	return contents, PutTestContentsMetadata(ctx, t, f, file, false, contents, true, mimeType, metadata)
}

// testPutLarge puts file to the remote, checks it and removes it on success.
//
// If stream is set, then it uploads the file with size -1