Statistics
| Branch: | Tag: | Revision:

root / tools / test @ 952f4da1

History | View | Annotate | Download (75 kB)

1
#!/usr/bin/env python
2
#coding=utf8
3

    
4
# Copyright 2011 GRNET S.A. All rights reserved.
5
# 
6
# Redistribution and use in source and binary forms, with or
7
# without modification, are permitted provided that the following
8
# conditions are met:
9
# 
10
#   1. Redistributions of source code must retain the above
11
#      copyright notice, this list of conditions and the following
12
#      disclaimer.
13
# 
14
#   2. Redistributions in binary form must reproduce the above
15
#      copyright notice, this list of conditions and the following
16
#      disclaimer in the documentation and/or other materials
17
#      provided with the distribution.
18
# 
19
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
20
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
23
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
# POSSIBILITY OF SUCH DAMAGE.
31
# 
32
# The views and conclusions contained in the software and
33
# documentation are those of the authors and should not be
34
# interpreted as representing official policies, either expressed
35
# or implied, of GRNET S.A.
36

    
37
from lib.client import Pithos_Client, Fault
38
from xml.dom import minidom
39
from StringIO import StringIO
40
import json
41
import unittest
42
import time as _time
43
import types
44
import hashlib
45
import os
46
import mimetypes
47
import random
48
import datetime
49
import string
50
import re
51

    
52
DATE_FORMATS = ["%a %b %d %H:%M:%S %Y",
53
                "%A, %d-%b-%y %H:%M:%S GMT",
54
                "%a, %d %b %Y %H:%M:%S GMT"]
55

    
56
#DEFAULT_HOST = 'pithos.dev.grnet.gr'
57
DEFAULT_HOST = '127.0.0.1:8000'
58
DEFAULT_API = 'v1'
59
DEFAULT_USER = 'test'
60
DEFAULT_AUTH = '0000'
61

    
62
OTHER_ACCOUNTS = {
63
    '0001': 'verigak',
64
    '0002': 'chazapis',
65
    '0003': 'gtsouk',
66
    '0004': 'papagian',
67
    '0005': 'louridas',
68
    '0006': 'chstath',
69
    '0007': 'pkanavos',
70
    '0008': 'mvasilak',
71
    '0009': 'διογένης'}
72

    
73
class BaseTestCase(unittest.TestCase):
74
    #TODO unauthorized request
75
    def setUp(self):
76
        self.client = Pithos_Client(DEFAULT_HOST,
77
                                    DEFAULT_AUTH,
78
                                    DEFAULT_USER,
79
                                    DEFAULT_API)
80
        self.invalid_client = Pithos_Client(DEFAULT_HOST,
81
                                                  DEFAULT_AUTH,
82
                                                  'invalid',
83
                                                  DEFAULT_API)
84
        #self.headers = {
85
        #    'account': ('x-account-container-count',
86
        #                'x-account-bytes-used',
87
        #                'last-modified',
88
        #                'content-length',
89
        #                'date',
90
        #                'content_type',
91
        #                'server',),
92
        #    'object': ('etag',
93
        #               'content-length',
94
        #               'content_type',
95
        #               'content-encoding',
96
        #               'last-modified',
97
        #               'date',
98
        #               'x-object-manifest',
99
        #               'content-range',
100
        #               'x-object-modified-by',
101
        #               'x-object-version',
102
        #               'x-object-version-timestamp',
103
        #               'server',),
104
        #    'container': ('x-container-object-count',
105
        #                  'x-container-bytes-used',
106
        #                  'content_type',
107
        #                  'last-modified',
108
        #                  'content-length',
109
        #                  'date',
110
        #                  'x-container-block-size',
111
        #                  'x-container-block-hash',
112
        #                  'x-container-policy-quota',
113
        #                  'x-container-policy-versioning',
114
        #                  'server',
115
        #                  'x-container-object-meta',
116
        #                  'x-container-policy-versioning',
117
        #                  'server',)}
118
        #
119
        #self.contentTypes = {'xml':'application/xml',
120
        #                     'json':'application/json',
121
        #                     '':'text/plain'}
122
        self.extended = {
123
            'container':(
124
                'name',
125
                'count',
126
                'bytes',
127
                'last_modified',
128
                'x_container_policy_quota',
129
                'x_container_policy_versioning',),
130
            'object':(
131
                'name',
132
                'hash',
133
                'bytes',
134
                'content_type',
135
                'content_encoding',
136
                'last_modified',)}
137
        self.return_codes = (400, 401, 404, 503,)
138
    
139
    def tearDown(self):
140
        for c in self.client.list_containers():
141
            while True:
142
                #list objects returns at most 10000 objects
143
                #so repeat until there are no more objects
144
                objects = self.client.list_objects(c)
145
                if not objects:
146
                    break
147
                for o in objects:
148
                    self.client.delete_object(c, o)
149
            self.client.delete_container(c)
150
    
151
    def assert_status(self, status, codes):
152
        l = [elem for elem in self.return_codes]
153
        if type(codes) == types.ListType:
154
            l.extend(codes)
155
        else:
156
            l.append(codes)
157
        self.assertTrue(status in l)
158
    
159
    #def assert_headers(self, headers, type, **exp_meta):
160
    #    prefix = 'x-%s-meta-' %type
161
    #    system_headers = [h for h in headers if not h.startswith(prefix)]
162
    #    for k,v in headers.items():
163
    #        if k in system_headers:
164
    #            self.assertTrue(k in headers[type])
165
    #        elif exp_meta:
166
    #            k = k.split(prefix)[-1]
167
    #            self.assertEqual(v, exp_meta[k])
168
    
169
    def assert_extended(self, data, format, type, size=10000):
170
        if format == 'xml':
171
            self._assert_xml(data, type, size)
172
        elif format == 'json':
173
            self._assert_json(data, type, size)
174
    
175
    def _assert_json(self, data, type, size):
176
        convert = lambda s: s.lower()
177
        info = [convert(elem) for elem in self.extended[type]]
178
        self.assertTrue(len(data) <= size)
179
        for item in info:
180
            for i in data:
181
                if 'subdir' in i.keys():
182
                    continue
183
                self.assertTrue(item in i.keys())
184
    
185
    def _assert_xml(self, data, type, size):
186
        convert = lambda s: s.lower()
187
        info = [convert(elem) for elem in self.extended[type]]
188
        try:
189
            info.remove('content_encoding')
190
        except ValueError:
191
            pass
192
        xml = data
193
        entities = xml.getElementsByTagName(type)
194
        self.assertTrue(len(entities) <= size)
195
        for e in entities:
196
            for item in info:
197
                self.assertTrue(e.getElementsByTagName(item))
198
    
199
    def assert_raises_fault(self, status, callableObj, *args, **kwargs):
200
        """
201
        asserts that a Fault with a specific status is raised
202
        when callableObj is called with the specific arguments
203
        """
204
        try:
205
            r = callableObj(*args, **kwargs)
206
            self.fail('Should never reach here')
207
        except Fault, f:
208
            self.failUnless(f.status == status)
209
    
210
    def assert_not_raises_fault(self, status, callableObj, *args, **kwargs):
211
        """
212
        asserts that a Fault with a specific status is not raised
213
        when callableObj is called with the specific arguments
214
        """
215
        try:
216
            r = callableObj(*args, **kwargs)
217
        except Fault, f:
218
            self.failIfEqual(f.status, status)
219
    
220
    def assert_container_exists(self, container):
221
        """
222
        asserts the existence of a container
223
        """
224
        try:
225
            self.client.retrieve_container_metadata(container)
226
        except Fault, f:
227
            self.failIf(f.status == 404)
228
    
229
    def assert_container_not_exists(self, container):
230
        """
231
        asserts there is no such a container
232
        """
233
        self.assert_raises_fault(404, self.client.retrieve_container_metadata,
234
                                 container)
235
    
236
    def assert_object_exists(self, container, object):
237
        """
238
        asserts the existence of an object
239
        """
240
        try:
241
            self.client.retrieve_object_metadata(container, object)
242
        except Fault, f:
243
            self.failIf(f.status == 404)
244
    
245
    def assert_object_not_exists(self, container, object):
246
        """
247
        asserts there is no such an object
248
        """
249
        self.assert_raises_fault(404, self.client.retrieve_object_metadata,
250
                                 container, object)
251
    
252
    def upload_random_data(self, container, name, length=1024, type=None,
253
                           enc=None, **meta):
254
        data = get_random_data(length)
255
        return self.upload_data(container, name, data, type, enc, **meta)
256
    
257
    def upload_data(self, container, name, data, type=None, enc=None, etag=None,
258
                    **meta):
259
        obj = {}
260
        obj['name'] = name
261
        try:
262
            obj['data'] = data
263
            obj['hash'] = compute_md5_hash(obj['data'])
264
            
265
            args = {}
266
            args['etag'] = etag if etag else obj['hash']
267
            
268
            try:
269
                guess = mimetypes.guess_type(name)
270
                type = type if type else guess[0]
271
                enc = enc if enc else guess[1]
272
            except:
273
                pass
274
            args['content_type'] = type if type else 'plain/text'
275
            args['content_encoding'] = enc if enc else None
276
            
277
            obj['meta'] = args
278
            
279
            path = '/%s/%s' % (container, name)
