]> git-server-git.apps.pok.os.sepia.ceph.com Git - s3-tests.git/commitdiff
Added testcases for listobjectsv2 267/head
authoralbIN7 <aantony@redhat.com>
Fri, 8 Mar 2019 10:11:16 +0000 (15:41 +0530)
committeralbIN7 <aantony@redhat.com>
Fri, 7 Jun 2019 11:56:42 +0000 (17:26 +0530)
Signed-off-by: Albin Antony <aantony@redhat.com>
s3tests_boto3/functional/test_s3.py

index bd509a2b32367ea5c1fe535cd11fad513e10ac62..d8d19894770f222ffb49f40a259a43e01a56c1c6 100644 (file)
@@ -153,6 +153,45 @@ def test_bucket_list_many():
     eq(response['IsTruncated'], False)
     eq(keys, ['foo'])
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list all keys')
+@attr(assertion='pagination w/max_keys=2, no marker')
+@attr('list-objects-v2')
+def test_bucket_listv2_many():
+    bucket_name = _create_objects(keys=['foo', 'bar', 'baz'])
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, MaxKeys=2)
+    keys = _get_keys(response)
+    eq(len(keys), 2)
+    eq(keys, ['bar', 'baz'])
+    eq(response['IsTruncated'], True)
+
+    response = client.list_objects_v2(Bucket=bucket_name, StartAfter='baz',MaxKeys=2)
+    keys = _get_keys(response)
+    eq(len(keys), 1)
+    eq(response['IsTruncated'], False)
+    eq(keys, ['foo'])
+
+
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list')
+@attr(assertion='keycount in listobjectsv2')
+@attr('list-objects-v2')
+def test_basic_key_count():
+    client = get_client()
+    bucket_names = []
+    bucket_name = get_new_bucket_name()
+    client.create_bucket(Bucket=bucket_name)
+    for j in range(5):
+            client.put_object(Bucket=bucket_name, Key=str(j))
+    response1 = client.list_objects_v2(Bucket=bucket_name)
+    eq(response1['KeyCount'], 5)
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list')
@@ -170,6 +209,24 @@ def test_bucket_list_delimiter_basic():
     eq(len(prefixes), 2)
     eq(prefixes, ['foo/', 'quux/'])
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list')
+@attr(assertion='prefixes in multi-component object names')
+@attr('list-objects-v2')
+def test_bucket_listv2_delimiter_basic():
+    bucket_name = _create_objects(keys=['foo/bar', 'foo/bar/xyzzy', 'quux/thud', 'asdf'])
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, Delimiter='/')
+    eq(response['Delimiter'], '/')
+    keys = _get_keys(response)
+    eq(keys, ['asdf'])
+
+    prefixes = _get_prefixes(response)
+    eq(len(prefixes), 2)
+    eq(prefixes, ['foo/', 'quux/'])
+
 def validate_bucket_list(bucket_name, prefix, delimiter, marker, max_keys,
                          is_truncated, check_objs, check_prefixes, next_marker):
     client = get_client()
