run-tests.py
12bc8cc8
 #!/usr/bin/env python2
f11319be
 # -*- coding=utf-8 -*-
330c51eb
 
 ## Amazon S3cmd - testsuite
 ## Author: Michal Ludvig <michal@logix.cz>
 ##         http://www.logix.cz/michal
 ## License: GPL Version 2
afd51b6c
 ## Copyright: TGRMN Software and contributors
330c51eb
 
38f1561c
 from __future__ import absolute_import, print_function
930b6e60
 
330c51eb
 import sys
9856527a
 import os
330c51eb
 import re
f886eda4
 import time
330c51eb
 from subprocess import Popen, PIPE, STDOUT
f891a814
 import locale
4459b9ad
 import getpass
e14a7511
 import S3.Exceptions
 import S3.Config
8214d4f0
 from S3.ExitCodes import *
330c51eb
 
03ce1d2f
 PY3 = (sys.version_info >= (3,0))
 
 try:
     unicode
 except NameError:
     # python 3 support
     # In python 3, unicode -> str, and str -> bytes
     unicode = str
 
330c51eb
 count_pass = 0
 count_fail = 0
ca86524c
 count_skip = 0
 
 test_counter = 0
 run_tests = []
 exclude_tests = []
 
3677a3ba
 verbose = False
 
20722601
 # https://stackoverflow.com/questions/377017/test-if-executable-exists-in-python/377028#377028
 def which(program):
     def is_exe(fpath):
         return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
 
     fpath, fname = os.path.split(program)
     if fpath:
         if is_exe(program):
             return program
     else:
         for path in os.environ["PATH"].split(os.pathsep):
             path = path.strip('"')
             exe_file = os.path.join(path, program)
             if is_exe(exe_file):
                 return exe_file
 
     return None
 
 if which('curl') is not None:
0e3cd57d
     have_curl = True
ca86524c
 else:
20722601
     have_curl = False
330c51eb
 
e14a7511
 config_file = None
 if os.getenv("HOME"):
     config_file = os.path.join(os.getenv("HOME"), ".s3cfg")
 elif os.name == "nt" and os.getenv("USERPROFILE"):
58431bc5
     config_file = os.path.join(os.getenv("USERPROFILE").decode('mbcs'), os.getenv("APPDATA").decode('mbcs') or 'Application Data', "s3cmd.ini")
e14a7511
 
 
96222f57
 ## Unpack testsuite/ directory
 if not os.path.isdir('testsuite') and os.path.isfile('testsuite.tar.gz'):
d439efb4
     os.system("tar -xz -f testsuite.tar.gz")
96222f57
 if not os.path.isdir('testsuite'):
930b6e60
     print("Something went wrong while unpacking testsuite.tar.gz")
d439efb4
     sys.exit(1)
96222f57
 
1ba1f69a
 os.system("tar -xf testsuite/checksum.tar -C testsuite")
 if not os.path.isfile('testsuite/checksum/cksum33.txt'):
930b6e60
     print("Something went wrong while unpacking testsuite/checkum.tar")
d439efb4
     sys.exit(1)
 
96222f57
 ## Fix up permissions for permission-denied tests
8b721608
 os.chmod("testsuite/permission-tests/permission-denied-dir", 0o444)
 os.chmod("testsuite/permission-tests/permission-denied.txt", 0o000)
96222f57
 
4986ae85
 ## Patterns for Unicode tests
 patterns = {}
 patterns['UTF-8'] = u"ŪņЇЌœđЗ/☺ unicode € rocks ™"
 patterns['GBK'] = u"12月31日/1-特色條目"
 
 encoding = locale.getpreferredencoding()
 if not encoding:
930b6e60
     print("Guessing current system encoding failed. Consider setting $LANG variable.")
d439efb4
     sys.exit(1)
5f7a2d5f
 else:
930b6e60
     print("System encoding: " + encoding)
4986ae85
 
 have_encoding = os.path.isdir('testsuite/encodings/' + encoding)
 if not have_encoding and os.path.isfile('testsuite/encodings/%s.tar.gz' % encoding):
d439efb4
     os.system("tar xvz -C testsuite/encodings -f testsuite/encodings/%s.tar.gz" % encoding)
     have_encoding = os.path.isdir('testsuite/encodings/' + encoding)
4986ae85
 
 if have_encoding:
d439efb4
     #enc_base_remote = "%s/xyz/%s/" % (pbucket(1), encoding)
     enc_pattern = patterns[encoding]
5f7a2d5f
 else:
930b6e60
     print(encoding + " specific files not found.")
4986ae85
 
03ce1d2f
 def unicodise(string):
     if type(string) == unicode:
         return string
 
     return unicode(string, "UTF-8", "replace")
 
 def deunicodise(string):
     if type(string) != unicode:
         return string
 
     return string.encode("UTF-8", "replace")
 
76ce6441
 if not os.path.isdir('testsuite/crappy-file-name'):
d439efb4
     os.system("tar xvz -C testsuite -f testsuite/crappy-file-name.tar.gz")
     # TODO: also unpack if the tarball is newer than the directory timestamp
     #       for instance when a new version was pulled from SVN.
76ce6441
 
e64678ae
 def test(label, cmd_args = [], retcode = 0, must_find = [], must_not_find = [], must_find_re = [], must_not_find_re = [], stdin = None):
d439efb4
     def command_output():
930b6e60
         print("----")
         print(" ".join([" " in arg and "'%s'" % arg or arg for arg in cmd_args]))
         print("----")
         print(stdout)
         print("----")
d439efb4
 
     def failure(message = ""):
         global count_fail
         if message:
08656ffe
             message = u"  (%r)" % message
930b6e60
         print(u"\x1b[31;1mFAIL%s\x1b[0m" % (message))
d439efb4
         count_fail += 1
         command_output()
         #return 1
         sys.exit(1)
     def success(message = ""):
         global count_pass
         if message:
             message = "  (%r)" % message
