run-tests.py 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. ## Amazon S3cmd - testsuite
  4. ## Author: Michal Ludvig <michal@logix.cz>
  5. ## http://www.logix.cz/michal
  6. ## License: GPL Version 2
  7. ## Copyright: TGRMN Software and contributors
  8. from __future__ import absolute_import, print_function
  9. import sys
  10. import os
  11. import re
  12. import time
  13. from subprocess import Popen, PIPE, STDOUT
  14. import locale
  15. import getpass
  16. import S3.Exceptions
  17. import S3.Config
  18. from S3.ExitCodes import *
  19. try:
  20. unicode
  21. except NameError:
  22. # python 3 support
  23. # In python 3, unicode -> str, and str -> bytes
  24. unicode = str
  25. ALLOWED_SERVER_PROFILES = ['aws', 'minio']
  26. count_pass = 0
  27. count_fail = 0
  28. count_skip = 0
  29. test_counter = 0
  30. run_tests = []
  31. exclude_tests = []
  32. verbose = False
  33. encoding = locale.getpreferredencoding()
  34. if not encoding:
  35. print("Guessing current system encoding failed. Consider setting $LANG variable.")
  36. sys.exit(1)
  37. else:
  38. print("System encoding: " + encoding)
  39. try:
  40. unicode
  41. except NameError:
  42. # python 3 support
  43. # In python 3, unicode -> str, and str -> bytes
  44. unicode = str
  45. def unicodise(string, encoding = "utf-8", errors = "replace"):
  46. """
  47. Convert 'string' to Unicode or raise an exception.
  48. Config can't use toolbox from Utils that is itself using Config
  49. """
  50. if type(string) == unicode:
  51. return string
  52. try:
  53. return unicode(string, encoding, errors)
  54. except UnicodeDecodeError:
  55. raise UnicodeDecodeError("Conversion to unicode failed: %r" % string)
  56. # https://stackoverflow.com/questions/377017/test-if-executable-exists-in-python/377028#377028
  57. def which(program):
  58. def is_exe(fpath):
  59. return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
  60. fpath, fname = os.path.split(program)
  61. if fpath:
  62. if is_exe(program):
  63. return program
  64. else:
  65. for path in os.environ["PATH"].split(os.pathsep):
  66. path = path.strip('"')
  67. exe_file = os.path.join(path, program)
  68. if is_exe(exe_file):
  69. return exe_file
  70. return None
  71. if which('curl') is not None:
  72. have_curl = True
  73. else:
  74. have_curl = False
  75. config_file = None
  76. if os.getenv("HOME"):
  77. config_file = os.path.join(unicodise(os.getenv("HOME"), encoding),
  78. ".s3cfg")
  79. elif os.name == "nt" and os.getenv("USERPROFILE"):
  80. config_file = os.path.join(
  81. unicodise(os.getenv("USERPROFILE"), encoding),
  82. os.getenv("APPDATA") and unicodise(os.getenv("APPDATA"), encoding)
  83. or 'Application Data',
  84. "s3cmd.ini")
  85. ## Unpack testsuite/ directory
  86. if not os.path.isdir('testsuite') and os.path.isfile('testsuite.tar.gz'):
  87. os.system("tar -xz -f testsuite.tar.gz")
  88. if not os.path.isdir('testsuite'):
  89. print("Something went wrong while unpacking testsuite.tar.gz")
  90. sys.exit(1)
  91. os.system("tar -xf testsuite/checksum.tar -C testsuite")
  92. if not os.path.isfile('testsuite/checksum/cksum33.txt'):
  93. print("Something went wrong while unpacking testsuite/checkum.tar")
  94. sys.exit(1)
  95. ## Fix up permissions for permission-denied tests
  96. os.chmod("testsuite/permission-tests/permission-denied-dir", 0o444)
  97. os.chmod("testsuite/permission-tests/permission-denied.txt", 0o000)
  98. ## Patterns for Unicode tests
  99. patterns = {}
  100. patterns['UTF-8'] = u"ŪņЇЌœđЗ/☺ unicode € rocks ™"
  101. patterns['GBK'] = u"12月31日/1-特色條目"
  102. have_encoding = os.path.isdir('testsuite/encodings/' + encoding)
  103. if not have_encoding and os.path.isfile('testsuite/encodings/%s.tar.gz' % encoding):
  104. os.system("tar xvz -C testsuite/encodings -f testsuite/encodings/%s.tar.gz" % encoding)
  105. have_encoding = os.path.isdir('testsuite/encodings/' + encoding)
  106. if have_encoding:
  107. #enc_base_remote = "%s/xyz/%s/" % (pbucket(1), encoding)
  108. enc_pattern = patterns[encoding]
  109. else:
  110. print(encoding + " specific files not found.")
  111. def unicodise(string):
  112. if type(string) == unicode:
  113. return string
  114. return unicode(string, "UTF-8", "replace")
  115. def deunicodise(string):
  116. if type(string) != unicode:
  117. return string
  118. return string.encode("UTF-8", "replace")
  119. if not os.path.isdir('testsuite/crappy-file-name'):
  120. os.system("tar xvz -C testsuite -f testsuite/crappy-file-name.tar.gz")
  121. # TODO: also unpack if the tarball is newer than the directory timestamp
  122. # for instance when a new version was pulled from SVN.
  123. def test(label, cmd_args = [], retcode = 0, must_find = [], must_not_find = [],
  124. must_find_re = [], must_not_find_re = [], stdin = None,
  125. skip_if_profile = None, skip_if_not_profile = None):
  126. def command_output():
  127. print("----")
  128. print(" ".join([" " in arg and "'%s'" % arg or arg for arg in cmd_args]))
  129. print("----")
  130. print(stdout)
  131. print("----")
  132. def failure(message = ""):
  133. global count_fail
  134. if message:
  135. message = u" (%r)" % message
  136. print(u"\x1b[31;1mFAIL%s\x1b[0m" % (message))
  137. count_fail += 1
  138. command_output()
  139. #return 1
  140. sys.exit(1)
  141. def success(message = ""):
  142. global count_pass
  143. if message:
  144. message = " (%r)" % message
  145. print("\x1b[32;1mOK\x1b[0m%s" % (message))
  146. count_pass += 1
  147. if verbose:
  148. command_output()
  149. return 0
  150. def skip(message = ""):
  151. global count_skip
  152. if message:
  153. message = " (%r)" % message
  154. print("\x1b[33;1mSKIP\x1b[0m%s" % (message))
  155. count_skip += 1
  156. return 0
  157. def compile_list(_list, regexps = False):
  158. if regexps == False:
  159. _list = [re.escape(item) for item in _list]
  160. return [re.compile(item, re.MULTILINE) for item in _list]
  161. global test_counter
  162. test_counter += 1
  163. print(("%3d %s " % (test_counter, label)).ljust(30, "."), end=' ')
  164. sys.stdout.flush()
  165. if run_tests.count(test_counter) == 0 or exclude_tests.count(test_counter) > 0:
  166. return skip()
  167. if not cmd_args:
  168. return skip()
  169. if skip_if_profile and server_profile in skip_if_profile:
  170. return skip()
  171. if skip_if_not_profile and server_profile not in skip_if_not_profile:
  172. return skip()
  173. p = Popen(cmd_args, stdin = stdin, stdout = PIPE, stderr = STDOUT, universal_newlines = True, close_fds = True)
  174. stdout, stderr = p.communicate()
  175. if type(retcode) not in [list, tuple]: retcode = [retcode]
  176. if p.returncode not in retcode:
  177. return failure("retcode: %d, expected one of: %s" % (p.returncode, retcode))
  178. if type(must_find) not in [ list, tuple ]: must_find = [must_find]
  179. if type(must_find_re) not in [ list, tuple ]: must_find_re = [must_find_re]
  180. if type(must_not_find) not in [ list, tuple ]: must_not_find = [must_not_find]
  181. if type(must_not_find_re) not in [ list, tuple ]: must_not_find_re = [must_not_find_re]
  182. find_list = []
  183. find_list.extend(compile_list(must_find))
  184. find_list.extend(compile_list(must_find_re, regexps = True))
  185. find_list_patterns = []
  186. find_list_patterns.extend(must_find)
  187. find_list_patterns.extend(must_find_re)
  188. not_find_list = []
  189. not_find_list.extend(compile_list(must_not_find))
  190. not_find_list.extend(compile_list(must_not_find_re, regexps = True))
  191. not_find_list_patterns = []
  192. not_find_list_patterns.extend(must_not_find)
  193. not_find_list_patterns.extend(must_not_find_re)
  194. for index in range(len(find_list)):
  195. stdout = unicodise(stdout)
  196. match = find_list[index].search(stdout)
  197. if not match:
  198. return failure("pattern not found: %s" % find_list_patterns[index])
  199. for index in range(len(not_find_list)):
  200. match = not_find_list[index].search(stdout)
  201. if match:
  202. return failure("pattern found: %s (match: %s)" % (not_find_list_patterns[index], match.group(0)))
  203. return success()
  204. def test_s3cmd(label, cmd_args = [], **kwargs):
  205. if not cmd_args[0].endswith("s3cmd"):
  206. cmd_args.insert(0, "python")
  207. cmd_args.insert(1, "s3cmd")
  208. if config_file:
  209. cmd_args.insert(2, "-c")
  210. cmd_args.insert(3, config_file)
  211. return test(label, cmd_args, **kwargs)
  212. def test_mkdir(label, dir_name):
  213. if os.name in ("posix", "nt"):
  214. cmd = ['mkdir', '-p']
  215. else:
  216. print("Unknown platform: %s" % os.name)
  217. sys.exit(1)
  218. cmd.append(dir_name)
  219. return test(label, cmd)
  220. def test_rmdir(label, dir_name):
  221. if os.path.isdir(dir_name):
  222. if os.name == "posix":
  223. cmd = ['rm', '-rf']
  224. elif os.name == "nt":
  225. cmd = ['rmdir', '/s/q']
  226. else:
  227. print("Unknown platform: %s" % os.name)
  228. sys.exit(1)
  229. cmd.append(dir_name)
  230. return test(label, cmd)
  231. else:
  232. return test(label, [])
  233. def test_flushdir(label, dir_name):
  234. test_rmdir(label + "(rm)", dir_name)
  235. return test_mkdir(label + "(mk)", dir_name)
  236. def test_copy(label, src_file, dst_file):
  237. if os.name == "posix":
  238. cmd = ['cp', '-f']
  239. elif os.name == "nt":
  240. cmd = ['copy']
  241. else:
  242. print("Unknown platform: %s" % os.name)
  243. sys.exit(1)
  244. cmd.append(src_file)
  245. cmd.append(dst_file)
  246. return test(label, cmd)
  247. def test_curl_HEAD(label, src_file, **kwargs):
  248. cmd = ['curl', '--silent', '--head', '-include', '--location']
  249. cmd.append(src_file)
  250. return test(label, cmd, **kwargs)
  251. bucket_prefix = u"%s-" % getpass.getuser().lower()
  252. server_profile = None
  253. argv = sys.argv[1:]
  254. while argv:
  255. arg = argv.pop(0)
  256. if arg.startswith('--bucket-prefix='):
  257. print("Usage: '--bucket-prefix PREFIX', not '--bucket-prefix=PREFIX'")
  258. sys.exit(0)
  259. if arg in ("-h", "--help"):
  260. print("%s A B K..O -N" % sys.argv[0])
  261. print("Run tests number A, B and K through to O, except for N")
  262. sys.exit(0)
  263. if arg in ("-c", "--config"):
  264. config_file = argv.pop(0)
  265. continue
  266. if arg in ("-l", "--list"):
  267. exclude_tests = range(0, 999)
  268. break
  269. if arg in ("-v", "--verbose"):
  270. verbose = True
  271. continue
  272. if arg in ("-p", "--bucket-prefix"):
  273. try:
  274. bucket_prefix = argv.pop(0)
  275. except IndexError:
  276. print("Bucket prefix option must explicitly supply a bucket name prefix")
  277. sys.exit(0)
  278. continue
  279. if arg in ("-s", "--server-profile"):
  280. try:
  281. server_profile = argv.pop(0)
  282. server_profile = server_profile.lower()
  283. except IndexError:
  284. print("Server profile option must explicitly supply a server profile name")
  285. sys.exit(0)
  286. if server_profile not in ALLOWED_SERVER_PROFILES:
  287. print("Server profile value must be one of %r" % ALLOWED_SERVER_PROFILES)
  288. sys.exit(0)
  289. continue
  290. if ".." in arg:
  291. range_idx = arg.find("..")
  292. range_start = arg[:range_idx] or 0
  293. range_end = arg[range_idx+2:] or 999
  294. run_tests.extend(range(int(range_start), int(range_end) + 1))
  295. elif arg.startswith("-"):
  296. exclude_tests.append(int(arg[1:]))
  297. else:
  298. run_tests.append(int(arg))
  299. print("Using bucket prefix: '%s'" % bucket_prefix)
  300. cfg = S3.Config.Config(config_file)
  301. # Autodetect server profile if not set:
  302. if server_profile is None:
  303. if 's3.amazonaws.com' in cfg.host_base:
  304. server_profile = 'aws'
  305. print("Using server profile: '%s'" % server_profile)
  306. if not run_tests:
  307. run_tests = range(0, 999)
  308. # helper functions for generating bucket names
  309. def bucket(tail):
  310. '''Test bucket name'''
  311. label = 'autotest'
  312. if str(tail) == '3':
  313. label = 'autotest'
  314. return '%ss3cmd-%s-%s' % (bucket_prefix, label, tail)
  315. def pbucket(tail):
  316. '''Like bucket(), but prepends "s3://" for you'''
  317. return 's3://' + bucket(tail)
  318. ## ====== Remove test buckets
  319. test_s3cmd("Remove test buckets", ['rb', '-r', '--force', pbucket(1), pbucket(2), pbucket(3)])
  320. ## ====== verify they were removed
  321. test_s3cmd("Verify no test buckets", ['ls'],
  322. must_not_find = [pbucket(1), pbucket(2), pbucket(3)])
  323. ## ====== Create one bucket (EU)
  324. test_s3cmd("Create one bucket (EU)", ['mb', '--bucket-location=EU', pbucket(1)],
  325. must_find = "Bucket '%s/' created" % pbucket(1))
  326. ## ====== Create multiple buckets
  327. test_s3cmd("Create multiple buckets", ['mb', pbucket(2), pbucket(3)],
  328. must_find = [ "Bucket '%s/' created" % pbucket(2), "Bucket '%s/' created" % pbucket(3)])
  329. ## ====== Invalid bucket name
  330. test_s3cmd("Invalid bucket name", ["mb", "--bucket-location=EU", pbucket('EU')],
  331. retcode = EX_USAGE,
  332. must_find = "ERROR: Parameter problem: Bucket name '%s' contains disallowed character" % bucket('EU'),
  333. must_not_find_re = "Bucket.*created")
  334. ## ====== Buckets list
  335. test_s3cmd("Buckets list", ["ls"],
  336. must_find = [ pbucket(1), pbucket(2), pbucket(3) ], must_not_find_re = pbucket('EU'))
  337. ## ====== Directory for cache
  338. test_flushdir("Create cache dir", "testsuite/cachetest")
  339. ## ====== Sync to S3
  340. test_s3cmd("Sync to S3", ['sync', 'testsuite/', pbucket(1) + '/xyz/', '--exclude', 'demo/*', '--exclude', '*.png', '--no-encrypt', '--exclude-from', 'testsuite/exclude.encodings', '--exclude', 'testsuite/cachetest/.s3cmdcache', '--cache-file', 'testsuite/cachetest/.s3cmdcache'],
  341. must_find = ["ERROR: Upload of 'testsuite/permission-tests/permission-denied.txt' is not possible (Reason: Permission denied)",
  342. "WARNING: 32 non-printable characters replaced in: crappy-file-name/non-printables",
  343. ],
  344. must_not_find_re = ["demo/", "^(?!WARNING: Skipping).*\.png$", "permission-denied-dir"],
  345. retcode = EX_PARTIAL)
  346. ## ====== Create new file and sync with caching enabled
  347. test_mkdir("Create cache dir", "testsuite/cachetest/content")
  348. if os.path.exists("testsuite/cachetest"):
  349. with open("testsuite/cachetest/content/testfile", "w"):
  350. pass
  351. test_s3cmd("Sync to S3 with caching", ['sync', 'testsuite/', pbucket(1) + '/xyz/', '--exclude', 'demo/*', '--exclude', '*.png', '--no-encrypt', '--exclude-from', 'testsuite/exclude.encodings', '--exclude', 'cachetest/.s3cmdcache', '--cache-file', 'testsuite/cachetest/.s3cmdcache' ],
  352. must_find = "upload: 'testsuite/cachetest/content/testfile' -> '%s/xyz/cachetest/content/testfile'" % pbucket(1),
  353. must_not_find = "upload 'testsuite/cachetest/.s3cmdcache'",
  354. retcode = EX_PARTIAL)
  355. ## ====== Remove content and retry cached sync with --delete-removed
  356. test_rmdir("Remove local file", "testsuite/cachetest/content")
  357. test_s3cmd("Sync to S3 and delete removed with caching", ['sync', 'testsuite/', pbucket(1) + '/xyz/', '--exclude', 'demo/*', '--exclude', '*.png', '--no-encrypt', '--exclude-from', 'testsuite/exclude.encodings', '--exclude', 'testsuite/cachetest/.s3cmdcache', '--cache-file', 'testsuite/cachetest/.s3cmdcache', '--delete-removed'],
  358. must_find = "delete: '%s/xyz/cachetest/content/testfile'" % pbucket(1),
  359. must_not_find = "dictionary changed size during iteration",
  360. retcode = EX_PARTIAL)
  361. ## ====== Remove cache directory and file
  362. test_rmdir("Remove cache dir", "testsuite/cachetest")
  363. if have_encoding:
  364. ## ====== Sync UTF-8 / GBK / ... to S3
  365. test_s3cmd(u"Sync %s to S3" % encoding, ['sync', 'testsuite/encodings/' + encoding, '%s/xyz/encodings/' % pbucket(1), '--exclude', 'demo/*', '--no-encrypt' ],
  366. 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)} ])
  367. ## ====== List bucket content
  368. test_s3cmd("List bucket content", ['ls', '%s/xyz/' % pbucket(1) ],
  369. must_find_re = [ u"DIR +%s/xyz/binary/$" % pbucket(1) , u"DIR +%s/xyz/etc/$" % pbucket(1) ],
  370. must_not_find = [ u"random-crap.md5", u"/demo" ])
  371. ## ====== List bucket recursive
  372. must_find = [ u"%s/xyz/binary/random-crap.md5" % pbucket(1) ]
  373. if have_encoding:
  374. must_find.append(u"%(pbucket)s/xyz/encodings/%(encoding)s/%(pattern)s" % { 'encoding' : encoding, 'pattern' : enc_pattern, 'pbucket' : pbucket(1) })
  375. test_s3cmd("List bucket recursive", ['ls', '--recursive', pbucket(1)],
  376. must_find = must_find,
  377. must_not_find = [ "logo.png" ])
  378. ## ====== FIXME
  379. test_s3cmd("Recursive put", ['put', '--recursive', 'testsuite/etc', '%s/xyz/' % pbucket(1) ])
  380. ## ====== Clean up local destination dir
  381. test_flushdir("Clean testsuite-out/", "testsuite-out")
  382. ## ====== Put from stdin
  383. f = open('testsuite/single-file/single-file.txt', 'r')
  384. test_s3cmd("Put from stdin", ['put', '-', '%s/single-file/single-file.txt' % pbucket(1)],
  385. must_find = ["'<stdin>' -> '%s/single-file/single-file.txt'" % pbucket(1)],
  386. stdin = f)
  387. f.close()
  388. ## ====== Multipart put
  389. os.system('mkdir -p testsuite-out')
  390. os.system('dd if=/dev/urandom of=testsuite-out/urandom.bin bs=1M count=16 > /dev/null 2>&1')
  391. test_s3cmd("Put multipart", ['put', '--multipart-chunk-size-mb=5', 'testsuite-out/urandom.bin', '%s/urandom.bin' % pbucket(1)],
  392. must_not_find = ['abortmp'])
  393. ## ====== Multipart put from stdin
  394. f = open('testsuite-out/urandom.bin', 'r')
  395. test_s3cmd("Multipart large put from stdin", ['put', '--multipart-chunk-size-mb=5', '-', '%s/urandom2.bin' % pbucket(1)],
  396. must_find = ['%s/urandom2.bin' % pbucket(1)],
  397. must_not_find = ['abortmp'],
  398. stdin = f)
  399. f.close()
  400. ## ====== Clean up local destination dir
  401. test_flushdir("Clean testsuite-out/", "testsuite-out")
  402. ## ====== Moving things without trailing '/'
  403. os.system('dd if=/dev/urandom of=testsuite-out/urandom1.bin bs=1k count=1 > /dev/null 2>&1')
  404. os.system('dd if=/dev/urandom of=testsuite-out/urandom2.bin bs=1k count=1 > /dev/null 2>&1')
  405. test_s3cmd("Put multiple files", ['put', 'testsuite-out/urandom1.bin', 'testsuite-out/urandom2.bin', '%s/' % pbucket(1)],
  406. must_find = ["%s/urandom1.bin" % pbucket(1), "%s/urandom2.bin" % pbucket(1)])
  407. test_s3cmd("Move without '/'", ['mv', '%s/urandom1.bin' % pbucket(1), '%s/urandom2.bin' % pbucket(1), '%s/dir' % pbucket(1)],
  408. retcode = 64,
  409. must_find = ['Destination must be a directory'])
  410. test_s3cmd("Move recursive w/a '/'",
  411. ['-r', 'mv', '%s/dir1' % pbucket(1), '%s/dir2' % pbucket(1)],
  412. retcode = 64,
  413. must_find = ['Destination must be a directory'])
  414. ## ====== Moving multiple files into directory with trailing '/'
  415. must_find = ["'%s/urandom1.bin' -> '%s/dir/urandom1.bin'" % (pbucket(1),pbucket(1)), "'%s/urandom2.bin' -> '%s/dir/urandom2.bin'" % (pbucket(1),pbucket(1))]
  416. must_not_find = ["'%s/urandom1.bin' -> '%s/dir'" % (pbucket(1),pbucket(1)), "'%s/urandom2.bin' -> '%s/dir'" % (pbucket(1),pbucket(1))]
  417. test_s3cmd("Move multiple files",
  418. ['mv', '%s/urandom1.bin' % pbucket(1), '%s/urandom2.bin' % pbucket(1), '%s/dir/' % pbucket(1)],
  419. must_find = must_find,
  420. must_not_find = must_not_find)
  421. ## ====== Clean up local destination dir
  422. test_flushdir("Clean testsuite-out/", "testsuite-out")
  423. ## ====== Sync from S3
  424. must_find = [ "'%s/xyz/binary/random-crap.md5' -> 'testsuite-out/xyz/binary/random-crap.md5'" % pbucket(1) ]
  425. if have_encoding:
  426. 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) })
  427. test_s3cmd("Sync from S3", ['sync', '%s/xyz' % pbucket(1), 'testsuite-out'],
  428. must_find = must_find)
  429. ## ====== Remove 'demo' directory
  430. test_rmdir("Remove 'dir-test/'", "testsuite-out/xyz/dir-test/")
  431. ## ====== Create dir with name of a file
  432. test_mkdir("Create file-dir dir", "testsuite-out/xyz/dir-test/file-dir")
  433. ## ====== Skip dst dirs
  434. test_s3cmd("Skip over dir", ['sync', '%s/xyz' % pbucket(1), 'testsuite-out'],
  435. must_find = "ERROR: Download of 'xyz/dir-test/file-dir' failed (Reason: testsuite-out/xyz/dir-test/file-dir is a directory)",
  436. retcode = EX_PARTIAL)
  437. ## ====== Clean up local destination dir
  438. test_flushdir("Clean testsuite-out/", "testsuite-out")
  439. ## ====== Put public, guess MIME
  440. test_s3cmd("Put public, guess MIME", ['put', '--guess-mime-type', '--acl-public', 'testsuite/etc/logo.png', '%s/xyz/etc/logo.png' % pbucket(1)],
  441. must_find = [ "-> '%s/xyz/etc/logo.png'" % pbucket(1) ])
  442. ## ====== Retrieve from URL
  443. if have_curl:
  444. test_curl_HEAD("Retrieve from URL", 'http://%s.%s/xyz/etc/logo.png' % (bucket(1), cfg.host_base),
  445. must_find_re = ['Content-Length: 22059'],
  446. skip_if_profile = ['minio'])
  447. ## ====== Change ACL to Private
  448. test_s3cmd("Change ACL to Private", ['setacl', '--acl-private', '%s/xyz/etc/l*.png' % pbucket(1)],
  449. must_find = [ "logo.png: ACL set to Private" ],
  450. skip_if_profile = ['minio'])
  451. ## ====== Verify Private ACL
  452. if have_curl:
  453. test_curl_HEAD("Verify Private ACL", 'http://%s.%s/xyz/etc/logo.png' % (bucket(1), cfg.host_base),
  454. must_find_re = [ '403 Forbidden' ],
  455. skip_if_profile = ['minio'])
  456. ## ====== Change ACL to Public
  457. test_s3cmd("Change ACL to Public", ['setacl', '--acl-public', '--recursive', '%s/xyz/etc/' % pbucket(1) , '-v'],
  458. must_find = [ "logo.png: ACL set to Public" ],
  459. skip_if_profile = ['minio'])
  460. ## ====== Verify Public ACL
  461. if have_curl:
  462. test_curl_HEAD("Verify Public ACL", 'http://%s.%s/xyz/etc/logo.png' % (bucket(1), cfg.host_base),
  463. must_find_re = [ '200 OK', 'Content-Length: 22059'],
  464. skip_if_profile = ['minio'])
  465. ## ====== Sync more to S3
  466. test_s3cmd("Sync more to S3", ['sync', 'testsuite/', 's3://%s/xyz/' % bucket(1), '--no-encrypt' ],
  467. must_find = [ "'testsuite/demo/some-file.xml' -> '%s/xyz/demo/some-file.xml' " % pbucket(1) ],
  468. must_not_find = [ "'testsuite/etc/linked.png' -> '%s/xyz/etc/linked.png'" % pbucket(1) ],
  469. retcode = EX_PARTIAL)
  470. ## ====== Don't check MD5 sum on Sync
  471. test_copy("Change file cksum1.txt", "testsuite/checksum/cksum2.txt", "testsuite/checksum/cksum1.txt")
  472. test_copy("Change file cksum33.txt", "testsuite/checksum/cksum2.txt", "testsuite/checksum/cksum33.txt")
  473. test_s3cmd("Don't check MD5", ['sync', 'testsuite/', 's3://%s/xyz/' % bucket(1), '--no-encrypt', '--no-check-md5'],
  474. must_find = [ "cksum33.txt" ],
  475. must_not_find = [ "cksum1.txt" ],
  476. retcode = EX_PARTIAL)
  477. ## ====== Check MD5 sum on Sync
  478. test_s3cmd("Check MD5", ['sync', 'testsuite/', 's3://%s/xyz/' % bucket(1), '--no-encrypt', '--check-md5'],
  479. must_find = [ "cksum1.txt" ],
  480. retcode = EX_PARTIAL)
  481. ## ====== Rename within S3
  482. test_s3cmd("Rename within S3", ['mv', '%s/xyz/etc/logo.png' % pbucket(1), '%s/xyz/etc2/Logo.PNG' % pbucket(1)],
  483. must_find = [ "move: '%s/xyz/etc/logo.png' -> '%s/xyz/etc2/Logo.PNG'" % (pbucket(1), pbucket(1))])
  484. ## ====== Rename (NoSuchKey)
  485. test_s3cmd("Rename (NoSuchKey)", ['mv', '%s/xyz/etc/logo.png' % pbucket(1), '%s/xyz/etc2/Logo.PNG' % pbucket(1)],
  486. retcode = EX_NOTFOUND,
  487. must_find_re = [ 'Key not found' ],
  488. must_not_find = [ "move: '%s/xyz/etc/logo.png' -> '%s/xyz/etc2/Logo.PNG'" % (pbucket(1), pbucket(1)) ])
  489. ## ====== Sync more from S3 (invalid src)
  490. test_s3cmd("Sync more from S3 (invalid src)", ['sync', '--delete-removed', '%s/xyz/DOESNOTEXIST' % pbucket(1), 'testsuite-out'],
  491. must_not_find = [ "delete: 'testsuite-out/logo.png'" ])
  492. ## ====== Sync more from S3
  493. test_s3cmd("Sync more from S3", ['sync', '--delete-removed', '%s/xyz' % pbucket(1), 'testsuite-out'],
  494. must_find = [ "'%s/xyz/etc2/Logo.PNG' -> 'testsuite-out/xyz/etc2/Logo.PNG'" % pbucket(1),
  495. "'%s/xyz/demo/some-file.xml' -> 'testsuite-out/xyz/demo/some-file.xml'" % pbucket(1) ],
  496. must_not_find_re = [ "not-deleted.*etc/logo.png", "delete: 'testsuite-out/logo.png'" ])
  497. ## ====== Make dst dir for get
  498. test_rmdir("Remove dst dir for get", "testsuite-out")
  499. ## ====== Get multiple files
  500. test_s3cmd("Get multiple files", ['get', '%s/xyz/etc2/Logo.PNG' % pbucket(1), '%s/xyz/etc/AtomicClockRadio.ttf' % pbucket(1), 'testsuite-out'],
  501. retcode = EX_USAGE,
  502. must_find = [ 'Destination must be a directory or stdout when downloading multiple sources.' ])
  503. ## ====== put/get non-ASCII filenames
  504. test_s3cmd("Put unicode filenames", ['put', u'testsuite/encodings/UTF-8/ŪņЇЌœđЗ/Žůžo', u'%s/xyz/encodings/UTF-8/ŪņЇЌœđЗ/Žůžo' % pbucket(1)],
  505. retcode = 0,
  506. must_find = [ '->' ])
  507. ## ====== Make dst dir for get
  508. test_mkdir("Make dst dir for get", "testsuite-out")
  509. ## ====== put/get non-ASCII filenames
  510. test_s3cmd("Get unicode filenames", ['get', u'%s/xyz/encodings/UTF-8/ŪņЇЌœđЗ/Žůžo' % pbucket(1), 'testsuite-out'],
  511. retcode = 0,
  512. must_find = [ '->' ])
  513. ## ====== Get multiple files
  514. test_s3cmd("Get multiple files", ['get', '%s/xyz/etc2/Logo.PNG' % pbucket(1), '%s/xyz/etc/AtomicClockRadio.ttf' % pbucket(1), 'testsuite-out'],
  515. must_find = [ u"-> 'testsuite-out/Logo.PNG'",
  516. u"-> 'testsuite-out/AtomicClockRadio.ttf'" ])
  517. ## ====== Upload files differing in capitalisation
  518. test_s3cmd("blah.txt / Blah.txt", ['put', '-r', 'testsuite/blahBlah', pbucket(1)],
  519. must_find = [ '%s/blahBlah/Blah.txt' % pbucket(1), '%s/blahBlah/blah.txt' % pbucket(1)])
  520. ## ====== Copy between buckets
  521. test_s3cmd("Copy between buckets", ['cp', '%s/xyz/etc2/Logo.PNG' % pbucket(1), '%s/xyz/etc2/logo.png' % pbucket(3)],
  522. must_find = [ "remote copy: '%s/xyz/etc2/Logo.PNG' -> '%s/xyz/etc2/logo.png'" % (pbucket(1), pbucket(3)) ])
  523. ## ====== Recursive copy
  524. 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*'],
  525. must_find = [ "remote copy: '%s/xyz/etc2/Logo.PNG' -> '%s/copy/etc2/Logo.PNG'" % (pbucket(1), pbucket(2)),
  526. "remote copy: '%s/xyz/blahBlah/Blah.txt' -> '%s/copy/blahBlah/Blah.txt'" % (pbucket(1), pbucket(2)),
  527. "remote copy: '%s/xyz/blahBlah/blah.txt' -> '%s/copy/blahBlah/blah.txt'" % (pbucket(1), pbucket(2)) ],
  528. must_not_find = [ "demo/dir1/file1-1.txt" ])
  529. ## ====== Verify ACL and MIME type
  530. test_s3cmd("Verify ACL and MIME type", ['info', '%s/copy/etc2/Logo.PNG' % pbucket(2) ],
  531. must_find_re = [ "MIME type:.*image/png",
  532. "ACL:.*\*anon\*: READ",
  533. "URL:.*https?://%s.%s/copy/etc2/Logo.PNG" % (bucket(2), cfg.host_base) ],
  534. skip_if_profile = ['minio'])
  535. # Minio does not support ACL checks
  536. test_s3cmd("Verify MIME type", ['info', '%s/copy/etc2/Logo.PNG' % pbucket(2) ],
  537. must_find_re = ["MIME type:.*image/png"],
  538. skip_if_not_profile = ['minio'])
  539. ## ====== modify MIME type
  540. test_s3cmd("Modify MIME type", ['modify', '--mime-type=binary/octet-stream', '%s/copy/etc2/Logo.PNG' % pbucket(2) ])
  541. test_s3cmd("Verify ACL and MIME type", ['info', '%s/copy/etc2/Logo.PNG' % pbucket(2) ],
  542. must_find_re = [ "MIME type:.*binary/octet-stream",
  543. "ACL:.*\*anon\*: READ",
  544. "URL:.*https?://%s.%s/copy/etc2/Logo.PNG" % (bucket(2), cfg.host_base) ],
  545. skip_if_profile = ['minio'])
  546. # Minio does not support ACL checks
  547. test_s3cmd("Verify MIME type", ['info', '%s/copy/etc2/Logo.PNG' % pbucket(2) ],
  548. must_find_re = ["MIME type:.*binary/octet-stream"],
  549. skip_if_not_profile = ['minio'])
  550. ## ====== reset MIME type
  551. test_s3cmd("Modify MIME type back", ['modify', '--mime-type=image/png', '%s/copy/etc2/Logo.PNG' % pbucket(2) ])
  552. test_s3cmd("Verify ACL and MIME type", ['info', '%s/copy/etc2/Logo.PNG' % pbucket(2) ],
  553. must_find_re = [ "MIME type:.*image/png",
  554. "ACL:.*\*anon\*: READ",
  555. "URL:.*https?://%s.%s/copy/etc2/Logo.PNG" % (bucket(2), cfg.host_base) ],
  556. skip_if_profile = ['minio'])
  557. # Minio does not support ACL checks
  558. test_s3cmd("Verify MIME type", ['info', '%s/copy/etc2/Logo.PNG' % pbucket(2) ],
  559. must_find_re = ["MIME type:.*image/png"],
  560. skip_if_not_profile = ['minio'])
  561. test_s3cmd("Add cache-control header", ['modify', '--add-header=cache-control: max-age=3600, public', '%s/copy/etc2/Logo.PNG' % pbucket(2) ],
  562. must_find_re = [ "modify: .*" ])
  563. if have_curl:
  564. test_curl_HEAD("HEAD check Cache-Control present", 'http://%s.%s/copy/etc2/Logo.PNG' % (bucket(2), cfg.host_base),
  565. must_find_re = [ "Cache-Control: max-age=3600" ],
  566. skip_if_profile = ['minio'])
  567. test_s3cmd("Remove cache-control header", ['modify', '--remove-header=cache-control', '%s/copy/etc2/Logo.PNG' % pbucket(2) ],
  568. must_find_re = [ "modify: .*" ])
  569. if have_curl:
  570. test_curl_HEAD("HEAD check Cache-Control not present", 'http://%s.%s/copy/etc2/Logo.PNG' % (bucket(2), cfg.host_base),
  571. must_not_find_re = [ "Cache-Control: max-age=3600" ],
  572. skip_if_profile = ['minio'])
  573. ## ====== sign
  574. test_s3cmd("sign string", ['sign', 's3cmd'], must_find_re = ["Signature:"])
  575. test_s3cmd("signurl time", ['signurl', '%s/copy/etc2/Logo.PNG' % pbucket(2), str(int(time.time()) + 60)], must_find_re = ["http://"])
  576. test_s3cmd("signurl time offset", ['signurl', '%s/copy/etc2/Logo.PNG' % pbucket(2), '+60'], must_find_re = ["https?://"])
  577. 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' ] )
  578. ## ====== Rename within S3
  579. test_s3cmd("Rename within S3", ['mv', '%s/copy/etc2/Logo.PNG' % pbucket(2), '%s/copy/etc/logo.png' % pbucket(2)],
  580. must_find = [ "move: '%s/copy/etc2/Logo.PNG' -> '%s/copy/etc/logo.png'" % (pbucket(2), pbucket(2))])
  581. ## ====== Sync between buckets
  582. test_s3cmd("Sync remote2remote", ['sync', '%s/xyz/' % pbucket(1), '%s/copy/' % pbucket(2), '--delete-removed', '--exclude', 'non-printables*'],
  583. must_find = [ "remote copy: '%s/xyz/demo/dir1/file1-1.txt' -> '%s/copy/demo/dir1/file1-1.txt'" % (pbucket(1), pbucket(2)),
  584. "remote copy: 'etc/logo.png' -> 'etc2/Logo.PNG'",
  585. "delete: '%s/copy/etc/logo.png'" % pbucket(2) ],
  586. must_not_find = [ "blah.txt" ])
  587. ## ====== Exclude directory
  588. test_s3cmd("Exclude directory", ['put', '-r', 'testsuite/demo/', pbucket(1) + '/xyz/demo/', '--exclude', 'dir1/', '-d'],
  589. must_find = ["'testsuite/demo/dir2/file2-1.bin' -> '%s/xyz/demo/dir2/file2-1.bin'" % pbucket(1),
  590. "DEBUG: EXCLUDE: 'testsuite/demo/dir1/'"], # whole directory is excluded
  591. must_not_find = ["'testsuite/demo/dir1/file1-1.txt' -> '%s/xyz/demo/dir1/file1-1.txt'" % pbucket(1),
  592. "DEBUG: EXCLUDE: 'dir1/file1-1.txt'" # file is not synced, but also single file is not excluded
  593. ])
  594. ## ====== Don't Put symbolic link
  595. test_s3cmd("Don't put symbolic links", ['put', 'testsuite/etc/linked1.png', 's3://%s/xyz/' % bucket(1),],
  596. retcode = EX_USAGE,
  597. must_find = ["WARNING: Skipping over symbolic link: testsuite/etc/linked1.png"],
  598. must_not_find_re = ["^(?!WARNING: Skipping).*linked1.png"])
  599. ## ====== Put symbolic link
  600. test_s3cmd("Put symbolic links", ['put', 'testsuite/etc/linked1.png', 's3://%s/xyz/' % bucket(1),'--follow-symlinks' ],
  601. must_find = [ "'testsuite/etc/linked1.png' -> '%s/xyz/linked1.png'" % pbucket(1)])
  602. ## ====== Sync symbolic links
  603. test_s3cmd("Sync symbolic links", ['sync', 'testsuite/', 's3://%s/xyz/' % bucket(1), '--no-encrypt', '--follow-symlinks' ],
  604. must_find = ["remote copy: 'etc2/Logo.PNG' -> 'etc/linked.png'"],
  605. # Don't want to recursively copy linked directories!
  606. must_not_find_re = ["etc/more/linked-dir/more/give-me-more.txt",
  607. "etc/brokenlink.png"],
  608. retcode = EX_PARTIAL)
  609. ## ====== Multi source move
  610. test_s3cmd("Multi-source move", ['mv', '-r', '%s/copy/blahBlah/Blah.txt' % pbucket(2), '%s/copy/etc/' % pbucket(2), '%s/moved/' % pbucket(2)],
  611. must_find = [ "move: '%s/copy/blahBlah/Blah.txt' -> '%s/moved/Blah.txt'" % (pbucket(2), pbucket(2)),
  612. "move: '%s/copy/etc/AtomicClockRadio.ttf' -> '%s/moved/AtomicClockRadio.ttf'" % (pbucket(2), pbucket(2)),
  613. "move: '%s/copy/etc/TypeRa.ttf' -> '%s/moved/TypeRa.ttf'" % (pbucket(2), pbucket(2)) ],
  614. must_not_find = [ "blah.txt" ])
  615. ## ====== Verify move
  616. test_s3cmd("Verify move", ['ls', '-r', pbucket(2)],
  617. must_find = [ "%s/moved/Blah.txt" % pbucket(2),
  618. "%s/moved/AtomicClockRadio.ttf" % pbucket(2),
  619. "%s/moved/TypeRa.ttf" % pbucket(2),
  620. "%s/copy/blahBlah/blah.txt" % pbucket(2) ],
  621. must_not_find = [ "%s/copy/blahBlah/Blah.txt" % pbucket(2),
  622. "%s/copy/etc/AtomicClockRadio.ttf" % pbucket(2),
  623. "%s/copy/etc/TypeRa.ttf" % pbucket(2) ])
  624. ## ====== List all
  625. test_s3cmd("List all", ['la'],
  626. must_find = [ "%s/urandom.bin" % pbucket(1)])
  627. ## ====== Simple delete
  628. test_s3cmd("Simple delete", ['del', '%s/xyz/etc2/Logo.PNG' % pbucket(1)],
  629. must_find = [ "delete: '%s/xyz/etc2/Logo.PNG'" % pbucket(1) ])
  630. ## ====== Simple delete with rm
  631. test_s3cmd("Simple delete with rm", ['rm', '%s/xyz/test_rm/TypeRa.ttf' % pbucket(1)],
  632. must_find = [ "delete: '%s/xyz/test_rm/TypeRa.ttf'" % pbucket(1) ])
  633. ## ====== Create expiration rule with days and prefix
  634. test_s3cmd("Create expiration rule with days and prefix", ['expire', pbucket(1), '--expiry-days=365', '--expiry-prefix=log/'],
  635. must_find = [ "Bucket '%s/': expiration configuration is set." % pbucket(1)])
  636. ## ====== Create expiration rule with date and prefix
  637. test_s3cmd("Create expiration rule with date and prefix", ['expire', pbucket(1), '--expiry-date=2020-12-31T00:00:00.000Z', '--expiry-prefix=log/'],
  638. must_find = [ "Bucket '%s/': expiration configuration is set." % pbucket(1)])
  639. ## ====== Create expiration rule with days only
  640. test_s3cmd("Create expiration rule with days only", ['expire', pbucket(1), '--expiry-days=365'],
  641. must_find = [ "Bucket '%s/': expiration configuration is set." % pbucket(1)])
  642. ## ====== Create expiration rule with date only
  643. test_s3cmd("Create expiration rule with date only", ['expire', pbucket(1), '--expiry-date=2020-12-31T00:00:00.000Z'],
  644. must_find = [ "Bucket '%s/': expiration configuration is set." % pbucket(1)])
  645. ## ====== Get current expiration setting
  646. test_s3cmd("Get current expiration setting", ['info', pbucket(1)],
  647. must_find_re = [ "Expiration Rule: all objects in this bucket will expire in '2020-12-31T00:00:00(?:.000)?Z'"])
  648. ## ====== Delete expiration rule
  649. test_s3cmd("Delete expiration rule", ['expire', pbucket(1)],
  650. must_find = [ "Bucket '%s/': expiration configuration is deleted." % pbucket(1)])
  651. ## ====== set Requester Pays flag
  652. test_s3cmd("Set requester pays", ['payer', '--requester-pays', pbucket(2)],
  653. skip_if_profile=['minio'])
  654. ## ====== get Requester Pays flag
  655. test_s3cmd("Get requester pays flag", ['info', pbucket(2)],
  656. must_find = [ "Payer: Requester"],
  657. skip_if_profile=['minio'])
  658. ## ====== ls using Requester Pays flag
  659. test_s3cmd("ls using requester pays flag", ['ls', '--requester-pays', pbucket(2)],
  660. skip_if_profile=['minio'])
  661. ## ====== clear Requester Pays flag
  662. test_s3cmd("Clear requester pays", ['payer', pbucket(2)],
  663. skip_if_profile=['minio'])
  664. ## ====== get Requester Pays flag
  665. test_s3cmd("Get requester pays flag", ['info', pbucket(2)],
  666. must_find = [ "Payer: BucketOwner"],
  667. skip_if_profile=['minio'])
  668. ## ====== Recursive delete maximum exceeed
  669. test_s3cmd("Recursive delete maximum exceeded", ['del', '--recursive', '--max-delete=1', '--exclude', 'Atomic*', '%s/xyz/etc' % pbucket(1)],
  670. must_not_find = [ "delete: '%s/xyz/etc/TypeRa.ttf'" % pbucket(1) ])
  671. ## ====== Recursive delete
  672. test_s3cmd("Recursive delete", ['del', '--recursive', '--exclude', 'Atomic*', '%s/xyz/etc' % pbucket(1)],
  673. must_find = [ "delete: '%s/xyz/etc/TypeRa.ttf'" % pbucket(1) ],
  674. must_find_re = [ "delete: '.*/etc/logo.png'" ],
  675. must_not_find = [ "AtomicClockRadio.ttf" ])
  676. ## ====== Recursive delete with rm
  677. test_s3cmd("Recursive delete with rm", ['rm', '--recursive', '--exclude', 'Atomic*', '%s/xyz/test_rm' % pbucket(1)],
  678. must_find = [ "delete: '%s/xyz/test_rm/more/give-me-more.txt'" % pbucket(1) ],
  679. must_find_re = [ "delete: '.*/test_rm/logo.png'" ],
  680. must_not_find = [ "AtomicClockRadio.ttf" ])
  681. ## ====== Recursive delete all
  682. test_s3cmd("Recursive delete all", ['del', '--recursive', '--force', pbucket(1)],
  683. must_find_re = [ "delete: '.*binary/random-crap'" ])
  684. ## ====== Remove empty bucket
  685. test_s3cmd("Remove empty bucket", ['rb', pbucket(1)],
  686. must_find = [ "Bucket '%s/' removed" % pbucket(1) ])
  687. ## ====== Remove remaining buckets
  688. test_s3cmd("Remove remaining buckets", ['rb', '--recursive', pbucket(2), pbucket(3)],
  689. must_find = [ "Bucket '%s/' removed" % pbucket(2),
  690. "Bucket '%s/' removed" % pbucket(3) ])
  691. # vim:et:ts=4:sts=4:ai