@@ -190,6 +247,26 @@ def validate_bucket_list(bucket_name, prefix, delimiter, marker, max_keys,
 
     return response['NextMarker']
 
+def validate_bucket_listv2(bucket_name, prefix, delimiter, continuation_token, max_keys,
+                         is_truncated, check_objs, check_prefixes, next_continuation_token):
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, Delimiter=delimiter, StartAfter=continuation_token, MaxKeys=max_keys, Prefix=prefix)
+    eq(response['IsTruncated'], is_truncated)
+    if 'NextContinuationToken' not in response:
+        response['NextContinuationToken'] = None
+    eq(response['NextContinuationToken'], next_continuation_token)
+
+    keys = _get_keys(response)
+    prefixes = _get_prefixes(response)
+
+    eq(len(keys), len(check_objs))
+    eq(len(prefixes), len(check_prefixes))
+    eq(keys, check_objs)
+    eq(prefixes, check_prefixes)
+
+    return response['NextContinuationToken']
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list')
@@ -215,6 +292,42 @@ def test_bucket_list_delimiter_prefix():
 
     marker = validate_bucket_list(bucket_name, prefix, delim, '', 2, False, ['boo/bar'], ['boo/baz/'], None)
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list')
+@attr(assertion='prefixes in multi-component object names')
+@attr('list-objects-v2')
+def test_bucket_listv2_delimiter_prefix():
+    bucket_name = _create_objects(keys=['asdf', 'boo/bar', 'boo/baz/xyzzy', 'cquux/thud', 'cquux/bla'])
+
+    delim = '/'
+    continuation_token = ''
+    prefix = ''
+
+    continuation_token = validate_bucket_listv2(bucket_name, prefix, delim, '', 1, True, ['asdf'], [], 'asdf')
+    continuation_token = validate_bucket_listv2(bucket_name, prefix, delim, continuation_token, 1, True, [], ['boo/'], 'boo/')
+    continuation_token = validate_bucket_listv2(bucket_name, prefix, delim, continuation_token, 1, False, [], ['cquux/'], None)
+
+    continuation_token = validate_bucket_listv2(bucket_name, prefix, delim, '', 2, True, ['asdf'], ['boo/'], 'boo/')
+    continuation_token = validate_bucket_listv2(bucket_name, prefix, delim, continuation_token, 2, False, [], ['cquux/'], None)
+
+    prefix = 'boo/'
+
+    continuation_token = validate_bucket_listv2(bucket_name, prefix, delim, '', 1, True, ['boo/bar'], [], 'boo/bar')
+    continuation_token = validate_bucket_listv2(bucket_name, prefix, delim, continuation_token, 1, False, [], ['boo/baz/'], None)
+
+    continuation_token = validate_bucket_listv2(bucket_name, prefix, delim, '', 2, False, ['boo/bar'], ['boo/baz/'], None)
+
+
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list')
+@attr(assertion='prefix and delimiter handling when object ends with delimiter')
+@attr('list-objects-v2')
+def test_bucket_listv2_delimiter_prefix_ends_with_delimiter():
+    bucket_name = _create_objects(keys=['asdf/'])
+    validate_bucket_listv2(bucket_name, 'asdf/', '/', '', 1000, False, ['asdf/'], [], None)
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list')
@@ -243,6 +356,26 @@ def test_bucket_list_delimiter_alt():
     eq(len(prefixes), 2)
     eq(prefixes, ['ba', 'ca'])
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(assertion='non-slash delimiter characters')
+@attr('list-objects-v2')
+def test_bucket_listv2_delimiter_alt():
+    bucket_name = _create_objects(keys=['bar', 'baz', 'cab', 'foo'])
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, Delimiter='a')
+    eq(response['Delimiter'], 'a')
+
+    keys = _get_keys(response)
+    # foo contains no 'a' and so is a complete key
+    eq(keys, ['foo'])
+
+    # bar, baz, and cab should be broken up by the 'a' delimiters
+    prefixes = _get_prefixes(response)
+    eq(len(prefixes), 2)
+    eq(prefixes, ['ba', 'ca'])
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list')
@@ -267,6 +400,32 @@ def test_bucket_list_delimiter_prefix_underscore():
 
     marker = validate_bucket_list(bucket_name, prefix, delim, '', 2, False, ['_under1/bar'], ['_under1/baz/'], None)
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list')
+@attr(assertion='prefixes starting with underscore')
+@attr('list-objects-v2')
+def test_bucket_listv2_delimiter_prefix_underscore():
+    bucket_name = _create_objects(keys=['_obj1_','_under1/bar', '_under1/baz/xyzzy', '_under2/thud', '_under2/bla'])
+
+    delim = '/'
+    continuation_token = ''
+    prefix = ''
+    continuation_token  = validate_bucket_listv2(bucket_name, prefix, delim, '', 1, True, ['_obj1_'], [], '_obj1_')
+    continuation_token  = validate_bucket_listv2(bucket_name, prefix, delim, continuation_token , 1, True, [], ['_under1/'], '_under1/')
+    continuation_token  = validate_bucket_listv2(bucket_name, prefix, delim, continuation_token , 1, False, [], ['_under2/'], None)
+
+    continuation_token  = validate_bucket_listv2(bucket_name, prefix, delim, '', 2, True, ['_obj1_'], ['_under1/'], '_under1/')
+    continuation_token  = validate_bucket_listv2(bucket_name, prefix, delim, continuation_token , 2, False, [], ['_under2/'], None)
+
+    prefix = '_under1/'
+
+    continuation_token  = validate_bucket_listv2(bucket_name, prefix, delim, '', 1, True, ['_under1/bar'], [], '_under1/bar')
+    continuation_token  = validate_bucket_listv2(bucket_name, prefix, delim, continuation_token , 1, False, [], ['_under1/baz/'], None)
+
+    continuation_token  = validate_bucket_listv2(bucket_name, prefix, delim, '', 2, False, ['_under1/bar'], ['_under1/baz/'], None)
+
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list')
@@ -286,6 +445,25 @@ def test_bucket_list_delimiter_percentage():
     # bar, baz, and cab should be broken up by the 'a' delimiters
     eq(prefixes, ['b%', 'c%'])
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(assertion='percentage delimiter characters')
+@attr('list-objects-v2')
+def test_bucket_listv2_delimiter_percentage():
+    bucket_name = _create_objects(keys=['b%ar', 'b%az', 'c%ab', 'foo'])
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, Delimiter='%')
+    eq(response['Delimiter'], '%')
+    keys = _get_keys(response)
+    # foo contains no 'a' and so is a complete key
+    eq(keys, ['foo'])
+
+    prefixes = _get_prefixes(response)
+    eq(len(prefixes), 2)
+    # bar, baz, and cab should be broken up by the 'a' delimiters
+    eq(prefixes, ['b%', 'c%'])
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list')
@@ -305,6 +483,25 @@ def test_bucket_list_delimiter_whitespace():
     # bar, baz, and cab should be broken up by the 'a' delimiters
     eq(prefixes, ['b ', 'c '])
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(assertion='whitespace delimiter characters')
+@attr('list-objects-v2')
+def test_bucket_listv2_delimiter_whitespace():
+    bucket_name = _create_objects(keys=['b ar', 'b az', 'c ab', 'foo'])
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, Delimiter=' ')
+    eq(response['Delimiter'], ' ')
+    keys = _get_keys(response)
+    # foo contains no 'a' and so is a complete key
+    eq(keys, ['foo'])
+
+    prefixes = _get_prefixes(response)
+    eq(len(prefixes), 2)
+    # bar, baz, and cab should be broken up by the 'a' delimiters
+    eq(prefixes, ['b ', 'c '])
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list')
@@ -324,6 +521,25 @@ def test_bucket_list_delimiter_dot():
     # bar, baz, and cab should be broken up by the 'a' delimiters
     eq(prefixes, ['b.', 'c.'])
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(assertion='dot delimiter characters')
+@attr('list-objects-v2')
+def test_bucket_listv2_delimiter_dot():
+    bucket_name = _create_objects(keys=['b.ar', 'b.az', 'c.ab', 'foo'])
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, Delimiter='.')
+    eq(response['Delimiter'], '.')
+    keys = _get_keys(response)
+    # foo contains no 'a' and so is a complete key
+    eq(keys, ['foo'])
+
+    prefixes = _get_prefixes(response)
+    eq(len(prefixes), 2)
+    # bar, baz, and cab should be broken up by the 'a' delimiters
+    eq(prefixes, ['b.', 'c.'])
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list')
@@ -341,6 +557,23 @@ def test_bucket_list_delimiter_unreadable():
     eq(keys, key_names)
     eq(prefixes, [])
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(assertion='non-printable delimiter can be specified')
+@attr('list-objects-v2')
+def test_bucket_listv2_delimiter_unreadable():
+    key_names=['bar', 'baz', 'cab', 'foo']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, Delimiter='\x0a')
+    eq(response['Delimiter'], '\x0a')
+
+    keys = _get_keys(response)
+    prefixes = _get_prefixes(response)
+    eq(keys, key_names)
+    eq(prefixes, [])
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list')
@@ -359,6 +592,24 @@ def test_bucket_list_delimiter_empty():
     eq(keys, key_names)
     eq(prefixes, [])
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(assertion='empty delimiter can be specified')
+@attr('list-objects-v2')
+def test_bucket_listv2_delimiter_empty():
+    key_names = ['bar', 'baz', 'cab', 'foo']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, Delimiter='')
+    # putting an empty value into Delimiter will not return a value in the response
+    eq('Delimiter' in response, False)
+
+    keys = _get_keys(response)
+    prefixes = _get_prefixes(response)
+    eq(keys, key_names)
+    eq(prefixes, [])
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list')
@@ -377,6 +628,57 @@ def test_bucket_list_delimiter_none():
     eq(keys, key_names)
     eq(prefixes, [])
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(assertion='unspecified delimiter defaults to none')
+@attr('list-objects-v2')
+def test_bucket_listv2_delimiter_none():
+    key_names = ['bar', 'baz', 'cab', 'foo']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name)
+    # putting an empty value into Delimiter will not return a value in the response
+    eq('Delimiter' in response, False)
+
+    keys = _get_keys(response)
+    prefixes = _get_prefixes(response)
+    eq(keys, key_names)
+    eq(prefixes, [])
+
+@attr('list-objects-v2')
+def test_bucket_listv2_fetchowner_notempty():
+    key_names = ['foo/bar', 'foo/baz', 'quux']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, FetchOwner=True)
+    objs_list = response['Contents']
+    eq('Owner' in objs_list[0], True)
+
+@attr('list-objects-v2')
+def test_bucket_listv2_fetchowner_defaultempty():
+    key_names = ['foo/bar', 'foo/baz', 'quux']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name)
+    objs_list = response['Contents']
+    eq('Owner' in objs_list[0], False)
+
+@attr('list-objects-v2')
+def test_bucket_listv2_fetchowner_empty():
+    key_names = ['foo/bar', 'foo/baz', 'quux']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, FetchOwner= False)
+    objs_list = response['Contents']
+    eq('Owner' in objs_list[0], False)
+
+
+
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list')
@@ -395,6 +697,24 @@ def test_bucket_list_delimiter_not_exist():
     eq(keys, key_names)
     eq(prefixes, [])
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(assertion='unused delimiter is not found')
+@attr('list-objects-v2')
+def test_bucket_listv2_delimiter_not_exist():
+    key_names = ['bar', 'baz', 'cab', 'foo']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, Delimiter='/')
+    # putting an empty value into Delimiter will not return a value in the response
+    eq(response['Delimiter'], '/')
+
+    keys = _get_keys(response)
+    prefixes = _get_prefixes(response)
+    eq(keys, key_names)
+    eq(prefixes, [])
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list under prefix')
@@ -412,6 +732,24 @@ def test_bucket_list_prefix_basic():
     eq(keys, ['foo/bar', 'foo/baz'])
     eq(prefixes, [])
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list under prefix with list-objects-v2')
+@attr(assertion='returns only objects under prefix')
+@attr('list-objects-v2')
+def test_bucket_listv2_prefix_basic():
+    key_names = ['foo/bar', 'foo/baz', 'quux']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, Prefix='foo/')
+    eq(response['Prefix'], 'foo/')
+
+    keys = _get_keys(response)
+    prefixes = _get_prefixes(response)
+    eq(keys, ['foo/bar', 'foo/baz'])
+    eq(prefixes, [])
+
 # just testing that we can do the delimeter and prefix logic on non-slashes
 @attr(resource='bucket')
 @attr(method='get')