280
            self.client.create_object(container, name, StringIO(obj['data']),
281
                                      meta, **args)
282
            
283
            return obj
284
        except IOError:
285
            return
286

    
287
class AccountHead(BaseTestCase):
288
    def setUp(self):
289
        BaseTestCase.setUp(self)
290
        self.account = 'test'
291
        self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
292
        for item in self.containers:
293
            self.client.create_container(item)
294
        
295
                #keep track of initial account groups
296
        self.initial_groups = self.client.retrieve_account_groups()
297
        
298
        #keep track of initial account meta
299
        self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
300
        
301
        meta = {'foo':'bar'}
302
        self.client.update_account_metadata(**meta)
303
        self.updated_meta = self.initial_meta.update(meta)
304
    
305
    def tearDown(self):
306
        #delete additionally created meta
307
        l = []
308
        for m in self.client.retrieve_account_metadata(restricted=True):
309
            if m not in self.initial_meta:
310
                l.append(m)
311
        self.client.delete_account_metadata(l)
312
        
313
        #delete additionally created groups
314
        l = []
315
        for g in self.client.retrieve_account_groups():
316
            if g not in self.initial_groups:
317
                l.append(g)
318
        self.client.unset_account_groups(l)
319
        
320
        BaseTestCase.tearDown(self)
321
    
322
    def test_get_account_meta(self):
323
        meta = self.client.retrieve_account_metadata()
324
        
325
        containers = self.client.list_containers()
326
        l = str(len(containers))
327
        self.assertEqual(meta['x-account-container-count'], l)
328
        size = 0
329
        for c in containers:
330
            m = self.client.retrieve_container_metadata(c)
331
            size = size + int(m['x-container-bytes-used'])
332
        self.assertEqual(meta['x-account-bytes-used'], str(size))
333
    
334
    def test_get_account_401(self):
335
        self.assert_raises_fault(401,
336
                                 self.invalid_client.retrieve_account_metadata)
337
    
338
    def test_get_account_meta_until(self):
339
        t = datetime.datetime.utcnow()
340
        past = t - datetime.timedelta(minutes=-15)
341
        past = int(_time.mktime(past.timetuple()))
342
        
343
        meta = {'premium':True}
344
        self.client.update_account_metadata(**meta)
345
        meta = self.client.retrieve_account_metadata(restricted=True,
346
                                                     until=past)
347
        self.assertTrue('premium' not in meta)
348
        
349
        meta = self.client.retrieve_account_metadata(restricted=True)
350
        self.assertTrue('premium' in meta)
351
    
352
    def test_get_account_meta_until_invalid_date(self):
353
        meta = {'premium':True}
354
        self.client.update_account_metadata(**meta)
355
        meta = self.client.retrieve_account_metadata(restricted=True,
356
                                                     until='kshfksfh')
357
        self.assertTrue('premium' in meta)
358
    
359
class AccountGet(BaseTestCase):
360
    def setUp(self):
361
        BaseTestCase.setUp(self)
362
        self.account = 'test'
363
        #create some containers
364
        self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
365
        for item in self.containers:
366
            self.client.create_container(item)
367
    
368
    def test_list(self):
369
        #list containers
370
        containers = self.client.list_containers()
371
        self.assertEquals(self.containers, containers)
372
    
373
    def test_list_401(self):
374
        self.assert_raises_fault(401, self.invalid_client.list_containers)
375
    
376
    def test_list_with_limit(self):
377
        limit = 2
378
        containers = self.client.list_containers(limit=limit)
379
        self.assertEquals(len(containers), limit)
380
        self.assertEquals(self.containers[:2], containers)
381
    
382
    def test_list_with_marker(self):
383
        l = 2
384
        m = 'bananas'
385
        containers = self.client.list_containers(limit=l, marker=m)
386
        i = self.containers.index(m) + 1
387
        self.assertEquals(self.containers[i:(i+l)], containers)
388
        
389
        m = 'oranges'
390
        containers = self.client.list_containers(limit=l, marker=m)
391
        i = self.containers.index(m) + 1
392
        self.assertEquals(self.containers[i:(i+l)], containers)
393
    
394
    def test_list_json_with_marker(self):
395
        l = 2
396
        m = 'bananas'
397
        containers = self.client.list_containers(limit=l, marker=m, format='json')
398
        self.assert_extended(containers, 'json', 'container', l)
399
        self.assertEqual(containers[0]['name'], 'kiwis')
400
        self.assertEqual(containers[1]['name'], 'oranges')
401
    
402
    def test_list_xml_with_marker(self):
403
        l = 2
404
        m = 'oranges'
405
        xml = self.client.list_containers(limit=l, marker=m, format='xml')
406
        self.assert_extended(xml, 'xml', 'container', l)
407
        nodes = xml.getElementsByTagName('name')
408
        self.assertEqual(len(nodes), 1)
409
        self.assertEqual(nodes[0].childNodes[0].data, 'pears')
410
    
411
    def test_if_modified_since(self):
412
        t = datetime.datetime.utcnow()
413
        t2 = t - datetime.timedelta(minutes=10)
414
        
415
        #add a new container
416
        self.client.create_container('dummy')
417
        
418
        for f in DATE_FORMATS:
419
            past = t2.strftime(f)
420
            try:
421
                c = self.client.list_containers(if_modified_since=past)
422
                self.assertEqual(len(c), len(self.containers) + 1)
423
            except Fault, f:
424
                self.failIf(f.status == 304) #fail if not modified
425
    
426
    def test_if_modified_since_invalid_date(self):
427
        c = self.client.list_containers(if_modified_since='')
428
        self.assertEqual(len(c), len(self.containers))
429
    
430
    def test_if_not_modified_since(self):
431
        now = datetime.datetime.utcnow()
432
        since = now + datetime.timedelta(1)
433
        
434
        for f in DATE_FORMATS:
435
            args = {'if_modified_since':'%s' %since.strftime(f)}
436
            
437
            #assert not modified
438
            self.assert_raises_fault(304, self.client.list_containers, **args)
439
    
440
    def test_if_unmodified_since(self):
441
        now = datetime.datetime.utcnow()
442
        since = now + datetime.timedelta(1)
443
        
444
        for f in DATE_FORMATS:
445
            c = self.client.list_containers(if_unmodified_since=since.strftime(f))
446
            
447
            #assert success
448
            self.assertEqual(self.containers, c)
449
    
450
    def test_if_unmodified_since_precondition_failed(self):
451
        t = datetime.datetime.utcnow()
452
        t2 = t - datetime.timedelta(minutes=10)
453
        
454
        #add a new container
455
        self.client.create_container('dummy')
456
        
457
        for f in DATE_FORMATS:
458
            past = t2.strftime(f)
459
            
460
            args = {'if_unmodified_since':'%s' %past}
461
            
462
            #assert precondition failed
463
            self.assert_raises_fault(412, self.client.list_containers, **args)
464
    
465
class AccountPost(BaseTestCase):
466
    def setUp(self):
467
        BaseTestCase.setUp(self)
468
        self.account = 'test'
469
        self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
470
        for item in self.containers:
471
            self.client.create_container(item)
472
        
473
        #keep track of initial account groups
474
        self.initial_groups = self.client.retrieve_account_groups()
475
        
476
        #keep track of initial account meta
477
        self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
478
        
479
        meta = {'foo':'bar'}
480
        self.client.update_account_metadata(**meta)
481
        self.updated_meta = self.initial_meta.update(meta)
482
    
483
    def tearDown(self):
484
        #delete additionally created meta
485
        l = []
486
        for m in self.client.retrieve_account_metadata(restricted=True):
487
            if m not in self.initial_meta:
488
                l.append(m)
489
        self.client.delete_account_metadata(l)
490
        
491
        #delete additionally created groups
492
        l = []
493
        for g in self.client.retrieve_account_groups():
494
            if g not in self.initial_groups:
495
                l.append(g)
496
        self.client.unset_account_groups(l)
497
        
498
        BaseTestCase.tearDown(self)
499
    
500
    def test_update_meta(self):
501
        with AssertMappingInvariant(self.client.retrieve_account_groups):
502
            meta = {'test':'test', 'tost':'tost'}
503
            self.client.update_account_metadata(**meta)
504
            
505
            meta.update(self.initial_meta)
506
            self.assertEqual(meta,
507
                             self.client.retrieve_account_metadata(
508
                                restricted=True))
509
        
510
    def test_invalid_account_update_meta(self):
511
        meta = {'test':'test', 'tost':'tost'}
512
        self.assert_raises_fault(401,
513
                                 self.invalid_client.update_account_metadata,
514
                                 **meta)
515
    
516
    def test_reset_meta(self):
517
        with AssertMappingInvariant(self.client.retrieve_account_groups):
518
            meta = {'test':'test', 'tost':'tost'}
519
            self.client.update_account_metadata(**meta)
520
            
521
            meta = {'test':'test33'}
522
            self.client.reset_account_metadata(**meta)
523
            
524
            self.assertEqual(meta, self.client.retrieve_account_metadata(restricted=True))
525
    
526
    def test_delete_meta(self):
527
        with AssertMappingInvariant(self.client.retrieve_account_groups):
528
            meta = {'test':'test', 'tost':'tost'}
529
            self.client.update_account_metadata(**meta)
530
            
