~bzr-pqm/bzr/bzr.dev

907.1.48 by John Arbash Meinel
Updated LocalTransport by passing it through the transport_test suite, and got it to pass.
1
# Copyright (C) 2004, 2005 by Canonical Ltd
2
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
18
import os
19
from cStringIO import StringIO
1442.1.44 by Robert Collins
Many transport related tweaks:
20
21
from bzrlib.errors import NoSuchFile, FileExists, TransportNotPossible
22
from bzrlib.selftest import TestCase, TestCaseInTempDir
1185.11.10 by John Arbash Meinel
Fixing up the test suite.
23
from bzrlib.selftest.HTTPTestUtil import TestCaseWithWebserver
1469 by Robert Collins
Change Transport.* to work with URL's.
24
from bzrlib.transport import memory, urlescape
1442.1.44 by Robert Collins
Many transport related tweaks:
25
1185.11.19 by John Arbash Meinel
Testing put and append, also testing agaist file-like objects as well as strings.
26
27
def _append(fn, txt):
28
    """Append the given text (file-like object) to the supplied filename."""
29
    f = open(fn, 'ab')
30
    f.write(txt)
31
    f.flush()
32
    f.close()
33
    del f
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
34
1469 by Robert Collins
Change Transport.* to work with URL's.
35
class TestTransport(TestCase):
36
    """Test the non transport-concrete class functionality."""
37
38
    def test_urlescape(self):
39
        self.assertEqual('%25', urlescape('%'))
40
41
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
42
class TestTransportMixIn(object):
43
    """Subclass this, and it will provide a series of tests for a Transport.
44
    It assumes that the Transport object is connected to the 
45
    current working directory.  So that whatever is done 
46
    through the transport, should show up in the working 
47
    directory, and vice-versa.
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
48
49
    This also tests to make sure that the functions work with both
50
    generators and lists (assuming iter(list) is effectively a generator)
51
    """
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
52
    readonly = False
53
    def get_transport(self):
54
        """Children should override this to return the Transport object.
55
        """
56
        raise NotImplementedError
57
58
    def test_has(self):
59
        t = self.get_transport()
60
1469 by Robert Collins
Change Transport.* to work with URL's.
61
        files = ['a', 'b', 'e', 'g', '%']
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
62
        self.build_tree(files)
63
        self.assertEqual(t.has('a'), True)
64
        self.assertEqual(t.has('c'), False)
1469 by Robert Collins
Change Transport.* to work with URL's.
65
        self.assertEqual(t.has(urlescape('%')), True)
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
66
        self.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])),
67
                [True, True, False, False, True, False, True, False])
1185.16.155 by John Arbash Meinel
Added a has_any function to the Transport API
68
        self.assertEqual(t.has_any(['a', 'b', 'c']), True)