@@ -430,6 +768,24 @@ def test_bucket_list_prefix_alt():
     eq(keys, ['bar', 'baz'])
     eq(prefixes, [])
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list under prefix with list-objects-v2')
+@attr(assertion='prefixes w/o delimiters')
+@attr('list-objects-v2')
+def test_bucket_listv2_prefix_alt():
+    key_names = ['bar', 'baz', 'foo']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, Prefix='ba')
+    eq(response['Prefix'], 'ba')
+
+    keys = _get_keys(response)
+    prefixes = _get_prefixes(response)
+    eq(keys, ['bar', 'baz'])
+    eq(prefixes, [])
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list under prefix')
@@ -447,6 +803,24 @@ def test_bucket_list_prefix_empty():
     eq(keys, key_names)
     eq(prefixes, [])
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list under prefix with list-objects-v2')
+@attr(assertion='empty prefix returns everything')
+@attr('list-objects-v2')
+def test_bucket_listv2_prefix_empty():
+    key_names = ['foo/bar', 'foo/baz', 'quux']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, Prefix='')
+    eq(response['Prefix'], '')
+
+    keys = _get_keys(response)
+    prefixes = _get_prefixes(response)
+    eq(keys, key_names)
+    eq(prefixes, [])
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list under prefix')
@@ -464,6 +838,24 @@ def test_bucket_list_prefix_none():
     eq(keys, key_names)
     eq(prefixes, [])
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list under prefix with list-objects-v2')
+@attr(assertion='unspecified prefix returns everything')
+@attr('list-objects-v2')
+def test_bucket_listv2_prefix_none():
+    key_names = ['foo/bar', 'foo/baz', 'quux']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, Prefix='')
+    eq(response['Prefix'], '')
+
+    keys = _get_keys(response)
+    prefixes = _get_prefixes(response)
+    eq(keys, key_names)
+    eq(prefixes, [])
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list under prefix')
@@ -473,8 +865,43 @@ def test_bucket_list_prefix_not_exist():
     bucket_name = _create_objects(keys=key_names)
     client = get_client()
 
-    response = client.list_objects(Bucket=bucket_name, Prefix='d')
-    eq(response['Prefix'], 'd')
+    response = client.list_objects(Bucket=bucket_name, Prefix='d')
+    eq(response['Prefix'], 'd')
+
+    keys = _get_keys(response)
+    prefixes = _get_prefixes(response)
+    eq(keys, [])
+    eq(prefixes, [])
+
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list under prefix with list-objects-v2')
+@attr(assertion='nonexistent prefix returns nothing')
+@attr('list-objects-v2')
+def test_bucket_listv2_prefix_not_exist():
+    key_names = ['foo/bar', 'foo/baz', 'quux']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, Prefix='d')
+    eq(response['Prefix'], 'd')
+
+    keys = _get_keys(response)
+    prefixes = _get_prefixes(response)
+    eq(keys, [])
+    eq(prefixes, [])
+
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list under prefix')
+@attr(assertion='non-printable prefix can be specified')
+def test_bucket_list_prefix_unreadable():
+    key_names = ['foo/bar', 'foo/baz', 'quux']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects(Bucket=bucket_name, Prefix='\x0a')
+    eq(response['Prefix'], '\x0a')
 
     keys = _get_keys(response)
     prefixes = _get_prefixes(response)
@@ -483,14 +910,15 @@ def test_bucket_list_prefix_not_exist():
 
 @attr(resource='bucket')
 @attr(method='get')