531
            self.client.delete_account_metadata(meta.keys())
532
            
533
            account_meta = self.client.retrieve_account_metadata(restricted=True)
534
            for m in meta:
535
                self.assertTrue(m not in account_meta.keys())
536
    
537
    def test_set_account_groups(self):
538
        with AssertMappingInvariant(self.client.retrieve_account_metadata):
539
            groups = {'pithosdev':'verigak,gtsouk,chazapis'}
540
            self.client.set_account_groups(**groups)
541
            
542
            self.assertEqual(groups, self.client.retrieve_account_groups())
543
            
544
            more_groups = {'clientsdev':'pkanavos,mvasilak'}
545
            self.client.set_account_groups(**more_groups)
546
            
547
            groups.update(more_groups)
548
            self.assertEqual(set(groups['pithosdev']),
549
                             set(self.client.retrieve_account_groups()['pithosdev']))
550
    
551
    def test_reset_account_groups(self):
552
        with AssertMappingInvariant(self.client.retrieve_account_metadata):
553
            groups = {'pithosdev':'verigak,gtsouk,chazapis',
554
                      'clientsdev':'pkanavos,mvasilak'}
555
            self.client.set_account_groups(**groups)
556
            
557
            self.assertEqual(groups, self.client.retrieve_account_groups())
558
            
559
            groups = {'pithosdev':'verigak,gtsouk,chazapis,papagian'}
560
            self.client.reset_account_groups(**groups)
561
            
562
            self.assertEqual(groups['pithosdev'].split(','),
563
                             self.client.retrieve_account_groups()['pithosdev'].split(','))
564
    
565
    def test_delete_account_groups(self):
566
        with AssertMappingInvariant(self.client.retrieve_account_metadata):
567
            groups = {'pithosdev':'verigak,gtsouk,chazapis',
568
                      'clientsdev':'pkanavos,mvasilak'}
569
            self.client.set_account_groups(**groups)
570
            
571
            self.client.unset_account_groups(groups.keys())
572
            
573
            self.assertEqual({}, self.client.retrieve_account_groups())
574
    
575
class ContainerHead(BaseTestCase):
576
    def setUp(self):
577
        BaseTestCase.setUp(self)
578
        self.account = 'test'
579
        self.container = 'apples'
580
        self.client.create_container(self.container)
581
    
582
    def test_get_meta(self):
583
        meta = {'trash':'true'}
584
        t1 = datetime.datetime.utcnow()
585
        o = self.upload_random_data(self.container, o_names[0], **meta)
586
        if o:
587
            headers = self.client.retrieve_container_metadata(self.container)
588
            self.assertEqual(headers['x-container-object-count'], '1')
589
            self.assertEqual(headers['x-container-bytes-used'], str(len(o['data'])))
590
            t2 = datetime.datetime.strptime(headers['last-modified'], DATE_FORMATS[2])
591
            delta = (t2 - t1)
592
            threashold = datetime.timedelta(seconds=1) 
593
            self.assertTrue(delta < threashold)
594
            self.assertTrue(headers['x-container-object-meta'])
595
            self.assertTrue('Trash' in headers['x-container-object-meta'])
596

    
597
class ContainerGet(BaseTestCase):
598
    def setUp(self):
599
        BaseTestCase.setUp(self)
600
        self.account = 'test'
601
        self.container = ['pears', 'apples']
602
        for c in self.container:
603
            self.client.create_container(c)
604
        self.obj = []
605
        for o in o_names[:8]:
606
            self.obj.append(self.upload_random_data(self.container[0], o))
607
        for o in o_names[8:]:
608
            self.obj.append(self.upload_random_data(self.container[1], o))
609
    
610
    def test_list_objects(self):
611
        objects = self.client.list_objects(self.container[0])
612
        l = [elem['name'] for elem in self.obj[:8]]
613
        l.sort()
614
        self.assertEqual(objects, l)
615
    
616
    def test_list_objects_containing_slash(self):
617
        self.client.create_container('test')
618
        self.upload_random_data('test', '/objectname')
619
        
620
        objects = self.client.list_objects('test')
621
        self.assertEqual(objects, ['/objectname'])
622
        
623
        objects = self.client.list_objects('test', format='json')
624
        self.assertEqual(objects[0]['name'], '/objectname')
625
        
626
        objects = self.client.list_objects('test', format='xml')
627
        self.assert_extended(objects, 'xml', 'object')
628
        node_name = objects.getElementsByTagName('name')[0]
629
        self.assertEqual(node_name.firstChild.data, '/objectname')
630
        
631
        #objects = self.client.list_objects('test', prefix='/')
632
        #self.assertEqual(objects, ['/objectname'])
633
        #
634
        #objects = self.client.list_objects('test', path='/')
635
        #self.assertEqual(objects, ['/objectname'])
636
        #
637
        #objects = self.client.list_objects('test', prefix='/', delimiter='n')
638
        #self.assertEqual(objects, ['/object'])
639

    
640
    def test_list_objects_with_limit_marker(self):
641
        objects = self.client.list_objects(self.container[0], limit=2)
642
        l = [elem['name'] for elem in self.obj[:8]]
643
        l.sort()
644
        self.assertEqual(objects, l[:2])
645
        
646
        markers = ['How To Win Friends And Influence People.pdf',
647
                   'moms_birthday.jpg']
648
        limit = 4
649
        for m in markers:
650
            objects = self.client.list_objects(self.container[0], limit=limit,
651
                                               marker=m)
652
            l = [elem['name'] for elem in self.obj[:8]]
653
            l.sort()
654
            start = l.index(m) + 1
655
            end = start + limit
656
            end = len(l) >= end and end or len(l)
657
            self.assertEqual(objects, l[start:end])
658
    
659
    #takes too long
660
    def _test_list_limit_exceeds(self):
661
        self.client.create_container('pithos')
662
        
663
        for i in range(10001):
664
            self.client.create_zero_length_object('pithos', i)
665
        
666
        self.assertEqual(10000, len(self.client.list_objects('pithos')))
667
    
668
    def test_list_empty_params(self):
669
        objects = self.client.get('/%s/%s' % (DEFAULT_USER, self.container[0]))[2]
670
        if objects:
671
            objects = objects.strip().split('\n')
672
        self.assertEqual(objects,
673
                         self.client.list_objects(self.container[0]))
674
    
675
    def test_list_pseudo_hierarchical_folders(self):
676
        objects = self.client.list_objects(self.container[1], prefix='photos',
677
                                           delimiter='/')
678
        self.assertEquals(['photos/animals/', 'photos/me.jpg',
679
                           'photos/plants/'], objects)
680
        
681
        objects = self.client.list_objects(self.container[1],
682
                                           prefix='photos/animals',
683
                                           delimiter='/')
684
        l = ['photos/animals/cats/', 'photos/animals/dogs/']
685
        self.assertEquals(l, objects)
686
        
687
        objects = self.client.list_objects(self.container[1], path='photos')
688
        self.assertEquals(['photos/me.jpg'], objects)
689
    
690
    def test_extended_list_json(self):
691
        objects = self.client.list_objects(self.container[1], format='json',
692
                                           limit=2, prefix='photos/animals',
693
                                           delimiter='/')
694
        self.assertEqual(objects[0]['subdir'], 'photos/animals/cats/')
695
        self.assertEqual(objects[1]['subdir'], 'photos/animals/dogs/')
696
    
697
    def test_extended_list_xml(self):
698
        xml = self.client.list_objects(self.container[1], format='xml', limit=4,
699
                                       prefix='photos', delimiter='/')
700
        self.assert_extended(xml, 'xml', 'object', size=4)
701
        dirs = xml.getElementsByTagName('subdir')
702
        self.assertEqual(len(dirs), 2)
703
        self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
704
        self.assertEqual(dirs[1].attributes['name'].value, 'photos/plants/')
705
        
706
        objects = xml.getElementsByTagName('name')
707
        self.assertEqual(len(objects), 1)
708
        self.assertEqual(objects[0].childNodes[0].data, 'photos/me.jpg')
709
    
710
    def test_list_meta_double_matching(self):
711
        meta = {'quality':'aaa', 'stock':'true'}
712
        self.client.update_object_metadata(self.container[0],
713
                                           self.obj[0]['name'], **meta)
714
        obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
715
        self.assertEqual(len(obj), 1)
716
        self.assertTrue(obj, self.obj[0]['name'])
717
    
718
    def test_list_using_meta(self):
719
        meta = {'quality':'aaa'}
720
        for o in self.obj[:2]:
721
            self.client.update_object_metadata(self.container[0], o['name'],
722
                                               **meta)
723
        meta = {'stock':'true'}
724
        for o in self.obj[3:5]:
725
            self.client.update_object_metadata(self.container[0], o['name'],
726
                                               **meta)
727
        
728
        obj = self.client.list_objects(self.container[0], meta='Quality')
729
        self.assertEqual(len(obj), 2)
730
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
731
        
732
        # test case insensitive
733
        obj = self.client.list_objects(self.container[0], meta='quality')
734
        self.assertEqual(len(obj), 2)
735
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
736
        
737
        # test multiple matches
738
        obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
739
        self.assertEqual(len(obj), 4)
740
        self.assertTrue(obj, [o['name'] for o in self.obj[:4]])
741
        