930b6e60
         print("\x1b[32;1mOK\x1b[0m%s" % (message))
d439efb4
         count_pass += 1
         if verbose:
             command_output()
         return 0
     def skip(message = ""):
         global count_skip
         if message:
             message = "  (%r)" % message
930b6e60
         print("\x1b[33;1mSKIP\x1b[0m%s" % (message))
d439efb4
         count_skip += 1
         return 0
     def compile_list(_list, regexps = False):
         if regexps == False:
03ce1d2f
             _list = [re.escape(item) for item in _list]
d439efb4
 
         return [re.compile(item, re.MULTILINE) for item in _list]
 
     global test_counter
     test_counter += 1
930b6e60
     print(("%3d  %s " % (test_counter, label)).ljust(30, "."), end=' ')
d439efb4
     sys.stdout.flush()
 
     if run_tests.count(test_counter) == 0 or exclude_tests.count(test_counter) > 0:
         return skip()
 
     if not cmd_args:
         return skip()
 
e64678ae
     p = Popen(cmd_args, stdin = stdin, stdout = PIPE, stderr = STDOUT, universal_newlines = True, close_fds = True)
d439efb4
     stdout, stderr = p.communicate()
547c1ba1
     if type(retcode) not in [list, tuple]: retcode = [retcode]
     if p.returncode not in retcode:
         return failure("retcode: %d, expected one of: %s" % (p.returncode, retcode))
d439efb4
 
     if type(must_find) not in [ list, tuple ]: must_find = [must_find]
     if type(must_find_re) not in [ list, tuple ]: must_find_re = [must_find_re]
     if type(must_not_find) not in [ list, tuple ]: must_not_find = [must_not_find]
     if type(must_not_find_re) not in [ list, tuple ]: must_not_find_re = [must_not_find_re]
 
     find_list = []
     find_list.extend(compile_list(must_find))
     find_list.extend(compile_list(must_find_re, regexps = True))
     find_list_patterns = []
     find_list_patterns.extend(must_find)
     find_list_patterns.extend(must_find_re)
 
     not_find_list = []
     not_find_list.extend(compile_list(must_not_find))
     not_find_list.extend(compile_list(must_not_find_re, regexps = True))
     not_find_list_patterns = []
     not_find_list_patterns.extend(must_not_find)
     not_find_list_patterns.extend(must_not_find_re)
 
     for index in range(len(find_list)):
03ce1d2f
         stdout = unicodise(stdout)
d439efb4
         match = find_list[index].search(stdout)
         if not match:
             return failure("pattern not found: %s" % find_list_patterns[index])
     for index in range(len(not_find_list)):
         match = not_find_list[index].search(stdout)
         if match:
             return failure("pattern found: %s (match: %s)" % (not_find_list_patterns[index], match.group(0)))
 
     return success()
330c51eb
 
 def test_s3cmd(label, cmd_args = [], **kwargs):
d439efb4
     if not cmd_args[0].endswith("s3cmd"):
38f1561c
         cmd_args.insert(0, "python")
d439efb4
         cmd_args.insert(1, "s3cmd")
52591402
         if config_file:
ec5e518c
             cmd_args.insert(2, "-c")
52591402
             cmd_args.insert(3, config_file)
9856527a
 
d439efb4
     return test(label, cmd_args, **kwargs)
330c51eb
 
ca86524c
 def test_mkdir(label, dir_name):
d439efb4
     if os.name in ("posix", "nt"):
         cmd = ['mkdir', '-p']
     else:
930b6e60
         print("Unknown platform: %s" % os.name)
d439efb4
         sys.exit(1)
     cmd.append(dir_name)
     return test(label, cmd)
ca86524c
 
 def test_rmdir(label, dir_name):
d439efb4
     if os.path.isdir(dir_name):
         if os.name == "posix":
             cmd = ['rm', '-rf']
         elif os.name == "nt":
             cmd = ['rmdir', '/s/q']
         else:
930b6e60
             print("Unknown platform: %s" % os.name)
d439efb4
             sys.exit(1)
         cmd.append(dir_name)
         return test(label, cmd)
     else:
         return test(label, [])
ca86524c
 
3894a49a
 def test_flushdir(label, dir_name):
d439efb4
     test_rmdir(label + "(rm)", dir_name)
     return test_mkdir(label + "(mk)", dir_name)
ca86524c
 
2d067a6d
 def test_copy(label, src_file, dst_file):
d439efb4
     if os.name == "posix":
         cmd = ['cp', '-f']
     elif os.name == "nt":
         cmd = ['copy']
     else:
930b6e60
         print("Unknown platform: %s" % os.name)
d439efb4
         sys.exit(1)
     cmd.append(src_file)
     cmd.append(dst_file)
     return test(label, cmd)
2d067a6d
 
0e3cd57d
 def test_curl_HEAD(label, src_file, **kwargs):
daf6da9c
     cmd = ['curl', '--silent', '--head', '-include', '--location']
f5ed9c91
     cmd.append(src_file)
     return test(label, cmd, **kwargs)
 
128c26dd
 bucket_prefix = u"%s-" % getpass.getuser().lower()
96222f57
 
ca86524c
 argv = sys.argv[1:]
 while argv:
d439efb4
     arg = argv.pop(0)
     if arg.startswith('--bucket-prefix='):
930b6e60
         print("Usage: '--bucket-prefix PREFIX', not '--bucket-prefix=PREFIX'")
d439efb4
         sys.exit(0)
     if arg in ("-h", "--help"):
930b6e60
         print("%s A B K..O -N" % sys.argv[0])
         print("Run tests number A, B and K through to O, except for N")
d439efb4
         sys.exit(0)
ec5e518c
 
     if arg in ("-c", "--config"):
52591402
         config_file = argv.pop(0)
