diff --git a/api-ref/source/samples/account-containers-list-response.json b/api-ref/source/samples/account-containers-list-response.json
index 4ae34aa4ca..d9864aa246 100644
--- a/api-ref/source/samples/account-containers-list-response.json
+++ b/api-ref/source/samples/account-containers-list-response.json
@@ -2,11 +2,13 @@
{
"count": 0,
"bytes": 0,
- "name": "janeausten"
+ "name": "janeausten",
+ "last_modified": "2013-11-19T20:08:13.283452"
},
{
"count": 1,
"bytes": 14,
- "name": "marktwain"
+ "name": "marktwain",
+ "last_modified": "2016-04-29T16:23:50.460230"
}
]
diff --git a/api-ref/source/samples/account-containers-list-response.xml b/api-ref/source/samples/account-containers-list-response.xml
index d8f51cfa0d..6e194aebce 100644
--- a/api-ref/source/samples/account-containers-list-response.xml
+++ b/api-ref/source/samples/account-containers-list-response.xml
@@ -4,10 +4,12 @@
janeausten
0
0
+ 2013-11-19T20:08:13.283452
marktwain
1
14
+ 2016-04-29T16:23:50.460230
diff --git a/swift/account/backend.py b/swift/account/backend.py
index 31f6470b99..1fd7e5272b 100644
--- a/swift/account/backend.py
+++ b/swift/account/backend.py
@@ -379,7 +379,8 @@ class AccountBroker(DatabaseBroker):
:param delimiter: delimiter for query
:param reverse: reverse the result order.
- :returns: list of tuples of (name, object_count, bytes_used, 0)
+ :returns: list of tuples of (name, object_count, bytes_used,
+ put_timestamp, 0)
"""
delim_force_gte = False
(marker, end_marker, prefix, delimiter) = utf8encode(
@@ -397,7 +398,7 @@ class AccountBroker(DatabaseBroker):
results = []
while len(results) < limit:
query = """
- SELECT name, object_count, bytes_used, 0
+ SELECT name, object_count, bytes_used, put_timestamp, 0
FROM container
WHERE """
query_args = []
@@ -459,7 +460,7 @@ class AccountBroker(DatabaseBroker):
delim_force_gte = True
dir_name = name[:end + 1]
if dir_name != orig_marker:
- results.append([dir_name, 0, 0, 1])
+ results.append([dir_name, 0, 0, '0', 1])
curs.close()
break
results.append(row)
diff --git a/swift/account/reaper.py b/swift/account/reaper.py
index 676fac55c6..3fd111ff2a 100644
--- a/swift/account/reaper.py
+++ b/swift/account/reaper.py
@@ -268,7 +268,7 @@ class AccountReaper(Daemon):
if not containers:
break
try:
- for (container, _junk, _junk, _junk) in containers:
+ for (container, _junk, _junk, _junk, _junk) in containers:
this_shard = int(md5(container).hexdigest(), 16) % \
len(nodes)
if container_shard not in (this_shard, None):
diff --git a/swift/account/utils.py b/swift/account/utils.py
index 49092b7f97..4446eb7d32 100644
--- a/swift/account/utils.py
+++ b/swift/account/utils.py
@@ -81,24 +81,30 @@ def account_listing_response(account, req, response_content_type, broker=None,
prefix, delimiter, reverse)
if response_content_type == 'application/json':
data = []
- for (name, object_count, bytes_used, is_subdir) in account_list:
+ for (name, object_count, bytes_used, put_timestamp, is_subdir) \
+ in account_list:
if is_subdir:
data.append({'subdir': name})
else:
- data.append({'name': name, 'count': object_count,
- 'bytes': bytes_used})
+ data.append(
+ {'name': name, 'count': object_count,
+ 'bytes': bytes_used,
+ 'last_modified': Timestamp(put_timestamp).isoformat})
account_list = json.dumps(data)
elif response_content_type.endswith('/xml'):
output_list = ['',
'' % saxutils.quoteattr(account)]
- for (name, object_count, bytes_used, is_subdir) in account_list:
+ for (name, object_count, bytes_used, put_timestamp, is_subdir) \
+ in account_list:
if is_subdir:
output_list.append(
'' % saxutils.quoteattr(name))
else:
item = '%s%s' \
- '%s' % \
- (saxutils.escape(name), object_count, bytes_used)
+ '%s%s' \
+ '' % \
+ (saxutils.escape(name), object_count,
+ bytes_used, Timestamp(put_timestamp).isoformat)
output_list.append(item)
output_list.append('')
account_list = '\n'.join(output_list)
diff --git a/test/functional/swift_test_client.py b/test/functional/swift_test_client.py
index a1c105b180..3b68d3a441 100644
--- a/test/functional/swift_test_client.py
+++ b/test/functional/swift_test_client.py
@@ -439,7 +439,7 @@ class Account(Base):
tree = minidom.parseString(self.conn.response.read())
for x in tree.getElementsByTagName('container'):
cont = {}
- for key in ['name', 'count', 'bytes']:
+ for key in ['name', 'count', 'bytes', 'last_modified']:
cont[key] = x.getElementsByTagName(key)[0].\
childNodes[0].nodeValue
conts.append(cont)
diff --git a/test/functional/tests.py b/test/functional/tests.py
index a27fec9818..15d09378d4 100644
--- a/test/functional/tests.py
+++ b/test/functional/tests.py
@@ -28,6 +28,7 @@ from copy import deepcopy
import eventlet
from unittest2 import SkipTest
from swift.common.http import is_success, is_client_error
+from email.utils import parsedate
from test.functional import normalized_urls, load_constraint, cluster_info
from test.functional import check_response, retry
@@ -299,6 +300,33 @@ class TestAccount(Base):
results = [r for r in results if r in expected]
self.assertEqual(expected, results)
+ def testContainerListingLastModified(self):
+ expected = {}
+ for container in self.env.containers:
+ res = container.info()
+ expected[container.name] = time.mktime(
+ parsedate(res['last_modified']))
+
+ for format_type in ['json', 'xml']:
+ actual = {}
+ containers = self.env.account.containers(
+ parms={'format': format_type})
+ if isinstance(containers[0], dict):
+ for container in containers:
+ self.assertIn('name', container) # sanity
+ self.assertIn('last_modified', container) # sanity
+ # ceil by hand (wants easier way!)
+ datetime_str, micro_sec_str = \
+ container['last_modified'].split('.')
+ timestamp = time.mktime(
+ time.strptime(datetime_str,
+ "%Y-%m-%dT%H:%M:%S"))
+ if int(micro_sec_str):
+ timestamp += 1
+ actual[container['name']] = timestamp
+
+ self.assertEqual(expected, actual)
+
def testInvalidAuthToken(self):
hdrs = {'X-Auth-Token': 'bogus_auth_token'}
self.assertRaises(ResponseError, self.env.account.info, hdrs=hdrs)
diff --git a/test/unit/account/test_backend.py b/test/unit/account/test_backend.py
index e9a5c51752..6810d77546 100644
--- a/test/unit/account/test_backend.py
+++ b/test/unit/account/test_backend.py
@@ -1225,18 +1225,19 @@ class TestAccountBrokerBeforeSPI(TestAccountBroker):
'from container table!')
# manually insert an existing row to avoid migration
+ timestamp = Timestamp(time()).internal
with broker.get() as conn:
conn.execute('''
INSERT INTO container (name, put_timestamp,
delete_timestamp, object_count, bytes_used,
deleted)
VALUES (?, ?, ?, ?, ?, ?)
- ''', ('test_name', Timestamp(time()).internal, 0, 1, 2, 0))
+ ''', ('test_name', timestamp, 0, 1, 2, 0))
conn.commit()
# make sure we can iter containers without the migration
for c in broker.list_containers_iter(1, None, None, None, None):
- self.assertEqual(c, ('test_name', 1, 2, 0))
+ self.assertEqual(c, ('test_name', 1, 2, timestamp, 0))
# stats table is mysteriously empty...
stats = broker.get_policy_stats()
@@ -1363,6 +1364,7 @@ class TestAccountBrokerBeforeSPI(TestAccountBroker):
new_broker = AccountBroker(os.path.join(tempdir, 'new_account.db'),
account='a')
new_broker.initialize(next(ts).internal)
+ timestamp = next(ts).internal
# manually insert an existing row to avoid migration for old database
with old_broker.get() as conn:
@@ -1371,7 +1373,7 @@ class TestAccountBrokerBeforeSPI(TestAccountBroker):
delete_timestamp, object_count, bytes_used,
deleted)
VALUES (?, ?, ?, ?, ?, ?)
- ''', ('test_name', next(ts).internal, 0, 1, 2, 0))
+ ''', ('test_name', timestamp, 0, 1, 2, 0))
conn.commit()
# get replication info and rows form old database
@@ -1384,7 +1386,7 @@ class TestAccountBrokerBeforeSPI(TestAccountBroker):
# make sure "test_name" container in new database
self.assertEqual(new_broker.get_info()['container_count'], 1)
for c in new_broker.list_containers_iter(1, None, None, None, None):
- self.assertEqual(c, ('test_name', 1, 2, 0))
+ self.assertEqual(c, ('test_name', 1, 2, timestamp, 0))
# full migration successful
with new_broker.get() as conn:
diff --git a/test/unit/account/test_reaper.py b/test/unit/account/test_reaper.py
index 4961f19acb..ddf38b3003 100644
--- a/test/unit/account/test_reaper.py
+++ b/test/unit/account/test_reaper.py
@@ -87,7 +87,7 @@ class FakeAccountBroker(object):
def list_containers_iter(self, *args):
for cont in self.containers:
- yield cont, None, None, None
+ yield cont, None, None, None, None
def is_status_deleted(self):
return True
@@ -749,7 +749,7 @@ class TestReaper(unittest.TestCase):
if container in self.containers_yielded:
continue
- yield container, None, None, None
+ yield container, None, None, None, None
self.containers_yielded.append(container)
def fake_reap_container(self, account, account_partition,
diff --git a/test/unit/account/test_server.py b/test/unit/account/test_server.py
index 3882c08aa2..3f61f2470f 100644
--- a/test/unit/account/test_server.py
+++ b/test/unit/account/test_server.py
@@ -34,7 +34,7 @@ from swift.common.swob import (Request, WsgiBytesIO, HTTPNoContent)
from swift.common import constraints
from swift.account.server import AccountController
from swift.common.utils import (normalize_timestamp, replication, public,
- mkdirs, storage_directory)
+ mkdirs, storage_directory, Timestamp)
from swift.common.request_helpers import get_sys_meta_prefix
from test.unit import patch_policies, debug_logger
from swift.common.storage_policy import StoragePolicy, POLICIES
@@ -847,18 +847,21 @@ class TestAccountController(unittest.TestCase):
self.assertEqual(resp.charset, 'utf-8')
def test_GET_with_containers_json(self):
+ put_timestamps = {}
req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'PUT',
'HTTP_X_TIMESTAMP': '0'})
req.get_response(self.controller)
+ put_timestamps['c1'] = normalize_timestamp(1)
req = Request.blank('/sda1/p/a/c1', environ={'REQUEST_METHOD': 'PUT'},
- headers={'X-Put-Timestamp': '1',
+ headers={'X-Put-Timestamp': put_timestamps['c1'],
'X-Delete-Timestamp': '0',
'X-Object-Count': '0',
'X-Bytes-Used': '0',
'X-Timestamp': normalize_timestamp(0)})
req.get_response(self.controller)
+ put_timestamps['c2'] = normalize_timestamp(2)
req = Request.blank('/sda1/p/a/c2', environ={'REQUEST_METHOD': 'PUT'},
- headers={'X-Put-Timestamp': '2',
+ headers={'X-Put-Timestamp': put_timestamps['c2'],
'X-Delete-Timestamp': '0',
'X-Object-Count': '0',
'X-Bytes-Used': '0',
@@ -868,18 +871,23 @@ class TestAccountController(unittest.TestCase):
environ={'REQUEST_METHOD': 'GET'})
resp = req.get_response(self.controller)
self.assertEqual(resp.status_int, 200)
- self.assertEqual(json.loads(resp.body),
- [{'count': 0, 'bytes': 0, 'name': 'c1'},
- {'count': 0, 'bytes': 0, 'name': 'c2'}])
+ self.assertEqual(
+ json.loads(resp.body),
+ [{'count': 0, 'bytes': 0, 'name': 'c1',
+ 'last_modified': Timestamp(put_timestamps['c1']).isoformat},
+ {'count': 0, 'bytes': 0, 'name': 'c2',
+ 'last_modified': Timestamp(put_timestamps['c2']).isoformat}])
+ put_timestamps['c1'] = normalize_timestamp(3)
req = Request.blank('/sda1/p/a/c1', environ={'REQUEST_METHOD': 'PUT'},
- headers={'X-Put-Timestamp': '1',
+ headers={'X-Put-Timestamp': put_timestamps['c1'],
'X-Delete-Timestamp': '0',
'X-Object-Count': '1',
'X-Bytes-Used': '2',
'X-Timestamp': normalize_timestamp(0)})
req.get_response(self.controller)
+ put_timestamps['c2'] = normalize_timestamp(4)
req = Request.blank('/sda1/p/a/c2', environ={'REQUEST_METHOD': 'PUT'},
- headers={'X-Put-Timestamp': '2',
+ headers={'X-Put-Timestamp': put_timestamps['c2'],
'X-Delete-Timestamp': '0',
'X-Object-Count': '3',
'X-Bytes-Used': '4',
@@ -889,25 +897,31 @@ class TestAccountController(unittest.TestCase):
environ={'REQUEST_METHOD': 'GET'})
resp = req.get_response(self.controller)
self.assertEqual(resp.status_int, 200)
- self.assertEqual(json.loads(resp.body),
- [{'count': 1, 'bytes': 2, 'name': 'c1'},
- {'count': 3, 'bytes': 4, 'name': 'c2'}])
+ self.assertEqual(
+ json.loads(resp.body),
+ [{'count': 1, 'bytes': 2, 'name': 'c1',
+ 'last_modified': Timestamp(put_timestamps['c1']).isoformat},
+ {'count': 3, 'bytes': 4, 'name': 'c2',
+ 'last_modified': Timestamp(put_timestamps['c2']).isoformat}])
self.assertEqual(resp.content_type, 'application/json')
self.assertEqual(resp.charset, 'utf-8')
def test_GET_with_containers_xml(self):
+ put_timestamps = {}
req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'PUT',
'HTTP_X_TIMESTAMP': '0'})
req.get_response(self.controller)
+ put_timestamps['c1'] = normalize_timestamp(1)
req = Request.blank('/sda1/p/a/c1', environ={'REQUEST_METHOD': 'PUT'},
- headers={'X-Put-Timestamp': '1',
+ headers={'X-Put-Timestamp': put_timestamps['c1'],
'X-Delete-Timestamp': '0',
'X-Object-Count': '0',
'X-Bytes-Used': '0',
'X-Timestamp': normalize_timestamp(0)})
req.get_response(self.controller)
+ put_timestamps['c2'] = normalize_timestamp(2)
req = Request.blank('/sda1/p/a/c2', environ={'REQUEST_METHOD': 'PUT'},
- headers={'X-Put-Timestamp': '2',
+ headers={'X-Put-Timestamp': put_timestamps['c2'],
'X-Delete-Timestamp': '0',
'X-Object-Count': '0',
'X-Bytes-Used': '0',
@@ -926,24 +940,30 @@ class TestAccountController(unittest.TestCase):
self.assertEqual(listing[0].nodeName, 'container')
container = [n for n in listing[0].childNodes if n.nodeName != '#text']
self.assertEqual(sorted([n.nodeName for n in container]),
- ['bytes', 'count', 'name'])
+ ['bytes', 'count', 'last_modified', 'name'])
node = [n for n in container if n.nodeName == 'name'][0]
self.assertEqual(node.firstChild.nodeValue, 'c1')
node = [n for n in container if n.nodeName == 'count'][0]
self.assertEqual(node.firstChild.nodeValue, '0')
node = [n for n in container if n.nodeName == 'bytes'][0]
self.assertEqual(node.firstChild.nodeValue, '0')
+ node = [n for n in container if n.nodeName == 'last_modified'][0]
+ self.assertEqual(node.firstChild.nodeValue,
+ Timestamp(put_timestamps['c1']).isoformat)
self.assertEqual(listing[-1].nodeName, 'container')
container = \
[n for n in listing[-1].childNodes if n.nodeName != '#text']
self.assertEqual(sorted([n.nodeName for n in container]),
- ['bytes', 'count', 'name'])
+ ['bytes', 'count', 'last_modified', 'name'])
node = [n for n in container if n.nodeName == 'name'][0]
self.assertEqual(node.firstChild.nodeValue, 'c2')
node = [n for n in container if n.nodeName == 'count'][0]
self.assertEqual(node.firstChild.nodeValue, '0')
node = [n for n in container if n.nodeName == 'bytes'][0]
self.assertEqual(node.firstChild.nodeValue, '0')
+ node = [n for n in container if n.nodeName == 'last_modified'][0]
+ self.assertEqual(node.firstChild.nodeValue,
+ Timestamp(put_timestamps['c2']).isoformat)
req = Request.blank('/sda1/p/a/c1', environ={'REQUEST_METHOD': 'PUT'},
headers={'X-Put-Timestamp': '1',
'X-Delete-Timestamp': '0',
@@ -970,24 +990,30 @@ class TestAccountController(unittest.TestCase):
self.assertEqual(listing[0].nodeName, 'container')
container = [n for n in listing[0].childNodes if n.nodeName != '#text']
self.assertEqual(sorted([n.nodeName for n in container]),
- ['bytes', 'count', 'name'])
+ ['bytes', 'count', 'last_modified', 'name'])
node = [n for n in container if n.nodeName == 'name'][0]
self.assertEqual(node.firstChild.nodeValue, 'c1')
node = [n for n in container if n.nodeName == 'count'][0]
self.assertEqual(node.firstChild.nodeValue, '1')
node = [n for n in container if n.nodeName == 'bytes'][0]
self.assertEqual(node.firstChild.nodeValue, '2')
+ node = [n for n in container if n.nodeName == 'last_modified'][0]
+ self.assertEqual(node.firstChild.nodeValue,
+ Timestamp(put_timestamps['c1']).isoformat)
self.assertEqual(listing[-1].nodeName, 'container')
container = [
n for n in listing[-1].childNodes if n.nodeName != '#text']
self.assertEqual(sorted([n.nodeName for n in container]),
- ['bytes', 'count', 'name'])
+ ['bytes', 'count', 'last_modified', 'name'])
node = [n for n in container if n.nodeName == 'name'][0]
self.assertEqual(node.firstChild.nodeValue, 'c2')
node = [n for n in container if n.nodeName == 'count'][0]
self.assertEqual(node.firstChild.nodeValue, '3')
node = [n for n in container if n.nodeName == 'bytes'][0]
self.assertEqual(node.firstChild.nodeValue, '4')
+ node = [n for n in container if n.nodeName == 'last_modified'][0]
+ self.assertEqual(node.firstChild.nodeValue,
+ Timestamp(put_timestamps['c2']).isoformat)
self.assertEqual(resp.charset, 'utf-8')
def test_GET_xml_escapes_account_name(self):
@@ -1054,15 +1080,16 @@ class TestAccountController(unittest.TestCase):
req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'PUT',
'HTTP_X_TIMESTAMP': '0'})
req.get_response(self.controller)
+ put_timestamp = normalize_timestamp(0)
for c in range(5):
req = Request.blank(
'/sda1/p/a/c%d' % c,
environ={'REQUEST_METHOD': 'PUT'},
- headers={'X-Put-Timestamp': str(c + 1),
+ headers={'X-Put-Timestamp': put_timestamp,
'X-Delete-Timestamp': '0',
'X-Object-Count': '2',
'X-Bytes-Used': '3',
- 'X-Timestamp': normalize_timestamp(0)})
+ 'X-Timestamp': put_timestamp})
req.get_response(self.controller)
req = Request.blank('/sda1/p/a?limit=3',
environ={'REQUEST_METHOD': 'GET'})
@@ -1079,31 +1106,38 @@ class TestAccountController(unittest.TestCase):
req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'PUT',
'HTTP_X_TIMESTAMP': '0'})
req.get_response(self.controller)
+ put_timestamp = normalize_timestamp(0)
for c in range(5):
req = Request.blank(
'/sda1/p/a/c%d' % c,
environ={'REQUEST_METHOD': 'PUT'},
- headers={'X-Put-Timestamp': str(c + 1),
+ headers={'X-Put-Timestamp': put_timestamp,
'X-Delete-Timestamp': '0',
'X-Object-Count': '2',
'X-Bytes-Used': '3',
- 'X-Timestamp': normalize_timestamp(0)})
+ 'X-Timestamp': put_timestamp})
req.get_response(self.controller)
req = Request.blank('/sda1/p/a?limit=3&format=json',
environ={'REQUEST_METHOD': 'GET'})
resp = req.get_response(self.controller)
self.assertEqual(resp.status_int, 200)
- self.assertEqual(json.loads(resp.body),
- [{'count': 2, 'bytes': 3, 'name': 'c0'},
- {'count': 2, 'bytes': 3, 'name': 'c1'},
- {'count': 2, 'bytes': 3, 'name': 'c2'}])
+ timestamp_str = Timestamp(put_timestamp).isoformat
+ expected = [{'count': 2, 'bytes': 3, 'name': 'c0',
+ 'last_modified': timestamp_str},
+ {'count': 2, 'bytes': 3, 'name': 'c1',
+ 'last_modified': timestamp_str},
+ {'count': 2, 'bytes': 3, 'name': 'c2',
+ 'last_modified': timestamp_str}]
+ self.assertEqual(json.loads(resp.body), expected)
req = Request.blank('/sda1/p/a?limit=3&marker=c2&format=json',
environ={'REQUEST_METHOD': 'GET'})
resp = req.get_response(self.controller)
self.assertEqual(resp.status_int, 200)
- self.assertEqual(json.loads(resp.body),
- [{'count': 2, 'bytes': 3, 'name': 'c3'},
- {'count': 2, 'bytes': 3, 'name': 'c4'}])
+ expected = [{'count': 2, 'bytes': 3, 'name': 'c3',
+ 'last_modified': timestamp_str},
+ {'count': 2, 'bytes': 3, 'name': 'c4',
+ 'last_modified': timestamp_str}]
+ self.assertEqual(json.loads(resp.body), expected)
def test_GET_limit_marker_xml(self):
req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'PUT',
@@ -1131,24 +1165,31 @@ class TestAccountController(unittest.TestCase):
self.assertEqual(listing[0].nodeName, 'container')
container = [n for n in listing[0].childNodes if n.nodeName != '#text']
self.assertEqual(sorted([n.nodeName for n in container]),
- ['bytes', 'count', 'name'])
+ ['bytes', 'count', 'last_modified', 'name'])
node = [n for n in container if n.nodeName == 'name'][0]
self.assertEqual(node.firstChild.nodeValue, 'c0')
node = [n for n in container if n.nodeName == 'count'][0]
self.assertEqual(node.firstChild.nodeValue, '2')
node = [n for n in container if n.nodeName == 'bytes'][0]
self.assertEqual(node.firstChild.nodeValue, '3')
+ node = [n for n in container if n.nodeName == 'last_modified'][0]
+ self.assertEqual(node.firstChild.nodeValue,
+ Timestamp('1').isoformat)
self.assertEqual(listing[-1].nodeName, 'container')
container = [
n for n in listing[-1].childNodes if n.nodeName != '#text']
self.assertEqual(sorted([n.nodeName for n in container]),
- ['bytes', 'count', 'name'])
+ ['bytes', 'count', 'last_modified', 'name'])
+ node = [n for n in container if n.nodeName == 'name'][0]
node = [n for n in container if n.nodeName == 'name'][0]
self.assertEqual(node.firstChild.nodeValue, 'c2')
node = [n for n in container if n.nodeName == 'count'][0]
self.assertEqual(node.firstChild.nodeValue, '2')
node = [n for n in container if n.nodeName == 'bytes'][0]
self.assertEqual(node.firstChild.nodeValue, '3')
+ node = [n for n in container if n.nodeName == 'last_modified'][0]
+ self.assertEqual(node.firstChild.nodeValue,
+ Timestamp('3').isoformat)
req = Request.blank('/sda1/p/a?limit=3&marker=c2&format=xml',
environ={'REQUEST_METHOD': 'GET'})
resp = req.get_response(self.controller)
@@ -1161,18 +1202,21 @@ class TestAccountController(unittest.TestCase):
self.assertEqual(listing[0].nodeName, 'container')
container = [n for n in listing[0].childNodes if n.nodeName != '#text']
self.assertEqual(sorted([n.nodeName for n in container]),
- ['bytes', 'count', 'name'])
+ ['bytes', 'count', 'last_modified', 'name'])
node = [n for n in container if n.nodeName == 'name'][0]
self.assertEqual(node.firstChild.nodeValue, 'c3')
node = [n for n in container if n.nodeName == 'count'][0]
self.assertEqual(node.firstChild.nodeValue, '2')
node = [n for n in container if n.nodeName == 'bytes'][0]
self.assertEqual(node.firstChild.nodeValue, '3')
+ node = [n for n in container if n.nodeName == 'last_modified'][0]
+ self.assertEqual(node.firstChild.nodeValue,
+ Timestamp('4').isoformat)
self.assertEqual(listing[-1].nodeName, 'container')
container = [
n for n in listing[-1].childNodes if n.nodeName != '#text']
self.assertEqual(sorted([n.nodeName for n in container]),
- ['bytes', 'count', 'name'])
+ ['bytes', 'count', 'last_modified', 'name'])
node = [n for n in container if n.nodeName == 'name'][0]
self.assertEqual(node.firstChild.nodeValue, 'c4')
node = [n for n in container if n.nodeName == 'count'][0]