742
        # test non 1-1 multiple match
743
        obj = self.client.list_objects(self.container[0], meta='Quality,aaaa')
744
        self.assertEqual(len(obj), 2)
745
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
746
    
747
    def test_if_modified_since(self):
748
        t = datetime.datetime.utcnow()
749
        t2 = t - datetime.timedelta(minutes=10)
750
        
751
        #add a new object
752
        self.upload_random_data(self.container[0], o_names[0])
753
        
754
        for f in DATE_FORMATS:
755
            past = t2.strftime(f)
756
            try:
757
                o = self.client.list_objects(self.container[0],
758
                                            if_modified_since=past)
759
                self.assertEqual(o,
760
                                 self.client.list_objects(self.container[0]))
761
            except Fault, f:
762
                self.failIf(f.status == 304) #fail if not modified
763
    
764
    def test_if_modified_since_invalid_date(self):
765
        headers = {'if-modified-since':''}
766
        o = self.client.list_objects(self.container[0], if_modified_since='')
767
        self.assertEqual(o, self.client.list_objects(self.container[0]))
768
    
769
    def test_if_not_modified_since(self):
770
        now = datetime.datetime.utcnow()
771
        since = now + datetime.timedelta(1)
772
        
773
        for f in DATE_FORMATS:
774
            args = {'if_modified_since':'%s' %since.strftime(f)}
775
            
776
            #assert not modified
777
            self.assert_raises_fault(304, self.client.list_objects,
778
                                     self.container[0], **args)
779
    
780
    def test_if_unmodified_since(self):
781
        now = datetime.datetime.utcnow()
782
        since = now + datetime.timedelta(1)
783
        
784
        for f in DATE_FORMATS:
785
            obj = self.client.list_objects(self.container[0],
786
                                           if_unmodified_since=since.strftime(f))
787
            
788
            #assert unmodified
789
            self.assertEqual(obj, self.client.list_objects(self.container[0]))
790
    
791
    def test_if_unmodified_since_precondition_failed(self):
792
        t = datetime.datetime.utcnow()
793
        t2 = t - datetime.timedelta(minutes=10)
794
        
795
        #add a new container
796
        self.client.create_container('dummy')
797
        
798
        for f in DATE_FORMATS:
799
            past = t2.strftime(f)
800
            
801
            args = {'if_unmodified_since':'%s' %past}
802
            
803
            #assert precondition failed
804
            self.assert_raises_fault(412, self.client.list_objects,
805
                                     self.container[0], **args)
806

    
807
class ContainerPut(BaseTestCase):
808
    def setUp(self):
809
        BaseTestCase.setUp(self)
810
        self.account = 'test'
811
        self.containers = ['c1', 'c2']
812
    
813
    def test_create(self):
814
        self.client.create_container(self.containers[0])
815
        containers = self.client.list_containers()
816
        self.assertTrue(self.containers[0] in containers)
817
        self.assert_container_exists(self.containers[0])
818
    
819
    def test_create_twice(self):
820
        self.client.create_container(self.containers[0])
821
        self.assertTrue(not self.client.create_container(self.containers[0]))
822
    
823
class ContainerPost(BaseTestCase):
824
    def setUp(self):
825
        BaseTestCase.setUp(self)
826
        self.account = 'test'
827
        self.container = 'apples'
828
        self.client.create_container(self.container)
829
    
830
    def test_update_meta(self):
831
        meta = {'test':'test33',
832
                'tost':'tost22'}
833
        self.client.update_container_metadata(self.container, **meta)
834
        headers = self.client.retrieve_container_metadata(self.container)
835
        for k,v in meta.items():
836
            k = 'x-container-meta-%s' % k
837
            self.assertTrue(headers[k])
838
            self.assertEqual(headers[k], v)
839

    
840
class ContainerDelete(BaseTestCase):
841
    def setUp(self):
842
        BaseTestCase.setUp(self)
843
        self.account = 'test'
844
        self.containers = ['c1', 'c2']
845
        for c in self.containers:
846
            self.client.create_container(c)
847
        self.upload_random_data(self.containers[1], o_names[0])
848
    
849
    def test_delete(self):
850
        status = self.client.delete_container(self.containers[0])[0]
851
        self.assertEqual(status, 204)
852
    
853
    def test_delete_non_empty(self):
854
        self.assert_raises_fault(409, self.client.delete_container,
855
                                 self.containers[1])
856
    
857
    def test_delete_invalid(self):
858
        self.assert_raises_fault(404, self.client.delete_container, 'c3')
859

    
860
class ObjectHead(BaseTestCase):
861
    pass
862

    
863
class ObjectGet(BaseTestCase):
864
    def setUp(self):
865
        BaseTestCase.setUp(self)
866
        self.account = 'test'
867
        self.containers = ['c1', 'c2']
868
        #create some containers
869
        for c in self.containers:
870
            self.client.create_container(c)
871
        
872
        #upload a file
873
        names = ('obj1', 'obj2')
874
        self.objects = []
875
        for n in names:
876
            self.objects.append(self.upload_random_data(self.containers[1], n))
877
    
878
    def test_get(self):
879
        #perform get
880
        o = self.client.retrieve_object(self.containers[1],
881
                                        self.objects[0]['name'],
882
                                        self.objects[0]['meta'])
883
        self.assertEqual(o, self.objects[0]['data'])
884
    
885
    def test_get_invalid(self):
886
        self.assert_raises_fault(404, self.client.retrieve_object,
887
                                 self.containers[0], self.objects[0]['name'])
888
    
889
    def test_get_partial(self):
890
        #perform get with range
891
        status, headers, data = self.client.request_object(self.containers[1],
892
                                                            self.objects[0]['name'],
893
                                                            range='bytes=0-499')
894
        
895
        #assert successful partial content
896
        self.assertEqual(status, 206)
897
        
898
        #assert content-type
899
        self.assertEqual(headers['content-type'],
900
                         self.objects[0]['meta']['content_type'])
901
        
902
        #assert content length
903
        self.assertEqual(int(headers['content-length']), 500)
904
        
905
        #assert content
906
        self.assertEqual(self.objects[0]['data'][:500], data)
907
    
908
    def test_get_final_500(self):
909
        #perform get with range
910
        headers = {'range':'bytes=-500'}
911
        status, headers, data = self.client.request_object(self.containers[1],
912
                                                            self.objects[0]['name'],
913
                                                            range='bytes=-500')
914
        
915
        #assert successful partial content
916
        self.assertEqual(status, 206)
917
        
918
        #assert content-type
919
        self.assertEqual(headers['content-type'],
920
                         self.objects[0]['meta']['content_type'])
921
        
922
        #assert content length
923
        self.assertEqual(int(headers['content-length']), 500)
924
        
925
        #assert content
926
        self.assertTrue(self.objects[0]['data'][-500:], data)
927
    
928
    def test_get_rest(self):
929
        #perform get with range
930
        offset = len(self.objects[0]['data']) - 500
931
        status, headers, data = self.client.request_object(self.containers[1],
932
                                                self.objects[0]['name'],
933
                                                range='bytes=%s-' %offset)
934
        
935
        #assert successful partial content
936
        self.assertEqual(status, 206)
937
        
938
        #assert content-type
939
        self.assertEqual(headers['content-type'],
940
                         self.objects[0]['meta']['content_type'])
941
        
942
        #assert content length
943
        self.assertEqual(int(headers['content-length']), 500)
944
        
945
        #assert content
946
        self.assertTrue(self.objects[0]['data'][-500:], data)
947
    
948
    def test_get_range_not_satisfiable(self):
949
        #perform get with range
950
        offset = len(self.objects[0]['data']) + 1
951
        
952
        #assert range not satisfiable
953
        self.assert_raises_fault(416, self.client.retrieve_object,
954
                                 self.containers[1], self.objects[0]['name'],
955
                                 range='bytes=0-%s' %offset)
956
    
957
    def test_multiple_range(self):
958
        #perform get with multiple range
959
        ranges = ['0-499', '-500', '1000-']
960
        bytes = 'bytes=%s' % ','.join(ranges)
961
        status, headers, data = self.client.request_object(self.containers[1],
962
                                                           self.objects[0]['name'],
963
                                                           range=bytes)
964
        
965
        # assert partial content
966
        self.assertEqual(status, 206)
967
        
968
        # assert Content-Type of the reply will be multipart/byteranges
969
        self.assertTrue(headers['content-type'])
970
        content_type_parts = headers['content-type'].split()
971
        self.assertEqual(content_type_parts[0], ('multipart/byteranges;'))
972
        
973
        boundary = '--%s' %content_type_parts[1].split('=')[-1:][0]
974
        cparts = data.split(boundary)[1:-1]
975
        
976
        # assert content parts are exactly 2
977
        self.assertEqual(len(cparts), len(ranges))
978
        
979
        # for each content part assert headers
980
        i = 0
981
        for cpart in cparts:
982
            content = cpart.split('\r\n')
983
            headers = content[1:3]
984
            content_range = headers[0].split(': ')
985
            self.assertEqual(content_range[0], 'Content-Range')
986
            
987
            r = ranges[i].split('-')
988
            if not r[0] and not r[1]:
989
                pass
990
            elif not r[0]:
991
                start = len(self.objects[0]['data']) - int(r[1])