ec5e518c
         continue
d439efb4
     if arg in ("-l", "--list"):
         exclude_tests = range(0, 999)
         break
     if arg in ("-v", "--verbose"):
         verbose = True
         continue
     if arg in ("-p", "--bucket-prefix"):
         try:
             bucket_prefix = argv.pop(0)
         except IndexError:
930b6e60
             print("Bucket prefix option must explicitly supply a bucket name prefix")
d439efb4
             sys.exit(0)
         continue
ba59d366
     if ".." in arg:
d439efb4
         range_idx = arg.find("..")
         range_start = arg[:range_idx] or 0
         range_end = arg[range_idx+2:] or 999
         run_tests.extend(range(int(range_start), int(range_end) + 1))
     elif arg.startswith("-"):
         exclude_tests.append(int(arg[1:]))
     else:
         run_tests.append(int(arg))
ca86524c
 
930b6e60
 print("Using bucket prefix: '%s'" % bucket_prefix)
efaa90fd
 
52591402
 cfg = S3.Config.Config(config_file)
 
ca86524c
 if not run_tests:
d439efb4
     run_tests = range(0, 999)
ca86524c
 
31fadab3
 # helper functions for generating bucket names
 def bucket(tail):
         '''Test bucket name'''
2895644c
         label = 'autotest'
         if str(tail) == '3':
                 label = 'Autotest'
         return '%ss3cmd-%s-%s' % (bucket_prefix, label, tail)
96222f57
 
31fadab3
 def pbucket(tail):
         '''Like bucket(), but prepends "s3://" for you'''
         return 's3://' + bucket(tail)
 
ca86524c
 ## ====== Remove test buckets
d56fa83a
 test_s3cmd("Remove test buckets", ['rb', '-r', '--force', pbucket(1), pbucket(2), pbucket(3)])
 
 ## ====== verify they were removed
 test_s3cmd("Verify no test buckets", ['ls'],
            must_not_find = [pbucket(1), pbucket(2), pbucket(3)])
f11319be
 
ca86524c
 
 ## ====== Create one bucket (EU)
d439efb4
 test_s3cmd("Create one bucket (EU)", ['mb', '--bucket-location=EU', pbucket(1)],
     must_find = "Bucket '%s/' created" % pbucket(1))
330c51eb
 
ca86524c
 
 
 ## ====== Create multiple buckets
d439efb4
 test_s3cmd("Create multiple buckets", ['mb', pbucket(2), pbucket(3)],
     must_find = [ "Bucket '%s/' created" % pbucket(2), "Bucket '%s/' created" % pbucket(3)])
330c51eb
 
ca86524c
 
 ## ====== Invalid bucket name
d439efb4
 test_s3cmd("Invalid bucket name", ["mb", "--bucket-location=EU", pbucket('EU')],
8214d4f0
     retcode = EX_USAGE,
d439efb4
     must_find = "ERROR: Parameter problem: Bucket name '%s' contains disallowed character" % bucket('EU'),
     must_not_find_re = "Bucket.*created")
330c51eb
 
ca86524c
 
 ## ====== Buckets list
d439efb4
 test_s3cmd("Buckets list", ["ls"],
     must_find = [ "autotest-1", "autotest-2", "Autotest-3" ], must_not_find_re = "autotest-EU")
f11319be
 
ca86524c
 
 ## ====== Sync to S3
96222f57
 test_s3cmd("Sync to S3", ['sync', 'testsuite/', pbucket(1) + '/xyz/', '--exclude', 'demo/*', '--exclude', '*.png', '--no-encrypt', '--exclude-from', 'testsuite/exclude.encodings' ],
69bb35df
            must_find = [ "ERROR: Upload of 'testsuite/permission-tests/permission-denied.txt' is not possible (Reason: Permission denied)",
f3c0a75d
                          "WARNING: 32 non-printable characters replaced in: crappy-file-name/non-printables",
            ],
efaa90fd
            must_not_find_re = [ "demo/", "^(?!WARNING: Skipping).*\.png$", "permission-denied-dir" ],
f3c0a75d
            retcode = EX_PARTIAL)
4986ae85
 
 if have_encoding:
d439efb4
     ## ====== Sync UTF-8 / GBK / ... to S3
08656ffe
     test_s3cmd(u"Sync %s to S3" % encoding, ['sync', 'testsuite/encodings/' + encoding, '%s/xyz/encodings/' % pbucket(1), '--exclude', 'demo/*', '--no-encrypt' ],
         must_find = [ u"'testsuite/encodings/%(encoding)s/%(pattern)s' -> '%(pbucket)s/xyz/encodings/%(encoding)s/%(pattern)s'" % { 'encoding' : encoding, 'pattern' : enc_pattern , 'pbucket' : pbucket(1)} ])
9856527a
 
 
ca86524c
 ## ====== List bucket content
31fadab3
 test_s3cmd("List bucket content", ['ls', '%s/xyz/' % pbucket(1) ],
87cd83e8
     must_find_re = [ u"DIR +%s/xyz/binary/$" % pbucket(1) , u"DIR +%s/xyz/etc/$" % pbucket(1) ],
d439efb4
     must_not_find = [ u"random-crap.md5", u"/demo" ])
9856527a
 
 
ca86524c
 ## ====== List bucket recursive
31fadab3
 must_find = [ u"%s/xyz/binary/random-crap.md5" % pbucket(1) ]
4986ae85
 if have_encoding:
d439efb4
     must_find.append(u"%(pbucket)s/xyz/encodings/%(encoding)s/%(pattern)s" % { 'encoding' : encoding, 'pattern' : enc_pattern, 'pbucket' : pbucket(1) })
96222f57
 
31fadab3
 test_s3cmd("List bucket recursive", ['ls', '--recursive', pbucket(1)],
d439efb4
     must_find = must_find,
     must_not_find = [ "logo.png" ])