-@attr(operation='list under prefix')
+@attr(operation='list under prefix with list-objects-v2')
 @attr(assertion='non-printable prefix can be specified')
-def test_bucket_list_prefix_unreadable():
+@attr('list-objects-v2')
+def test_bucket_listv2_prefix_unreadable():
     key_names = ['foo/bar', 'foo/baz', 'quux']
     bucket_name = _create_objects(keys=key_names)
     client = get_client()
 
-    response = client.list_objects(Bucket=bucket_name, Prefix='\x0a')
+    response = client.list_objects_v2(Bucket=bucket_name, Prefix='\x0a')
     eq(response['Prefix'], '\x0a')
 
     keys = _get_keys(response)
@@ -516,6 +944,25 @@ def test_bucket_list_prefix_delimiter_basic():
     eq(keys, ['foo/bar'])
     eq(prefixes, ['foo/baz/'])
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list-objects-v2 under prefix w/delimiter')
+@attr(assertion='returns only objects directly under prefix')
+@attr('list-objects-v2')
+def test_bucket_listv2_prefix_delimiter_basic():
+    key_names = ['foo/bar', 'foo/baz/xyzzy', 'quux/thud', 'asdf']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, Delimiter='/', Prefix='foo/')
+    eq(response['Prefix'], 'foo/')
+    eq(response['Delimiter'], '/')
+
+    keys = _get_keys(response)
+    prefixes = _get_prefixes(response)
+    eq(keys, ['foo/bar'])
+    eq(prefixes, ['foo/baz/'])
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list under prefix w/delimiter')
@@ -534,6 +981,21 @@ def test_bucket_list_prefix_delimiter_alt():
     eq(keys, ['bar'])
     eq(prefixes, ['baza'])
 
+@attr('list-objects-v2')
+def test_bucket_listv2_prefix_delimiter_alt():
+    key_names = ['bar', 'bazar', 'cab', 'foo']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, Delimiter='a', Prefix='ba')
+    eq(response['Prefix'], 'ba')
+    eq(response['Delimiter'], 'a')
+
+    keys = _get_keys(response)
+    prefixes = _get_prefixes(response)
+    eq(keys, ['bar'])
+    eq(prefixes, ['baza'])
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list under prefix w/delimiter')
@@ -550,6 +1012,23 @@ def test_bucket_list_prefix_delimiter_prefix_not_exist():
     eq(keys, [])
     eq(prefixes, [])
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list-objects-v2 under prefix w/delimiter')
+@attr(assertion='finds nothing w/unmatched prefix')
+@attr('list-objects-v2')
+def test_bucket_listv2_prefix_delimiter_prefix_not_exist():
+    key_names = ['b/a/r', 'b/a/c', 'b/a/g', 'g']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, Delimiter='d', Prefix='/')
+
+    keys = _get_keys(response)
+    prefixes = _get_prefixes(response)
+    eq(keys, [])
+    eq(prefixes, [])
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list under prefix w/delimiter')
@@ -566,6 +1045,23 @@ def test_bucket_list_prefix_delimiter_delimiter_not_exist():
     eq(keys, ['b/a/c', 'b/a/g', 'b/a/r'])
     eq(prefixes, [])
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list-objects-v2 under prefix w/delimiter')
+@attr(assertion='over-ridden slash ceases to be a delimiter')
+@attr('list-objects-v2')
+def test_bucket_listv2_prefix_delimiter_delimiter_not_exist():
+    key_names = ['b/a/c', 'b/a/g', 'b/a/r', 'g']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, Delimiter='z', Prefix='b')
+
+    keys = _get_keys(response)
+    prefixes = _get_prefixes(response)
+    eq(keys, ['b/a/c', 'b/a/g', 'b/a/r'])
+    eq(prefixes, [])
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list under prefix w/delimiter')
@@ -582,6 +1078,23 @@ def test_bucket_list_prefix_delimiter_prefix_delimiter_not_exist():
     eq(keys, [])
     eq(prefixes, [])
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list-objects-v2 under prefix w/delimiter')
+@attr(assertion='finds nothing w/unmatched prefix and delimiter')
+@attr('list-objects-v2')
+def test_bucket_listv2_prefix_delimiter_prefix_delimiter_not_exist():
+    key_names = ['b/a/c', 'b/a/g', 'b/a/r', 'g']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, Delimiter='z', Prefix='y')
+
+    keys = _get_keys(response)
+    prefixes = _get_prefixes(response)
+    eq(keys, [])
+    eq(prefixes, [])
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list all keys')
@@ -603,6 +1116,28 @@ def test_bucket_list_maxkeys_one():
     keys = _get_keys(response)
     eq(keys, key_names[1:])
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list all keys with list-objects-v2')
+@attr(assertion='pagination w/max_keys=1, marker')
+@attr('list-objects-v2')
+def test_bucket_listv2_maxkeys_one():
+    key_names = ['bar', 'baz', 'foo', 'quxx']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, MaxKeys=1)
+    eq(response['IsTruncated'], True)
+
+    keys = _get_keys(response)
+    eq(keys, key_names[0:1])
+
+    response = client.list_objects_v2(Bucket=bucket_name, StartAfter=key_names[0])
+    eq(response['IsTruncated'], False)
+
+    keys = _get_keys(response)
+    eq(keys, key_names[1:])
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list all keys')
@@ -618,6 +1153,22 @@ def test_bucket_list_maxkeys_zero():
     keys = _get_keys(response)
     eq(keys, [])
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list all keys with list-objects-v2')
+@attr(assertion='pagination w/max_keys=0')
+@attr('list-objects-v2')
+def test_bucket_listv2_maxkeys_zero():
+    key_names = ['bar', 'baz', 'foo', 'quxx']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, MaxKeys=0)
+
+    eq(response['IsTruncated'], False)
+    keys = _get_keys(response)
+    eq(keys, [])
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list all keys')
@@ -633,6 +1184,22 @@ def test_bucket_list_maxkeys_none():
     eq(keys, key_names)
     eq(response['MaxKeys'], 1000)
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list all keys with list-objects-v2')
+@attr(assertion='pagination w/o max_keys')
+@attr('list-objects-v2')
+def test_bucket_listv2_maxkeys_none():
+    key_names = ['bar', 'baz', 'foo', 'quxx']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name)
+    eq(response['IsTruncated'], False)
+    keys = _get_keys(response)
+    eq(keys, key_names)
+    eq(response['MaxKeys'], 1000)
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list all keys')
@@ -689,6 +1256,64 @@ def test_bucket_list_unordered():
     eq(status, 400)
     eq(error_code, 'InvalidArgument')
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list all keys with list-objects-v2')
+@attr(assertion='bucket list unordered')
+@attr('fails_on_aws') # allow-unordered is a non-standard extension
+@attr('list-objects-v2')
+def test_bucket_listv2_unordered():
+    # boto3.set_stream_logger(name='botocore')
+    keys_in = ['ado', 'bot', 'cob', 'dog', 'emu', 'fez', 'gnu', 'hex',
+               'abc/ink', 'abc/jet', 'abc/kin', 'abc/lax', 'abc/mux',
+               'def/nim', 'def/owl', 'def/pie', 'def/qed', 'def/rye',
+               'ghi/sew', 'ghi/tor', 'ghi/uke', 'ghi/via', 'ghi/wit',
+               'xix', 'yak', 'zoo']
+    bucket_name = _create_objects(keys=keys_in)
+    client = get_client()
+
+    # adds the unordered query parameter
+    def add_unordered(**kwargs):
+        kwargs['params']['url'] += "&allow-unordered=true"
+    client.meta.events.register('before-call.s3.ListObjects', add_unordered)
+
+    # test simple retrieval
+    response = client.list_objects_v2(Bucket=bucket_name, MaxKeys=1000)
+    unordered_keys_out = _get_keys(response)
+    eq(len(keys_in), len(unordered_keys_out))
+    eq(keys_in.sort(), unordered_keys_out.sort())
+
+    # test retrieval with prefix
+    response = client.list_objects_v2(Bucket=bucket_name,
+                                   MaxKeys=1000,
+                                   Prefix="abc/")
+    unordered_keys_out = _get_keys(response)
+    eq(5, len(unordered_keys_out))
+
+    # test incremental retrieval with marker
+    response = client.list_objects_v2(Bucket=bucket_name, MaxKeys=6)
+    unordered_keys_out = _get_keys(response)
+    eq(6, len(unordered_keys_out))
+
+    # now get the next bunch
+    response = client.list_objects_v2(Bucket=bucket_name,
+                                   MaxKeys=6,
+                                   StartAfter=unordered_keys_out[-1])
+    unordered_keys_out2 = _get_keys(response)
+    eq(6, len(unordered_keys_out2))
+
+    # make sure there's no overlap between the incremental retrievals
+    intersect = set(unordered_keys_out).intersection(unordered_keys_out2)
+    eq(0, len(intersect))
+
+    # verify that unordered used with delimiter results in error
+    e = assert_raises(ClientError,
+                      client.list_objects, Bucket=bucket_name, Delimiter="/")
+    status, error_code = _get_status_and_error_code(e.response)
+    eq(status, 400)
+    eq(error_code, 'InvalidArgument')
+
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list all keys')
@@ -709,6 +1334,8 @@ def test_bucket_list_maxkeys_invalid():
     eq(status, 400)
     eq(error_code, 'InvalidArgument')
 