992
                end = len(self.objects[0]['data'])
993
            elif not r[1]:
994
                start = int(r[0])
995
                end = len(self.objects[0]['data'])
996
            else:
997
                start = int(r[0])
998
                end = int(r[1]) + 1
999
            fdata = self.objects[0]['data'][start:end]
1000
            sdata = '\r\n'.join(content[4:-1])
1001
            self.assertEqual(len(fdata), len(sdata))
1002
            self.assertEquals(fdata, sdata)
1003
            i+=1
1004
    
1005
    def test_multiple_range_not_satisfiable(self):
1006
        #perform get with multiple range
1007
        out_of_range = len(self.objects[0]['data']) + 1
1008
        ranges = ['0-499', '-500', '%d-' %out_of_range]
1009
        bytes = 'bytes=%s' % ','.join(ranges)
1010
        
1011
        # assert partial content
1012
        self.assert_raises_fault(416, self.client.retrieve_object,
1013
                                 self.containers[1],
1014
                                 self.objects[0]['name'], range=bytes)
1015
    
1016
    def test_get_with_if_match(self):
1017
        #perform get with If-Match
1018
        etag = self.objects[0]['hash']
1019
        status, headers, data = self.client.request_object(self.containers[1],
1020
                                                           self.objects[0]['name'],
1021
                                                           if_match=etag)
1022
        #assert get success
1023
        self.assertEqual(status, 200)
1024
        
1025
        #assert content-type
1026
        self.assertEqual(headers['content-type'],
1027
                         self.objects[0]['meta']['content_type'])
1028
        
1029
        #assert response content
1030
        self.assertEqual(self.objects[0]['data'], data)
1031
    
1032
    def test_get_with_if_match_star(self):
1033
        #perform get with If-Match *
1034
        headers = {'if-match':'*'}
1035
        status, headers, data = self.client.request_object(self.containers[1],
1036
                                                self.objects[0]['name'],
1037
                                                **headers)
1038
        #assert get success
1039
        self.assertEqual(status, 200)
1040
        
1041
        #assert content-type
1042
        self.assertEqual(headers['content-type'],
1043
                         self.objects[0]['meta']['content_type'])
1044
        
1045
        #assert response content
1046
        self.assertEqual(self.objects[0]['data'], data)
1047
    
1048
    def test_get_with_multiple_if_match(self):
1049
        #perform get with If-Match
1050
        etags = [i['hash'] for i in self.objects if i]
1051
        etags = ','.join('"%s"' % etag for etag in etags)
1052
        status, headers, data = self.client.request_object(self.containers[1],
1053
                                                           self.objects[0]['name'],
1054
                                                           if_match=etags)
1055
        #assert get success
1056
        self.assertEqual(status, 200)
1057
        
1058
        #assert content-type
1059
        self.assertEqual(headers['content-type'],
1060
                         self.objects[0]['meta']['content_type'])
1061
        
1062
        #assert content-type
1063
        self.assertEqual(headers['content-type'],
1064
                         self.objects[0]['meta']['content_type'])
1065
        
1066
        #assert response content
1067
        self.assertEqual(self.objects[0]['data'], data)
1068
    
1069
    def test_if_match_precondition_failed(self):
1070
        #assert precondition failed
1071
        self.assert_raises_fault(412, self.client.retrieve_object,
1072
                                 self.containers[1],
1073
                                 self.objects[0]['name'], if_match='123')
1074
    
1075
    def test_if_none_match(self):
1076
        #perform get with If-None-Match
1077
        status, headers, data = self.client.request_object(self.containers[1],
1078
                                                           self.objects[0]['name'],
1079
                                                           if_none_match='123')
1080
        
1081
        #assert get success
1082
        self.assertEqual(status, 200)
1083
        
1084
        #assert content-type
1085
        self.assertEqual(headers['content_type'],
1086
                         self.objects[0]['meta']['content_type'])
1087
    
1088
    def test_if_none_match(self):
1089
        #perform get with If-None-Match * and assert not modified
1090
        self.assert_raises_fault(304, self.client.retrieve_object,
1091
                                 self.containers[1],
1092
                                 self.objects[0]['name'],
1093
                                 if_none_match='*')
1094
    
1095
    def test_if_none_match_not_modified(self):
1096
        #perform get with If-None-Match and assert not modified
1097
        self.assert_raises_fault(304, self.client.retrieve_object,
1098
                                 self.containers[1],
1099
                                 self.objects[0]['name'],
1100
                                 if_none_match=self.objects[0]['hash'])
1101
        
1102
        meta = self.client.retrieve_object_metadata(self.containers[1],
1103
                                                    self.objects[0]['name'])
1104
        self.assertEqual(meta['etag'], self.objects[0]['hash'])
1105
    
1106
    def test_if_modified_since(self):
1107
        t = datetime.datetime.utcnow()
1108
        t2 = t - datetime.timedelta(minutes=10)
1109
        
1110
        #modify the object
1111
        self.upload_data(self.containers[1],
1112
                           self.objects[0]['name'],
1113
                           self.objects[0]['data'][:200])
1114
        
1115
        for f in DATE_FORMATS:
1116
            past = t2.strftime(f)
1117
            
1118
            headers = {'if-modified-since':'%s' %past}
1119
            try:
1120
                o = self.client.retrieve_object(self.containers[1],
1121
                                                self.objects[0]['name'],
1122
                                                if_modified_since=past)
1123
                self.assertEqual(o,
1124
                                 self.client.retrieve_object(self.containers[1],
1125
                                                             self.objects[0]['name']))
1126
            except Fault, f:
1127
                self.failIf(f.status == 304)
1128
    
1129
    def test_if_modified_since_invalid_date(self):
1130
        o = self.client.retrieve_object(self.containers[1],
1131
                                        self.objects[0]['name'],
1132
                                        if_modified_since='')
1133
        self.assertEqual(o, self.client.retrieve_object(self.containers[1],
1134
                                                        self.objects[0]['name']))
1135
            
1136
    def test_if_not_modified_since(self):
1137
        now = datetime.datetime.utcnow()
1138
        since = now + datetime.timedelta(1)
1139
        
1140
        for f in DATE_FORMATS:
1141
            #assert not modified
1142
            self.assert_raises_fault(304, self.client.retrieve_object,
1143
                                     self.containers[1], self.objects[0]['name'],
1144
                                     if_modified_since=since.strftime(f))
1145
    
1146
    def test_if_unmodified_since(self):
1147
        now = datetime.datetime.utcnow()
1148
        since = now + datetime.timedelta(1)
1149
        
1150
        for f in DATE_FORMATS:
1151
            t = since.strftime(f)
1152
            status, headers, data = self.client.request_object(self.containers[1],
1153
                                                               self.objects[0]['name'],
1154
                                                               if_unmodified_since=t)
1155
            #assert success
1156
            self.assertEqual(status, 200)
1157
            self.assertEqual(self.objects[0]['data'], data)
1158
            
1159
            #assert content-type
1160
            self.assertEqual(headers['content-type'],
1161
                             self.objects[0]['meta']['content_type'])
1162
    
1163
    def test_if_unmodified_since_precondition_failed(self):
1164
        t = datetime.datetime.utcnow()
1165
        t2 = t - datetime.timedelta(minutes=10)
1166
        
1167
        #modify the object
1168
        self.upload_data(self.containers[1],
1169
                           self.objects[0]['name'],
1170
                           self.objects[0]['data'][:200])
1171
        
1172
        for f in DATE_FORMATS:
1173
            past = t2.strftime(f)
1174
            #assert precondition failed
1175
            self.assert_raises_fault(412, self.client.retrieve_object,
1176
                                     self.containers[1], self.objects[0]['name'],
1177
                                     if_unmodified_since=past)
1178
    
1179
    def test_hashes(self):
1180
        l = 8388609
1181
        fname = 'largefile'
1182
        o = self.upload_random_data(self.containers[1], fname, l)
1183
        if o:
1184
            data = self.client.retrieve_object(self.containers[1], fname,
1185
                                               format='json')
1186
            body = json.loads(data)
1187
            hashes = body['hashes']
1188
            block_size = body['block_size']
1189
            block_hash = body['block_hash']
1190
            block_num = l/block_size == 0 and l/block_size or l/block_size + 1
1191
            self.assertTrue(len(hashes), block_num)
1192
            i = 0
1193
            for h in hashes:
1194
                start = i * block_size
1195
                end = (i + 1) * block_size
1196
                hash = compute_block_hash(o['data'][start:end], block_hash)
1197
                self.assertEqual(h, hash)
1198
                i += 1
1199

    
1200
class ObjectPut(BaseTestCase):
1201
    def setUp(self):
1202
        BaseTestCase.setUp(self)
1203
        self.account = 'test'
1204
        self.container = 'c1'
1205
        self.client.create_container(self.container)
1206
    
1207
    def test_upload(self):
1208
        name = o_names[0]
1209
        meta = {'test':'test1'}
1210
        o = self.upload_random_data(self.container, name, **meta)
1211
        
1212
        headers = self.client.retrieve_object_metadata(self.container,
1213
                                                       name,
1214
                                                       restricted=True)
1215
        self.assertTrue('test' in headers.keys())
1216
        self.assertEqual(headers['test'], meta['test'])
1217
        
1218
        #assert uploaded content