9856527a
 
ca86524c
 ## ====== FIXME
a237d8f6
 test_s3cmd("Recursive put", ['put', '--recursive', 'testsuite/etc', '%s/xyz/' % pbucket(1) ])
9856527a
 
f11319be
 
3894a49a
 ## ====== Clean up local destination dir
 test_flushdir("Clean testsuite-out/", "testsuite-out")
f11319be
 
f4e85335
 ## ====== Put from stdin
e64678ae
 f = open('testsuite/single-file/single-file.txt', 'r')
 test_s3cmd("Put from stdin", ['put', '-', '%s/single-file/single-file.txt' % pbucket(1)],
0bb9c318
            must_find = ["'<stdin>' -> '%s/single-file/single-file.txt'" % pbucket(1)],
e64678ae
            stdin = f)
 f.close()
f11319be
 
0699fa23
 ## ====== Multipart put
03482922
 os.system('mkdir -p testsuite-out')
e64678ae
 os.system('dd if=/dev/urandom of=testsuite-out/urandom.bin bs=1M count=16 > /dev/null 2>&1')
0699fa23
 test_s3cmd("Put multipart", ['put', '--multipart-chunk-size-mb=5', 'testsuite-out/urandom.bin', '%s/urandom.bin' % pbucket(1)],
e64678ae
            must_not_find = ['abortmp'])
0699fa23
 
5d7af455
 ## ====== Multipart put from stdin
e64678ae
 f = open('testsuite-out/urandom.bin', 'r')
 test_s3cmd("Multipart large put from stdin", ['put', '--multipart-chunk-size-mb=5', '-', '%s/urandom2.bin' % pbucket(1)],
            must_find = ['%s/urandom2.bin' % pbucket(1)],
            must_not_find = ['abortmp'],
            stdin = f)
 f.close()
5d7af455
 
0699fa23
 ## ====== Clean up local destination dir
 test_flushdir("Clean testsuite-out/", "testsuite-out")
 
4def8a48
 ## ====== Moving things without trailing '/'
83662f2c
 os.system('dd if=/dev/urandom of=testsuite-out/urandom1.bin bs=1k count=1 > /dev/null 2>&1')
 os.system('dd if=/dev/urandom of=testsuite-out/urandom2.bin bs=1k count=1 > /dev/null 2>&1')
 test_s3cmd("Put multiple files", ['put', 'testsuite-out/urandom1.bin', 'testsuite-out/urandom2.bin', '%s/' % pbucket(1)],
            must_find = ["%s/urandom1.bin" % pbucket(1), "%s/urandom2.bin" % pbucket(1)])
4def8a48
 
 test_s3cmd("Move without '/'", ['mv', '%s/urandom1.bin' % pbucket(1), '%s/urandom2.bin' % pbucket(1), '%s/dir' % pbucket(1)],
            retcode = 64,
            must_find = ['Destination must be a directory'])
 
 test_s3cmd("Move recursive w/a '/'",
            ['-r', 'mv', '%s/dir1' % pbucket(1), '%s/dir2' % pbucket(1)],
            retcode = 64,
            must_find = ['Destination must be a directory'])
 
 ## ====== Moving multiple files into directory with trailing '/'
 must_find = ["'%s/urandom1.bin' -> '%s/dir/urandom1.bin'" % (pbucket(1),pbucket(1)), "'%s/urandom2.bin' -> '%s/dir/urandom2.bin'" % (pbucket(1),pbucket(1))]
 must_not_find = ["'%s/urandom1.bin' -> '%s/dir'" % (pbucket(1),pbucket(1)), "'%s/urandom2.bin' -> '%s/dir'" % (pbucket(1),pbucket(1))]
 test_s3cmd("Move multiple files",
            ['mv', '%s/urandom1.bin' % pbucket(1), '%s/urandom2.bin' % pbucket(1), '%s/dir/' % pbucket(1)],
83662f2c
            must_find = must_find,
            must_not_find = must_not_find)
 
 ## ====== Clean up local destination dir
 test_flushdir("Clean testsuite-out/", "testsuite-out")
 
ca86524c
 ## ====== Sync from S3
08656ffe
 must_find = [ "'%s/xyz/binary/random-crap.md5' -> 'testsuite-out/xyz/binary/random-crap.md5'" % pbucket(1) ]
4986ae85
 if have_encoding:
08656ffe
     must_find.append(u"'%(pbucket)s/xyz/encodings/%(encoding)s/%(pattern)s' -> 'testsuite-out/xyz/encodings/%(encoding)s/%(pattern)s' " % { 'encoding' : encoding, 'pattern' : enc_pattern, 'pbucket' : pbucket(1) })
31fadab3
 test_s3cmd("Sync from S3", ['sync', '%s/xyz' % pbucket(1), 'testsuite-out'],
d439efb4
     must_find = must_find)
f11319be
 
73bf1c4d
 ## ====== Remove 'demo' directory
96222f57
 test_rmdir("Remove 'dir-test/'", "testsuite-out/xyz/dir-test/")
73bf1c4d
 
 
 ## ====== Create dir with name of a file
96222f57
 test_mkdir("Create file-dir dir", "testsuite-out/xyz/dir-test/file-dir")
73bf1c4d
 
 
 ## ====== Skip dst dirs
 test_s3cmd("Skip over dir", ['sync', '%s/xyz' % pbucket(1), 'testsuite-out'],
69bb35df
            must_find = "ERROR: Download of 'xyz/dir-test/file-dir' failed (Reason: testsuite-out/xyz/dir-test/file-dir is a directory)",
f3c0a75d
            retcode = EX_PARTIAL)
73bf1c4d
 
 
3894a49a
 ## ====== Clean up local destination dir
 test_flushdir("Clean testsuite-out/", "testsuite-out")
 
 
e3244a8c
 ## ====== Put public, guess MIME