69
        self.assertEqual(t.has_any(['c', 'd', 'f', urlescape('%%')]), False)
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
70
        self.assertEqual(list(t.has_multi(iter(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']))),
71
                [True, True, False, False, True, False, True, False])
1185.16.155 by John Arbash Meinel
Added a has_any function to the Transport API
72
        self.assertEqual(t.has_any(['c', 'c', 'c']), False)
73
        self.assertEqual(t.has_any(['b', 'b', 'b']), True)
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
74
75
    def test_get(self):
76
        t = self.get_transport()
77
78
        files = ['a', 'b', 'e', 'g']
79
        self.build_tree(files)
80
        self.assertEqual(t.get('a').read(), open('a').read())
81
        content_f = t.get_multi(files)
82
        for path,f in zip(files, content_f):
83
            self.assertEqual(open(path).read(), f.read())
84
85
        content_f = t.get_multi(iter(files))
86
        for path,f in zip(files, content_f):
87
            self.assertEqual(open(path).read(), f.read())
88
89
        self.assertRaises(NoSuchFile, t.get, 'c')
90
        try:
91
            files = list(t.get_multi(['a', 'b', 'c']))
92
        except NoSuchFile:
93
            pass
94
        else:
95
            self.fail('Failed to raise NoSuchFile for missing file in get_multi')
96
        try:
97
            files = list(t.get_multi(iter(['a', 'b', 'c', 'e'])))
98
        except NoSuchFile:
99
            pass
100
        else:
101
            self.fail('Failed to raise NoSuchFile for missing file in get_multi')
102
103
    def test_put(self):
104
        t = self.get_transport()
105
106
        if self.readonly:
107
            self.assertRaises(TransportNotPossible,
108
                    t.put, 'a', 'some text for a\n')
109
            open('a', 'wb').write('some text for a\n')
110
        else:
111
            t.put('a', 'some text for a\n')
112
        self.assert_(os.path.exists('a'))
113
        self.check_file_contents('a', 'some text for a\n')
114
        self.assertEqual(t.get('a').read(), 'some text for a\n')
115
        # Make sure 'has' is updated
116
        self.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd', 'e'])),
117
                [True, False, False, False, False])
118
        if self.readonly:
119
            self.assertRaises(TransportNotPossible,
120
                    t.put_multi,
121
                    [('a', 'new\ncontents for\na\n'),
122
                        ('d', 'contents\nfor d\n')])
123
            open('a', 'wb').write('new\ncontents for\na\n')
124
            open('d', 'wb').write('contents\nfor d\n')
125
        else:
126
            # Put also replaces contents
127
            self.assertEqual(t.put_multi([('a', 'new\ncontents for\na\n'),
128
                                          ('d', 'contents\nfor d\n')]),
129
                             2)
130
        self.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd', 'e'])),
131
                [True, False, False, True, False])
132
        self.check_file_contents('a', 'new\ncontents for\na\n')
133
        self.check_file_contents('d', 'contents\nfor d\n')
134
135
        if self.readonly:
136
            self.assertRaises(TransportNotPossible,
137
                t.put_multi, iter([('a', 'diff\ncontents for\na\n'),
138
                                  ('d', 'another contents\nfor d\n')]))
139
            open('a', 'wb').write('diff\ncontents for\na\n')
140
            open('d', 'wb').write('another contents\nfor d\n')
141
        else:
142
            self.assertEqual(
143
                t.put_multi(iter([('a', 'diff\ncontents for\na\n'),
144
                                  ('d', 'another contents\nfor d\n')]))
145
                             , 2)
146
        self.check_file_contents('a', 'diff\ncontents for\na\n')
147
        self.check_file_contents('d', 'another contents\nfor d\n')
148
149
        if self.readonly:
150
            self.assertRaises(TransportNotPossible,
151
                    t.put, 'path/doesnt/exist/c', 'contents')
152
        else:
153
            self.assertRaises(NoSuchFile,
154
                    t.put, 'path/doesnt/exist/c', 'contents')
155
156
    def test_put_file(self):
157
        t = self.get_transport()
158
159
        # Test that StringIO can be used as a file-like object with put
160
        f1 = StringIO('this is a string\nand some more stuff\n')
161
        if self.readonly:
162
            open('f1', 'wb').write(f1.read())
163
        else:
164
            t.put('f1', f1)
165
166
        del f1
167
168
        self.check_file_contents('f1', 
169
                'this is a string\nand some more stuff\n')
170
171
        f2 = StringIO('here is some text\nand a bit more\n')
172
        f3 = StringIO('some text for the\nthird file created\n')
173
174
        if self.readonly:
175
            open('f2', 'wb').write(f2.read())
176
            open('f3', 'wb').write(f3.read())
177
        else:
178
            t.put_multi([('f2', f2), ('f3', f3)])
179
180
        del f2, f3
181
182
        self.check_file_contents('f2', 'here is some text\nand a bit more\n')
183
        self.check_file_contents('f3', 'some text for the\nthird file created\n')
184
185
        # Test that an actual file object can be used with put
186
        f4 = open('f1', 'rb')
187
        if self.readonly:
188
            open('f4', 'wb').write(f4.read())
189
        else:
190
            t.put('f4', f4)
191
192
        del f4
193
194
        self.check_file_contents('f4', 
195
                'this is a string\nand some more stuff\n')
196
197
        f5 = open('f2', 'rb')
198
        f6 = open('f3', 'rb')
199
        if self.readonly:
200
            open('f5', 'wb').write(f5.read())
201
            open('f6', 'wb').write(f6.read())
202
        else:
203
            t.put_multi([('f5', f5), ('f6', f6)])
204
205
        del f5, f6
206
207
        self.check_file_contents('f5', 'here is some text\nand a bit more\n')
208
        self.check_file_contents('f6', 'some text for the\nthird file created\n')
209
210
211
212
    def test_mkdir(self):
213
        t = self.get_transport()
214
215
        # Test mkdir
216
        os.mkdir('dir_a')
217
        self.assertEqual(t.has('dir_a'), True)
218
        self.assertEqual(t.has('dir_b'), False)
219
220
        if self.readonly:
221
            self.assertRaises(TransportNotPossible,
222
                    t.mkdir, 'dir_b')
223
            os.mkdir('dir_b')
224
        else:
225
            t.mkdir('dir_b')
226
        self.assertEqual(t.has('dir_b'), True)
227
        self.assert_(os.path.isdir('dir_b'))
228
229
        if self.readonly:
230
            self.assertRaises(TransportNotPossible,
231
                    t.mkdir_multi, ['dir_c', 'dir_d'])
232
            os.mkdir('dir_c')
233
            os.mkdir('dir_d')
234
        else:
235
            t.mkdir_multi(['dir_c', 'dir_d'])
236
237
        if self.readonly:
238
            self.assertRaises(TransportNotPossible,
239
                    t.mkdir_multi, iter(['dir_e', 'dir_f']))
240
            os.mkdir('dir_e')
241
            os.mkdir('dir_f')
242
        else:
243
            t.mkdir_multi(iter(['dir_e', 'dir_f']))
244
        self.assertEqual(list(t.has_multi(
245
            ['dir_a', 'dir_b', 'dir_c', 'dir_q',
246
             'dir_d', 'dir_e', 'dir_f', 'dir_b'])),
247
            [True, True, True, False,
248
             True, True, True, True])
249
        for d in ['dir_a', 'dir_b', 'dir_c', 'dir_d', 'dir_e', 'dir_f']:
250
            self.assert_(os.path.isdir(d))
251
252
        if not self.readonly:
253
            self.assertRaises(NoSuchFile, t.mkdir, 'path/doesnt/exist')
254
            self.assertRaises(FileExists, t.mkdir, 'dir_a') # Creating a directory again should fail
255
256
        # Make sure the transport recognizes when a
257
        # directory is created by other means
258
        # Caching Transports will fail, because dir_e was already seen not
259
        # to exist. So instead, we will search for a new directory
260
        #os.mkdir('dir_e')
261
        #if not self.readonly:
262
        #    self.assertRaises(FileExists, t.mkdir, 'dir_e')
263
264
        os.mkdir('dir_g')
265
        if not self.readonly:
266
            self.assertRaises(FileExists, t.mkdir, 'dir_g')
267
268
        # Test get/put in sub-directories
269
        if self.readonly:
270
            open('dir_a/a', 'wb').write('contents of dir_a/a')
271
            open('dir_b/b', 'wb').write('contents of dir_b/b')
272
        else:
273
            self.assertEqual(
274
                t.put_multi([('dir_a/a', 'contents of dir_a/a'),
275
                             ('dir_b/b', 'contents of dir_b/b')])
276
                          , 2)
277
        for f in ('dir_a/a', 'dir_b/b'):
278
            self.assertEqual(t.get(f).read(), open(f).read())
279
280
    def test_copy_to(self):
281
        import tempfile
282
        from bzrlib.transport.local import LocalTransport
283
284
        t = self.get_transport()
285
286
        files = ['a', 'b', 'c', 'd']
287
        self.build_tree(files)
288
289
        dtmp = tempfile.mkdtemp(dir='.', prefix='test-transport-')
290
        dtmp_base = os.path.basename(dtmp)
291
        local_t = LocalTransport(dtmp)
292
293
        t.copy_to(files, local_t)
294
        for f in files:
295
            self.assertEquals(open(f).read(),
296
                    open(os.path.join(dtmp_base, f)).read())
297
298
        del dtmp, dtmp_base, local_t
299
300
        dtmp = tempfile.mkdtemp(dir='.', prefix='test-transport-')
301
        dtmp_base = os.path.basename(dtmp)
302
        local_t = LocalTransport(dtmp)
303
304
        files = ['a', 'b', 'c', 'd']
305
        t.copy_to(iter(files), local_t)
306
        for f in files:
307
            self.assertEquals(open(f).read(),
308
                    open(os.path.join(dtmp_base, f)).read())
309
310
        del dtmp, dtmp_base, local_t
311
312
    def test_append(self):
313
        t = self.get_transport()
314
315
        if self.readonly:
316
            open('a', 'wb').write('diff\ncontents for\na\n')
317
            open('b', 'wb').write('contents\nfor b\n')
318
        else:
319
            t.put_multi([
320
                    ('a', 'diff\ncontents for\na\n'),
321
                    ('b', 'contents\nfor b\n')
322
                    ])
323
324
        if self.readonly:
325
            self.assertRaises(TransportNotPossible,
326
                    t.append, 'a', 'add\nsome\nmore\ncontents\n')
327
            _append('a', 'add\nsome\nmore\ncontents\n')
328
        else:
329
            t.append('a', 'add\nsome\nmore\ncontents\n')
330
331
        self.check_file_contents('a', 
332
            'diff\ncontents for\na\nadd\nsome\nmore\ncontents\n')
333
334
        if self.readonly:
335
            self.assertRaises(TransportNotPossible,
336
                    t.append_multi,
337
                        [('a', 'and\nthen\nsome\nmore\n'),
338
                         ('b', 'some\nmore\nfor\nb\n')])
339
            _append('a', 'and\nthen\nsome\nmore\n')
340
            _append('b', 'some\nmore\nfor\nb\n')
341
        else:
342
            t.append_multi([('a', 'and\nthen\nsome\nmore\n'),
343
                    ('b', 'some\nmore\nfor\nb\n')])
344
        self.check_file_contents('a', 
345
            'diff\ncontents for\na\n'
346
            'add\nsome\nmore\ncontents\n'
347
            'and\nthen\nsome\nmore\n')
348
        self.check_file_contents('b', 
349
                'contents\nfor b\n'
350
                'some\nmore\nfor\nb\n')
351
352
        if self.readonly:
353
            _append('a', 'a little bit more\n')
354
            _append('b', 'from an iterator\n')
355
        else:
356
            t.append_multi(iter([('a', 'a little bit more\n'),
357
                    ('b', 'from an iterator\n')]))
358
        self.check_file_contents('a', 
359
            'diff\ncontents for\na\n'
360
            'add\nsome\nmore\ncontents\n'
361
            'and\nthen\nsome\nmore\n'
362
            'a little bit more\n')
363
        self.check_file_contents('b', 
364
                'contents\nfor b\n'
365
                'some\nmore\nfor\nb\n'
366
                'from an iterator\n')
367
368
    def test_append_file(self):
369
        t = self.get_transport()
370
371
        contents = [
372
            ('f1', 'this is a string\nand some more stuff\n'),
373
            ('f2', 'here is some text\nand a bit more\n'),
374
            ('f3', 'some text for the\nthird file created\n'),
375
            ('f4', 'this is a string\nand some more stuff\n'),
376
            ('f5', 'here is some text\nand a bit more\n'),
377
            ('f6', 'some text for the\nthird file created\n')
378
        ]
379
        
380
        if self.readonly:
381
            for f, val in contents:
382
                open(f, 'wb').write(val)
383
        else:
384
            t.put_multi(contents)
385
386
        a1 = StringIO('appending to\none\n')
387
        if self.readonly:
388
            _append('f1', a1.read())
389
        else:
390
            t.append('f1', a1)
391
392
        del a1
393
394
        self.check_file_contents('f1', 
395
                'this is a string\nand some more stuff\n'
396
                'appending to\none\n')
397
398
        a2 = StringIO('adding more\ntext to two\n')
399
        a3 = StringIO('some garbage\nto put in three\n')
400
401
        if self.readonly:
402
            _append('f2', a2.read())
403
            _append('f3', a3.read())
404
        else:
405
            t.append_multi([('f2', a2), ('f3', a3)])
406
407
        del a2, a3
408
409
        self.check_file_contents('f2',
410
                'here is some text\nand a bit more\n'
411
                'adding more\ntext to two\n')
412
        self.check_file_contents('f3', 
413
                'some text for the\nthird file created\n'
414
                'some garbage\nto put in three\n')
415
416
        # Test that an actual file object can be used with put
417
        a4 = open('f1', 'rb')
418
        if self.readonly:
419
            _append('f4', a4.read())
420
        else:
421
            t.append('f4', a4)
422
423
        del a4
424
425
        self.check_file_contents('f4', 
426
                'this is a string\nand some more stuff\n'
427
                'this is a string\nand some more stuff\n'
428
                'appending to\none\n')
429
430
        a5 = open('f2', 'rb')
431
        a6 = open('f3', 'rb')
432
        if self.readonly:
433
            _append('f5', a5.read())
434
            _append('f6', a6.read())
435
        else:
436
            t.append_multi([('f5', a5), ('f6', a6)])
437
438
        del a5, a6
439
440
        self.check_file_contents('f5',
441
                'here is some text\nand a bit more\n'
442
                'here is some text\nand a bit more\n'
443
                'adding more\ntext to two\n')
444
        self.check_file_contents('f6',
445
                'some text for the\nthird file created\n'
446
                'some text for the\nthird file created\n'
447
                'some garbage\nto put in three\n')
448
449
    def test_delete(self):
450
        # TODO: Test Transport.delete
451
        pass
452
453
    def test_move(self):
454
        # TODO: Test Transport.move
455
        pass
456
1442.1.44 by Robert Collins
Many transport related tweaks:
457
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
458
class LocalTransportTest(TestCaseInTempDir, TestTransportMixIn):
459
    def get_transport(self):
460
        from bzrlib.transport.local import LocalTransport
461
        return LocalTransport('.')
462
1442.1.44 by Robert Collins
Many transport related tweaks:
463
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
464
class HttpTransportTest(TestCaseWithWebserver, TestTransportMixIn):
1442.1.44 by Robert Collins
Many transport related tweaks:
465
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
466
    readonly = True
1442.1.44 by Robert Collins
Many transport related tweaks:
467
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
468
    def get_transport(self):
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
469
        from bzrlib.transport.http import HttpTransport
1185.11.15 by John Arbash Meinel
Got HttpTransport tests to pass. Check for EAGAIN, pass permit_failure around, etc
470
        url = self.get_remote_url('.')
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
471
        return HttpTransport(url)
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
472
1442.1.44 by Robert Collins
Many transport related tweaks:
473
474
class TestMemoryTransport(TestCase):
475
476
    def test_get_transport(self):
477
        memory.MemoryTransport()
478
479
    def test_clone(self):
480
        transport = memory.MemoryTransport()
481
        self.failUnless(transport.clone() is transport)
482
483
    def test_abspath(self):
484
        transport = memory.MemoryTransport()
485
        self.assertEqual("in-memory:relpath", transport.abspath('relpath'))
486
487
    def test_relpath(self):
488
        transport = memory.MemoryTransport()
489
490
    def test_append_and_get(self):
491
        transport = memory.MemoryTransport()
492
        transport.append('path', StringIO('content'))
493
        self.assertEqual(transport.get('path').read(), 'content')
494
        transport.append('path', StringIO('content'))
495
        self.assertEqual(transport.get('path').read(), 'contentcontent')
496
497
    def test_put_and_get(self):
498
        transport = memory.MemoryTransport()
499
        transport.put('path', StringIO('content'))
500
        self.assertEqual(transport.get('path').read(), 'content')
501
        transport.put('path', StringIO('content'))
502
        self.assertEqual(transport.get('path').read(), 'content')
503
504
    def test_append_without_dir_fails(self):
505
        transport = memory.MemoryTransport()
506
        self.assertRaises(NoSuchFile,
507
                          transport.append, 'dir/path', StringIO('content'))
508
509
    def test_put_without_dir_fails(self):
510
        transport = memory.MemoryTransport()
511
        self.assertRaises(NoSuchFile,
512
                          transport.put, 'dir/path', StringIO('content'))
513
514
    def test_get_missing(self):
515
        transport = memory.MemoryTransport()
516
        self.assertRaises(NoSuchFile, transport.get, 'foo')
517
518
    def test_has_missing(self):
519
        transport = memory.MemoryTransport()
520
        self.assertEquals(False, transport.has('foo'))
521
522
    def test_has_present(self):
523
        transport = memory.MemoryTransport()
524
        transport.append('foo', StringIO('content'))
525
        self.assertEquals(True, transport.has('foo'))
526
527
    def test_mkdir(self):
528
        transport = memory.MemoryTransport()
529
        transport.mkdir('dir')
530
        transport.append('dir/path', StringIO('content'))
531
        self.assertEqual(transport.get('dir/path').read(), 'content')
532
533
    def test_mkdir_missing_parent(self):
534
        transport = memory.MemoryTransport()
535
        self.assertRaises(NoSuchFile,
536
                          transport.mkdir, 'dir/dir')
537
538
    def test_mkdir_twice(self):
539
        transport = memory.MemoryTransport()
540
        transport.mkdir('dir')
541
        self.assertRaises(FileExists, transport.mkdir, 'dir')
542
543
    def test_parameters(self):
544
        transport = memory.MemoryTransport()
545
        self.assertEqual(True, transport.listable())
546
        self.assertEqual(False, transport.should_cache())
547
548
    def test_iter_files_recursive(self):
549
        transport = memory.MemoryTransport()
550
        transport.mkdir('dir')
551
        transport.put('dir/foo', StringIO('content'))
552
        transport.put('dir/bar', StringIO('content'))
553
        transport.put('bar', StringIO('content'))
554
        paths = set(transport.iter_files_recursive())
555
        self.assertEqual(set(['dir/foo', 'dir/bar', 'bar']), paths)
556
557
    def test_stat(self):
558
        transport = memory.MemoryTransport()
559
        transport.put('foo', StringIO('content'))
560
        transport.put('bar', StringIO('phowar'))
561
        self.assertEqual(7, transport.stat('foo').st_size)
562
        self.assertEqual(6, transport.stat('bar').st_size)
563