+
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list all keys')
@@ -721,6 +1348,19 @@ def test_bucket_list_marker_none():
     response = client.list_objects(Bucket=bucket_name)
     eq(response['Marker'], '')
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list all keys with list-objects-v2')
+@attr(assertion='no pagination, no continuationtoken')
+@attr('list-objects-v2')
+def test_bucket_listv2_continuationtoken_none():
+    key_names = ['bar', 'baz', 'foo', 'quxx']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name)
+    eq(response['ContinuationToken'], '')
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list all keys')
@@ -736,6 +1376,22 @@ def test_bucket_list_marker_empty():
     keys = _get_keys(response)
     eq(keys, key_names)
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list all keys with list-objects-v2')
+@attr(assertion='no pagination, empty continuationtoken')
+@attr('list-objects-v2')
+def test_bucket_listv2_continuationtoken_empty():
+    key_names = ['bar', 'baz', 'foo', 'quxx']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, ContinuationToken='')
+    eq(response['ContinuationToken'], '')
+    eq(response['IsTruncated'], False)
+    keys = _get_keys(response)
+    eq(keys, key_names)
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list all keys')
@@ -751,6 +1407,22 @@ def test_bucket_list_marker_unreadable():
     keys = _get_keys(response)
     eq(keys, key_names)
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list all keys with list-objects-v2')
+@attr(assertion='non-printing startafter')
+@attr('list-objects-v2')
+def test_bucket_listv2_startafter_unreadable():
+    key_names = ['bar', 'baz', 'foo', 'quxx']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, StartAfter='\x0a')
+    eq(response['StartAfter'], '\x0a')
+    eq(response['IsTruncated'], False)
+    keys = _get_keys(response)
+    eq(keys, key_names)
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list all keys')
@@ -763,6 +1435,21 @@ def test_bucket_list_marker_not_in_list():
     response = client.list_objects(Bucket=bucket_name, Marker='blah')
     eq(response['Marker'], 'blah')
     keys = _get_keys(response)
+    eq(keys, [ 'foo','quxx'])
+
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list all keys with list-objects-v2')
+@attr(assertion='startafter not-in-list')
+@attr('list-objects-v2')
+def test_bucket_listv2_startafter_not_in_list():
+    key_names = ['bar', 'baz', 'foo', 'quxx']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, StartAfter='blah')
+    eq(response['StartAfter'], 'blah')
+    keys = _get_keys(response)
     eq(keys, ['foo', 'quxx'])
 
 @attr(resource='bucket')