31fadab3
 test_s3cmd("Put public, guess MIME", ['put', '--guess-mime-type', '--acl-public', 'testsuite/etc/logo.png', '%s/xyz/etc/logo.png' % pbucket(1)],
08656ffe
     must_find = [ "-> '%s/xyz/etc/logo.png'" % pbucket(1) ])
e3244a8c
 
 
ca86524c
 ## ====== Retrieve from URL
0e3cd57d
 if have_curl:
daf6da9c
     test_curl_HEAD("Retrieve from URL", 'http://%s.%s/xyz/etc/logo.png' % (bucket(1), cfg.host_base),
                    must_find_re = ['Content-Length: 22059'])
e3244a8c
 
 ## ====== Change ACL to Private
31fadab3
 test_s3cmd("Change ACL to Private", ['setacl', '--acl-private', '%s/xyz/etc/l*.png' % pbucket(1)],
d439efb4
     must_find = [ "logo.png: ACL set to Private" ])
e3244a8c
 
 
 ## ====== Verify Private ACL
0e3cd57d
 if have_curl:
daf6da9c
     test_curl_HEAD("Verify Private ACL", 'http://%s.%s/xyz/etc/logo.png' % (bucket(1), cfg.host_base),
                    must_find_re = [ '403 Forbidden' ])
e3244a8c
 
 
 ## ====== Change ACL to Public
31fadab3
 test_s3cmd("Change ACL to Public", ['setacl', '--acl-public', '--recursive', '%s/xyz/etc/' % pbucket(1) , '-v'],
d439efb4
     must_find = [ "logo.png: ACL set to Public" ])
e3244a8c
 
 
 ## ====== Verify Public ACL
0e3cd57d
 if have_curl:
daf6da9c
     test_curl_HEAD("Verify Public ACL", 'http://%s.%s/xyz/etc/logo.png' % (bucket(1), cfg.host_base),
                    must_find_re = [ '200 OK',
                                     'Content-Length: 22059'])
ca86524c
 
f11319be
 
ca86524c
 ## ====== Sync more to S3
31fadab3
 test_s3cmd("Sync more to S3", ['sync', 'testsuite/', 's3://%s/xyz/' % bucket(1), '--no-encrypt' ],
08656ffe
            must_find = [ "'testsuite/demo/some-file.xml' -> '%s/xyz/demo/some-file.xml' " % pbucket(1) ],
            must_not_find = [ "'testsuite/etc/linked.png' -> '%s/xyz/etc/linked.png'" % pbucket(1) ],
f3c0a75d
            retcode = EX_PARTIAL)
d439efb4
 
f11319be
 
1ba1f69a
 ## ====== Don't check MD5 sum on Sync
 test_copy("Change file cksum1.txt", "testsuite/checksum/cksum2.txt", "testsuite/checksum/cksum1.txt")
 test_copy("Change file cksum33.txt", "testsuite/checksum/cksum2.txt", "testsuite/checksum/cksum33.txt")
 test_s3cmd("Don't check MD5", ['sync', 'testsuite/', 's3://%s/xyz/' % bucket(1), '--no-encrypt', '--no-check-md5'],
f3c0a75d
            must_find = [ "cksum33.txt" ],
            must_not_find = [ "cksum1.txt" ],
            retcode = EX_PARTIAL)
1ba1f69a
 
 
 ## ====== Check MD5 sum on Sync
 test_s3cmd("Check MD5", ['sync', 'testsuite/', 's3://%s/xyz/' % bucket(1), '--no-encrypt', '--check-md5'],
f3c0a75d
            must_find = [ "cksum1.txt" ],
            retcode = EX_PARTIAL)
1ba1f69a
 
 
ca86524c
 ## ====== Rename within S3
31fadab3
 test_s3cmd("Rename within S3", ['mv', '%s/xyz/etc/logo.png' % pbucket(1), '%s/xyz/etc2/Logo.PNG' % pbucket(1)],
08656ffe
     must_find = [ "move: '%s/xyz/etc/logo.png' -> '%s/xyz/etc2/Logo.PNG'" % (pbucket(1), pbucket(1))])
f11319be
 
ca86524c
 
 ## ====== Rename (NoSuchKey)
31fadab3
 test_s3cmd("Rename (NoSuchKey)", ['mv', '%s/xyz/etc/logo.png' % pbucket(1), '%s/xyz/etc2/Logo.PNG' % pbucket(1)],
f05fb73f
     retcode = EX_NOTFOUND,
5fd8fd45
     must_find_re = [ 'Key not found' ],
08656ffe
     must_not_find = [ "move: '%s/xyz/etc/logo.png' -> '%s/xyz/etc2/Logo.PNG'" % (pbucket(1), pbucket(1)) ])
f11319be
 
248a3fa4
 ## ====== Sync more from S3 (invalid src)
 test_s3cmd("Sync more from S3 (invalid src)", ['sync', '--delete-removed', '%s/xyz/DOESNOTEXIST' % pbucket(1), 'testsuite-out'],
08656ffe
     must_not_find = [ "delete: 'testsuite-out/logo.png'" ])
ca86524c
 
257d6321
 ## ====== Sync more from S3
31fadab3
 test_s3cmd("Sync more from S3", ['sync', '--delete-removed', '%s/xyz' % pbucket(1), 'testsuite-out'],
00f51292
     must_find = [ "'%s/xyz/etc2/Logo.PNG' -> 'testsuite-out/xyz/etc2/Logo.PNG'" % pbucket(1),
08656ffe
                   "'%s/xyz/demo/some-file.xml' -> 'testsuite-out/xyz/demo/some-file.xml'" % pbucket(1) ],
00f51292
     must_not_find_re = [ "not-deleted.*etc/logo.png", "delete: 'testsuite-out/logo.png'" ])