1219
        status, h, data = self.client.request_object(self.container, name)
1220
        self.assertEqual(len(o['data']), int(h['content-length']))
1221
        self.assertEqual(o['data'], data)
1222
        
1223
        #assert content-type
1224
        self.assertEqual(h['content-type'], o['meta']['content_type'])
1225
    
1226
    def _test_maximum_upload_size_exceeds(self):
1227
        name = o_names[0]
1228
        meta = {'test':'test1'}
1229
        #upload 100MB
1230
        length=1024*1024*100
1231
        self.assert_raises_fault(400, self.upload_random_data, self.container,
1232
                                 name, length, **meta)
1233
    
1234
    def test_upload_with_name_containing_slash(self):
1235
        name = '/%s' % o_names[0]
1236
        meta = {'test':'test1'}
1237
        o = self.upload_random_data(self.container, name, **meta)
1238
        
1239
        self.assertEqual(o['data'],
1240
                         self.client.retrieve_object(self.container, name))
1241
        
1242
        self.assertTrue(name in self.client.list_objects(self.container))
1243
    
1244
    def test_create_directory_marker(self):
1245
        self.client.create_directory_marker(self.container, 'foo')
1246
        meta = self.client.retrieve_object_metadata(self.container, 'foo')
1247
        self.assertEqual(meta['content-length'], '0')
1248
        self.assertEqual(meta['content-type'], 'application/directory')
1249

    
1250
    def test_upload_unprocessable_entity(self):
1251
        meta={'etag':'123', 'test':'test1'}
1252
        
1253
        #assert unprocessable entity
1254
        self.assert_raises_fault(422, self.upload_random_data, self.container,
1255
                                 o_names[0], **meta)
1256
    
1257
    def test_chunked_transfer(self):
1258
        data = get_random_data()
1259
        objname = 'object'
1260
        self.client.create_object_using_chunks(self.container, objname,
1261
                                               StringIO(data))
1262
        
1263
        uploaded_data = self.client.retrieve_object(self.container, objname)
1264
        self.assertEqual(data, uploaded_data)
1265
    
1266
    def test_manifestation(self):
1267
        prefix = 'myobject/'
1268
        data = ''
1269
        for i in range(5):
1270
            part = '%s%d' %(prefix, i)
1271
            o = self.upload_random_data(self.container, part)
1272
            data += o['data']
1273
        
1274
        manifest = '%s/%s' %(self.container, prefix)
1275
        self.client.create_manifestation(self.container, 'large-object', manifest)
1276
        
1277
        self.assert_object_exists(self.container, 'large-object')
1278
        self.assertEqual(data, self.client.retrieve_object(self.container,
1279
                                                           'large-object'))
1280
        
1281
        #wrong manifestation
1282
        self.client.create_manifestation(self.container, 'large-object',
1283
                                         '%s/invalid' % self.container)
1284
        self.assertEqual('', self.client.retrieve_object(self.container,
1285
                                                         'large-object'))
1286
    
1287
class ObjectCopy(BaseTestCase):
1288
    def setUp(self):
1289
        BaseTestCase.setUp(self)
1290
        self.account = 'test'
1291
        self.containers = ['c1', 'c2']
1292
        for c in self.containers:
1293
            self.client.create_container(c)
1294
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1295
    
1296
    def tearDown(self):
1297
        pass
1298
    
1299
    def test_copy(self):
1300
        with AssertMappingInvariant(self.client.retrieve_object_metadata,
1301
                             self.containers[0], self.obj['name']):
1302
            #perform copy
1303
            meta = {'test':'testcopy'}
1304
            status = self.client.copy_object(self.containers[0],
1305
                                              self.obj['name'],
1306
                                              self.containers[0],
1307
                                              'testcopy',
1308
                                              meta)[0]
1309
            
1310
            #assert copy success
1311
            self.assertEqual(status, 201)
1312
            
1313
            #assert access the new object
1314
            headers = self.client.retrieve_object_metadata(self.containers[0],
1315
                                                           'testcopy')
1316
            self.assertTrue('x-object-meta-test' in headers.keys())
1317
            self.assertTrue(headers['x-object-meta-test'], 'testcopy')
1318
            
1319
            #assert etag is the same
1320
            self.assertEqual(headers['etag'], self.obj['hash'])
1321
            
1322
            #assert src object still exists
1323
            self.assert_object_exists(self.containers[0], self.obj['name'])
1324
    
1325
    def test_copy_from_different_container(self):
1326
        with AssertMappingInvariant(self.client.retrieve_object_metadata,
1327
                             self.containers[0], self.obj['name']):
1328
            meta = {'test':'testcopy'}
1329
            status = self.client.copy_object(self.containers[0],
1330
                                             self.obj['name'],
1331
                                             self.containers[1],
1332
                                             'testcopy',
1333
                                             meta)[0]
1334
            self.assertEqual(status, 201)
1335
            
1336
            # assert updated metadata
1337
            meta = self.client.retrieve_object_metadata(self.containers[1],
1338
                                                           'testcopy',
1339
                                                           restricted=True)
1340
            self.assertTrue('test' in meta.keys())
1341
            self.assertTrue(meta['test'], 'testcopy')
1342
            
1343
            #assert src object still exists
1344
            self.assert_object_exists(self.containers[0], self.obj['name'])
1345
    
1346
    def test_copy_invalid(self):
1347
        #copy from invalid object
1348
        meta = {'test':'testcopy'}
1349
        self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1350
                                 'test.py', self.containers[1], 'testcopy', meta)
1351
        
1352
        #copy from invalid container
1353
        meta = {'test':'testcopy'}
1354
        self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1355
                                 self.obj['name'], self.containers[1],
1356
                                 'testcopy', meta)
1357

    
1358
class ObjectMove(BaseTestCase):
1359
    def setUp(self):
1360
        BaseTestCase.setUp(self)
1361
        self.account = 'test'
1362
        self.containers = ['c1', 'c2']
1363
        for c in self.containers:
1364
            self.client.create_container(c)
1365
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1366
    
1367
    def test_move(self):
1368
        #perform move
1369
        meta = {'test':'testcopy'}
1370
        src_path = '/'.join(('/', self.containers[0], self.obj['name']))
1371
        status = self.client.move_object(self.containers[0], self.obj['name'],
1372
                                         self.containers[0], 'testcopy',
1373
                                         meta)[0]
1374
        
1375
        #assert successful move
1376
        self.assertEqual(status, 201)
1377
        
1378
        #assert updated metadata
1379
        meta = self.client.retrieve_object_metadata(self.containers[0],
1380
                                                    'testcopy',
1381
                                                    restricted=True)
1382
        self.assertTrue('test' in meta.keys())
1383
        self.assertTrue(meta['test'], 'testcopy')
1384
        
1385
        #assert src object no more exists
1386
        self.assert_object_not_exists(self.containers[0], self.obj['name'])
1387

    
1388
class ObjectPost(BaseTestCase):
1389
    def setUp(self):
1390
        BaseTestCase.setUp(self)
1391
        self.account = 'test'
1392
        self.containers = ['c1', 'c2']
1393
        for c in self.containers:
1394
            self.client.create_container(c)
1395
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1396
    
1397
    def test_update_meta(self):
1398
        #perform update metadata
1399
        more = {'foo':'foo', 'bar':'bar'}
1400
        status = self.client.update_object_metadata(self.containers[0],
1401
                                                    self.obj['name'],
1402
                                                    **more)[0]
1403
        #assert request accepted
1404
        self.assertEqual(status, 202)
1405
        
1406
        #assert old metadata are still there
1407
        headers = self.client.retrieve_object_metadata(self.containers[0],
1408
                                                       self.obj['name'],
1409
                                                       restricted=True)
1410
        #assert new metadata have been updated
1411
        for k,v in more.items():
1412
            self.assertTrue(k in headers.keys())
1413
            self.assertTrue(headers[k], v)
1414
    
1415
    def test_update_object(self,
1416
                           first_byte_pos=0,
1417
                           last_byte_pos=499,
1418
                           instance_length = True,
1419
                           content_length = 500):
1420
        l = len(self.obj['data'])
1421
        length = l if instance_length else '*'
1422
        range = 'bytes %d-%d/%s' %(first_byte_pos,
1423
                                       last_byte_pos,
1424
                                       length)
1425
        partial = last_byte_pos - first_byte_pos + 1
1426
        data = get_random_data(partial)
1427
        args = {'content_type':'application/octet-stream',
1428
                'content_range':'%s' %range}
1429
        if content_length:
1430
            args['content_length'] = content_length
1431
        
1432
        status = self.client.update_object(self.containers[0], self.obj['name'],
1433
                                  StringIO(data), **args)[0]
1434
        
1435
        if partial < 0 or (instance_length and l <= last_byte_pos):
1436
            self.assertEqual(status, 202)    
1437
        else:
1438
            self.assertEqual(status, 204)           
1439
            #check modified object
1440
            content = self.client.retrieve_object(self.containers[0],
1441
                                              self.obj['name'])
1442
            self.assertEqual(content[0:partial], data)
1443
            self.assertEqual(content[partial:l], self.obj['data'][partial:l])
1444
    
1445
    def test_update_object_no_content_length(self):
1446
        self.test_update_object(content_length = None)
1447
    