@@ -780,6 +1467,22 @@ def test_bucket_list_marker_after_list():
     eq(response['IsTruncated'], False)
     eq(keys, [])
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list all keys with list-objects-v2')
+@attr(assertion='startafter after list')
+@attr('list-objects-v2')
+def test_bucket_listv2_startafter_after_list():
+    key_names = ['bar', 'baz', 'foo', 'quxx']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+
+    response = client.list_objects_v2(Bucket=bucket_name, StartAfter='zzz')
+    eq(response['StartAfter'], 'zzz')
+    keys = _get_keys(response)
+    eq(response['IsTruncated'], False)
+    eq(keys, [])
+
 def _compare_dates(datetime1, datetime2):
     """
     changes ms from datetime1 to 0, compares it to datetime2
@@ -899,6 +1602,19 @@ def test_bucket_list_objects_anonymous():
     unauthenticated_client = get_unauthenticated_client()
     unauthenticated_client.list_objects(Bucket=bucket_name)
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list all objects (anonymous) with list-objects-v2')
+@attr(assertion='succeeds')
+@attr('list-objects-v2')
+def test_bucket_listv2_objects_anonymous():
+    bucket_name = get_new_bucket() 
+    client = get_client()
+    client.put_bucket_acl(Bucket=bucket_name, ACL='public-read')
+
+    unauthenticated_client = get_unauthenticated_client()
+    unauthenticated_client.list_objects_v2(Bucket=bucket_name)
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='list all objects (anonymous)')
@@ -913,6 +1629,21 @@ def test_bucket_list_objects_anonymous_fail():
     eq(status, 403)
     eq(error_code, 'AccessDenied')
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='list all objects (anonymous) with list-objects-v2')
+@attr(assertion='fails')
+@attr('list-objects-v2')
+def test_bucket_listv2_objects_anonymous_fail():
+    bucket_name = get_new_bucket() 
+
+    unauthenticated_client = get_unauthenticated_client()
+    e = assert_raises(ClientError, unauthenticated_client.list_objects_v2, Bucket=bucket_name)
+
+    status, error_code = _get_status_and_error_code(e.response)
+    eq(status, 403)
+    eq(error_code, 'AccessDenied')
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='non-existant bucket')
@@ -927,6 +1658,21 @@ def test_bucket_notexist():
     eq(status, 404)
     eq(error_code, 'NoSuchBucket')
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='non-existant bucket with list-objects-v2')
+@attr(assertion='fails 404')
+@attr('list-objects-v2')
+def test_bucketv2_notexist():
+    bucket_name = get_new_bucket_name() 
+    client = get_client()
+
+    e = assert_raises(ClientError, client.list_objects_v2, Bucket=bucket_name)
+
+    status, error_code = _get_status_and_error_code(e.response)
+    eq(status, 404)
+    eq(error_code, 'NoSuchBucket')
+
 @attr(resource='bucket')
 @attr(method='delete')
 @attr(operation='non-existant bucket')
@@ -1071,13 +1817,39 @@ def _make_objs_dict(key_names):
 
 @attr(resource='object')
 @attr(method='post')
-@attr(operation='delete multiple objects')
+@attr(operation='delete multiple objects')
+@attr(assertion='deletes multiple objects with a single call')
+def test_multi_object_delete():
+    key_names = ['key0', 'key1', 'key2']
+    bucket_name = _create_objects(keys=key_names)
+    client = get_client()
+    response = client.list_objects(Bucket=bucket_name)
+    eq(len(response['Contents']), 3)
+    
+    objs_dict = _make_objs_dict(key_names=key_names)
+    response = client.delete_objects(Bucket=bucket_name, Delete=objs_dict) 
+
+    eq(len(response['Deleted']), 3)
+    assert 'Errors' not in response
+    response = client.list_objects(Bucket=bucket_name)
+    assert 'Contents' not in response
+
+    response = client.delete_objects(Bucket=bucket_name, Delete=objs_dict) 
+    eq(len(response['Deleted']), 3)
+    assert 'Errors' not in response
+    response = client.list_objects(Bucket=bucket_name)
+    assert 'Contents' not in response
+
+@attr(resource='object')
+@attr(method='post')
+@attr(operation='delete multiple objects with list-objects-v2')
 @attr(assertion='deletes multiple objects with a single call')
-def test_multi_object_delete():
+@attr('list-objects-v2')
+def test_multi_objectv2_delete():
     key_names = ['key0', 'key1', 'key2']
     bucket_name = _create_objects(keys=key_names)
     client = get_client()
-    response = client.list_objects(Bucket=bucket_name)
+    response = client.list_objects_v2(Bucket=bucket_name)
     eq(len(response['Contents']), 3)
     
     objs_dict = _make_objs_dict(key_names=key_names)
@@ -1085,13 +1857,13 @@ def test_multi_object_delete():
 
     eq(len(response['Deleted']), 3)
     assert 'Errors' not in response
-    response = client.list_objects(Bucket=bucket_name)
+    response = client.list_objects_v2(Bucket=bucket_name)
     assert 'Contents' not in response
 
     response = client.delete_objects(Bucket=bucket_name, Delete=objs_dict) 
     eq(len(response['Deleted']), 3)
     assert 'Errors' not in response
-    response = client.list_objects(Bucket=bucket_name)
+    response = client.list_objects_v2(Bucket=bucket_name)
     assert 'Contents' not in response
 
 @attr(resource='object')
@@ -4785,6 +5557,38 @@ def test_access_bucket_private_object_private():
     alt_client3 = get_alt_client()
     check_access_denied(alt_client3.put_object, Bucket=bucket_name, Key=newkey, Body='newcontent')
 
+@attr(resource='object')
+@attr(method='ACLs')
+@attr(operation='set bucket/object acls: private/private with list-objects-v2')
+@attr(assertion='public has no access to bucket or objects')
+@attr('list-objects-v2')
+def test_access_bucket_private_objectv2_private():
+    # all the test_access_* tests follow this template
+    bucket_name, key1, key2, newkey = _setup_access(bucket_acl='private', object_acl='private')
+
+    alt_client = get_alt_client()
+    # acled object read fail
+    check_access_denied(alt_client.get_object, Bucket=bucket_name, Key=key1)
+    # default object read fail
+    check_access_denied(alt_client.get_object, Bucket=bucket_name, Key=key2)
+    # bucket read fail
+    check_access_denied(alt_client.list_objects_v2, Bucket=bucket_name)
+
+    # acled object write fail
+    check_access_denied(alt_client.put_object, Bucket=bucket_name, Key=key1, Body='barcontent')
+    # NOTE: The above put's causes the connection to go bad, therefore the client can't be used 
+    # anymore. This can be solved either by:
+    # 1) putting an empty string ('') in the 'Body' field of those put_object calls
+    # 2) getting a new client hence the creation of alt_client{2,3} for the tests below
+    # TODO: Test it from another host and on AWS, Report this to Amazon, if findings are identical
+
+    alt_client2 = get_alt_client()
+    # default object write fail
+    check_access_denied(alt_client2.put_object, Bucket=bucket_name, Key=key2, Body='baroverwrite')
+    # bucket write fail
+    alt_client3 = get_alt_client()
+    check_access_denied(alt_client3.put_object, Bucket=bucket_name, Key=newkey, Body='newcontent')
+
 @attr(resource='object')
 @attr(method='ACLs')
 @attr(operation='set bucket/object acls: private/public-read')
@@ -4809,6 +5613,31 @@ def test_access_bucket_private_object_publicread():
     check_access_denied(alt_client3.list_objects, Bucket=bucket_name)
     check_access_denied(alt_client3.put_object, Bucket=bucket_name, Key=newkey, Body='newcontent')
 
+@attr(resource='object')
+@attr(method='ACLs')
+@attr(operation='set bucket/object acls: private/public-read with list-objects-v2')
+@attr(assertion='public can only read readable object')
+@attr('list-objects-v2')
+def test_access_bucket_private_objectv2_publicread():
+
+    bucket_name, key1, key2, newkey = _setup_access(bucket_acl='private', object_acl='public-read')
+    alt_client = get_alt_client()
+    response = alt_client.get_object(Bucket=bucket_name, Key=key1)
+
+    body = _get_body(response)
+
+    # a should be public-read, b gets default (private)
+    eq(body, 'foocontent')
+
+    check_access_denied(alt_client.put_object, Bucket=bucket_name, Key=key1, Body='foooverwrite')
+    alt_client2 = get_alt_client()
+    check_access_denied(alt_client2.get_object, Bucket=bucket_name, Key=key2)
+    check_access_denied(alt_client2.put_object, Bucket=bucket_name, Key=key2, Body='baroverwrite')
+
+    alt_client3 = get_alt_client()
+    check_access_denied(alt_client3.list_objects_v2, Bucket=bucket_name)
+    check_access_denied(alt_client3.put_object, Bucket=bucket_name, Key=newkey, Body='newcontent')
+
 @attr(resource='object')
 @attr(method='ACLs')
 @attr(operation='set bucket/object acls: private/public-read/write')
@@ -4833,6 +5662,31 @@ def test_access_bucket_private_object_publicreadwrite():
     check_access_denied(alt_client3.list_objects, Bucket=bucket_name)
     check_access_denied(alt_client3.put_object, Bucket=bucket_name, Key=newkey, Body='newcontent')
 
+@attr(resource='object')
+@attr(method='ACLs')
+@attr(operation='set bucket/object acls: private/public-read/write with list-objects-v2')
+@attr(assertion='public can only read the readable object')
+@attr('list-objects-v2')
+def test_access_bucket_private_objectv2_publicreadwrite():
+    bucket_name, key1, key2, newkey = _setup_access(bucket_acl='private', object_acl='public-read-write')
+    alt_client = get_alt_client()
+    response = alt_client.get_object(Bucket=bucket_name, Key=key1)
+
+    body = _get_body(response)
+
+    # a should be public-read-only ... because it is in a private bucket
+    # b gets default (private)
+    eq(body, 'foocontent')
+
+    check_access_denied(alt_client.put_object, Bucket=bucket_name, Key=key1, Body='foooverwrite')
+    alt_client2 = get_alt_client()
+    check_access_denied(alt_client2.get_object, Bucket=bucket_name, Key=key2)
+    check_access_denied(alt_client2.put_object, Bucket=bucket_name, Key=key2, Body='baroverwrite')
+
+    alt_client3 = get_alt_client()
+    check_access_denied(alt_client3.list_objects_v2, Bucket=bucket_name)
+    check_access_denied(alt_client3.put_object, Bucket=bucket_name, Key=newkey, Body='newcontent')
+
 @attr(resource='object')
 @attr(method='ACLs')
 @attr(operation='set bucket/object acls: public-read/private')
@@ -7920,6 +8774,41 @@ def test_lifecycle_expiration():
     response = client.list_objects(Bucket=bucket_name)
     expire3_objects = response['Contents']
 
+    eq(len(init_objects), 6)
+    eq(len(expire1_objects), 6)
+    eq(len(keep2_objects), 6)
+    eq(len(expire3_objects), 6)
+
+@attr(resource='bucket')
+@attr(method='put')
+@attr(operation='test lifecycle expiration with list-objects-v2')
+@attr('lifecycle')
+@attr('lifecycle_expiration')
+@attr('fails_on_aws')
+@attr('list-objects-v2')
+def test_lifecyclev2_expiration():
+    bucket_name = _create_objects(keys=['expire1/foo', 'expire1/bar', 'keep2/foo',
+                                        'keep2/bar', 'expire3/foo', 'expire3/bar'])
+    client = get_client()
+    rules=[{'ID': 'rule1', 'Expiration': {'Days': 1}, 'Prefix': 'expire1/', 'Status':'Enabled'},
+           {'ID': 'rule2', 'Expiration': {'Days': 4}, 'Prefix': 'expire3/', 'Status':'Enabled'}]
+    lifecycle = {'Rules': rules}
+    client.put_bucket_lifecycle_configuration(Bucket=bucket_name, LifecycleConfiguration=lifecycle)
+    response = client.list_objects_v2(Bucket=bucket_name)
+    init_objects = response['Contents']
+
+    time.sleep(28)
+    response = client.list_objects_v2(Bucket=bucket_name)
+    expire1_objects = response['Contents']
+
+    time.sleep(10)
+    response = client.list_objects_v2(Bucket=bucket_name)
+    keep2_objects = response['Contents']
+
+    time.sleep(20)
+    response = client.list_objects_v2(Bucket=bucket_name)
+    expire3_objects = response['Contents']
+
     eq(len(init_objects), 6)
     eq(len(expire1_objects), 4)
     eq(len(keep2_objects), 4)
@@ -9195,6 +10084,36 @@ def test_bucket_policy():
     response = alt_client.list_objects(Bucket=bucket_name)
     eq(len(response['Contents']), 1)
 
+@attr('bucket-policy')
+@attr('list-objects-v2')
+def test_bucketv2_policy():
+    bucket_name = get_new_bucket()
+    client = get_client()
+    key = 'asdf'
+    client.put_object(Bucket=bucket_name, Key=key, Body='asdf')
+
+    resource1 = "arn:aws:s3:::" + bucket_name
+    resource2 = "arn:aws:s3:::" + bucket_name + "/*"
+    policy_document = json.dumps(
+    {
+        "Version": "2012-10-17",
+        "Statement": [{
+        "Effect": "Allow",
+        "Principal": {"AWS": "*"},
+        "Action": "s3:ListBucket",
+        "Resource": [
+            "{}".format(resource1),
+            "{}".format(resource2)
+          ]
+        }]
+     })
+
+    client.put_bucket_policy(Bucket=bucket_name, Policy=policy_document)
+
+    alt_client = get_alt_client()
+    response = alt_client.list_objects_v2(Bucket=bucket_name)
+    eq(len(response['Contents']), 1)
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='Test Bucket Policy and ACL')
@@ -9234,6 +10153,46 @@ def test_bucket_policy_acl():
     client.delete_bucket_policy(Bucket=bucket_name)
     client.put_bucket_acl(Bucket=bucket_name, ACL='public-read')
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='Test Bucket Policy and ACL with list-objects-v2')
+@attr(assertion='fails')
+@attr('bucket-policy')
+@attr('list-objects-v2')
+def test_bucketv2_policy_acl():
+    bucket_name = get_new_bucket()
+    client = get_client()
+    key = 'asdf'
+    client.put_object(Bucket=bucket_name, Key=key, Body='asdf')
+
+    resource1 = "arn:aws:s3:::" + bucket_name
+    resource2 = "arn:aws:s3:::" + bucket_name + "/*"
+    policy_document =  json.dumps(
+    {
+        "Version": "2012-10-17",
+        "Statement": [{
+        "Effect": "Deny",
+        "Principal": {"AWS": "*"},
+        "Action": "s3:ListBucket",
+        "Resource": [
+            "{}".format(resource1),
+            "{}".format(resource2)
+          ]
+        }]
+     })
+
+    client.put_bucket_acl(Bucket=bucket_name, ACL='authenticated-read')
+    client.put_bucket_policy(Bucket=bucket_name, Policy=policy_document)
+
+    alt_client = get_alt_client()
+    e = assert_raises(ClientError, alt_client.list_objects_v2, Bucket=bucket_name)
+    status, error_code = _get_status_and_error_code(e.response)
+    eq(status, 403)
+    eq(error_code, 'AccessDenied')
+
+    client.delete_bucket_policy(Bucket=bucket_name)
+    client.put_bucket_acl(Bucket=bucket_name, ACL='public-read')
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='Test Bucket Policy for a user belonging to a different tenant')
@@ -9282,6 +10241,55 @@ def test_bucket_policy_different_tenant():
 
     eq(len(response['Contents']), 1)
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='Test Bucket Policy for a user belonging to a different tenant')
+@attr(assertion='succeeds')
+@attr('bucket-policy')
+# TODO: remove this fails_on_rgw when I fix it
+@attr('fails_on_rgw')
+@attr('list-objects-v2')
+def test_bucketv2_policy_different_tenant():
+    bucket_name = get_new_bucket()
+    client = get_client()
+    key = 'asdf'
+    client.put_object(Bucket=bucket_name, Key=key, Body='asdf')
+
+    resource1 = "arn:aws:s3::*:" + bucket_name
+    resource2 = "arn:aws:s3::*:" + bucket_name + "/*"
+    policy_document = json.dumps(
+    {
+        "Version": "2012-10-17",
+        "Statement": [{
+        "Effect": "Allow",
+        "Principal": {"AWS": "*"},
+        "Action": "s3:ListBucket",
+        "Resource": [
+            "{}".format(resource1),
+            "{}".format(resource2)
+          ]
+        }]
+     })
+
+    client.put_bucket_policy(Bucket=bucket_name, Policy=policy_document)
+
+    # TODO: figure out how to change the bucketname
+    def change_bucket_name(**kwargs):
+        kwargs['params']['url'] = "http://localhost:8000/:{bucket_name}?encoding-type=url".format(bucket_name=bucket_name)
+        kwargs['params']['url_path'] = "/:{bucket_name}".format(bucket_name=bucket_name)
+        kwargs['params']['context']['signing']['bucket'] = ":{bucket_name}".format(bucket_name=bucket_name)
+        print kwargs['request_signer']
+        print kwargs
+
+    #bucket_name = ":" + bucket_name
+    tenant_client = get_tenant_client()
+    tenant_client.meta.events.register('before-call.s3.ListObjects', change_bucket_name)
+    response = tenant_client.list_objects_v2(Bucket=bucket_name)
+    #alt_client = get_alt_client()
+    #response = alt_client.list_objects_v2(Bucket=bucket_name)
+
+    eq(len(response['Contents']), 1)
+
 @attr(resource='bucket')
 @attr(method='get')
 @attr(operation='Test Bucket Policy on another bucket')
@@ -9323,6 +10331,48 @@ def test_bucket_policy_another_bucket():
     response = alt_client.list_objects(Bucket=bucket_name2)
     eq(len(response['Contents']), 1)
 
+@attr(resource='bucket')
+@attr(method='get')
+@attr(operation='Test Bucket Policy on another bucket with list-objects-v2')
+@attr(assertion='succeeds')
+@attr('bucket-policy')
+@attr('list-objects-v2')
+def test_bucketv2_policy_another_bucket():
+    bucket_name = get_new_bucket()
+    bucket_name2 = get_new_bucket()
+    client = get_client()
+    key = 'asdf'
+    key2 = 'abcd'
+    client.put_object(Bucket=bucket_name, Key=key, Body='asdf')
+    client.put_object(Bucket=bucket_name2, Key=key2, Body='abcd')
+    policy_document = json.dumps(
+    {
+        "Version": "2012-10-17",
+        "Statement": [{
+        "Effect": "Allow",
+        "Principal": {"AWS": "*"},
+        "Action": "s3:ListBucket",
+        "Resource": [
+            "arn:aws:s3:::*",
+            "arn:aws:s3:::*/*"
+          ]
+        }]
+     })
+
+    client.put_bucket_policy(Bucket=bucket_name, Policy=policy_document)
+    response = client.get_bucket_policy(Bucket=bucket_name)
+    response_policy = response['Policy']
+
+    client.put_bucket_policy(Bucket=bucket_name2, Policy=response_policy)
+
+    alt_client = get_alt_client()
+    response = alt_client.list_objects_v2(Bucket=bucket_name)
+    eq(len(response['Contents']), 1)
+
+    alt_client = get_alt_client()
+    response = alt_client.list_objects_v2(Bucket=bucket_name2)
+    eq(len(response['Contents']), 1)
+
 @attr(resource='bucket')
 @attr(method='put')
 @attr(operation='Test put condition operator end with ifExists')