257d6321
 
 
ca86524c
 ## ====== Make dst dir for get
 test_rmdir("Remove dst dir for get", "testsuite-out")
 
 
 ## ====== Get multiple files
31fadab3
 test_s3cmd("Get multiple files", ['get', '%s/xyz/etc2/Logo.PNG' % pbucket(1), '%s/xyz/etc/AtomicClockRadio.ttf' % pbucket(1), 'testsuite-out'],
8214d4f0
     retcode = EX_USAGE,
2933252b
     must_find = [ 'Destination must be a directory or stdout when downloading multiple sources.' ])
ca86524c
 
cd76140b
 ## ====== put/get non-ASCII filenames
 test_s3cmd("Put unicode filenames", ['put', u'testsuite/encodings/UTF-8/ŪņЇЌœđЗ/Žůžo',  u'%s/xyz/encodings/UTF-8/ŪņЇЌœđЗ/Žůžo' % pbucket(1)],
            retcode = 0,
f3c0a75d
            must_find = [ '->' ])
cd76140b
 
ca86524c
 
 ## ====== Make dst dir for get
 test_mkdir("Make dst dir for get", "testsuite-out")
 
 
cd76140b
 ## ====== put/get non-ASCII filenames
 test_s3cmd("Get unicode filenames", ['get', u'%s/xyz/encodings/UTF-8/ŪņЇЌœđЗ/Žůžo' % pbucket(1), 'testsuite-out'],
            retcode = 0,
f3c0a75d
            must_find = [ '->' ])
cd76140b
 
 
ca86524c
 ## ====== Get multiple files
31fadab3
 test_s3cmd("Get multiple files", ['get', '%s/xyz/etc2/Logo.PNG' % pbucket(1), '%s/xyz/etc/AtomicClockRadio.ttf' % pbucket(1), 'testsuite-out'],
08656ffe
     must_find = [ u"-> 'testsuite-out/Logo.PNG'",
                   u"-> 'testsuite-out/AtomicClockRadio.ttf'" ])
ca86524c
 
dc1c96cf
 ## ====== Upload files differing in capitalisation
31fadab3
 test_s3cmd("blah.txt / Blah.txt", ['put', '-r', 'testsuite/blahBlah', pbucket(1)],
d439efb4
     must_find = [ '%s/blahBlah/Blah.txt' % pbucket(1), '%s/blahBlah/blah.txt' % pbucket(1)])
ca86524c
 
e0b946c0
 ## ====== Copy between buckets
31fadab3
 test_s3cmd("Copy between buckets", ['cp', '%s/xyz/etc2/Logo.PNG' % pbucket(1), '%s/xyz/etc2/logo.png' % pbucket(3)],
08656ffe
     must_find = [ "remote copy: '%s/xyz/etc2/Logo.PNG' -> '%s/xyz/etc2/logo.png'" % (pbucket(1), pbucket(3)) ])
e0b946c0
 
 ## ====== Recursive copy
d293b507
 test_s3cmd("Recursive copy, set ACL", ['cp', '-r', '--acl-public', '%s/xyz/' % pbucket(1), '%s/copy/' % pbucket(2), '--exclude', 'demo/dir?/*.txt', '--exclude', 'non-printables*'],
08656ffe
     must_find = [ "remote copy: '%s/xyz/etc2/Logo.PNG' -> '%s/copy/etc2/Logo.PNG'" % (pbucket(1), pbucket(2)),
                   "remote copy: '%s/xyz/blahBlah/Blah.txt' -> '%s/copy/blahBlah/Blah.txt'" % (pbucket(1), pbucket(2)),
                   "remote copy: '%s/xyz/blahBlah/blah.txt' -> '%s/copy/blahBlah/blah.txt'" % (pbucket(1), pbucket(2)) ],
d439efb4
     must_not_find = [ "demo/dir1/file1-1.txt" ])
13fc0d5f
 
 ## ====== Verify ACL and MIME type
 test_s3cmd("Verify ACL and MIME type", ['info', '%s/copy/etc2/Logo.PNG' % pbucket(2) ],
d439efb4
     must_find_re = [ "MIME type:.*image/png",
                      "ACL:.*\*anon\*: READ",
e14a7511
                      "URL:.*http://%s.%s/copy/etc2/Logo.PNG" % (bucket(2), cfg.host_base) ])
13fc0d5f
 
61cddf01
 ## ====== modify MIME type
da091eee
 test_s3cmd("Modify MIME type", ['modify', '--mime-type=binary/octet-stream', '%s/copy/etc2/Logo.PNG' % pbucket(2) ])
61cddf01
 
 test_s3cmd("Verify ACL and MIME type", ['info', '%s/copy/etc2/Logo.PNG' % pbucket(2) ],
     must_find_re = [ "MIME type:.*binary/octet-stream",
                      "ACL:.*\*anon\*: READ",
                      "URL:.*http://%s.%s/copy/etc2/Logo.PNG" % (bucket(2), cfg.host_base) ])
 
da091eee
 test_s3cmd("Modify MIME type back", ['modify', '--mime-type=image/png', '%s/copy/etc2/Logo.PNG' % pbucket(2) ])
61cddf01
 
 test_s3cmd("Verify ACL and MIME type", ['info', '%s/copy/etc2/Logo.PNG' % pbucket(2) ],
     must_find_re = [ "MIME type:.*image/png",
                      "ACL:.*\*anon\*: READ",
                      "URL:.*http://%s.%s/copy/etc2/Logo.PNG" % (bucket(2), cfg.host_base) ])
 
a6366f8d
 test_s3cmd("Add cache-control header", ['modify', '--add-header=cache-control: max-age=3600, public', '%s/copy/etc2/Logo.PNG' % pbucket(2) ],
08656ffe
     must_find_re = [ "modify: .*" ])
f5ed9c91
 
