Repository: peak/s5cmd
Pull request #511 (2022-09-13 11:43:05)

storage/url: add nil check for URL.filterRegex in URL.Match
URL.Match method assumes that URL.filterRegex is not nil and used it without check. But if the URL was created with `raw` option or was created with type literal then it might be nil. So if Match was called for such URLs then it panics.

ps. It occured in an old variants of autocompletion PR. But that part of the code was changed now, so we won't include this change there. 



Patch:
diff --git a/go.mod b/go.mod
index 6db9ffb..28fd4f4 100644
--- a/go.mod
+++ b/go.mod
@@ -7,7 +7,7 @@ require (
 	github.com/google/go-cmp v0.4.0
 	github.com/hashicorp/go-multierror v1.0.0
 	github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334
-	github.com/igungor/gofakes3 v0.0.11
+	github.com/igungor/gofakes3 v0.0.13
 	github.com/karrick/godirwalk v1.15.3
 	github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
 	github.com/posener/complete v1.2.3
diff --git a/go.sum b/go.sum
index 152f273..b35ed76 100644
--- a/go.sum
+++ b/go.sum
@@ -16,8 +16,8 @@ github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uP
 github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
 github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 h1:VHgatEHNcBFEB7inlalqfNqw65aNkM1lGX2yt3NmbS8=
 github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
-github.com/igungor/gofakes3 v0.0.11 h1:KqVnxdhlrc5LQYUBifexI/E0X7V5CUonTmvfujkmKao=
-github.com/igungor/gofakes3 v0.0.11/go.mod h1:id+2y7yvTVoN1HW6VOtnfSqFt4EAfH5piVL4p9JsmQc=
+github.com/igungor/gofakes3 v0.0.13 h1:rMi9KRo3jrqMyNnR9rc+d+cWTFR3mIQXnO1y+5gL98A=
+github.com/igungor/gofakes3 v0.0.13/go.mod h1:id+2y7yvTVoN1HW6VOtnfSqFt4EAfH5piVL4p9JsmQc=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
 github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
diff --git a/storage/s3.go b/storage/s3.go
index 778413d..701b525 100644
--- a/storage/s3.go
+++ b/storage/s3.go
@@ -150,7 +150,7 @@ func (s *S3) Stat(ctx context.Context, url *url.URL) (*Object, error) {
 // keys. If no object found or an error is encountered during this period,
 // it sends these errors to object channel.
 func (s *S3) List(ctx context.Context, url *url.URL, _ bool) <-chan *Object {
-	if isGoogleEndpoint(s.endpointURL) || s.useListObjectsV1 {
+	if IsGoogleEndpoint(s.endpointURL) || s.useListObjectsV1 {
 		return s.listObjects(ctx, url)
 	}
 
@@ -631,6 +631,12 @@ type chunk struct {
 func (s *S3) calculateChunks(ch <-chan *url.URL) <-chan chunk {
 	chunkch := make(chan chunk)
 
+	chunkSize := deleteObjectsMax
+	// delete each object individually if using gcs.
+	if IsGoogleEndpoint(s.endpointURL) {
+		chunkSize = 1
+	}
+
 	go func() {
 		defer close(chunkch)
 
@@ -645,7 +651,7 @@ func (s *S3) calculateChunks(ch <-chan *url.URL) <-chan chunk {
 
 			objid := &s3.ObjectIdentifier{Key: aws.String(url.Path)}
 			keys = append(keys, objid)
-			if len(keys) == deleteObjectsMax {
+			if len(keys) == chunkSize {
 				chunkch <- chunk{
 					Bucket: bucket,
 					Keys:   keys,
@@ -694,6 +700,25 @@ func (s *S3) doDelete(ctx context.Context, chunk chunk, resultch chan *Object) {
 		return
 	}
 
+	// GCS does not support multi delete.
+	if IsGoogleEndpoint(s.endpointURL) {
+		for _, k := range chunk.Keys {
+			_, err := s.api.DeleteObjectWithContext(ctx, &s3.DeleteObjectInput{
+				Bucket:       aws.String(chunk.Bucket),
+				Key:          k.Key,
+				RequestPayer: s.RequestPayer(),
+			})
+			if err != nil {
+				resultch <- &Object{Err: err}
+				return
+			}
+			key := fmt.Sprintf("s3://%v/%v", chunk.Bucket, aws.StringValue(k.Key))
+			url, _ := url.New(key)
+			resultch <- &Object{URL: url}
+		}
+		return
+	}
+
 	bucket := chunk.Bucket
 	o, err := s.api.DeleteObjectsWithContext(ctx, &s3.DeleteObjectsInput{
 		Bucket:       aws.String(bucket),
@@ -1008,7 +1033,7 @@ func supportsTransferAcceleration(endpoint urlpkg.URL) bool {
 	return endpoint.Hostname() == transferAccelEndpoint
 }
 
-func isGoogleEndpoint(endpoint urlpkg.URL) bool {
+func IsGoogleEndpoint(endpoint urlpkg.URL) bool {
 	return endpoint.Hostname() == gcsEndpoint
 }
 
@@ -1016,7 +1041,7 @@ func isGoogleEndpoint(endpoint urlpkg.URL) bool {
 // host style bucket name resolving. If a custom S3 API compatible endpoint is
 // given, resolve the bucketname from the URL path.
 func isVirtualHostStyle(endpoint urlpkg.URL) bool {
-	return endpoint == sentinelURL || supportsTransferAcceleration(endpoint) || isGoogleEndpoint(endpoint)
+	return endpoint == sentinelURL || supportsTransferAcceleration(endpoint) || IsGoogleEndpoint(endpoint)
 }
 
 func errHasCode(err error, code string) bool {
diff --git a/storage/url/url.go b/storage/url/url.go
index 22ab37d..755227f 100644
--- a/storage/url/url.go
+++ b/storage/url/url.go
@@ -326,6 +326,10 @@ func (u *URL) SetRelative(base *URL) {
 
 // Match reports whether if given key matches with the object.
 func (u *URL) Match(key string) bool {
+	if u.filterRegex == nil {
+		return false
+	}
+
 	if !u.filterRegex.MatchString(key) {
 		return false
 	}
diff --git a/vendor/github.com/igungor/gofakes3/backend.go b/vendor/github.com/igungor/gofakes3/backend.go
index f350560..d55798a 100644
--- a/vendor/github.com/igungor/gofakes3/backend.go
+++ b/vendor/github.com/igungor/gofakes3/backend.go
@@ -292,6 +292,9 @@ type VersionedBackend interface {
 	// ObjectDeleteResult and a nil error.
 	DeleteObjectVersion(bucketName, objectName string, versionID VersionID) (ObjectDeleteResult, error)
 
+	// DeleteMultiVersions permanently deletes all of the specified Object Versions
+	DeleteMultiVersions(bucketName string, objects ...ObjectID) (MultiDeleteResult, error)
+
 	// Backend implementers can assume the ListBucketVersionsPage is valid:
 	// KeyMarker and VersionIDMarker will either both be set, or both be unset. No
 	// other combination will be present (S300004).
diff --git a/vendor/github.com/igungor/gofakes3/backend/s3mem/backend.go b/vendor/github.com/igungor/gofakes3/backend/s3mem/backend.go
index 1483b56..27c7a4f 100644
--- a/vendor/github.com/igungor/gofakes3/backend/s3mem/backend.go
+++ b/vendor/github.com/igungor/gofakes3/backend/s3mem/backend.go
@@ -104,7 +104,8 @@ func (db *Backend) ListBucket(name string, prefix *gofakes3.Prefix, page gofakes
 
 		if !prefix.Match(item.data.name, &match) {
 			continue
-
+		} else if item.data.deleteMarker {
+			continue
 		} else if match.CommonPrefix {
 			if match.MatchedPart == lastMatchedPart {
 				continue // Should not count towards keys
@@ -298,6 +299,43 @@ func (db *Backend) DeleteMulti(bucketName string, objects ...string) (result gof
 	return result, nil
 }
 
+func (db *Backend) DeleteMultiVersions(bucketName string, objects ...gofakes3.ObjectID) (result gofakes3.MultiDeleteResult, err error) {
+	db.lock.Lock()
+	defer db.lock.Unlock()
+
+	bucket := db.buckets[bucketName]
+	if bucket == nil {
+		return result, gofakes3.BucketNotFound(bucketName)
+	}
+
+	now := db.timeSource.Now()
+
+	for _, object := range objects {
+		var dresult gofakes3.ObjectDeleteResult
+		var err error
+		if object.VersionID != "" {
+			_, err = bucket.rmVersion(object.Key, gofakes3.VersionID(object.VersionID), now)
+		} else {
+			dresult, err = bucket.rm(object.Key, now)
+			_ = dresult // FIXME: what to do with rm result in multi delete?
+		}
+
+		if err != nil {
+			errres := gofakes3.ErrorResultFromError(err)
+			if errres.Code == gofakes3.ErrInternal {
+				// FIXME: log
+			}
+
+			result.Error = append(result.Error, errres)
+
+		} else {
+			result.Deleted = append(result.Deleted, object)
+		}
+	}
+
+	return result, nil
+}
+
 func (db *Backend) VersioningConfiguration(bucketName string) (versioning gofakes3.VersioningConfiguration, rerr error) {
 	db.lock.RLock()
 	defer db.lock.RUnlock()
@@ -334,6 +372,9 @@ func (db *Backend) GetObjectVersion(
 	bucketName, objectName string,
 	versionID gofakes3.VersionID,
 	rangeRequest *gofakes3.ObjectRangeRequest) (*gofakes3.Object, error) {
+	if versionID == "" {
+		return db.GetObject(bucketName, objectName, rangeRequest)
+	}
 
 	db.lock.RLock()
 	defer db.lock.RUnlock()
@@ -352,6 +393,10 @@ func (db *Backend) GetObjectVersion(
 }
 
 func (db *Backend) HeadObjectVersion(bucketName, objectName string, versionID gofakes3.VersionID) (*gofakes3.Object, error) {
+	if versionID == "" {
+		return db.HeadObject(bucketName, objectName)
+	}
+
 	db.lock.RLock()
 	defer db.lock.RUnlock()
 
diff --git a/vendor/github.com/igungor/gofakes3/backend/s3mem/bucket.go b/vendor/github.com/igungor/gofakes3/backend/s3mem/bucket.go
index 1f9165b..f12d38e 100644
--- a/vendor/github.com/igungor/gofakes3/backend/s3mem/bucket.go
+++ b/vendor/github.com/igungor/gofakes3/backend/s3mem/bucket.go
@@ -179,6 +179,13 @@ func (b *bucket) object(objectName string) (obj *bucketObject) {
 
 func (b *bucket) objectVersion(objectName string, versionID gofakes3.VersionID) (*bucketData, error) {
 	obj := b.object(objectName)
+	if versionID == "" {
+		if obj.data.deleteMarker {
+			return nil, gofakes3.KeyNotFound(objectName)
+		}
+		return obj.data, nil
+	}
+
 	if obj == nil {
 		return nil, gofakes3.KeyNotFound(objectName)
 	}
diff --git a/vendor/github.com/igungor/gofakes3/gofakes3.go b/vendor/github.com/igungor/gofakes3/gofakes3.go
index be35b6a..9e15f58 100644
--- a/vendor/github.com/igungor/gofakes3/gofakes3.go
+++ b/vendor/github.com/igungor/gofakes3/gofakes3.go
@@ -737,12 +737,19 @@ func (g *GoFakeS3) deleteMulti(bucket string, w http.ResponseWriter, r *http.Req
 		return ErrorMessage(ErrMalformedXML, err.Error())
 	}
 
-	keys := make([]string, len(in.Objects))
-	for i, o := range in.Objects {
-		keys[i] = o.Key
+	var err error
+	var out MultiDeleteResult
+	if g.versioned == nil {
+		keys := make([]string, len(in.Objects))
+		for i, o := range in.Objects {
+			keys[i] = o.Key
+		}
+
+		out, err = g.storage.DeleteMulti(bucket, keys...)
+	} else {
+		out, err = g.versioned.DeleteMultiVersions(bucket, in.Objects...)
 	}
 
-	out, err := g.storage.DeleteMulti(bucket, keys...)
 	if err != nil {
 		return err
 	}
@@ -1013,7 +1020,7 @@ func metadataSize(meta map[string]string) int {
 func metadataHeaders(headers map[string][]string, at time.Time, sizeLimit int) (map[string]string, error) {
 	meta := make(map[string]string)
 	for hk, hv := range headers {
-		if strings.HasPrefix(hk, "X-Amz-") {
+		if strings.HasPrefix(hk, "X-Amz-") || hk == "Content-Type" {
 			meta[hk] = hv[0]
 		}
 	}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index ea39d6d..bf0e8aa 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -76,7 +76,7 @@ github.com/hashicorp/go-multierror
 # github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334
 ## explicit
 github.com/iancoleman/strcase
-# github.com/igungor/gofakes3 v0.0.11
+# github.com/igungor/gofakes3 v0.0.13
 ## explicit; go 1.13
 github.com/igungor/gofakes3
 github.com/igungor/gofakes3/backend/s3bolt


Test patch:
diff --git a/command/cp_test.go b/command/cp_test.go
index 9127d8d..914d2cc 100644
--- a/command/cp_test.go
+++ b/command/cp_test.go
@@ -40,6 +40,22 @@ func TestGuessContentType(t *testing.T) {
 					`,
 			expectedContentType: "text/html; charset=utf-8",
 		},
+		// check file extension first without checking the content
+		{
+			filename: "index*.txt",
+			content: `
+					<!DOCTYPE html>
+					<html>
+						<head>
+							<title>Hello World</title>
+						</head>
+						<body>
+							<p>Hello, World! I am s5cmd :)</p>
+						</body>
+					</html>
+					`,
+			expectedContentType: "text/plain; charset=utf-8",
+		},
 	}
 
 	for _, tc := range testcases {
diff --git a/e2e/app_test.go b/e2e/app_test.go
index b24646f..3446fb0 100644
--- a/e2e/app_test.go
+++ b/e2e/app_test.go
@@ -2,6 +2,7 @@ package e2e
 
 import (
 	"fmt"
+	"net/url"
 	"os"
 	"strconv"
 	"strings"
@@ -78,7 +79,6 @@ func TestAppDashStat(t *testing.T) {
 	t.Parallel()
 
 	const (
-		bucket                  = "bucket"
 		fileContent             = "this is a file content"
 		src                     = "file1.txt"
 		expectedOutputIfPrinted = "Operation\tTotal\tError\tSuccess\t"
@@ -89,19 +89,19 @@ func TestAppDashStat(t *testing.T) {
 		isPrintExpected bool
 	}{
 		{
-			command:         fmt.Sprintf("--stat --log %v cp s3://bucket/%v .", "trace", src),
+			command:         fmt.Sprintf("--stat --log %v ls", "trace"),
 			isPrintExpected: true,
 		},
 		{
-			command:         fmt.Sprintf("--stat --log %v cp s3://bucket/%v .", "debug", src),
+			command:         fmt.Sprintf("--stat --log %v ls", "debug"),
 			isPrintExpected: true,
 		},
 		{
-			command:         fmt.Sprintf("--stat --log %v cp s3://bucket/%v .", "info", src),
+			command:         fmt.Sprintf("--stat --log %v ls", "info"),
 			isPrintExpected: true,
 		},
 		{
-			command:         fmt.Sprintf("--stat --log %v cp s3://bucket/%v .", "error", src),
+			command:         fmt.Sprintf("--stat --log %v ls", "error"),
 			isPrintExpected: true,
 		},
 		// if level is an empty string, it ignores log levels and uses default.
@@ -120,8 +120,11 @@ func TestAppDashStat(t *testing.T) {
 			t.Parallel()
 			s3client, s5cmd := setup(t)
 
+			bucket := s3BucketFromTestName(t)
+
 			createBucket(t, s3client, bucket)
 			putFile(t, s3client, bucket, src, fileContent)
+
 			cmd := s5cmd(strings.Fields(tc.command)...)
 
 			result := icmd.RunCmd(cmd)
@@ -134,7 +137,6 @@ func TestAppDashStat(t *testing.T) {
 }
 
 func TestAppProxy(t *testing.T) {
-
 	testcases := []struct {
 		name string
 		flag string
@@ -156,6 +158,21 @@ func TestAppProxy(t *testing.T) {
 			proxy := httpProxy{}
 			pxyUrl := setupProxy(t, &proxy)
 
+			// set endpoint scheme to 'http'
+			if os.Getenv(s5cmdTestEndpointEnv) != "" {
+				origEndpoint := os.Getenv(s5cmdTestEndpointEnv)
+				endpoint, err := url.Parse(origEndpoint)
+				if err != nil {
+					t.Fatal(err)
+				}
+				endpoint.Scheme = "http"
+				os.Setenv(s5cmdTestEndpointEnv, endpoint.String())
+
+				defer func() {
+					os.Setenv(s5cmdTestEndpointEnv, origEndpoint)
+				}()
+			}
+
 			os.Setenv("http_proxy", pxyUrl)
 
 			_, s5cmd := setup(t, withProxy())
diff --git a/e2e/cat_test.go b/e2e/cat_test.go
index d8b70c4..af5e31d 100644
--- a/e2e/cat_test.go
+++ b/e2e/cat_test.go
@@ -12,11 +12,8 @@ func TestCatS3Object(t *testing.T) {
 	t.Parallel()
 
 	const (
-		bucket   = "bucket"
 		filename = "file.txt"
 	)
-
-	src := fmt.Sprintf("s3://%v/%v", bucket, filename)
 	contents, expected := getSequentialFileContent()
 
 	testcases := []struct {
@@ -29,7 +26,6 @@ func TestCatS3Object(t *testing.T) {
 			name: "cat remote object",
 			cmd: []string{
 				"cat",
-				src,
 			},
 			expected: expected,
 		},
@@ -38,7 +34,6 @@ func TestCatS3Object(t *testing.T) {
 			cmd: []string{
 				"--json",
 				"cat",
-				src,
 			},
 			expected: expected,
 			assertOps: []assertOp{
@@ -53,9 +48,14 @@ func TestCatS3Object(t *testing.T) {
 
 			s3client, s5cmd := setup(t)
 
+			bucket := s3BucketFromTestName(t)
+
 			createBucket(t, s3client, bucket)
 			putFile(t, s3client, bucket, filename, contents)
 
+			src := fmt.Sprintf("s3://%v/%v", bucket, filename)
+			tc.cmd = append(tc.cmd, src)
+
 			cmd := s5cmd(tc.cmd...)
 			result := icmd.RunCmd(cmd)
 
@@ -70,67 +70,59 @@ func TestCatS3Object(t *testing.T) {
 func TestCatS3ObjectFail(t *testing.T) {
 	t.Parallel()
 
-	const (
-		bucket   = "bucket"
-		filename = "file.txt"
-	)
-
-	bucketSrc := fmt.Sprintf("s3://%v", bucket)
-	prefixSrc := fmt.Sprintf("%v/prefix", bucketSrc)
-	src := fmt.Sprintf("%s/%v", prefixSrc, filename)
-
 	testcases := []struct {
+		src       string
 		name      string
 		cmd       []string
 		expected  map[int]compareFunc
 		assertOps []assertOp
 	}{
 		{
+			src:  "s3://%v/prefix/file.txt",
 			name: "cat non existent remote object",
 			cmd: []string{
 				"cat",
-				src,
 			},
 			expected: map[int]compareFunc{
-				0: contains(`ERROR "cat s3://bucket/prefix/file.txt": NoSuchKey: status code: 404`),
+				0: match(`ERROR "cat s3://(.*)/prefix/file\.txt": NoSuchKey:`),
 			},
 		},
 		{
+			src:  "s3://%v/prefix/file.txt",
 			name: "cat non existent remote object with json flag",
 			cmd: []string{
 				"--json",
 				"cat",
-				src,
 			},
 			expected: map[int]compareFunc{
-				0: contains(`{"operation":"cat","command":"cat s3://bucket/prefix/file.txt","error":"NoSuchKey: status code: 404,`),
+				0: match(`{"operation":"cat","command":"cat s3:\/\/(.*)\/prefix\/file\.txt","error":"NoSuchKey:`),
 			},
 			assertOps: []assertOp{
 				jsonCheck(true),
 			},
 		},
 		{
+			src:  "s3://%v/prefix/file.txt/*",
 			name: "cat remote object with glob",
 			cmd: []string{
 				"--json",
 				"cat",
-				src + "/*",
 			},
 			expected: map[int]compareFunc{
-				0: equals(`{"operation":"cat","command":"cat s3://bucket/prefix/file.txt/*","error":"remote source \"s3://bucket/prefix/file.txt/*\" can not contain glob characters"}`),
+				0: match(`{"operation":"cat","command":"cat s3:\/\/(.+)?\/prefix\/file\.txt\/\*","error":"remote source \\"s3:\/\/(.*)\/prefix\/file\.txt\/\*\\" can not contain glob characters"}`),
 			},
 			assertOps: []assertOp{
 				jsonCheck(true),
 			},
 		},
 		{
+			src:  "s3://%v/prefix/",
 			name: "cat bucket",
 			cmd: []string{
 				"cat",
-				bucketSrc,
 			},
 			expected: map[int]compareFunc{
-				0: contains(`ERROR "cat s3://bucket": remote source must be an object`),
+				0: match(`ERROR "cat s3://(.+)?": remote source must be an object`),
 			},
 		},
 	}
@@ -141,9 +133,12 @@ func TestCatS3ObjectFail(t *testing.T) {
 			t.Parallel()
 			s3client, s5cmd := setup(t)
 
+			bucket := s3BucketFromTestName(t)
 			createBucket(t, s3client, bucket)
 
+			tc.cmd = append(tc.cmd, fmt.Sprintf(tc.src, bucket))
 			cmd := s5cmd(tc.cmd...)
+
 			result := icmd.RunCmd(cmd)
 
 			result.Assert(t, icmd.Expected{ExitCode: 1})
diff --git a/e2e/cp_test.go b/e2e/cp_test.go
index af6791b..6e2d21a 100644
--- a/e2e/cp_test.go
+++ b/e2e/cp_test.go
@@ -39,73 +39,72 @@ func TestCopySingleS3ObjectToLocal(t *testing.T) {
 	t.Parallel()
 
 	const (
-		bucket      = "bucket"
 		fileContent = "this is a file content"
 	)
 
 	testcases := []struct {
-		name           string
-		src            string
-		dst            string
-		expected       fs.PathOp
-		expectedOutput string
+		name        string
+		src         string
+		dst         string
+		expected    fs.PathOp
+		expectedDst string
 	}{
 		{
-			name:           "cp s3://bucket/object .",
-			src:            "file1.txt",
-			dst:            ".",
-			expected:       fs.WithFile("file1.txt", fileContent, fs.WithMode(0644)),
-			expectedOutput: "cp s3://bucket/file1.txt file1.txt",
+			name:        "cp s3://bucket/object .",
+			src:         "file1.txt",
+			dst:         ".",
+			expected:    fs.WithFile("file1.txt", fileContent, fs.WithMode(0644)),
+			expectedDst: "file1.txt",
 		},
 		{
-			name:           "cp s3://bucket/object file",
-			src:            "file1.txt",
-			dst:            "file1.txt",
-			expected:       fs.WithFile("file1.txt", fileContent, fs.WithMode(0644)),
-			expectedOutput: "cp s3://bucket/file1.txt file1.txt",
+			name:        "cp s3://bucket/object file",
+			src:         "file1.txt",
+			dst:         "file1.txt",
+			expected:    fs.WithFile("file1.txt", fileContent, fs.WithMode(0644)),
+			expectedDst: "file1.txt",
 		},
 		{
-			name:           "cp s3://bucket/object dir/",
-			src:            "file1.txt",
-			dst:            "dir/",
-			expected:       fs.WithDir("dir", fs.WithFile("file1.txt", fileContent, fs.WithMode(0644))),
-			expectedOutput: "cp s3://bucket/file1.txt dir/file1.txt",
+			name:        "cp s3://bucket/object dir/",
+			src:         "file1.txt",
+			dst:         "dir/",
+			expected:    fs.WithDir("dir", fs.WithFile("file1.txt", fileContent, fs.WithMode(0644))),
+			expectedDst: "dir/file1.txt",
 		},
 		{
-			name:           "cp s3://bucket/object dir/file",
-			src:            "file1.txt",
-			dst:            "dir/file1.txt",
-			expected:       fs.WithDir("dir", fs.WithFile("file1.txt", fileContent, fs.WithMode(0644))),
-			expectedOutput: "cp s3://bucket/file1.txt dir/file1.txt",
+			name:        "cp s3://bucket/object dir/file",
+			src:         "file1.txt",
+			dst:         "dir/file1.txt",
+			expected:    fs.WithDir("dir", fs.WithFile("file1.txt", fileContent, fs.WithMode(0644))),
+			expectedDst: "dir/file1.txt",
 		},
 		// Cases with adjacent slashes. Expected behavior is to remove all duplicate slashes in local files.
 		{
-			name:           "cp s3://bucket//a/b///c////object .",
-			src:            "/a/b///c////file1.txt",
-			dst:            ".",
-			expected:       fs.WithFile("file1.txt", fileContent, fs.WithMode(0644)),
-			expectedOutput: "cp s3://bucket//a/b///c////file1.txt file1.txt",
+			name:        "cp s3://bucket//a/b///c////object .",
+			src:         "/a/b///c////file1.txt",
+			dst:         ".",
+			expected:    fs.WithFile("file1.txt", fileContent, fs.WithMode(0644)),
+			expectedDst: "file1.txt",
 		},
 		{
-			name:           "cp s3://bucket//a/b///c////object file",
-			src:            "/a/b///c////file1.txt",
-			dst:            "file1.txt",
-			expected:       fs.WithFile("file1.txt", fileContent, fs.WithMode(0644)),
-			expectedOutput: "cp s3://bucket//a/b///c////file1.txt file1.txt",
+			name:        "cp s3://bucket//a/b///c////object file",
+			src:         "/a/b///c////file1.txt",
+			dst:         "file1.txt",
+			expected:    fs.WithFile("file1.txt", fileContent, fs.WithMode(0644)),
+			expectedDst: "file1.txt",
 		},
 		{
-			name:           "cp s3://bucket//a/b///c////object dir/",
-			src:            "/a/b///c////file1.txt",
-			dst:            "dir/",
-			expected:       fs.WithDir("dir", fs.WithFile("file1.txt", fileContent, fs.WithMode(0644))),
-			expectedOutput: "cp s3://bucket//a/b///c////file1.txt dir/file1.txt",
+			name:        "cp s3://bucket//a/b///c////object dir/",
+			src:         "/a/b///c////file1.txt",
+			dst:         "dir/",
+			expected:    fs.WithDir("dir", fs.WithFile("file1.txt", fileContent, fs.WithMode(0644))),
+			expectedDst: "dir/file1.txt",
 		},
 		{
-			name:           "cp s3://bucket//a/b///c////object dir/file",
-			src:            "/a/b///c////file1.txt",
-			dst:            "dir/file1.txt",
-			expected:       fs.WithDir("dir", fs.WithFile("file1.txt", fileContent, fs.WithMode(0644))),
-			expectedOutput: "cp s3://bucket//a/b///c////file1.txt dir/file1.txt",
+			name:        "cp s3://bucket//a/b///c////object dir/file",
+			src:         "/a/b///c////file1.txt",
+			dst:         "dir/file1.txt",
+			expected:    fs.WithDir("dir", fs.WithFile("file1.txt", fileContent, fs.WithMode(0644))),
+			expectedDst: "dir/file1.txt",
 		},
 	}
 
@@ -114,8 +113,9 @@ func TestCopySingleS3ObjectToLocal(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			t.Parallel()
 
-			s3client, s5cmd := setup(t)
+			bucket := s3BucketFromTestName(t)
 
+			s3client, s5cmd := setup(t)
 			createBucket(t, s3client, bucket)
 
 			putFile(t, s3client, bucket, tc.src, fileContent)
@@ -125,9 +125,9 @@ func TestCopySingleS3ObjectToLocal(t *testing.T) {
 			result := icmd.RunCmd(cmd)
 
 			result.Assert(t, icmd.Success)
-
+			expectedOutput := fmt.Sprintf("cp s3://%v/%v %v", bucket, tc.src, tc.expectedDst)
 			assertLines(t, result.Stdout(), map[int]compareFunc{
-				0: equals(tc.expectedOutput),
+				0: equals(expectedOutput),
 			})
 
 			// assert local filesystem
@@ -144,10 +144,9 @@ func TestCopySingleS3ObjectToLocal(t *testing.T) {
 func TestCopySingleS3ObjectToLocalJSON(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const (
@@ -191,10 +190,9 @@ func TestCopySingleS3ObjectToLocalJSON(t *testing.T) {
 func TestCopySingleS3ObjectToLocalWithDestinationWildcard(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const (
@@ -226,10 +224,9 @@ func TestCopySingleS3ObjectToLocalWithDestinationWildcard(t *testing.T) {
 func TestCopyS3PrefixToLocalMustReturnError(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const (
@@ -262,10 +259,9 @@ func TestCopyS3PrefixToLocalMustReturnError(t *testing.T) {
 func TestCopyMultipleFlatS3ObjectsToLocal(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -312,10 +308,9 @@ func TestCopyMultipleFlatS3ObjectsToLocal(t *testing.T) {
 func TestCopyMultipleFlatS3ObjectsToLocalWithPartialMatching(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -357,10 +352,9 @@ func TestCopyMultipleFlatS3ObjectsToLocalWithPartialMatching(t *testing.T) {
 func TestCopyMultipleFlatNestedS3ObjectsToLocalWithPartialMatching(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -396,10 +390,9 @@ func TestCopyMultipleFlatNestedS3ObjectsToLocalWithPartialMatching(t *testing.T)
 func TestCopyMultipleFlatS3ObjectsToLocalJSON(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -490,10 +483,9 @@ func TestCopyMultipleFlatS3ObjectsToLocalJSON(t *testing.T) {
 func TestCopyMultipleNestedS3ObjectsToLocal(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -560,10 +552,9 @@ func TestCopyMultipleNestedS3ObjectsToLocal(t *testing.T) {
 func TestCopyMultipleNestedS3ObjectsToLocalWithPartial(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -619,10 +610,9 @@ func TestCopyMultipleNestedS3ObjectsToLocalWithPartial(t *testing.T) {
 func TestCopyMultipleS3ObjectsToGivenLocalDirectory(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -675,15 +665,14 @@ func TestCopyMultipleS3ObjectsToGivenLocalDirectory(t *testing.T) {
 func TestCopySingleFileToS3(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const (
-		// make sure that Put reads the file header, not the extension
-		filename = "index.txt"
+		// make sure that Put reads the file header and guess Content-Type correctly.
+		filename = "index"
 		content  = `
 <html lang="en">
 	<head>
@@ -729,7 +718,6 @@ func TestCopySingleFileToS3WithAdjacentSlashes(t *testing.T) {
 	t.Parallel()
 
 	const (
-		bucket   = "testbucket"
 		filename = "index.txt"
 		content  = "this is a test file"
 	)
@@ -772,6 +760,8 @@ func TestCopySingleFileToS3WithAdjacentSlashes(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			t.Parallel()
 
+			bucket := s3BucketFromTestName(t)
+
 			s3client, s5cmd := setup(t)
 
 			createBucket(t, s3client, bucket)
@@ -805,10 +795,9 @@ func TestCopySingleFileToS3WithAdjacentSlashes(t *testing.T) {
 func TestCopySingleFileToS3JSON(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const (
@@ -855,10 +844,9 @@ func TestCopySingleFileToS3JSON(t *testing.T) {
 func TestCopyDirToS3(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	folderLayout := []fs.PathOp{
@@ -906,10 +894,9 @@ func TestCopyDirBackslashedToS3(t *testing.T) {
 	}
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	folderLayout := []fs.PathOp{
@@ -948,10 +935,12 @@ func TestCopyDirBackslashedToS3(t *testing.T) {
 func TestCopySingleFileToS3WithStorageClassGlacier(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
+	// storage class GLACIER does not exist in GCS.
+	skipTestIfGCS(t, "storage class GLACIER does not exist in GCS.")
 
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const (
@@ -989,10 +978,9 @@ func TestCopySingleFileToS3WithStorageClassGlacier(t *testing.T) {
 func TestFlattenCopyDirToS3(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	folderLayout := []fs.PathOp{
@@ -1037,10 +1025,9 @@ func TestFlattenCopyDirToS3(t *testing.T) {
 func TestCopyMultipleFilesToS3Bucket(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	folderLayout := []fs.PathOp{
@@ -1094,10 +1081,9 @@ func TestCopyMultipleFilesToS3Bucket(t *testing.T) {
 func TestCopyMultipleFilesWithWildcardedDirectoryToS3Bucket(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	folderLayout := []fs.PathOp{
@@ -1146,10 +1132,9 @@ func TestCopyMultipleFilesWithWildcardedDirectoryToS3Bucket(t *testing.T) {
 func TestCopyMultipleFilesEndWildcardedToS3Bucket(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	folderLayout := []fs.PathOp{
@@ -1198,10 +1183,9 @@ func TestCopyMultipleFilesEndWildcardedToS3Bucket(t *testing.T) {
 func TestCopyMultipleFilesMiddleWildcardedDirectoryToS3Bucket(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	folderLayout := []fs.PathOp{
@@ -1243,10 +1227,9 @@ func TestCopyMultipleFilesMiddleWildcardedDirectoryToS3Bucket(t *testing.T) {
 func TestFlattenCopyMultipleFilesToS3Bucket(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	folderLayout := []fs.PathOp{
@@ -1301,10 +1284,9 @@ func TestFlattenCopyMultipleFilesToS3Bucket(t *testing.T) {
 func TestCopyMultipleFilesToS3WithPrefixWithoutSlash(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	folderLayout := []fs.PathOp{
@@ -1349,10 +1331,9 @@ func TestCopyDirectoryWithGlobCharactersToS3Bucket(t *testing.T) {
 		t.Skip("Files in Windows cannot contain glob(*) characters")
 	}
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	folderLayout := []fs.PathOp{
@@ -1400,10 +1381,9 @@ func TestCopyDirectoryWithGlobCharactersToS3Bucket(t *testing.T) {
 func TestCopyMultipleFilesToS3WithPrefixWithSlash(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	folderLayout := []fs.PathOp{
@@ -1461,10 +1441,9 @@ func TestCopyMultipleFilesToS3WithPrefixWithSlash(t *testing.T) {
 func TestFlattenCopyMultipleFilesToS3WithPrefixWithSlash(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	folderLayout := []fs.PathOp{
@@ -1522,10 +1501,9 @@ func TestFlattenCopyMultipleFilesToS3WithPrefixWithSlash(t *testing.T) {
 func TestCopyLocalDirectoryToS3WithPrefixWithSlash(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	folderLayout := []fs.PathOp{
@@ -1581,10 +1559,9 @@ func TestCopyLocalDirectoryToS3WithPrefixWithSlash(t *testing.T) {
 func TestFlattenCopyLocalDirectoryToS3WithPrefixWithSlash(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	folderLayout := []fs.PathOp{
@@ -1643,10 +1620,9 @@ func TestFlattenCopyLocalDirectoryToS3WithPrefixWithSlash(t *testing.T) {
 func TestCopyLocalDirectoryToS3WithPrefixWithoutSlash(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -1687,10 +1663,9 @@ func TestCopyLocalDirectoryToS3WithPrefixWithoutSlash(t *testing.T) {
 func TestCopySingleS3ObjectToS3(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const (
@@ -1724,10 +1699,9 @@ func TestCopySingleS3ObjectToS3(t *testing.T) {
 func TestCopySingleS3ObjectToS3JSON(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const (
@@ -1775,7 +1749,7 @@ func TestCopySingleS3ObjectIntoAnotherBucket(t *testing.T) {
 	t.Parallel()
 
 	srcbucket := s3BucketFromTestName(t)
-	dstbucket := "copy-" + s3BucketFromTestName(t)
+	dstbucket := s3BucketFromTestNameWithPrefix(t, "copy")
 
 	s3client, s5cmd := setup(t)
 
@@ -1813,7 +1787,7 @@ func TestFlattenCopySingleS3ObjectIntoAnotherBucket(t *testing.T) {
 	t.Parallel()
 
 	srcbucket := s3BucketFromTestName(t)
-	dstbucket := "copy-" + s3BucketFromTestName(t)
+	dstbucket := s3BucketFromTestNameWithPrefix(t, "copy")
 
 	s3client, s5cmd := setup(t)
 
@@ -1850,10 +1824,8 @@ func TestFlattenCopySingleS3ObjectIntoAnotherBucket(t *testing.T) {
 func TestCopySingleS3ObjectIntoAnotherBucketWithObjName(t *testing.T) {
 	t.Parallel()
 
-	const (
-		srcbucket = "bucket"
-		dstbucket = "dstbucket"
-	)
+	srcbucket := s3BucketFromTestNameWithPrefix(t, "src")
+	dstbucket := s3BucketFromTestNameWithPrefix(t, "dst")
 
 	s3client, s5cmd := setup(t)
 
@@ -1890,10 +1862,9 @@ func TestCopySingleS3ObjectIntoAnotherBucketWithObjName(t *testing.T) {
 func TestCopySingleS3ObjectIntoAnotherBucketWithPrefix(t *testing.T) {
 	t.Parallel()
 
-	const bucket = "bucket"
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const (
@@ -1926,10 +1897,13 @@ func TestCopySingleS3ObjectIntoAnotherBucketWithPrefix(t *testing.T) {
 func TestCopyAllObjectsIntoAnotherBucketIncludingSpecialCharacter(t *testing.T) {
 	t.Parallel()
 
-	const (
-		srcbucket = "bucket"
-		dstbucket = "dstbucket"
-	)
+	// This test fails with GCS as it accepts percent encoding
+	// for `X-Amz-Copy-Source` and does not respect `+` as `space character`.
+	// skip it as it is not planned to write a workaround for GCS.
+	skipTestIfGCS(t, "GCS does not respect `+` as `space character` in `X-Amz-Copy-Source`")
+
+	srcbucket := s3BucketFromTestNameWithPrefix(t, "src")
+	dstbucket := s3BucketFromTestNameWithPrefix(t, "dst")
 
 	s3client, s5cmd := setup(t)
 
@@ -1968,10 +1942,9 @@ func TestCopyAllObjectsIntoAnotherBucketIncludingSpecialCharacter(t *testing.T)
 func TestCopyMultipleS3ObjectsToS3WithPrefix(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -2015,10 +1988,9 @@ func TestCopyMultipleS3ObjectsToS3WithPrefix(t *testing.T) {
 func TestFlattenCopyMultipleS3ObjectsToS3WithPrefix(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -2069,8 +2041,7 @@ func TestFlattenCopyMultipleS3ObjectsToS3WithPrefix(t *testing.T) {
 func TestCopyMultipleS3ObjectsToS3WithPrefixWithoutSlash(t *testing.T) {
 	t.Parallel()
 
-	const bucket = "bucket"
-
+	bucket := s3BucketFromTestName(t)
 	s3client, s5cmd := setup(t)
 
 	createBucket(t, s3client, bucket)
@@ -2109,10 +2080,9 @@ func TestCopyMultipleS3ObjectsToS3WithPrefixWithoutSlash(t *testing.T) {
 func TestCopyMultipleS3ObjectsToS3JSON(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -2174,10 +2144,9 @@ func TestCopyMultipleS3ObjectsToS3JSON(t *testing.T) {
 func TestCopyMultipleS3ObjectsToS3_Issue70(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -2407,10 +2376,9 @@ func TestCopyS3ToLocalWithSameFilenameDontOverrideIfS3ObjectIsOlder(t *testing.T
 func TestCopyS3ToLocal_Issue70(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -2708,6 +2676,9 @@ func TestCopyLocalFileToS3WithFilePermissions(t *testing.T) {
 	t.Parallel()
 
 	bucket := s3BucketFromTestName(t)
+	s3client, s5cmd := setup(t)
+
+	createBucket(t, s3client, bucket)
 
 	const (
 		filename = "testfile1.txt"
@@ -2717,9 +2688,6 @@ func TestCopyLocalFileToS3WithFilePermissions(t *testing.T) {
 	fileModes := []os.FileMode{0400, 0440, 0444, 0600, 0640, 0644, 0700, 0750, 0755}
 
 	for _, fileMode := range fileModes {
-		s3client, s5cmd := setup(t)
-
-		createBucket(t, s3client, bucket)
 
 		workdir := fs.NewDir(t, t.Name(), fs.WithFile(filename, content, fs.WithMode(fileMode)))
 		defer workdir.Remove()
@@ -2861,7 +2829,7 @@ func TestCopyMultipleLocalNestedFilesToS3(t *testing.T) {
 
 	s3client, s5cmd := setup(t)
 
-	const bucket = "bucket"
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	// nested folder layout
@@ -2930,7 +2898,7 @@ func TestCopyLinkToASingleFileWithFollowSymlinkDisabled(t *testing.T) {
 
 	s3client, s5cmd := setup(t)
 
-	const bucket = "bucket"
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	fileContent := "CAFEBABE"
@@ -2962,7 +2930,7 @@ func TestCopyWithFollowSymlink(t *testing.T) {
 
 	s3client, s5cmd := setup(t)
 
-	const bucket = "bucket"
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	fileContent := "CAFEBABE"
@@ -3004,7 +2972,7 @@ func TestCopyErrorWhenGivenObjectIsNotFoundUsingWildcard(t *testing.T) {
 
 	s3client, s5cmd := setup(t)
 
-	const bucket = "bucket"
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	folderLayout := []fs.PathOp{
@@ -3035,7 +3003,7 @@ func TestCopyWithNoFollowSymlink(t *testing.T) {
 
 	s3client, s5cmd := setup(t)
 
-	const bucket = "bucket"
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	fileContent := "CAFEBABE"
@@ -3072,10 +3040,9 @@ func TestCopyWithNoFollowSymlink(t *testing.T) {
 func TestCopyDirToS3DryRun(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	folderLayout := []fs.PathOp{
@@ -3118,10 +3085,9 @@ func TestCopyDirToS3DryRun(t *testing.T) {
 func TestCopyS3ToDirDryRun(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	files := [...]string{"c/file2.txt", "file1.txt"}
@@ -3157,15 +3123,10 @@ func TestCopyLocalObjectstoS3WithRawFlag(t *testing.T) {
 	}
 
 	t.Parallel()
-	const (
-		bucket      = "bucket"
-		fileContent = "this is a file content"
-	)
 
 	testcases := []struct {
 		name             string
 		src              []fs.PathOp
-		dst              string
 		wantedFile       string
 		expectedFiles    []string
 		nonExpectedFiles []string
@@ -3180,7 +3141,6 @@ func TestCopyLocalObjectstoS3WithRawFlag(t *testing.T) {
 				fs.WithFile("file*2.txt", "content"),
 			},
 			wantedFile:       "file*.txt",
-			dst:              "s3://bucket/",
 			expectedFiles:    []string{"file*.txt"},
 			nonExpectedFiles: []string{"file*1.txt", "file*file.txt", "file*2.txt"},
 			rawFlag:          "--raw",
@@ -3194,7 +3154,6 @@ func TestCopyLocalObjectstoS3WithRawFlag(t *testing.T) {
 				fs.WithFile("file*2.txt", "content"),
 			},
 			wantedFile:       "file*.txt",
-			dst:              "s3://bucket/",
 			expectedFiles:    []string{"file*.txt", "file*1.txt", "file*file.txt", "file*2.txt"},
 			nonExpectedFiles: []string{},
 			rawFlag:          "",
@@ -3216,7 +3175,6 @@ func TestCopyLocalObjectstoS3WithRawFlag(t *testing.T) {
 				fs.WithFile("file4.txt", "content"),
 			},
 			wantedFile:       "a*/file*.txt",
-			dst:              "s3://bucket/",
 			expectedFiles:    []string{"file*.txt"}, // when full path entered, the base part is uploaded.
 			nonExpectedFiles: []string{"a*/file*.txt", "a*/file*1.txt", "a*b/file*2.txt", "a*/file*3.txt", "file*4.txt", "file*1.txt", "file*2.txt", "file*3.txt"},
 			rawFlag:          "--raw",
@@ -3228,6 +3186,8 @@ func TestCopyLocalObjectstoS3WithRawFlag(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			t.Parallel()
 
+			bucket := s3BucketFromTestName(t)
+
 			s3client, s5cmd := setup(t)
 
 			createBucket(t, s3client, bucket)
@@ -3236,10 +3196,11 @@ func TestCopyLocalObjectstoS3WithRawFlag(t *testing.T) {
 			defer workdir.Remove()
 
 			srcpath := filepath.ToSlash(workdir.Join(tc.wantedFile))
+			dst := fmt.Sprintf("s3://%v", bucket)
 
-			cmd := s5cmd("cp", srcpath, tc.dst)
+			cmd := s5cmd("cp", srcpath, dst)
 			if tc.rawFlag != "" {
-				cmd = s5cmd("cp", tc.rawFlag, srcpath, tc.dst)
+				cmd = s5cmd("cp", tc.rawFlag, srcpath, dst)
 			}
 
 			result := icmd.RunCmd(cmd)
@@ -3272,10 +3233,9 @@ func TestCopyDirToS3WithRawFlag(t *testing.T) {
 
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	folderLayout := []fs.PathOp{
@@ -3335,7 +3295,6 @@ func TestCopyS3ObjectstoLocalWithRawFlag(t *testing.T) {
 
 	t.Parallel()
 	const (
-		bucket      = "bucket"
 		fileContent = "this is a file content"
 	)
 
@@ -3396,6 +3355,8 @@ func TestCopyS3ObjectstoLocalWithRawFlag(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			t.Parallel()
 
+			bucket := s3BucketFromTestName(t)
+
 			s3client, s5cmd := setup(t)
 
 			createBucket(t, s3client, bucket)
@@ -3429,13 +3390,13 @@ func TestCopyS3ObjectstoLocalWithRawFlag(t *testing.T) {
 func TestCopyMultipleS3ObjectsToS3WithRawMode(t *testing.T) {
 	t.Parallel()
 
-	const bucket = "bucket"
-	const destBucket = "destbucket"
+	srcbucket := s3BucketFromTestNameWithPrefix(t, "src")
+	dstbucket := s3BucketFromTestNameWithPrefix(t, "dst")
 
 	s3client, s5cmd := setup(t)
 
-	createBucket(t, s3client, bucket)
-	createBucket(t, s3client, destBucket)
+	createBucket(t, s3client, srcbucket)
+	createBucket(t, s3client, dstbucket)
 
 	filesToContent := map[string]string{
 		"file*.txt":      "this is a test file 1",
@@ -3445,11 +3406,11 @@ func TestCopyMultipleS3ObjectsToS3WithRawMode(t *testing.T) {
 	}
 
 	for filename, content := range filesToContent {
-		putFile(t, s3client, bucket, filename, content)
+		putFile(t, s3client, srcbucket, filename, content)
 	}
 
-	src := fmt.Sprintf("s3://%v/file*.txt", bucket)
-	dst := fmt.Sprintf("s3://%v", destBucket)
+	src := fmt.Sprintf("s3://%v/file*.txt", srcbucket)
+	dst := fmt.Sprintf("s3://%v", dstbucket)
 
 	cmd := s5cmd("cp", "--raw", src, dst)
 	result := icmd.RunCmd(cmd)
@@ -3462,7 +3423,7 @@ func TestCopyMultipleS3ObjectsToS3WithRawMode(t *testing.T) {
 
 	// assert s3 source objects
 	for filename, content := range filesToContent {
-		assert.Assert(t, ensureS3Object(s3client, bucket, filename, content))
+		assert.Assert(t, ensureS3Object(s3client, srcbucket, filename, content))
 	}
 
 	expectedFiles := map[string]string{
@@ -3471,21 +3432,21 @@ func TestCopyMultipleS3ObjectsToS3WithRawMode(t *testing.T) {
 
 	// assert s3 objects in destination.
 	for filename, content := range expectedFiles {
-		assert.Assert(t, ensureS3Object(s3client, destBucket, filename, content))
+		assert.Assert(t, ensureS3Object(s3client, dstbucket, filename, content))
 	}
 }
 
-// cp --raw s3://bucket/file* s3://destbucket
+// cp --raw s3://srcbucket/file* s3://dstbucket
 func TestCopyMultipleS3ObjectsWithPrefixToS3WithRawMode(t *testing.T) {
 	t.Parallel()
 
-	const bucket = "bucket"
-	const destBucket = "destbucket"
+	srcbucket := s3BucketFromTestNameWithPrefix(t, "src")
+	dstbucket := s3BucketFromTestNameWithPrefix(t, "dst")
 
 	s3client, s5cmd := setup(t)
 
-	createBucket(t, s3client, bucket)
-	createBucket(t, s3client, destBucket)
+	createBucket(t, s3client, srcbucket)
+	createBucket(t, s3client, dstbucket)
 
 	filesToContent := map[string]string{
 		"file*/file.txt":   "this is a test file 1 in file*",
@@ -3495,11 +3456,11 @@ func TestCopyMultipleS3ObjectsWithPrefixToS3WithRawMode(t *testing.T) {
 	}
 
 	for filename, content := range filesToContent {
-		putFile(t, s3client, bucket, filename, content)
+		putFile(t, s3client, srcbucket, filename, content)
 	}
 
-	src := fmt.Sprintf("s3://%v/file*", bucket)
-	dst := fmt.Sprintf("s3://%v", destBucket)
+	src := fmt.Sprintf("s3://%v/file*", srcbucket)
+	dst := fmt.Sprintf("s3://%v", dstbucket)
 
 	cmd := s5cmd("cp", "--raw", src, dst)
 	result := icmd.RunCmd(cmd)
@@ -3517,10 +3478,9 @@ func TestCopyMultipleS3ObjectsWithPrefixToS3WithRawMode(t *testing.T) {
 func TestCopyRawModeAllowDestinationWithoutPrefix(t *testing.T) {
 	t.Parallel()
 
-	const bucket = "bucket"
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -3570,10 +3530,9 @@ func TestCopyRawModeAllowDestinationWithoutPrefix(t *testing.T) {
 func TestCopyS3ObjectsWithExcludeFilter(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const (
@@ -3623,10 +3582,9 @@ func TestCopyS3ObjectsWithExcludeFilter(t *testing.T) {
 func TestCopyS3ObjectsWithExcludeFilters(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const (
@@ -3676,10 +3634,9 @@ func TestCopyS3ObjectsWithExcludeFilters(t *testing.T) {
 func TestCopyS3ObjectsWithPrefixWithExcludeFilters(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const (
@@ -3759,7 +3716,7 @@ func TestCopyLocalDirectoryToS3WithExcludeFilter(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			t.Parallel()
 
-			bucket := "testbucket"
+			bucket := s3BucketFromTestName(t)
 
 			s3client, s5cmd := setup(t)
 
@@ -3825,10 +3782,9 @@ func TestCopyLocalDirectoryToS3WithExcludeFilter(t *testing.T) {
 func TestCopyLocalDirectoryToS3WithExcludeFilters(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	folderLayout := []fs.PathOp{
@@ -3895,10 +3851,8 @@ func TestCopyLocalDirectoryToS3WithExcludeFilters(t *testing.T) {
 func TestCopySingleS3ObjectsIntoAnotherBucketWithExcludeFilter(t *testing.T) {
 	t.Parallel()
 
-	const (
-		srcbucket = "bucket"
-		dstbucket = "dstbucket"
-	)
+	srcbucket := s3BucketFromTestNameWithPrefix(t, "src")
+	dstbucket := s3BucketFromTestNameWithPrefix(t, "dst")
 
 	s3client, s5cmd := setup(t)
 
@@ -3971,10 +3925,8 @@ func TestCopySingleS3ObjectsIntoAnotherBucketWithExcludeFilter(t *testing.T) {
 func TestCopySingleS3ObjectsIntoAnotherBucketWithExcludeFilters(t *testing.T) {
 	t.Parallel()
 
-	const (
-		srcbucket = "bucket"
-		dstbucket = "dstbucket"
-	)
+	srcbucket := s3BucketFromTestNameWithPrefix(t, "src")
+	dstbucket := s3BucketFromTestNameWithPrefix(t, "dst")
 
 	s3client, s5cmd := setup(t)
 
@@ -4071,10 +4023,9 @@ func TestCopyExpectExitCode1OnUnreachableHost(t *testing.T) {
 func TestCopySingleFileToS3WithNoSuchUploadRetryCount(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const (
diff --git a/e2e/du_test.go b/e2e/du_test.go
index d7ba6f5..b72b2ef 100644
--- a/e2e/du_test.go
+++ b/e2e/du_test.go
@@ -10,10 +10,9 @@ import (
 func TestDiskUsageSingleS3Object(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	// create 2 files, expect 1.
@@ -33,10 +32,8 @@ func TestDiskUsageSingleS3Object(t *testing.T) {
 func TestDiskUsageSingleS3ObjectJSON(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
-
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	// create 2 files, expect 1.
@@ -51,21 +48,19 @@ func TestDiskUsageSingleS3ObjectJSON(t *testing.T) {
 	assertLines(t, result.Stdout(), map[int]compareFunc{
 		0: json(`
 			{
-				"source": "s3://test-disk-usage-single-s-3-object-json/testfile1.txt",
+				"source": "s3://%v/testfile1.txt",
 				"count":1,
 				"size":22
 			}
-		`),
+		`, bucket),
 	})
 }
 
 func TestDiskUsageMultipleS3Objects(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
-
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	putFile(t, s3client, bucket, "testfile1.txt", "this is a file content")
@@ -85,10 +80,9 @@ func TestDiskUsageMultipleS3Objects(t *testing.T) {
 func TestDiskUsageWildcard(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 	putFile(t, s3client, bucket, "testfile1.txt", "this is a file content")
 	putFile(t, s3client, bucket, "testfile2.txt", "this is also a file content")
@@ -108,10 +102,9 @@ func TestDiskUsageWildcard(t *testing.T) {
 func TestDiskUsageS3ObjectsAndFolders(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 	putFile(t, s3client, bucket, "testfile1.txt", "content")
 	putFile(t, s3client, bucket, "report.gz", "content")
@@ -137,10 +130,9 @@ func TestDiskUsageS3ObjectsAndFolders(t *testing.T) {
 func TestDiskUsageWildcardS3ObjectsWithDashH(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	putFile(t, s3client, bucket, "testfile1.txt", strings.Repeat("this is a file content", 10000))
@@ -160,10 +152,9 @@ func TestDiskUsageWildcardS3ObjectsWithDashH(t *testing.T) {
 func TestDiskUsageMissingObject(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	cmd := s5cmd("du", "s3://"+bucket+"/non-existent-file")
diff --git a/e2e/ls_test.go b/e2e/ls_test.go
index eb1f75b..e699874 100644
--- a/e2e/ls_test.go
+++ b/e2e/ls_test.go
@@ -28,13 +28,17 @@ func TestListBuckets(t *testing.T) {
 
 	result.Assert(t, icmd.Success)
 
+	// find the first created bucket
+	startIdx := strings.Index(result.Stdout(), fmt.Sprintf("s3://%v-1", bucketPrefix))
+	got := result.Stdout()[startIdx:]
+
 	// expect ordered list
-	assertLines(t, result.Stdout(), map[int]compareFunc{
+	assertLines(t, got, map[int]compareFunc{
 		0: suffix("s3://%v-1", bucketPrefix),
 		1: suffix("s3://%v-2", bucketPrefix),
 		2: suffix("s3://%v-3", bucketPrefix),
 		3: suffix("s3://%v-4", bucketPrefix),
-	})
+	}, strictLineCheck(false))
 }
 
 // -json ls bucket
@@ -52,26 +56,31 @@ func TestListBucketsJSON(t *testing.T) {
 
 	cmd := s5cmd("--json", "ls")
 	result := icmd.RunCmd(cmd)
-
 	result.Assert(t, icmd.Success)
 
+	stdout := result.Stdout()
+	// find the first created bucket
+	startIdx := strings.Index(stdout, fmt.Sprintf(`"name":"%v-1"`, bucketPrefix))
+	// find the start of the line
+	startOfLine := strings.LastIndex(stdout[:startIdx], "{")
+	beginningOfOutput := stdout[startOfLine:]
+
 	// expect ordered list
-	assertLines(t, result.Stdout(), map[int]compareFunc{
+	assertLines(t, beginningOfOutput, map[int]compareFunc{
 		0: suffix(`"name":"%v-1"}`, bucketPrefix),
 		1: suffix(`"name":"%v-2"}`, bucketPrefix),
 		2: suffix(`"name":"%v-3"}`, bucketPrefix),
 		3: suffix(`"name":"%v-4"}`, bucketPrefix),
-	}, jsonCheck(true))
+	}, jsonCheck(true), strictLineCheck(false))
 }
 
 // ls bucket/object
 func TestListSingleS3Object(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	// create 2 files, expect 1.
@@ -92,10 +101,9 @@ func TestListSingleS3Object(t *testing.T) {
 func TestListSingleS3ObjectJSON(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	// create 2 files, expect 1.
@@ -116,10 +124,9 @@ func TestListSingleS3ObjectJSON(t *testing.T) {
 func TestListSingleWildcardS3Object(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 	putFile(t, s3client, bucket, "testfile1.txt", "this is a file content")
 	putFile(t, s3client, bucket, "testfile2.txt", "this is also a file content")
@@ -141,10 +148,9 @@ func TestListSingleWildcardS3Object(t *testing.T) {
 func TestListS3ObjectsWithDashS(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 	putFile(t, s3client, bucket, "testfile1.txt", "this is a file content")
 
@@ -160,10 +166,9 @@ func TestListS3ObjectsWithDashS(t *testing.T) {
 func TestListMultipleWildcardS3Object(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 	putFile(t, s3client, bucket, "a/testfile1.txt", "content")
 	putFile(t, s3client, bucket, "a/testfile2.txt", "content")
@@ -196,10 +201,9 @@ func TestListMultipleWildcardS3Object(t *testing.T) {
 func TestListMultipleWildcardS3ObjectWithPrefix(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 	putFile(t, s3client, bucket, "a/testfile1.txt", "content")
 	putFile(t, s3client, bucket, "a/testfile2.txt", "content")
@@ -229,21 +233,20 @@ func TestListMultipleWildcardS3ObjectWithPrefix(t *testing.T) {
 func TestListS3ObjectsAndFolders(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 	putFile(t, s3client, bucket, "testfile1.txt", "content")
 	putFile(t, s3client, bucket, "report.gz", "content")
-	putFile(t, s3client, bucket, "/a/testfile2.txt", "content")
-	putFile(t, s3client, bucket, "/b/testfile3.txt", "content")
-	putFile(t, s3client, bucket, "/b/testfile4.txt", "content")
-	putFile(t, s3client, bucket, "/c/testfile5.gz", "content")
-	putFile(t, s3client, bucket, "/d/foo/bar/file7.txt", "content")
-	putFile(t, s3client, bucket, "/d/foo/bar/testfile8.txt", "content")
-	putFile(t, s3client, bucket, "/e/txt/testfile9.txt.gz", "content")
-	putFile(t, s3client, bucket, "/f/txt/testfile10.txt", "content")
+	putFile(t, s3client, bucket, "a/testfile2.txt", "content")
+	putFile(t, s3client, bucket, "b/testfile3.txt", "content")
+	putFile(t, s3client, bucket, "b/testfile4.txt", "content")
+	putFile(t, s3client, bucket, "c/testfile5.gz", "content")
+	putFile(t, s3client, bucket, "d/foo/bar/file7.txt", "content")
+	putFile(t, s3client, bucket, "d/foo/bar/testfile8.txt", "content")
+	putFile(t, s3client, bucket, "e/txt/testfile9.txt.gz", "content")
+	putFile(t, s3client, bucket, "f/txt/testfile10.txt", "content")
 
 	cmd := s5cmd("ls", "s3://"+bucket)
 	result := icmd.RunCmd(cmd)
@@ -266,15 +269,14 @@ func TestListS3ObjectsAndFolders(t *testing.T) {
 func TestListS3ObjectsAndFoldersWithPrefix(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 	putFile(t, s3client, bucket, "testfile1.txt", "content")
 	putFile(t, s3client, bucket, "report.gz", "content")
-	putFile(t, s3client, bucket, "/a/testfile2.txt", "content")
-	putFile(t, s3client, bucket, "/t/testfile3.txt", "content")
+	putFile(t, s3client, bucket, "a/testfile2.txt", "content")
+	putFile(t, s3client, bucket, "t/testfile3.txt", "content")
 
 	// search with prefix t
 	cmd := s5cmd("ls", "s3://"+bucket+"/t")
@@ -292,10 +294,9 @@ func TestListS3ObjectsAndFoldersWithPrefix(t *testing.T) {
 func TestListNonexistingS3ObjectInGivenPrefix(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const pattern = "/*/testfile*.txt"
@@ -307,7 +308,7 @@ func TestListNonexistingS3ObjectInGivenPrefix(t *testing.T) {
 	assertLines(t, result.Stdout(), map[int]compareFunc{})
 
 	assertLines(t, result.Stderr(), map[int]compareFunc{
-		0: equals(`ERROR "ls s3://test-list-nonexisting-s-3-object-in-given-prefix/*/testfile*.txt": no object found`),
+		0: equals(`ERROR "ls s3://%v/*/testfile*.txt": no object found`, bucket),
 	}, strictLineCheck(false))
 }
 
@@ -315,10 +316,9 @@ func TestListNonexistingS3ObjectInGivenPrefix(t *testing.T) {
 func TestListNonexistingS3Object(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	cmd := s5cmd("ls", "s3://"+bucket+"/nosuchobject")
@@ -337,10 +337,9 @@ func TestListNonexistingS3Object(t *testing.T) {
 func TestListS3ObjectsWithDashE(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	putFile(t, s3client, bucket, "testfile1.txt", strings.Repeat("this is a file content", 10000))
@@ -361,10 +360,9 @@ func TestListS3ObjectsWithDashE(t *testing.T) {
 func TestListS3ObjectsWithDashH(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	putFile(t, s3client, bucket, "testfile1.txt", strings.Repeat("this is a file content", 10000))
diff --git a/e2e/mb_test.go b/e2e/mb_test.go
index bc40a96..1053ae4 100644
--- a/e2e/mb_test.go
+++ b/e2e/mb_test.go
@@ -14,8 +14,8 @@ func TestMakeBucket_success(t *testing.T) {
 
 	s3client, s5cmd := setup(t)
 
-	bucketName := "test-bucket"
-	src := fmt.Sprintf("s3://%s", bucketName)
+	bucket := s3BucketFromTestName(t)
+	src := fmt.Sprintf("s3://%s", bucket)
 
 	cmd := s5cmd("mb", src)
 	result := icmd.RunCmd(cmd)
@@ -26,10 +26,18 @@ func TestMakeBucket_success(t *testing.T) {
 		0: equals(`mb %v`, src),
 	})
 
-	_, err := s3client.HeadBucket(&s3.HeadBucketInput{Bucket: aws.String(bucketName)})
+	_, err := s3client.HeadBucket(&s3.HeadBucketInput{Bucket: aws.String(bucket)})
 	if err != nil {
 		t.Errorf("unexpected error %v", err)
 	}
+
+	// cleanup the bucket later:
+	_, err = s3client.DeleteBucket(&s3.DeleteBucketInput{
+		Bucket: aws.String(bucket),
+	})
+	if err != nil {
+		t.Fatal(err)
+	}
 }
 
 func TestMakeBucket_success_json(t *testing.T) {
@@ -37,8 +45,8 @@ func TestMakeBucket_success_json(t *testing.T) {
 
 	s3client, s5cmd := setup(t)
 
-	bucketName := "test-bucket"
-	src := fmt.Sprintf("s3://%s", bucketName)
+	bucket := s3BucketFromTestName(t)
+	src := fmt.Sprintf("s3://%s", bucket)
 
 	cmd := s5cmd("--json", "mb", src)
 	result := icmd.RunCmd(cmd)
@@ -57,10 +65,18 @@ func TestMakeBucket_success_json(t *testing.T) {
 		0: json(jsonText, src),
 	}, jsonCheck(true))
 
-	_, err := s3client.HeadBucket(&s3.HeadBucketInput{Bucket: aws.String(bucketName)})
+	_, err := s3client.HeadBucket(&s3.HeadBucketInput{Bucket: aws.String(bucket)})
 	if err != nil {
 		t.Errorf("unexpected error %v", err)
 	}
+
+	// cleanup the bucket later:
+	_, err = s3client.DeleteBucket(&s3.DeleteBucketInput{
+		Bucket: aws.String(bucket),
+	})
+	if err != nil {
+		t.Fatal(err)
+	}
 }
 
 func TestMakeBucket_failure(t *testing.T) {
@@ -68,8 +84,8 @@ func TestMakeBucket_failure(t *testing.T) {
 
 	_, s5cmd := setup(t)
 
-	bucketName := "invalid/bucket/name"
-	src := fmt.Sprintf("s3://%s", bucketName)
+	bucket := "invalid/bucket/name"
+	src := fmt.Sprintf("s3://%s", bucket)
 	cmd := s5cmd("mb", src)
 
 	result := icmd.RunCmd(cmd)
@@ -86,8 +102,8 @@ func TestMakeBucket_failure_json(t *testing.T) {
 
 	_, s5cmd := setup(t)
 
-	bucketName := "invalid/bucket/name"
-	src := fmt.Sprintf("s3://%s", bucketName)
+	bucket := "invalid/bucket/name"
+	src := fmt.Sprintf("s3://%s", bucket)
 	cmd := s5cmd("--json", "mb", src)
 
 	result := icmd.RunCmd(cmd)
diff --git a/e2e/mv_test.go b/e2e/mv_test.go
index 272dbd4..510e7aa 100644
--- a/e2e/mv_test.go
+++ b/e2e/mv_test.go
@@ -15,10 +15,9 @@ import (
 func TestMoveSingleS3ObjectToLocal(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const (
@@ -50,10 +49,9 @@ func TestMoveSingleS3ObjectToLocal(t *testing.T) {
 func TestMoveMultipleS3ObjectsToLocal(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -99,10 +97,9 @@ func TestMoveMultipleS3ObjectsToLocal(t *testing.T) {
 func TestMoveSingleFileToS3(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const content = "this is a test file"
@@ -135,10 +132,9 @@ func TestMoveSingleFileToS3(t *testing.T) {
 func TestMoveMultipleFilesToS3(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -186,10 +182,9 @@ func TestMoveMultipleFilesToS3(t *testing.T) {
 func TestMoveSingleS3ObjectToS3(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const (
@@ -224,7 +219,7 @@ func TestMoveSingleS3ObjectIntoAnotherBucket(t *testing.T) {
 	t.Parallel()
 
 	srcbucket := s3BucketFromTestName(t)
-	dstbucket := "copy-" + s3BucketFromTestName(t)
+	dstbucket := s3BucketFromTestNameWithPrefix(t, "copy")
 
 	s3client, s5cmd := setup(t)
 
@@ -262,10 +257,9 @@ func TestMoveSingleS3ObjectIntoAnotherBucket(t *testing.T) {
 func TestMoveMultipleS3ObjectsToS3(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -310,10 +304,9 @@ func TestMoveMultipleS3ObjectsToS3(t *testing.T) {
 func TestMoveMultipleS3ObjectsToS3DryRun(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -362,10 +355,9 @@ func TestMoveLocalObjectToS3WithRawFlag(t *testing.T) {
 
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	objectsToMove := []fs.PathOp{
diff --git a/e2e/rb_test.go b/e2e/rb_test.go
index 9fac987..bf74e86 100644
--- a/e2e/rb_test.go
+++ b/e2e/rb_test.go
@@ -15,10 +15,10 @@ func TestRemoveBucketSuccess(t *testing.T) {
 
 	s3client, s5cmd := setup(t)
 
-	bucketName := "bucket"
-	src := fmt.Sprintf("s3://%v", bucketName)
+	bucket := s3BucketFromTestName(t)
+	src := fmt.Sprintf("s3://%v", bucket)
 
-	createBucket(t, s3client, bucketName)
+	createBucket(t, s3client, bucket)
 
 	cmd := s5cmd("rb", src)
 	result := icmd.RunCmd(cmd)
@@ -29,7 +29,7 @@ func TestRemoveBucketSuccess(t *testing.T) {
 		0: equals(`rb %v`, src),
 	})
 
-	_, err := s3client.HeadBucket(&s3.HeadBucketInput{Bucket: aws.String(bucketName)})
+	_, err := s3client.HeadBucket(&s3.HeadBucketInput{Bucket: aws.String(bucket)})
 
 	if err == nil {
 		t.Errorf("bucket still exists after remove bucket operation\n")
@@ -41,10 +41,10 @@ func TestRemoveBucketSuccessJson(t *testing.T) {
 
 	s3client, s5cmd := setup(t)
 
-	bucketName := "bucket"
-	src := fmt.Sprintf("s3://%v", bucketName)
+	bucket := s3BucketFromTestName(t)
+	src := fmt.Sprintf("s3://%v", bucket)
 
-	createBucket(t, s3client, bucketName)
+	createBucket(t, s3client, bucket)
 
 	cmd := s5cmd("--json", "rb", src)
 	result := icmd.RunCmd(cmd)
@@ -63,7 +63,7 @@ func TestRemoveBucketSuccessJson(t *testing.T) {
 		0: json(jsonText, src),
 	}, jsonCheck(true))
 
-	_, err := s3client.HeadBucket(&s3.HeadBucketInput{Bucket: aws.String(bucketName)})
+	_, err := s3client.HeadBucket(&s3.HeadBucketInput{Bucket: aws.String(bucket)})
 	if err == nil {
 		t.Errorf("bucket still exists after remove bucket operation\n")
 	}
@@ -74,8 +74,8 @@ func TestRemoveBucketFailure(t *testing.T) {
 
 	_, s5cmd := setup(t)
 
-	bucketName := "invalid/bucket/name"
-	src := fmt.Sprintf("s3://%s", bucketName)
+	bucket := "invalid/bucket/name"
+	src := fmt.Sprintf("s3://%s", bucket)
 	cmd := s5cmd("rb", src)
 	result := icmd.RunCmd(cmd)
 
@@ -91,8 +91,8 @@ func TestRemoveBucketFailureJson(t *testing.T) {
 
 	_, s5cmd := setup(t)
 
-	bucketName := "invalid/bucket/name"
-	src := fmt.Sprintf("s3://%s", bucketName)
+	bucket := "invalid/bucket/name"
+	src := fmt.Sprintf("s3://%s", bucket)
 	cmd := s5cmd("--json", "rb", src)
 	result := icmd.RunCmd(cmd)
 
@@ -107,23 +107,22 @@ func TestRemoveBucketWithObject(t *testing.T) {
 	t.Parallel()
 
 	const (
-		bucket      = "test-bucket"
 		fileContent = "this is a file content"
 		fileName    = "file1.txt"
 	)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 	putFile(t, s3client, bucket, fileName, fileContent)
 
-	bucketName := fmt.Sprintf("s3://%v", bucket)
-	cmd := s5cmd("rb", bucketName)
+	src := fmt.Sprintf("s3://%v", bucket)
+	cmd := s5cmd("rb", src)
 	result := icmd.RunCmd(cmd)
 
 	result.Assert(t, icmd.Expected{ExitCode: 1})
 
-	expected := fmt.Sprintf(`ERROR "rb %v": BucketNotEmpty:`, bucketName) // error due to non-empty bucket.
+	expected := fmt.Sprintf(`ERROR "rb %v": BucketNotEmpty:`, src) // error due to non-empty bucket.
 
 	assertLines(t, result.Stderr(), map[int]compareFunc{
 		0: match(expected),
diff --git a/e2e/rm_test.go b/e2e/rm_test.go
index daa942d..4efbd62 100644
--- a/e2e/rm_test.go
+++ b/e2e/rm_test.go
@@ -15,10 +15,9 @@ import (
 func TestRemoveSingleS3Object(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const (
@@ -48,10 +47,9 @@ func TestRemoveSingleS3Object(t *testing.T) {
 func TestRemoveSingleS3ObjectJSON(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const (
@@ -87,10 +85,9 @@ func TestRemoveSingleS3ObjectJSON(t *testing.T) {
 func TestRemoveMultipleS3Objects(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -129,10 +126,9 @@ func TestRemoveMultipleS3Objects(t *testing.T) {
 func TestRemoveMultipleS3ObjectsJSON(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -199,12 +195,11 @@ func TestRemoveTenThousandS3Objects(t *testing.T) {
 	// flaky test, skip it
 	t.Skip()
 
-	bucket := s3BucketFromTestName(t)
-
 	// ten thousand s3 objects are created for this test. by default, s3 backend is
 	// bolt but we need speed for this test, hence use in-memory storage.
 	s3client, s5cmd := setup(t, withS3Backend("mem"))
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const filecount = 10000
@@ -246,10 +241,14 @@ func TestRemoveTenThousandS3Objects(t *testing.T) {
 func TestRemoveS3PrefixWithoutSlash(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
+	// GCS throws key does not exist error, if an object can't be found
+	// as it does not use multi-delete. This behavior is different from
+	// Amazon S3, thus skip this test when used with GCS.
+	skipTestIfGCS(t, "GCS throws key does not exist error, which is a different output then aws")
 
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const prefix = "prefix"
@@ -471,6 +470,11 @@ func TestVariadicMultipleLocalFilesWithDirectory(t *testing.T) {
 func TestVariadicRemoveS3Objects(t *testing.T) {
 	t.Parallel()
 
+	// GCS throws key does not exist error, if an object can't be found
+	// as it does not use multi-delete. This behavior is different from
+	// Amazon S3, thus skip this test when used with GCS.
+	skipTestIfGCS(t, "GCS throws key does not exist error, which is a different output then aws")
+
 	s3client, s5cmd := setup(t)
 
 	bucket := s3BucketFromTestName(t)
@@ -572,7 +576,7 @@ func TestRemoveMultipleMixedObjects(t *testing.T) {
 
 	s3client, s5cmd := setup(t)
 
-	const bucket = "bucket"
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const (
@@ -610,10 +614,9 @@ func TestRemoveMultipleMixedObjects(t *testing.T) {
 func TestRemoveMultipleS3ObjectsDryRun(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -650,10 +653,9 @@ func TestRemoveMultipleS3ObjectsDryRun(t *testing.T) {
 func TestRemoveS3ObjectRawFlag(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -703,10 +705,9 @@ func TestRemoveS3ObjectRawFlag(t *testing.T) {
 func TestRemoveS3ObjectsPrefixRawFlag(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -748,10 +749,14 @@ func TestRemoveS3ObjectsPrefixRawFlag(t *testing.T) {
 func TestRemoveS3PrefixRawFlag(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
+	// GCS gives key does not exist error, if an object can't be found
+	// as it does not use multi-delete. This behavior is different from
+	// Amazon S3, thus skip this test when used with GCS.
+	skipTestIfGCS(t, "GCS throws key does not exist error, which is a different output then aws")
 
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	filesToContent := map[string]string{
@@ -787,10 +792,9 @@ func TestRemoveS3PrefixRawFlag(t *testing.T) {
 func TestRemoveMultipleS3ObjectsWithExcludeFilter(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 	const excludePattern = "*.txt"
 
@@ -850,10 +854,9 @@ func TestRemoveMultipleS3ObjectsWithExcludeFilter(t *testing.T) {
 func TestRemoveMultipleS3ObjectsWithExcludeFilters(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	const (
diff --git a/e2e/run_test.go b/e2e/run_test.go
index 002fa3e..91e72bc 100644
--- a/e2e/run_test.go
+++ b/e2e/run_test.go
@@ -14,11 +14,11 @@ import (
 func TestRunFromStdin(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
+
 	putFile(t, s3client, bucket, "file1.txt", "content")
 	putFile(t, s3client, bucket, "file2.txt", "content")
 
@@ -46,10 +46,14 @@ func TestRunFromStdin(t *testing.T) {
 func TestRunFromStdinIssue309(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
+	// This test fails with GCS as it accepts percent encoding
+	// for `X-Amz-Copy-Source` and does not respect `+` as `space character`.
+	// skip it as it is not planned to write a workaround for GCS.
+	skipTestIfGCS(t, "GCS does not respect `+` as `space character` in `X-Amz-Copy-Source`")
 
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 	putFile(t, s3client, bucket, "test #3 /bar.jpg", "content")
 
@@ -65,7 +69,7 @@ func TestRunFromStdinIssue309(t *testing.T) {
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: suffix("mv s3://test-run-from-stdin-issue-309/test #3 /bar.jpg s3://test-run-from-stdin-issue-309/test #3/bar.jpg"),
+		0: suffix("mv s3://%v/test #3 /bar.jpg s3://%v/test #3/bar.jpg", bucket, bucket),
 	})
 
 	assertLines(t, result.Stderr(), map[int]compareFunc{})
@@ -74,10 +78,9 @@ func TestRunFromStdinIssue309(t *testing.T) {
 func TestRunFromStdinWithErrors(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	input := strings.NewReader(
@@ -94,7 +97,7 @@ func TestRunFromStdinWithErrors(t *testing.T) {
 	assertLines(t, result.Stdout(), map[int]compareFunc{})
 
 	assertLines(t, result.Stderr(), map[int]compareFunc{
-		0: contains(`ERROR "cp s3://%v/nonexistentobject nonexistentobject": NoSuchKey: status code: 404`, bucket),
+		0: contains(`ERROR "cp s3://%v/nonexistentobject nonexistentobject": NoSuchKey:`, bucket),
 		1: equals(`ERROR "ls s3/": given object s3/ not found`),
 	}, sortInput(true))
 }
@@ -102,10 +105,9 @@ func TestRunFromStdinWithErrors(t *testing.T) {
 func TestRunFromStdinJSON(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 	putFile(t, s3client, bucket, "file1.txt", "content")
 	putFile(t, s3client, bucket, "file2.txt", "content")
@@ -132,10 +134,9 @@ func TestRunFromStdinJSON(t *testing.T) {
 func TestRunFromFile(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 	putFile(t, s3client, bucket, "file1.txt", "content")
 	putFile(t, s3client, bucket, "file2.txt", "content")
@@ -164,10 +165,9 @@ func TestRunFromFile(t *testing.T) {
 func TestRunFromFileJSON(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 	putFile(t, s3client, bucket, "file1.txt", "content")
 	putFile(t, s3client, bucket, "file2.txt", "content")
@@ -213,10 +213,9 @@ func TestRunFromFileThatDoesntExist(t *testing.T) {
 func TestRunWildcardCountGreaterEqualThanWorkerCount(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 	putFile(t, s3client, bucket, "file.txt", "content")
 
@@ -230,7 +229,9 @@ func TestRunWildcardCountGreaterEqualThanWorkerCount(t *testing.T) {
 
 	// worker count < len(wildcards)
 	cmd := s5cmd("--numworkers", "2", "run", file.Path())
-	cmd.Timeout = time.Second
+
+	// increased timeout to be able to test with other endpoints.
+	cmd.Timeout = time.Second * 30
 	result := icmd.RunCmd(cmd)
 	result.Assert(t, icmd.Success)
 
@@ -276,10 +277,9 @@ func TestRunSpecialCharactersInPrefix(t *testing.T) {
 func TestRunDryRun(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	files := [...]string{"cpfile.txt", "mvfile.txt", "rmfile.txt"}
@@ -324,10 +324,9 @@ func TestRunDryRun(t *testing.T) {
 func TestRunFixDataRace_Issue301(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 	putFile(t, s3client, bucket, "file1.txt", "content")
 
diff --git a/e2e/sync_test.go b/e2e/sync_test.go
index 26864b5..2df6f25 100644
--- a/e2e/sync_test.go
+++ b/e2e/sync_test.go
@@ -20,9 +20,9 @@ func TestSyncFailForNonsharedFlagsFromCopyCommand(t *testing.T) {
 
 	const (
 		filename = "source.go"
-		bucket   = "bucket"
 	)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 	putFile(t, s3client, bucket, filename, "content")
 
@@ -69,9 +69,9 @@ func TestSyncSingleS3ObjectToLocalTwice(t *testing.T) {
 
 	const (
 		filename = "source.go"
-		bucket   = "bucket"
 	)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 	putFile(t, s3client, bucket, filename, "content")
 
@@ -95,14 +95,14 @@ func TestSyncSingleS3ObjectToLocalTwice(t *testing.T) {
 func TestSyncLocalFileToS3Twice(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
 	const (
 		filename = "testfile1.txt"
 		content  = "this is the content"
 	)
+
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	// the file to be uploaded is modified
@@ -135,10 +135,9 @@ func TestCopyLocalFilestoS3WithRawFlag(t *testing.T) {
 
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	files := []fs.PathOp{
@@ -244,10 +243,9 @@ func TestSyncLocalFolderToS3EmptyBucket(t *testing.T) {
 func TestSyncMultipleFilesWithWildcardedDirectoryToS3Bucket(t *testing.T) {
 	t.Parallel()
 
-	bucket := s3BucketFromTestName(t)
-
 	s3client, s5cmd := setup(t)
 
+	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
 	folderLayout := []fs.PathOp{
@@ -360,12 +358,13 @@ func TestSyncS3BucketToEmptyS3Bucket(t *testing.T) {
 	s3client, s5cmd := setup(t)
 
 	bucket := s3BucketFromTestName(t)
+	dstbucket := s3BucketFromTestNameWithPrefix(t, "dst")
+
 	const (
-		destbucket = "destbucket"
-		prefix     = "prefix"
+		prefix = "prefix"
 	)
 	createBucket(t, s3client, bucket)
-	createBucket(t, s3client, destbucket)
+	createBucket(t, s3client, dstbucket)
 
 	S3Content := map[string]string{
 		"testfile.txt":            "S: this is a test file",
@@ -380,7 +379,7 @@ func TestSyncS3BucketToEmptyS3Bucket(t *testing.T) {
 
 	bucketPath := fmt.Sprintf("s3://%v", bucket)
 	src := fmt.Sprintf("%v/*", bucketPath)
-	dst := fmt.Sprintf("s3://%v/%v/", destbucket, prefix)
+	dst := fmt.Sprintf("s3://%v/%v/", dstbucket, prefix)
 
 	cmd := s5cmd("sync", src, dst)
 	result := icmd.RunCmd(cmd)
@@ -402,7 +401,7 @@ func TestSyncS3BucketToEmptyS3Bucket(t *testing.T) {
 	// assert s3 objects in dest bucket
 	for key, content := range S3Content {
 		key = fmt.Sprintf("%s/%s", prefix, key) // add the prefix
-		assert.Assert(t, ensureS3Object(s3client, destbucket, key, content))
+		assert.Assert(t, ensureS3Object(s3client, dstbucket, key, content))
 	}
 }
 
@@ -560,21 +559,21 @@ func TestSyncLocalFolderToS3BucketSourceNewer(t *testing.T) {
 	}
 }
 
-// sync s3://bucket/* folder/ (same objects, source older)
+// sync s3://bucket/* folder/ (same objects, source older, destination newer)
 func TestSyncS3BucketToLocalFolderSameObjectsSourceOlder(t *testing.T) {
 	t.Parallel()
 
-	now := time.Now()
-	timeSource := newFixedTimeSource(now)
-	s3client, s5cmd := setup(t, withTimeSource(timeSource))
+	newer := time.Now().Add(time.Minute)
+
+	s3client, s5cmd := setup(t)
 
 	bucket := s3BucketFromTestName(t)
 	createBucket(t, s3client, bucket)
 
-	// local files are 1 minute older than the remote ones
+	// local files are 1 minute newer than the remote ones
 	timestamp := fs.WithTimestamps(
-		now,
-		now,
+		newer,
+		newer,
 	)
 
 	folderLayout := []fs.PathOp{
@@ -597,12 +596,9 @@ func TestSyncS3BucketToLocalFolderSameObjectsSourceOlder(t *testing.T) {
 		"a/another_test_file.txt": "S: yet another txt file",  // content different from local
 	}
 
-	// remote files are 1 minute older
-	timeSource.Advance(-time.Minute)
 	for filename, content := range S3Content {
 		putFile(t, s3client, bucket, filename, content)
 	}
-	timeSource.Advance(time.Minute)
 
 	bucketPath := fmt.Sprintf("s3://%v", bucket)
 	src := fmt.Sprintf("%s/*", bucketPath)
@@ -726,10 +722,10 @@ func TestSyncS3BucketToS3BucketSameSizesSourceNewer(t *testing.T) {
 	s3client, s5cmd := setup(t, withTimeSource(timeSource))
 
 	bucket := s3BucketFromTestName(t)
-	destbucket := "destbucket"
+	dstbucket := s3BucketFromTestNameWithPrefix(t, "dst")
 
 	createBucket(t, s3client, bucket)
-	createBucket(t, s3client, destbucket)
+	createBucket(t, s3client, dstbucket)
 
 	sourceS3Content := map[string]string{
 		"main.py":                 "S: this is a python file",
@@ -746,20 +742,20 @@ func TestSyncS3BucketToS3BucketSameSizesSourceNewer(t *testing.T) {
 		"a/another_test_file.txt": "D: yet another txt file",
 	}
 
-	for filename, content := range sourceS3Content {
-		putFile(t, s3client, bucket, filename, content)
-	}
-
 	// make destination files 1 minute older
 	timeSource.Advance(-time.Minute)
 	for filename, content := range destS3Content {
-		putFile(t, s3client, destbucket, filename, content)
+		putFile(t, s3client, dstbucket, filename, content)
 	}
+
 	timeSource.Advance(time.Minute)
+	for filename, content := range sourceS3Content {
+		putFile(t, s3client, bucket, filename, content)
+	}
 
 	bucketPath := fmt.Sprintf("s3://%v", bucket)
 	src := fmt.Sprintf("%s/*", bucketPath)
-	dst := fmt.Sprintf("s3://%v/", destbucket)
+	dst := fmt.Sprintf("s3://%v/", dstbucket)
 
 	// log debug
 	cmd := s5cmd("--log", "debug", "sync", src, dst)
@@ -781,7 +777,7 @@ func TestSyncS3BucketToS3BucketSameSizesSourceNewer(t *testing.T) {
 
 	// assert s3 objects in destination (should be same as source)
 	for key, content := range sourceS3Content {
-		assert.Assert(t, ensureS3Object(s3client, destbucket, key, content))
+		assert.Assert(t, ensureS3Object(s3client, dstbucket, key, content))
 	}
 }
 
@@ -794,10 +790,10 @@ func TestSyncS3BucketToS3BucketSameSizesSourceOlder(t *testing.T) {
 	s3client, s5cmd := setup(t, withTimeSource(timeSource))
 
 	bucket := s3BucketFromTestName(t)
-	destbucket := "destbucket"
+	dstbucket := s3BucketFromTestNameWithPrefix(t, "dst")
 
 	createBucket(t, s3client, bucket)
-	createBucket(t, s3client, destbucket)
+	createBucket(t, s3client, dstbucket)
 
 	sourceS3Content := map[string]string{
 		"main.py":                 "S: this is a python file",
@@ -822,12 +818,12 @@ func TestSyncS3BucketToS3BucketSameSizesSourceOlder(t *testing.T) {
 	timeSource.Advance(time.Minute)
 
 	for filename, content := range destS3Content {
-		putFile(t, s3client, destbucket, filename, content)
+		putFile(t, s3client, dstbucket, filename, content)
 	}
 
 	bucketPath := fmt.Sprintf("s3://%v", bucket)
 	src := fmt.Sprintf("%s/*", bucketPath)
-	dst := fmt.Sprintf("s3://%v/", destbucket)
+	dst := fmt.Sprintf("s3://%v/", dstbucket)
 
 	// log debug
 	cmd := s5cmd("--log", "debug", "sync", src, dst)
@@ -849,7 +845,7 @@ func TestSyncS3BucketToS3BucketSameSizesSourceOlder(t *testing.T) {
 
 	// assert s3 objects in destination (should not change).
 	for key, content := range destS3Content {
-		assert.Assert(t, ensureS3Object(s3client, destbucket, key, content))
+		assert.Assert(t, ensureS3Object(s3client, dstbucket, key, content))
 	}
 }
 
@@ -1010,9 +1006,9 @@ func TestSyncS3BucketToS3BucketSizeOnly(t *testing.T) {
 	s3client, s5cmd := setup(t, withTimeSource(timeSource))
 
 	bucket := s3BucketFromTestName(t)
-	destbucket := "destbucket"
+	dstbucket := s3BucketFromTestNameWithPrefix(t, "dst")
 	createBucket(t, s3client, bucket)
-	createBucket(t, s3client, destbucket)
+	createBucket(t, s3client, dstbucket)
 
 	sourceS3Content := map[string]string{
 		"main.py":                 "S: this is an updated python file",
@@ -1032,7 +1028,7 @@ func TestSyncS3BucketToS3BucketSizeOnly(t *testing.T) {
 	// timestamps should be ignored with --size-only flag
 	timeSource.Advance(-time.Minute)
 	for filename, content := range destS3Content {
-		putFile(t, s3client, destbucket, filename, content)
+		putFile(t, s3client, dstbucket, filename, content)
 	}
 	timeSource.Advance(time.Minute)
 
@@ -1042,7 +1038,7 @@ func TestSyncS3BucketToS3BucketSizeOnly(t *testing.T) {
 
 	bucketPath := fmt.Sprintf("s3://%v", bucket)
 	src := fmt.Sprintf("%s/*", bucketPath)
-	dst := fmt.Sprintf("s3://%v/", destbucket)
+	dst := fmt.Sprintf("s3://%v/", dstbucket)
 
 	// log debug
 	cmd := s5cmd("--log", "debug", "sync", "--size-only", src, dst)
@@ -1071,7 +1067,7 @@ func TestSyncS3BucketToS3BucketSizeOnly(t *testing.T) {
 
 	// assert s3 objects in destination
 	for key, content := range expectedDestS3Content {
-		assert.Assert(t, ensureS3Object(s3client, destbucket, key, content))
+		assert.Assert(t, ensureS3Object(s3client, dstbucket, key, content))
 	}
 }
 
@@ -1209,9 +1205,9 @@ func TestSyncS3BucketToS3BucketWithDelete(t *testing.T) {
 	s3client, s5cmd := setup(t)
 
 	bucket := s3BucketFromTestName(t)
-	destbucket := "destbucket"
+	dstbucket := s3BucketFromTestNameWithPrefix(t, "dst")
 	createBucket(t, s3client, bucket)
-	createBucket(t, s3client, destbucket)
+	createBucket(t, s3client, dstbucket)
 
 	sourceS3Content := map[string]string{
 		"readme.md":    "S: this is a readme file",
@@ -1231,11 +1227,11 @@ func TestSyncS3BucketToS3BucketWithDelete(t *testing.T) {
 	}
 
 	for filename, content := range destS3Content {
-		putFile(t, s3client, destbucket, filename, content)
+		putFile(t, s3client, dstbucket, filename, content)
 	}
 
 	src := fmt.Sprintf("s3://%v/", bucket)
-	dst := fmt.Sprintf("s3://%v/", destbucket)
+	dst := fmt.Sprintf("s3://%v/", dstbucket)
 
 	cmd := s5cmd("sync", "--delete", "--size-only", src+"*", dst)
 	result := icmd.RunCmd(cmd)
@@ -1270,12 +1266,12 @@ func TestSyncS3BucketToS3BucketWithDelete(t *testing.T) {
 
 	// assert s3 objects in destination. (should be)
 	for key, content := range expectedDestS3Content {
-		assert.Assert(t, ensureS3Object(s3client, destbucket, key, content))
+		assert.Assert(t, ensureS3Object(s3client, dstbucket, key, content))
 	}
 
 	// assert s3 objects should be deleted.
 	for key, content := range nonExpectedDestS3Content {
-		err := ensureS3Object(s3client, destbucket, key, content)
+		err := ensureS3Object(s3client, dstbucket, key, content)
 		if err == nil {
 			t.Errorf("File %v is not deleted in remote : %v\n", key, err)
 		}
@@ -1477,10 +1473,8 @@ func TestSyncLocalFilesWithNoFollowSymlinksToS3Bucket(t *testing.T) {
 func TestSyncS3ObjectsIntoAnotherBucketWithExcludeFilters(t *testing.T) {
 	t.Parallel()
 
-	const (
-		srcbucket = "bucket"
-		dstbucket = "dstbucket"
-	)
+	srcbucket := s3BucketFromTestNameWithPrefix(t, "src")
+	dstbucket := s3BucketFromTestNameWithPrefix(t, "dst")
 
 	s3client, s5cmd := setup(t)
 
@@ -1586,7 +1580,7 @@ func TestSyncLocalDirectoryToS3WithExcludeFilter(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			t.Parallel()
 
-			bucket := "testbucket"
+			bucket := s3BucketFromTestName(t)
 
 			s3client, s5cmd := setup(t)
 
@@ -1652,6 +1646,9 @@ func TestSyncLocalDirectoryToS3WithExcludeFilter(t *testing.T) {
 func TestIssue435(t *testing.T) {
 	t.Parallel()
 
+	// Skip this as it takes too long to complete with GCS.
+	skipTestIfGCS(t, "takes too long to complete")
+
 	bucket := s3BucketFromTestName(t)
 
 	s3client, s5cmd := setup(t, withS3Backend("mem"))
diff --git a/e2e/util_test.go b/e2e/util_test.go
index be27a48..2557175 100644
--- a/e2e/util_test.go
+++ b/e2e/util_test.go
@@ -10,28 +10,31 @@ import (
 	"fmt"
 	"io"
 	"math/rand"
+	urlpkg "net/url"
 	"os"
 	"os/exec"
 	"path/filepath"
 	"regexp"
 	"runtime"
 	"sort"
+	"strconv"
 	"strings"
 	"sync"
 	"testing"
 	"time"
 
-	"github.com/peak/s5cmd/storage"
-
 	"github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/aws/awserr"
+	"github.com/aws/aws-sdk-go/aws/client"
 	"github.com/aws/aws-sdk-go/aws/credentials"
 	"github.com/aws/aws-sdk-go/aws/endpoints"
+	"github.com/aws/aws-sdk-go/aws/request"
 	"github.com/aws/aws-sdk-go/aws/session"
 	"github.com/aws/aws-sdk-go/service/s3"
 	"github.com/google/go-cmp/cmp"
 	"github.com/iancoleman/strcase"
 	"github.com/igungor/gofakes3"
+	"github.com/peak/s5cmd/storage"
 	"gotest.tools/v3/assert"
 	"gotest.tools/v3/fs"
 	"gotest.tools/v3/icmd"
@@ -39,8 +42,16 @@ import (
 
 const (
 	// Don't use "race" flag in the build arguments.
-	testDisableRaceFlagKey = "S5CMD_BUILD_BINARY_WITHOUT_RACE_FLAG"
-	testDisableRaceFlagVal = "1"
+	testDisableRaceFlagKey       = "S5CMD_BUILD_BINARY_WITHOUT_RACE_FLAG"
+	testDisableRaceFlagVal       = "1"
+	s5cmdTestIdEnv               = "S5CMD_ACCESS_KEY_ID"
+	s5cmdTestSecretEnv           = "S5CMD_SECRET_ACCESS_KEY"
+	s5cmdTestEndpointEnv         = "S5CMD_ENDPOINT_URL"
+	s5cmdTestIsVirtualHost       = "S5CMD_IS_VIRTUAL_HOST"
+	s5cmdTestRegionEnv           = "S5CMD_REGION"
+	s5cmdTestIKnowWhatImDoingEnv = "S5CMD_I_KNOW_WHAT_IM_DOING"
+	maxRetries                   = 10
+	deleteObjectsMax             = 1000
 )
 
 var (
@@ -104,8 +115,22 @@ func setup(t *testing.T, options ...option) (*s3.S3, func(...string) icmd.Cmd) {
 	for _, option := range options {
 		option(opts)
 	}
+	testdir, workdir := workdir(t, opts)
+
+	endpoint := ""
+
+	// don't create a local s3 server if tests will run in another endpoint
+	if isEndpointFromEnv() {
+		endpoint = os.Getenv(s5cmdTestEndpointEnv)
+	} else {
+		endpoint = server(t, testdir, opts)
+	}
 
-	endpoint, workdir := server(t, opts)
+	// one of the tests check if s5cmd correctly fails when an incorrect endpoint is given.
+	// if test specified an endpoint url, then try to use that url.
+	if opts.endpointURL != "" {
+		endpoint = opts.endpointURL
+	}
 	client := s3client(t, storage.Options{
 		Endpoint:    endpoint,
 		NoVerifySSL: true,
@@ -114,14 +139,12 @@ func setup(t *testing.T, options ...option) (*s3.S3, func(...string) icmd.Cmd) {
 	return client, s5cmd(workdir, endpoint)
 }
 
-func server(t *testing.T, opts *setupOpts) (string, string) {
-	t.Helper()
-
-	// testdir := fs.NewDir() tries to create a new directory which
-	// has a prefix = [test function name][operation name]
+func workdir(t *testing.T, opts *setupOpts) (*fs.Dir, string) {
+	// testdir := fs.NewDir() tries to create a new directory which has a
+	// prefix = [test function name][operation name]
 	// e.g., prefix' = "TestCopySingleS3ObjectToLocal/cp_s3://bucket/object_file"
-	// but on windows, directories cannot contain a colon
-	// so we replace them with hyphen
+	// but on windows, directories cannot contain a colon so we replace them
+	// with hyphen.
 	prefix := t.Name()
 	if runtime.GOOS == "windows" {
 		prefix = strings.ReplaceAll(prefix, ":", "-")
@@ -129,6 +152,11 @@ func server(t *testing.T, opts *setupOpts) (string, string) {
 
 	testdir := fs.NewDir(t, prefix, fs.WithDir("workdir", fs.WithMode(0700)))
 	workdir := testdir.Join("workdir")
+	return testdir, workdir
+}
+
+func server(t *testing.T, testdir *fs.Dir, opts *setupOpts) string {
+	t.Helper()
 
 	s3LogLevel := *flagTestLogLevel
 
@@ -137,15 +165,8 @@ func server(t *testing.T, opts *setupOpts) (string, string) {
 	}
 
 	endpoint := s3ServerEndpoint(t, testdir, s3LogLevel, opts.s3backend, opts.timeSource, opts.enableProxy)
-	if opts.endpointURL != "" {
-		endpoint = opts.endpointURL
-	}
-	t.Cleanup(func() {
-		testdir.Remove()
 
-	})
-
-	return endpoint, workdir
+	return endpoint
 }
 
 func s3client(t *testing.T, options storage.Options) *s3.S3 {
@@ -155,16 +176,36 @@ func s3client(t *testing.T, options storage.Options) *s3.S3 {
 	if *flagTestLogLevel == "debug" {
 		awsLogLevel = aws.LogDebug
 	}
+	s3Config := aws.NewConfig()
+
+	id := defaultAccessKeyID
+	key := defaultSecretAccessKey
+	endpoint := options.Endpoint
+	region := endpoints.UsEast1RegionID
+	isVirtualHost := false
+
+	// get environment variables and use external endpoint url.
+	// this can be used to test s3 sources such as GCS, amazon, wasabi etc.
+	if isEndpointFromEnv() {
+		id = os.Getenv(s5cmdTestIdEnv)
+		key = os.Getenv(s5cmdTestSecretEnv)
+		endpoint = os.Getenv(s5cmdTestEndpointEnv)
+		region = os.Getenv(s5cmdTestRegionEnv)
+		s3Config.Retryer = newSlowDownRetryer(maxRetries)
+		isVirtualHost = isVirtualHostFromEnv(t)
+	}
+
 	// WithDisableRestProtocolURICleaning is added to allow adjacent slashes to be used in s3 object keys.
-	s3Config := aws.NewConfig().
-		WithEndpoint(options.Endpoint).
-		WithRegion(endpoints.UsEast1RegionID).
-		WithCredentials(credentials.NewStaticCredentials(defaultAccessKeyID, defaultSecretAccessKey, "")).
+	s3Config = s3Config.
+		WithCredentials(credentials.NewStaticCredentials(id, key, "")).
+		WithEndpoint(endpoint).
 		WithDisableSSL(options.NoVerifySSL).
+		// allow adjacent slashes to be used in s3 object keys
 		WithDisableRestProtocolURICleaning(true).
-		WithS3ForcePathStyle(true).
 		WithCredentialsChainVerboseErrors(true).
-		WithLogLevel(awsLogLevel)
+		WithLogLevel(awsLogLevel).
+		WithRegion(region).
+		WithS3ForcePathStyle(!isVirtualHost)
 
 	sess, err := session.NewSession(s3Config)
 	assert.NilError(t, err)
@@ -172,6 +213,60 @@ func s3client(t *testing.T, options storage.Options) *s3.S3 {
 	return s3.New(sess)
 }
 
+func isVirtualHostFromEnv(t *testing.T) bool {
+	isVirtual, err := strconv.ParseBool(os.Getenv(s5cmdTestIsVirtualHost))
+	if err != nil {
+		t.Fatal(err)
+	}
+	return isVirtual
+}
+
+// slowDownRetryer wraps the SDK's built in DefaultRetryer adding additional
+// retry for SlowDown code.
+type slowDownRetryer struct {
+	client.DefaultRetryer
+}
+
+func newSlowDownRetryer(maxRetries int) *slowDownRetryer {
+	return &slowDownRetryer{
+		DefaultRetryer: client.DefaultRetryer{
+			NumMaxRetries: maxRetries,
+		},
+	}
+}
+
+func (c *slowDownRetryer) ShouldRetry(req *request.Request) bool {
+	var awsErr awserr.Error
+	if errors.As(req.Error, &awsErr) {
+		if awsErr.Code() == "SlowDown" {
+			return true
+		}
+	}
+
+	return c.DefaultRetryer.ShouldRetry(req)
+}
+
+func isEndpointFromEnv() bool {
+	return os.Getenv(s5cmdTestIdEnv) != "" &&
+		os.Getenv(s5cmdTestSecretEnv) != "" &&
+		os.Getenv(s5cmdTestEndpointEnv) != "" &&
+		os.Getenv(s5cmdTestRegionEnv) != "" &&
+		os.Getenv(s5cmdTestIsVirtualHost) != "" &&
+		os.Getenv(s5cmdTestIKnowWhatImDoingEnv) == "1"
+}
+
+// skip the test if testing with google endpoint.
+func skipTestIfGCS(t *testing.T, format string) {
+	endpoint, err := urlpkg.Parse(os.Getenv(s5cmdTestEndpointEnv))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if storage.IsGoogleEndpoint(*endpoint) {
+		t.Skip(format)
+	}
+}
+
 func s5cmd(workdir, endpoint string) func(args ...string) icmd.Cmd {
 	return func(args ...string) icmd.Cmd {
 		endpoint := []string{"--endpoint-url", endpoint}
@@ -179,11 +274,26 @@ func s5cmd(workdir, endpoint string) func(args ...string) icmd.Cmd {
 
 		cmd := icmd.Command(s5cmdPath, args...)
 		env := os.Environ()
+
+		id := defaultAccessKeyID
+		secret := defaultSecretAccessKey
+
+		if isEndpointFromEnv() {
+			id = os.Getenv(s5cmdTestIdEnv)
+			secret = os.Getenv(s5cmdTestSecretEnv)
+			env = append(
+				env,
+				[]string{
+					fmt.Sprintf("AWS_REGION=%v", os.Getenv(s5cmdTestRegionEnv)),
+				}...,
+			)
+		}
+
 		env = append(
 			env,
 			[]string{
-				fmt.Sprintf("AWS_ACCESS_KEY_ID=%v", defaultAccessKeyID),
-				fmt.Sprintf("AWS_SECRET_ACCESS_KEY=%v", defaultSecretAccessKey),
+				fmt.Sprintf("AWS_ACCESS_KEY_ID=%v", id),
+				fmt.Sprintf("AWS_SECRET_ACCESS_KEY=%v", secret),
 			}...,
 		)
 		cmd.Env = env
@@ -267,6 +377,89 @@ func createBucket(t *testing.T, client *s3.S3, bucket string) {
 	if err != nil {
 		t.Fatal(err)
 	}
+
+	t.Cleanup(func() {
+		// cleanup if bucket exists.
+		_, err := client.HeadBucket(&s3.HeadBucketInput{Bucket: aws.String(bucket)})
+		if err == nil {
+
+			listInput := s3.ListObjectsInput{
+				Bucket: aws.String(bucket),
+			}
+
+			//remove objects first.
+			// delete each object individually if using GCS.
+			if isGoogleEndpointFromEnv(t) {
+				err = client.ListObjectsPages(&listInput, func(p *s3.ListObjectsOutput, lastPage bool) bool {
+					for _, c := range p.Contents {
+						client.DeleteObject(&s3.DeleteObjectInput{
+							Bucket: aws.String(bucket),
+							Key:    c.Key,
+						})
+					}
+					return !lastPage
+				})
+				if err != nil {
+					t.Fatal(err)
+				}
+			}
+
+			chunkSize := deleteObjectsMax
+
+			var keys []*s3.ObjectIdentifier
+			initKeys := func() {
+				keys = make([]*s3.ObjectIdentifier, 0)
+			}
+
+			err = client.ListObjectsPages(&listInput, func(p *s3.ListObjectsOutput, lastPage bool) bool {
+				for _, c := range p.Contents {
+					objid := &s3.ObjectIdentifier{Key: c.Key}
+					keys = append(keys, objid)
+
+					if len(keys) == chunkSize {
+						_, err := client.DeleteObjects(&s3.DeleteObjectsInput{
+							Bucket: aws.String(bucket),
+							Delete: &s3.Delete{Objects: keys},
+						})
+						if err != nil {
+							t.Fatal(err)
+						}
+						initKeys()
+					}
+				}
+				return !lastPage
+			})
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			if len(keys) > 0 {
+				_, err := client.DeleteObjects(&s3.DeleteObjectsInput{
+					Delete: &s3.Delete{Objects: keys},
+					Bucket: aws.String(bucket),
+				})
+				if err != nil {
+					t.Fatal(err)
+				}
+			}
+			// delete bucket after.
+			_, err = client.DeleteBucket(&s3.DeleteBucketInput{
+				Bucket: aws.String(bucket),
+			})
+			if err != nil {
+				t.Fatal(err)
+			}
+		}
+	})
+
+}
+
+func isGoogleEndpointFromEnv(t *testing.T) bool {
+	endpoint, err := urlpkg.Parse(os.Getenv(s5cmdTestEndpointEnv))
+	if err != nil {
+		t.Fatal(err)
+	}
+	return storage.IsGoogleEndpoint(*endpoint)
 }
 
 var errS3NoSuchKey = fmt.Errorf("s3: no such key")
@@ -368,15 +561,116 @@ func replaceMatchWithSpace(input string, match ...string) string {
 	return input
 }
 
-func s3BucketFromTestName(t *testing.T) string {
+func s3BucketFromTestNameWithPrefix(t *testing.T, prefix string) string {
 	t.Helper()
 	bucket := strcase.ToKebab(t.Name())
 
-	if len(bucket) > 63 {
-		bucket = fmt.Sprintf("%v-%v", bucket[:55], randomString(7))
+	reg, _ := regexp.Compile("[^-a-z0-9]+")
+
+	if prefix != "" {
+		bucket = fmt.Sprintf("%v-%v", prefix, bucket)
+	}
+
+	bucket = reg.ReplaceAllString(bucket, "")
+
+	return addRandomSuffixTo(bucket)
+}
+
+func TestS3BucketFromTestNameWithPrefix(t *testing.T) {
+	t.Parallel()
+	testcases := []struct {
+		name          string
+		prefix        string
+		expectedRegex string
+	}{
+		{
+			name:          "./*?",
+			prefix:        "",
+			expectedRegex: "test-s-3-bucket-from-test-name-with-prefix-.{7}$",
+		},
+		{
+			name:          "don't_use_",
+			prefix:        "",
+			expectedRegex: "test-s-3-bucket-from-test-name-with-prefix-.{7}$",
+		},
+		{
+			name:          "pref",
+			prefix:        "pref",
+			expectedRegex: "pref-test-s-3-bucket-from-test-name-with-p-.{7}$",
+		},
+		{
+			name:          "chars",
+			prefix:        "./*?",
+			expectedRegex: "test-s-3-bucket-from-test-name-with-prefi-.{7}$",
+		},
+	}
+	for _, tc := range testcases {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			// adds tc.name as suffix to "TestS3BucketFromTestNameWithPrefix" and then modifies it.
+			result := s3BucketFromTestNameWithPrefix(t, tc.prefix)
+
+			assertLines(t, result, map[int]compareFunc{
+				0: match(tc.expectedRegex),
+			})
+		})
+
+	}
+}
+
+func s3BucketFromTestName(t *testing.T) string {
+	t.Helper()
+	return s3BucketFromTestNameWithPrefix(t, "")
+}
+
+// addRandomSuffixTo appends random 7 characters to given bucket name.
+// If bucket name is longer than 50 chars, trims it down to 50 chars.
+func addRandomSuffixTo(bucketName string) string {
+	bucketName = fmt.Sprintf("%v-%v", bucketName, randomString(7))
+
+	if len(bucketName) > 50 {
+		bucketName = fmt.Sprintf("%v-%v", bucketName[:42], randomString(7))
+	}
+
+	return bucketName
+}
+
+func TestAddRandomSuffixTo(t *testing.T) {
+	t.Parallel()
+	testcases := []struct {
+		name          string
+		bucketName    string
+		expectedRegex string
+	}{
+		{
+			name:          "shorter-than-50-chars",
+			bucketName:    "TestName",
+			expectedRegex: "TestName-.{7}$",
+		},
+		{
+			name:          "between-42-and-50-chars",
+			bucketName:    "ThisTestStringIsSupposedToBeInBetween42And50Chars",
+			expectedRegex: "ThisTestStringIsSupposedToBeInBetween42And-.{7}$",
+		},
+		{
+			name:          "longer-than-50-chars",
+			bucketName:    "ThisTestStringIsSupposedToBeMuchMuchLongerThanFiftyCharacters",
+			expectedRegex: "ThisTestStringIsSupposedToBeMuchMuchLonger-.{7}$",
+		},
 	}
+	for _, tc := range testcases {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			result := addRandomSuffixTo(tc.bucketName)
 
-	return bucket
+			assert.Assert(t, len(result) <= 63)
+
+			assertLines(t, result, map[int]compareFunc{
+				0: match(tc.expectedRegex),
+			})
+		})
+
+	}
 }
 
 func randomString(n int) string {