1448
    def test_update_object_invalid_content_length(self):
1449
        with AssertContentInvariant(self.client.retrieve_object,
1450
                                    self.containers[0], self.obj['name']):
1451
            self.assert_raises_fault(400, self.test_update_object,
1452
                                     content_length = 1000)
1453
    
1454
    def test_update_object_invalid_range(self):
1455
        with AssertContentInvariant(self.client.retrieve_object,
1456
                                    self.containers[0], self.obj['name']):
1457
            self.assert_raises_fault(416, self.test_update_object, 499, 0, True)
1458
    
1459
    def test_update_object_invalid_range_and_length(self):
1460
        with AssertContentInvariant(self.client.retrieve_object,
1461
                                    self.containers[0], self.obj['name']):
1462
            self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1463
                                     -1)
1464
    
1465
    def test_update_object_invalid_range_with_no_content_length(self):
1466
        with AssertContentInvariant(self.client.retrieve_object,
1467
                                    self.containers[0], self.obj['name']):
1468
            self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1469
                                     content_length = None)
1470
    
1471
    def test_update_object_out_of_limits(self):    
1472
        with AssertContentInvariant(self.client.retrieve_object,
1473
                                    self.containers[0], self.obj['name']):
1474
            l = len(self.obj['data'])
1475
            self.assert_raises_fault(416, self.test_update_object, 0, l+1, True)
1476
    
1477
    def test_append(self):
1478
        data = get_random_data(500)
1479
        headers = {}
1480
        self.client.update_object(self.containers[0], self.obj['name'],
1481
                                  StringIO(data), content_length=500,
1482
                                  content_type='application/octet-stream')
1483
        
1484
        content = self.client.retrieve_object(self.containers[0],
1485
                                              self.obj['name'])
1486
        self.assertEqual(len(content), len(self.obj['data']) + 500)
1487
        self.assertEqual(content[:-500], self.obj['data'])
1488
    
1489
    def test_update_with_chunked_transfer(self):
1490
        data = get_random_data(500)
1491
        dl = len(data)
1492
        fl = len(self.obj['data'])
1493
        
1494
        self.client.update_object_using_chunks(self.containers[0],
1495
                                               self.obj['name'], StringIO(data),
1496
                                               offset=0,
1497
                                               content_type='application/octet-stream')
1498
        
1499
        #check modified object
1500
        content = self.client.retrieve_object(self.containers[0],
1501
                                              self.obj['name'])
1502
        self.assertEqual(content[0:dl], data)
1503
        self.assertEqual(content[dl:fl], self.obj['data'][dl:fl])
1504

    
1505
class ObjectDelete(BaseTestCase):
1506
    def setUp(self):
1507
        BaseTestCase.setUp(self)
1508
        self.account = 'test'
1509
        self.containers = ['c1', 'c2']
1510
        for c in self.containers:
1511
            self.client.create_container(c)
1512
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1513
    
1514
    def test_delete(self):
1515
        #perform delete object
1516
        self.client.delete_object(self.containers[0], self.obj['name'])[0]
1517
    
1518
    def test_delete_invalid(self):
1519
        #assert item not found
1520
        self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1521
                                 self.obj['name'])
1522

    
1523
class ListSharing(BaseTestCase):
1524
    def setUp(self):
1525
        BaseTestCase.setUp(self)
1526
        self.client.create_container('c')
1527
        for i in range(2):
1528
            self.upload_random_data('c', 'o%s' %i)
1529
        accounts = OTHER_ACCOUNTS.copy()
1530
        self.o1_sharing_with = accounts.popitem()
1531
        self.o1_sharing = [self.o1_sharing_with[1]]
1532
        self.client.share_object('c', 'o1', self.o1_sharing, read=True)
1533
        
1534
        l = []
1535
        for i in range(2):
1536
            l.append(accounts.popitem())
1537
        #self.client.set_account_groups({'pithos-dev':'chazapis,verigak,papagian'})
1538
        #self.o2_sharing = 'write=%s' % 
1539
        #self.client.share_object('c', 'o2', self.o2_sharing)
1540
    
1541
    def test_listing(self):
1542
        self.other = Pithos_Client(DEFAULT_HOST,
1543
                              self.o1_sharing_with[0],
1544
                              self.o1_sharing_with[1],
1545
                              DEFAULT_API)
1546
        self.assertTrue('test' in self.other.list_shared_by_others())
1547

    
1548
class TestGreek(BaseTestCase):
1549
    def setUp(self):
1550
        BaseTestCase.setUp(self)
1551
        #keep track of initial account groups
1552
        self.initial_groups = self.client.retrieve_account_groups()
1553
        
1554
        #keep track of initial account meta
1555
        self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
1556
    
1557
    def tearDown(self):
1558
        #delete additionally created meta
1559
        l = []
1560
        for m in self.client.retrieve_account_metadata(restricted=True):
1561
            if m not in self.initial_meta:
1562
                l.append(m)
1563
        self.client.delete_account_metadata(l)
1564
        
1565
        #delete additionally created groups
1566
        l = []
1567
        for g in self.client.retrieve_account_groups():
1568
            if g not in self.initial_groups:
1569
                l.append(g)
1570
        self.client.unset_account_groups(l)
1571
        
1572
        BaseTestCase.tearDown(self)
1573
    
1574
    def test_create_container(self):
1575
        self.client.create_container('φάκελος')
1576
        self.assert_container_exists('φάκελος')
1577
        
1578
        self.assertTrue('φάκελος' in self.client.list_containers())
1579
    
1580
    def test_create_object(self):
1581
        self.client.create_container('φάκελος')
1582
        self.upload_random_data('φάκελος', 'αντικείμενο')
1583
        
1584
        self.assert_object_exists('φάκελος', 'αντικείμενο')
1585
        self.assertTrue('αντικείμενο' in self.client.list_objects('φάκελος'))
1586
    
1587
    def test_copy_object(self):
1588
        src_container = 'φάκελος'
1589
        src_object = 'αντικείμενο'
1590
        dest_container = 'αντίγραφα'
1591
        dest_object = 'ασφαλές-αντίγραφο'
1592
        
1593
        self.client.create_container(src_container)
1594
        self.upload_random_data(src_container, src_object)
1595
        
1596
        self.client.create_container(dest_container)
1597
        self.client.copy_object(src_container, src_object, dest_container,
1598
                                dest_object)
1599
        
1600
        self.assert_object_exists(src_container, src_object)
1601
        self.assert_object_exists(dest_container, dest_object)
1602
        self.assertTrue(dest_object in self.client.list_objects(dest_container))
1603
    
1604
    def test_move_object(self):
1605
        src_container = 'φάκελος'
1606
        src_object = 'αντικείμενο'
1607
        dest_container = 'αντίγραφα'
1608
        dest_object = 'ασφαλές-αντίγραφο'
1609
        
1610
        self.client.create_container(src_container)
1611
        self.upload_random_data(src_container, src_object)
1612
        
1613
        self.client.create_container(dest_container)
1614
        self.client.move_object(src_container, src_object, dest_container,
1615
                                dest_object)
1616
        
1617
        self.assert_object_not_exists(src_container, src_object)
1618
        self.assert_object_exists(dest_container, dest_object)
1619
        self.assertTrue(dest_object in self.client.list_objects(dest_container))
1620
    
1621
    def test_delete_object(self):
1622
        self.client.create_container('φάκελος')
1623
        self.upload_random_data('φάκελος', 'αντικείμενο')
1624
        self.assert_object_exists('φάκελος', 'αντικείμενο')
1625
    
1626
        self.client.delete_object('φάκελος', 'αντικείμενο')
1627
        self.assert_object_not_exists('φάκελος', 'αντικείμενο')
1628
        self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
1629
    
1630
    def test_delete_container(self):
1631
        self.client.create_container('φάκελος')
1632
        self.assert_container_exists('φάκελος')
1633
        
1634
        self.client.delete_container('φάκελος')
1635
        self.assert_container_not_exists('φάκελος')
1636
        self.assertTrue('φάκελος' not in self.client.list_containers())
1637

    
1638
    def test_account_meta(self):
1639
        meta = {'ποιότητα':'ΑΑΑ'}
1640
        self.client.update_account_metadata(**meta)
1641
        meta = self.client.retrieve_account_metadata(restricted=True)
1642
        self.assertTrue('ποιότητα' in meta.keys())
1643
        self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1644
    
1645
    def test_container_meta(self):
1646
        meta = {'ποιότητα':'ΑΑΑ'}
1647
        self.client.create_container('φάκελος', **meta)
1648
        
1649
        meta = self.client.retrieve_container_metadata('φάκελος', restricted=True)
1650
        self.assertTrue('ποιότητα' in meta.keys())
1651
        self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1652
    
1653
    def test_object_meta(self):
1654
        self.client.create_container('φάκελος')
1655
        meta = {'ποιότητα':'ΑΑΑ'}
1656
        self.upload_random_data('φάκελος', 'αντικείμενο', **meta)
1657
        
1658
        meta = self.client.retrieve_object_metadata('φάκελος', 'αντικείμενο',
1659
                                                    restricted=True)
1660
        self.assertTrue('ποιότητα' in meta.keys())
1661
        self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')    
1662
    
1663
    def test_list_meta_filtering(self):