0e3cd57d
 if have_curl:
     test_curl_HEAD("HEAD check Cache-Control present", 'http://%s.%s/copy/etc2/Logo.PNG' % (bucket(2), cfg.host_base),
68f43b60
                    must_find_re = [ "Cache-Control: max-age=3600" ])
f5ed9c91
 
 test_s3cmd("Remove cache-control header", ['modify', '--remove-header=cache-control', '%s/copy/etc2/Logo.PNG' % pbucket(2) ],
daf6da9c
            must_find_re = [ "modify: .*" ])
f5ed9c91
 
0e3cd57d
 if have_curl:
     test_curl_HEAD("HEAD check Cache-Control not present", 'http://%s.%s/copy/etc2/Logo.PNG' % (bucket(2), cfg.host_base),
68f43b60
                    must_not_find_re = [ "Cache-Control: max-age=3600" ])
f5ed9c91
 
f886eda4
 ## ====== sign
 test_s3cmd("sign string", ['sign', 's3cmd'], must_find_re = ["Signature:"])
 test_s3cmd("signurl time", ['signurl', '%s/copy/etc2/Logo.PNG' % pbucket(2), str(int(time.time()) + 60)], must_find_re = ["http://"])
9d392fa6
 test_s3cmd("signurl time offset", ['signurl', '%s/copy/etc2/Logo.PNG' % pbucket(2), '+60'], must_find_re = ["https?://"])
f2fc0daa
 test_s3cmd("signurl content disposition and type", ['signurl', '%s/copy/etc2/Logo.PNG' % pbucket(2), '+60', '--content-disposition=inline; filename=video.mp4', '--content-type=video/mp4'], must_find_re = [ 'response-content-disposition', 'response-content-type' ] )
61cddf01
 
13fc0d5f
 ## ====== Rename within S3
 test_s3cmd("Rename within S3", ['mv', '%s/copy/etc2/Logo.PNG' % pbucket(2), '%s/copy/etc/logo.png' % pbucket(2)],
08656ffe
     must_find = [ "move: '%s/copy/etc2/Logo.PNG' -> '%s/copy/etc/logo.png'" % (pbucket(2), pbucket(2))])
13fc0d5f
 
 ## ====== Sync between buckets
 test_s3cmd("Sync remote2remote", ['sync', '%s/xyz/' % pbucket(1), '%s/copy/' % pbucket(2), '--delete-removed', '--exclude', 'non-printables*'],
08656ffe
     must_find = [ "remote copy: '%s/xyz/demo/dir1/file1-1.txt' -> '%s/copy/demo/dir1/file1-1.txt'" % (pbucket(1), pbucket(2)),
                   "remote copy: 'etc/logo.png' -> 'etc2/Logo.PNG'",
                   "delete: '%s/copy/etc/logo.png'" % pbucket(2) ],
d439efb4
     must_not_find = [ "blah.txt" ])
e0b946c0
 
8ab3c3ac
 ## ====== Don't Put symbolic link
6332b625
 test_s3cmd("Don't put symbolic links", ['put', 'testsuite/etc/linked1.png', 's3://%s/xyz/' % bucket(1),],
81e3842f
            retcode = EX_USAGE,
efaa90fd
            must_find = ["WARNING: Skipping over symbolic link: testsuite/etc/linked1.png"],
            must_not_find_re = ["^(?!WARNING: Skipping).*linked1.png"])
8ab3c3ac
 
8c3bd026
 ## ====== Put symbolic link
 test_s3cmd("Put symbolic links", ['put', 'testsuite/etc/linked1.png', 's3://%s/xyz/' % bucket(1),'--follow-symlinks' ],
08656ffe
            must_find = [ "'testsuite/etc/linked1.png' -> '%s/xyz/linked1.png'" % pbucket(1)])
8c3bd026
 
7b5df262
 ## ====== Sync symbolic links
 test_s3cmd("Sync symbolic links", ['sync', 'testsuite/', 's3://%s/xyz/' % bucket(1), '--no-encrypt', '--follow-symlinks' ],
08656ffe
     must_find = ["remote copy: 'etc2/Logo.PNG' -> 'etc/linked.png'"],
6332b625
            # Don't want to recursively copy linked directories!
            must_not_find_re = ["etc/more/linked-dir/more/give-me-more.txt",
                                "etc/brokenlink.png"],
f3c0a75d
            retcode = EX_PARTIAL)
7b5df262
 
e0b946c0
 ## ====== Multi source move
31fadab3
 test_s3cmd("Multi-source move", ['mv', '-r', '%s/copy/blahBlah/Blah.txt' % pbucket(2), '%s/copy/etc/' % pbucket(2), '%s/moved/' % pbucket(2)],
08656ffe
     must_find = [ "move: '%s/copy/blahBlah/Blah.txt' -> '%s/moved/Blah.txt'" % (pbucket(2), pbucket(2)),
                   "move: '%s/copy/etc/AtomicClockRadio.ttf' -> '%s/moved/AtomicClockRadio.ttf'" % (pbucket(2), pbucket(2)),
                   "move: '%s/copy/etc/TypeRa.ttf' -> '%s/moved/TypeRa.ttf'" % (pbucket(2), pbucket(2)) ],
d439efb4
     must_not_find = [ "blah.txt" ])
e0b946c0
 
 ## ====== Verify move
31fadab3
 test_s3cmd("Verify move", ['ls', '-r', pbucket(2)],
d439efb4
     must_find = [ "%s/moved/Blah.txt" % pbucket(2),
                   "%s/moved/AtomicClockRadio.ttf" % pbucket(2),
                   "%s/moved/TypeRa.ttf" % pbucket(2),
                   "%s/copy/blahBlah/blah.txt" % pbucket(2) ],
     must_not_find = [ "%s/copy/blahBlah/Blah.txt" % pbucket(2),
                       "%s/copy/etc/AtomicClockRadio.ttf" % pbucket(2),
                       "%s/copy/etc/TypeRa.ttf" % pbucket(2) ])
e0b946c0
 
57e3e414
 ## ====== List all
 test_s3cmd("List all", ['la'],
            must_find = [ "%s/urandom.bin" % pbucket(1)])
 
ca86524c
 ## ====== Simple delete
31fadab3
 test_s3cmd("Simple delete", ['del', '%s/xyz/etc2/Logo.PNG' % pbucket(1)],
08656ffe
     must_find = [ "delete: '%s/xyz/etc2/Logo.PNG'" % pbucket(1) ])
f11319be
 
7268831a
 ## ====== Simple delete with rm
 test_s3cmd("Simple delete with rm", ['rm', '%s/xyz/test_rm/TypeRa.ttf' % pbucket(1)],
08656ffe
     must_find = [ "delete: '%s/xyz/test_rm/TypeRa.ttf'" % pbucket(1) ])
7268831a
 
9384a2d5
 ## ====== Create expiration rule with days and prefix
 test_s3cmd("Create expiration rule with days and prefix", ['expire', pbucket(1), '--expiry-days=365', '--expiry-prefix=log/'],
     must_find = [ "Bucket '%s/': expiration configuration is set." % pbucket(1)])
 
 ## ====== Create expiration rule with date and prefix
55527632
 test_s3cmd("Create expiration rule with date and prefix", ['expire', pbucket(1), '--expiry-date=2020-12-31T00:00:00.000Z', '--expiry-prefix=log/'],
9384a2d5
     must_find = [ "Bucket '%s/': expiration configuration is set." % pbucket(1)])
 
 ## ====== Create expiration rule with days only
 test_s3cmd("Create expiration rule with days only", ['expire', pbucket(1), '--expiry-days=365'],
     must_find = [ "Bucket '%s/': expiration configuration is set." % pbucket(1)])
 
 ## ====== Create expiration rule with date only
55527632
 test_s3cmd("Create expiration rule with date only", ['expire', pbucket(1), '--expiry-date=2020-12-31T00:00:00.000Z'],
9384a2d5
     must_find = [ "Bucket '%s/': expiration configuration is set." % pbucket(1)])
 
 ## ====== Get current expiration setting
 test_s3cmd("Get current expiration setting", ['info', pbucket(1)],
55527632
     must_find = [ "Expiration Rule: all objects in this bucket will expire in '2020-12-31T00:00:00.000Z'"])
9384a2d5
 
 ## ====== Delete expiration rule
 test_s3cmd("Delete expiration rule", ['expire', pbucket(1)],
     must_find = [ "Bucket '%s/': expiration configuration is deleted." % pbucket(1)])
f11319be
 
e7fad103
 ## ====== set Requester Pays flag
 test_s3cmd("Set requester pays", ['payer', '--requester-pays', pbucket(2)])
 
 ## ====== get Requester Pays flag
 test_s3cmd("Get requester pays flag", ['info', pbucket(2)],
efaa90fd
     must_find = [ "Payer:     Requester"])
e7fad103
 
 ## ====== ls using Requester Pays flag
 test_s3cmd("ls using requester pays flag", ['ls', '--requester-pays', pbucket(2)])
 
 ## ====== clear Requester Pays flag
 test_s3cmd("Clear requester pays", ['payer', pbucket(2)])
 
 ## ====== get Requester Pays flag
 test_s3cmd("Get requester pays flag", ['info', pbucket(2)],
efaa90fd
     must_find = [ "Payer:     BucketOwner"])
e7fad103
 
f230f799
 ## ====== Recursive delete maximum exceeed
 test_s3cmd("Recursive delete maximum exceeded", ['del', '--recursive', '--max-delete=1', '--exclude', 'Atomic*', '%s/xyz/etc' % pbucket(1)],
08656ffe
     must_not_find = [ "delete: '%s/xyz/etc/TypeRa.ttf'" % pbucket(1) ])
f230f799
 
ca86524c
 ## ====== Recursive delete
31fadab3
 test_s3cmd("Recursive delete", ['del', '--recursive', '--exclude', 'Atomic*', '%s/xyz/etc' % pbucket(1)],
08656ffe
     must_find = [ "delete: '%s/xyz/etc/TypeRa.ttf'" % pbucket(1) ],
     must_find_re = [ "delete: '.*/etc/logo.png'" ],
d439efb4
     must_not_find = [ "AtomicClockRadio.ttf" ])
ca86524c
 
7268831a
 ## ====== Recursive delete with rm
 test_s3cmd("Recursive delete with rm", ['rm', '--recursive', '--exclude', 'Atomic*', '%s/xyz/test_rm' % pbucket(1)],
08656ffe
     must_find = [ "delete: '%s/xyz/test_rm/more/give-me-more.txt'" % pbucket(1) ],
     must_find_re = [ "delete: '.*/test_rm/logo.png'" ],
7268831a
     must_not_find = [ "AtomicClockRadio.ttf" ])
 
ca86524c
 ## ====== Recursive delete all
31fadab3
 test_s3cmd("Recursive delete all", ['del', '--recursive', '--force', pbucket(1)],
08656ffe
     must_find_re = [ "delete: '.*binary/random-crap'" ])
f11319be
 
ca86524c
 ## ====== Remove empty bucket
31fadab3
 test_s3cmd("Remove empty bucket", ['rb', pbucket(1)],
d439efb4
     must_find = [ "Bucket '%s/' removed" % pbucket(1) ])
330c51eb
 
ca86524c
 ## ====== Remove remaining buckets
31fadab3
 test_s3cmd("Remove remaining buckets", ['rb', '--recursive', pbucket(2), pbucket(3)],
d439efb4
     must_find = [ "Bucket '%s/' removed" % pbucket(2),
               "Bucket '%s/' removed" % pbucket(3) ])
 
 # vim:et:ts=4:sts=4:ai