1664
        self.client.create_container('φάκελος')
1665
        meta = {'ποιότητα':'ΑΑΑ'}
1666
        self.upload_random_data('φάκελος', 'ο1', **meta)
1667
        self.upload_random_data('φάκελος', 'ο2')
1668
        self.upload_random_data('φάκελος', 'ο3')
1669
        
1670
        meta = {'ποσότητα':'μεγάλη'}
1671
        self.client.update_object_metadata('φάκελος', 'ο2', **meta)
1672
        objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
1673
        self.assertTrue('ο1' in objects)
1674
        self.assertTrue('ο2' in objects)
1675
        self.assertTrue('ο3' not in objects)
1676
    
1677
    def test_groups(self):
1678
        #create a group
1679
        groups = {'σεφς':'chazapis,διογένης'}
1680
        self.client.set_account_groups(**groups)
1681
        groups.update(self.initial_groups)
1682
        self.assertEqual(groups['σεφς'],
1683
                         self.client.retrieve_account_groups()['σεφς'])
1684
        
1685
        #check read access
1686
        self.client.create_container('φάκελος')
1687
        o = self.upload_random_data('φάκελος', 'ο1')
1688
        self.client.share_object('φάκελος', 'ο1', ['test:σεφς'])
1689
        chef = Pithos_Client(DEFAULT_HOST,
1690
                            '0009',
1691
                            'διογένης',
1692
                            DEFAULT_API)
1693
        self.assert_not_raises_fault(401, chef.retrieve_object_metadata,
1694
                                     'φάκελος', 'ο1', account=DEFAULT_USER)
1695
        
1696
        #check write access
1697
        self.client.share_object('φάκελος', 'ο1', ['διογένης'], read=False)
1698
        new_data = get_random_data()
1699
        self.assert_not_raises_fault(401, chef.update_object,
1700
                                     'φάκελος', 'ο1', StringIO(new_data),
1701
                                     account=DEFAULT_USER)
1702
        
1703
        server_data = self.client.retrieve_object('φάκελος', 'ο1')
1704
        self.assertEqual(server_data[:len(o['data'])], o['data'])
1705
        self.assertEqual(server_data[len(o['data']):], new_data)
1706
    
1707
    def test_manifestation(self):
1708
        self.client.create_container('κουβάς')
1709
        prefix = 'μέρη/'
1710
        data = ''
1711
        for i in range(5):
1712
            part = '%s%d' %(prefix, i)
1713
            o = self.upload_random_data('κουβάς', part)
1714
            data += o['data']
1715
        
1716
        self.client.create_container('φάκελος')
1717
        manifest = '%s/%s' %('κουβάς', prefix)
1718
        self.client.create_manifestation('φάκελος', 'άπαντα', manifest)
1719
        
1720
        self.assert_object_exists('φάκελος', 'άπαντα')
1721
        self.assertEqual(data, self.client.retrieve_object('φάκελος',
1722
                                                           'άπαντα'))
1723
        
1724
        #wrong manifestation
1725
        self.client.create_manifestation('φάκελος', 'άπαντα', 'κουβάς/άκυρο')
1726
        self.assertEqual('', self.client.retrieve_object('φάκελος', 'άπαντα'))
1727
    
1728
    def test_update_from_another_object(self):
1729
        pass
1730

    
1731
#class TestUnicode(BaseTestCase):
1732
#    def test_create_container(self):
1733
#        self.client.create_container(u'φάκελος')
1734
#        self.assert_container_exists(u'φάκελος')
1735
#        
1736
#        self.assertTrue(u'φάκελος' in self.client.list_containers())
1737

    
1738
class TestPermissions(BaseTestCase):
1739
    def setUp(self):
1740
        BaseTestCase.setUp(self)
1741
        #keep track of initial account groups
1742
        self.initial_groups = self.client.retrieve_account_groups()
1743
        #keep track of initial account meta
1744
        self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
1745
        
1746
        #create a group
1747
        self.authorized = ['chazapis', 'verigak', 'gtsouk', 'papagian']
1748
        groups = {'pithosdev':','.join(self.authorized)}
1749
        self.client.set_account_groups(**groups)
1750
    
1751
    def tearDown(self):
1752
        #delete additionally created meta
1753
        l = []
1754
        for m in self.client.retrieve_account_metadata(restricted=True):
1755
            if m not in self.initial_meta:
1756
                l.append(m)
1757
        self.client.delete_account_metadata(l)
1758
        
1759
        #delete additionally created groups
1760
        l = []
1761
        for g in self.client.retrieve_account_groups():
1762
            if g not in self.initial_groups:
1763
                l.append(g)
1764
        self.client.unset_account_groups(l)
1765
        
1766
        BaseTestCase.tearDown(self)
1767
    
1768
    def test_read_access(self):
1769
        self.client.create_container('c')
1770
        o = self.upload_random_data('c', 'o')
1771
        self.client.share_object('c', 'o', ['test:pithosdev'])
1772
        for token, account in OTHER_ACCOUNTS.items():
1773
            cl = Pithos_Client(DEFAULT_HOST, token, account, DEFAULT_API) 
1774
            if account in self.authorized:
1775
                self.assert_not_raises_fault(401, cl.retrieve_object_metadata,
1776
                                             'c', 'o', account=DEFAULT_USER)
1777
            else:
1778
                self.assert_raises_fault(401, cl.retrieve_object_metadata,
1779
                                         'c', 'o', account=DEFAULT_USER)
1780
        
1781
    
1782
    #def test_write_access(self):
1783
    #    self.client.share_object('φάκελος', 'ο1', ['διογένης'], read=False)
1784
    #    new_data = get_random_data()
1785
    #    chef.update_object('φάκελος', 'ο1', StringIO(new_data))
1786
    #    self.assert_not_raises_fault(401, chef.update_object,
1787
    #                                 'φάκελος', 'ο1', StringIO(new_data),
1788
    #                                 account=DEFAULT_USER)
1789
    #    
1790
    #    server_data = self.client.retrieve_object('φάκελος', 'ο1')
1791
    #    self.assertEqual(server_data[:len(o['data'])], o['data'])
1792
    #    self.assertEqual(server_data[len(o['data']):], new_data)
1793

    
1794
class AssertMappingInvariant(object):
1795
    def __init__(self, callable, *args, **kwargs):
1796
        self.callable = callable
1797
        self.args = args
1798
        self.kwargs = kwargs
1799
    
1800
    def __enter__(self):
1801
        self.map = self.callable(*self.args, **self.kwargs)
1802
        return self.map
1803
    
1804
    def __exit__(self, type, value, tb):
1805
        map = self.callable(*self.args, **self.kwargs)
1806
        for k in self.map.keys():
1807
            if is_date(self.map[k]):
1808
                continue
1809
            assert map[k] == self.map[k]
1810

    
1811
class AssertContentInvariant(object):
1812
    def __init__(self, callable, *args, **kwargs):
1813
        self.callable = callable
1814
        self.args = args
1815
        self.kwargs = kwargs
1816
    
1817
    def __enter__(self):
1818
        self.content = self.callable(*self.args, **self.kwargs)[2]
1819
        return self.content
1820
    
1821
    def __exit__(self, type, value, tb):
1822
        content = self.callable(*self.args, **self.kwargs)[2]
1823
        assert self.content == content
1824

    
1825
def get_content_splitted(response):
1826
    if response:
1827
        return response.content.split('\n')
1828

    
1829
def compute_md5_hash(data):
1830
    md5 = hashlib.md5()
1831
    offset = 0
1832
    md5.update(data)
1833
    return md5.hexdigest().lower()
1834

    
1835
def compute_block_hash(data, algorithm):
1836
    h = hashlib.new(algorithm)
1837
    h.update(data.rstrip('\x00'))
1838
    return h.hexdigest()
1839

    
1840
def get_random_data(length=500):
1841
    char_set = string.ascii_uppercase + string.digits
1842
    return ''.join(random.choice(char_set) for x in range(length))
1843

    
1844
def is_date(date):
1845
    MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
1846
    __D = r'(?P<day>\d{2})'
1847
    __D2 = r'(?P<day>[ \d]\d)'
1848
    __M = r'(?P<mon>\w{3})'
1849
    __Y = r'(?P<year>\d{4})'
1850
    __Y2 = r'(?P<year>\d{2})'
1851
    __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
1852
    RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
1853
    RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
1854
    ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
1855
    for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
1856
        m = regex.match(date)
1857
        if m is not None:
1858
            return True
1859
    return False
1860

    
1861
o_names = ['kate.jpg',
1862
           'kate_beckinsale.jpg',
1863
           'How To Win Friends And Influence People.pdf',
1864
           'moms_birthday.jpg',
1865
           'poodle_strut.mov',
1866
           'Disturbed - Down With The Sickness.mp3',
1867
           'army_of_darkness.avi',
1868
           'the_mad.avi',
1869
           'photos/animals/dogs/poodle.jpg',
1870
           'photos/animals/dogs/terrier.jpg',
1871
           'photos/animals/cats/persian.jpg',
1872
           'photos/animals/cats/siamese.jpg',
1873
           'photos/plants/fern.jpg',
1874
           'photos/plants/rose.jpg',
1875
           'photos/me.jpg']
1876

    
1877
if __name__ == "__main__":
1878
    unittest.main()