~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
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
21
from bzrlib.errors import (NoSuchFile, FileExists,
22
                           TransportNotPossible, ConnectionError)
1185.31.25 by John Arbash Meinel
Renamed all of the tests from selftest/foo.py to tests/test_foo.py
23
from bzrlib.tests import TestCase, TestCaseInTempDir
24
from bzrlib.tests.HTTPTestUtil import TestCaseWithWebserver
1469 by Robert Collins
Change Transport.* to work with URL's.
25
from bzrlib.transport import memory, urlescape
1185.31.33 by John Arbash Meinel
A couple more path.join statements needed changing.
26
from bzrlib.osutils import pathjoin
1442.1.44 by Robert Collins
Many transport related tweaks:
27
1185.11.19 by John Arbash Meinel
Testing put and append, also testing agaist file-like objects as well as strings.
28
29
def _append(fn, txt):
30
    """Append the given text (file-like object) to the supplied filename."""
31
    f = open(fn, 'ab')
32
    f.write(txt)
33
    f.flush()
34
    f.close()
35
    del f
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
36
1469 by Robert Collins
Change Transport.* to work with URL's.
37
class TestTransport(TestCase):
38
    """Test the non transport-concrete class functionality."""
39
40
    def test_urlescape(self):
41
        self.assertEqual('%25', urlescape('%'))
42
43
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
44
class TestTransportMixIn(object):
45
    """Subclass this, and it will provide a series of tests for a Transport.
46
    It assumes that the Transport object is connected to the 
47
    current working directory.  So that whatever is done 
48
    through the transport, should show up in the working 
49
    directory, and vice-versa.
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
50
51
    This also tests to make sure that the functions work with both
52
    generators and lists (assuming iter(list) is effectively a generator)
53
    """
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
54
    readonly = False
55
    def get_transport(self):
56
        """Children should override this to return the Transport object.
57
        """
58
        raise NotImplementedError
59
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
60
    def assertListRaises(self, excClass, func, *args, **kwargs):
61
        """Many transport functions can return generators this makes sure
62
        to wrap them in a list() call to make sure the whole generator
63
        is run, and that the proper exception is raised.
64
        """
65
        try:
66
            list(func(*args, **kwargs))
67
        except excClass:
68
            return
69
        else:
70
            if hasattr(excClass,'__name__'): excName = excClass.__name__
71
            else: excName = str(excClass)
72
            raise self.failureException, "%s not raised" % excName
73
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
74
    def test_has(self):
75
        t = self.get_transport()
76
1469 by Robert Collins
Change Transport.* to work with URL's.
77
        files = ['a', 'b', 'e', 'g', '%']
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
78
        self.build_tree(files)
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
79
        self.assertEqual(True, t.has('a'))
80
        self.assertEqual(False, t.has('c'))
81
        self.assertEqual(True, t.has(urlescape('%')))
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
82
        self.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])),
83
                [True, True, False, False, True, False, True, False])
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
84
        self.assertEqual(True, t.has_any(['a', 'b', 'c']))
85
        self.assertEqual(False, t.has_any(['c', 'd', 'f', urlescape('%%')]))
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
86
        self.assertEqual(list(t.has_multi(iter(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']))),
87
                [True, True, False, False, True, False, True, False])
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
88
        self.assertEqual(False, t.has_any(['c', 'c', 'c']))
89
        self.assertEqual(True, t.has_any(['b', 'b', 'b']))
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
90
91
    def test_get(self):
92
        t = self.get_transport()
93
94
        files = ['a', 'b', 'e', 'g']
95
        self.build_tree(files)
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
96
        self.assertEqual(open('a', 'rb').read(), t.get('a').read())
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
97
        content_f = t.get_multi(files)
98
        for path,f in zip(files, content_f):
1185.31.43 by John Arbash Meinel
Reintroduced ensure_config_dir_exists() for sftp
99
            self.assertEqual(f.read(), open(path, 'rb').read())
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
100
101
        content_f = t.get_multi(iter(files))
102
        for path,f in zip(files, content_f):
1185.31.43 by John Arbash Meinel
Reintroduced ensure_config_dir_exists() for sftp
103
            self.assertEqual(f.read(), open(path, 'rb').read())
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
104
105
        self.assertRaises(NoSuchFile, t.get, 'c')
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
106
        self.assertListRaises(NoSuchFile, t.get_multi, ['a', 'b', 'c'])
107
        self.assertListRaises(NoSuchFile, t.get_multi, iter(['a', 'b', 'c']))
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
108
109
    def test_put(self):
110
        t = self.get_transport()
111
112
        if self.readonly:
113
            self.assertRaises(TransportNotPossible,
114
                    t.put, 'a', 'some text for a\n')
115
            open('a', 'wb').write('some text for a\n')
116
        else:
117
            t.put('a', 'some text for a\n')
118
        self.assert_(os.path.exists('a'))
119
        self.check_file_contents('a', 'some text for a\n')
120
        self.assertEqual(t.get('a').read(), 'some text for a\n')
121
        # Make sure 'has' is updated
122
        self.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd', 'e'])),
123
                [True, False, False, False, False])
124
        if self.readonly:
125
            self.assertRaises(TransportNotPossible,
126
                    t.put_multi,
127
                    [('a', 'new\ncontents for\na\n'),
128
                        ('d', 'contents\nfor d\n')])
129
            open('a', 'wb').write('new\ncontents for\na\n')
130
            open('d', 'wb').write('contents\nfor d\n')
131
        else:
132
            # Put also replaces contents
133
            self.assertEqual(t.put_multi([('a', 'new\ncontents for\na\n'),
134
                                          ('d', 'contents\nfor d\n')]),
135
                             2)
136
        self.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd', 'e'])),
137
                [True, False, False, True, False])
138
        self.check_file_contents('a', 'new\ncontents for\na\n')
139
        self.check_file_contents('d', 'contents\nfor d\n')
140
141
        if self.readonly:
142
            self.assertRaises(TransportNotPossible,
143
                t.put_multi, iter([('a', 'diff\ncontents for\na\n'),
144
                                  ('d', 'another contents\nfor d\n')]))
145
            open('a', 'wb').write('diff\ncontents for\na\n')
146
            open('d', 'wb').write('another contents\nfor d\n')
147
        else:
148
            self.assertEqual(
149
                t.put_multi(iter([('a', 'diff\ncontents for\na\n'),
150
                                  ('d', 'another contents\nfor d\n')]))
151
                             , 2)
152
        self.check_file_contents('a', 'diff\ncontents for\na\n')
153
        self.check_file_contents('d', 'another contents\nfor d\n')
154
155
        if self.readonly:
156
            self.assertRaises(TransportNotPossible,
157
                    t.put, 'path/doesnt/exist/c', 'contents')
158
        else:
159
            self.assertRaises(NoSuchFile,
160
                    t.put, 'path/doesnt/exist/c', 'contents')
161
162
    def test_put_file(self):
163
        t = self.get_transport()
164
165
        # Test that StringIO can be used as a file-like object with put
166
        f1 = StringIO('this is a string\nand some more stuff\n')
167
        if self.readonly:
168
            open('f1', 'wb').write(f1.read())
169
        else:
170
            t.put('f1', f1)
171
172
        del f1
173
174
        self.check_file_contents('f1', 
175
                'this is a string\nand some more stuff\n')
176
177
        f2 = StringIO('here is some text\nand a bit more\n')
178
        f3 = StringIO('some text for the\nthird file created\n')
179
180
        if self.readonly:
181
            open('f2', 'wb').write(f2.read())
182
            open('f3', 'wb').write(f3.read())
183
        else:
184
            t.put_multi([('f2', f2), ('f3', f3)])
185
186
        del f2, f3
187
188
        self.check_file_contents('f2', 'here is some text\nand a bit more\n')
189
        self.check_file_contents('f3', 'some text for the\nthird file created\n')
190
191
        # Test that an actual file object can be used with put
192
        f4 = open('f1', 'rb')
193
        if self.readonly:
194
            open('f4', 'wb').write(f4.read())
195
        else:
196
            t.put('f4', f4)
197
198
        del f4
199
200
        self.check_file_contents('f4', 
201
                'this is a string\nand some more stuff\n')
202
203
        f5 = open('f2', 'rb')
204
        f6 = open('f3', 'rb')
205
        if self.readonly:
206
            open('f5', 'wb').write(f5.read())
207
            open('f6', 'wb').write(f6.read())
208
        else:
209
            t.put_multi([('f5', f5), ('f6', f6)])
210
211
        del f5, f6
212
213
        self.check_file_contents('f5', 'here is some text\nand a bit more\n')
214
        self.check_file_contents('f6', 'some text for the\nthird file created\n')
215
216
    def test_mkdir(self):
217
        t = self.get_transport()
218
219
        # Test mkdir
220
        os.mkdir('dir_a')
221
        self.assertEqual(t.has('dir_a'), True)
222
        self.assertEqual(t.has('dir_b'), False)
223
224
        if self.readonly:
225
            self.assertRaises(TransportNotPossible,
226
                    t.mkdir, 'dir_b')
227
            os.mkdir('dir_b')
228
        else:
229
            t.mkdir('dir_b')
230
        self.assertEqual(t.has('dir_b'), True)
231
        self.assert_(os.path.isdir('dir_b'))
232
233
        if self.readonly:
234
            self.assertRaises(TransportNotPossible,
235
                    t.mkdir_multi, ['dir_c', 'dir_d'])
236
            os.mkdir('dir_c')
237
            os.mkdir('dir_d')
238
        else:
239
            t.mkdir_multi(['dir_c', 'dir_d'])
240
241
        if self.readonly:
242
            self.assertRaises(TransportNotPossible,
243
                    t.mkdir_multi, iter(['dir_e', 'dir_f']))
244
            os.mkdir('dir_e')
245
            os.mkdir('dir_f')
246
        else:
247
            t.mkdir_multi(iter(['dir_e', 'dir_f']))
248
        self.assertEqual(list(t.has_multi(
249
            ['dir_a', 'dir_b', 'dir_c', 'dir_q',
250
             'dir_d', 'dir_e', 'dir_f', 'dir_b'])),
251
            [True, True, True, False,
252
             True, True, True, True])
253
        for d in ['dir_a', 'dir_b', 'dir_c', 'dir_d', 'dir_e', 'dir_f']:
254
            self.assert_(os.path.isdir(d))
255
256
        if not self.readonly:
257
            self.assertRaises(NoSuchFile, t.mkdir, 'path/doesnt/exist')
258
            self.assertRaises(FileExists, t.mkdir, 'dir_a') # Creating a directory again should fail
259
260
        # Make sure the transport recognizes when a
261
        # directory is created by other means
262
        # Caching Transports will fail, because dir_e was already seen not
263
        # to exist. So instead, we will search for a new directory
264
        #os.mkdir('dir_e')
265
        #if not self.readonly:
266
        #    self.assertRaises(FileExists, t.mkdir, 'dir_e')
267
268
        os.mkdir('dir_g')
269
        if not self.readonly:
270
            self.assertRaises(FileExists, t.mkdir, 'dir_g')
271
272
        # Test get/put in sub-directories
273
        if self.readonly:
274
            open('dir_a/a', 'wb').write('contents of dir_a/a')
275
            open('dir_b/b', 'wb').write('contents of dir_b/b')
276
        else:
277
            self.assertEqual(
278
                t.put_multi([('dir_a/a', 'contents of dir_a/a'),
279
                             ('dir_b/b', 'contents of dir_b/b')])
280
                          , 2)
281
        for f in ('dir_a/a', 'dir_b/b'):
1185.31.43 by John Arbash Meinel
Reintroduced ensure_config_dir_exists() for sftp
282
            self.assertEqual(t.get(f).read(), open(f, 'rb').read())
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
283
284
    def test_copy_to(self):
285
        import tempfile
286
        from bzrlib.transport.local import LocalTransport
287
288
        t = self.get_transport()
289
290
        files = ['a', 'b', 'c', 'd']
291
        self.build_tree(files)
292
1185.33.66 by Martin Pool
[patch] use unicode literals for all hardcoded paths (Alexander Belchenko)
293
        dtmp = tempfile.mkdtemp(dir=u'.', prefix='test-transport-')
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
294
        dtmp_base = os.path.basename(dtmp)
295
        local_t = LocalTransport(dtmp)
296
297
        t.copy_to(files, local_t)
298
        for f in files:
1185.31.43 by John Arbash Meinel
Reintroduced ensure_config_dir_exists() for sftp
299
            self.assertEquals(open(f, 'rb').read(),
300
                    open(pathjoin(dtmp_base, f), 'rb').read())
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
301
1185.16.158 by John Arbash Meinel
Added a test that copy_to raises NoSuchFile when a directory is missing (not IOError)
302
        # Test that copying into a missing directory raises
303
        # NoSuchFile
304
        os.mkdir('e')
305
        open('e/f', 'wb').write('contents of e')
306
        self.assertRaises(NoSuchFile, t.copy_to, ['e/f'], local_t)
307
1185.31.33 by John Arbash Meinel
A couple more path.join statements needed changing.
308
        os.mkdir(pathjoin(dtmp_base, 'e'))
1185.16.158 by John Arbash Meinel
Added a test that copy_to raises NoSuchFile when a directory is missing (not IOError)
309
        t.copy_to(['e/f'], local_t)
310
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
311
        del dtmp, dtmp_base, local_t
312
1185.33.66 by Martin Pool
[patch] use unicode literals for all hardcoded paths (Alexander Belchenko)
313
        dtmp = tempfile.mkdtemp(dir=u'.', prefix='test-transport-')
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
314
        dtmp_base = os.path.basename(dtmp)
315
        local_t = LocalTransport(dtmp)
316
317
        files = ['a', 'b', 'c', 'd']
318
        t.copy_to(iter(files), local_t)
319
        for f in files:
1185.31.43 by John Arbash Meinel
Reintroduced ensure_config_dir_exists() for sftp
320
            self.assertEquals(open(f, 'rb').read(),
321
                    open(pathjoin(dtmp_base, f), 'rb').read())
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
322
323
        del dtmp, dtmp_base, local_t
324
325
    def test_append(self):
326
        t = self.get_transport()
327
328
        if self.readonly:
329
            open('a', 'wb').write('diff\ncontents for\na\n')
330
            open('b', 'wb').write('contents\nfor b\n')
331
        else:
332
            t.put_multi([
333
                    ('a', 'diff\ncontents for\na\n'),
334
                    ('b', 'contents\nfor b\n')
335
                    ])
336
337
        if self.readonly:
338
            self.assertRaises(TransportNotPossible,
339
                    t.append, 'a', 'add\nsome\nmore\ncontents\n')
340
            _append('a', 'add\nsome\nmore\ncontents\n')
341
        else:
342
            t.append('a', 'add\nsome\nmore\ncontents\n')
343
344
        self.check_file_contents('a', 
345
            'diff\ncontents for\na\nadd\nsome\nmore\ncontents\n')
346
347
        if self.readonly:
348
            self.assertRaises(TransportNotPossible,
349
                    t.append_multi,
350
                        [('a', 'and\nthen\nsome\nmore\n'),
351
                         ('b', 'some\nmore\nfor\nb\n')])
352
            _append('a', 'and\nthen\nsome\nmore\n')
353
            _append('b', 'some\nmore\nfor\nb\n')
354
        else:
355
            t.append_multi([('a', 'and\nthen\nsome\nmore\n'),
356
                    ('b', 'some\nmore\nfor\nb\n')])
357
        self.check_file_contents('a', 
358
            'diff\ncontents for\na\n'
359
            'add\nsome\nmore\ncontents\n'
360
            'and\nthen\nsome\nmore\n')
361
        self.check_file_contents('b', 
362
                'contents\nfor b\n'
363
                'some\nmore\nfor\nb\n')
364
365
        if self.readonly:
366
            _append('a', 'a little bit more\n')
367
            _append('b', 'from an iterator\n')
368
        else:
369
            t.append_multi(iter([('a', 'a little bit more\n'),
370
                    ('b', 'from an iterator\n')]))
371
        self.check_file_contents('a', 
372
            'diff\ncontents for\na\n'
373
            'add\nsome\nmore\ncontents\n'
374
            'and\nthen\nsome\nmore\n'
375
            'a little bit more\n')
376
        self.check_file_contents('b', 
377
                'contents\nfor b\n'
378
                'some\nmore\nfor\nb\n'
379
                'from an iterator\n')
380
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
381
        if self.readonly:
382
            _append('c', 'some text\nfor a missing file\n')
383
            _append('a', 'some text in a\n')
384
            _append('d', 'missing file r\n')
385
        else:
386
            t.append('c', 'some text\nfor a missing file\n')
387
            t.append_multi([('a', 'some text in a\n'),
388
                            ('d', 'missing file r\n')])
389
        self.check_file_contents('a', 
390
            'diff\ncontents for\na\n'
391
            'add\nsome\nmore\ncontents\n'
392
            'and\nthen\nsome\nmore\n'
393
            'a little bit more\n'
394
            'some text in a\n')
395
        self.check_file_contents('c', 'some text\nfor a missing file\n')
396
        self.check_file_contents('d', 'missing file r\n')
397
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
398
    def test_append_file(self):
399
        t = self.get_transport()
400
401
        contents = [
402
            ('f1', 'this is a string\nand some more stuff\n'),
403
            ('f2', 'here is some text\nand a bit more\n'),
404
            ('f3', 'some text for the\nthird file created\n'),
405
            ('f4', 'this is a string\nand some more stuff\n'),
406
            ('f5', 'here is some text\nand a bit more\n'),
407
            ('f6', 'some text for the\nthird file created\n')
408
        ]
409
        
410
        if self.readonly:
411
            for f, val in contents:
412
                open(f, 'wb').write(val)
413
        else:
414
            t.put_multi(contents)
415
416
        a1 = StringIO('appending to\none\n')
417
        if self.readonly:
418
            _append('f1', a1.read())
419
        else:
420
            t.append('f1', a1)
421
422
        del a1
423
424
        self.check_file_contents('f1', 
425
                'this is a string\nand some more stuff\n'
426
                'appending to\none\n')
427
428
        a2 = StringIO('adding more\ntext to two\n')
429
        a3 = StringIO('some garbage\nto put in three\n')
430
431
        if self.readonly:
432
            _append('f2', a2.read())
433
            _append('f3', a3.read())
434
        else:
435
            t.append_multi([('f2', a2), ('f3', a3)])
436
437
        del a2, a3
438
439
        self.check_file_contents('f2',
440
                'here is some text\nand a bit more\n'
441
                'adding more\ntext to two\n')
442
        self.check_file_contents('f3', 
443
                'some text for the\nthird file created\n'
444
                'some garbage\nto put in three\n')
445
446
        # Test that an actual file object can be used with put
447
        a4 = open('f1', 'rb')
448
        if self.readonly:
449
            _append('f4', a4.read())
450
        else:
451
            t.append('f4', a4)
452
453
        del a4
454
455
        self.check_file_contents('f4', 
456
                'this is a string\nand some more stuff\n'
457
                'this is a string\nand some more stuff\n'
458
                'appending to\none\n')
459
460
        a5 = open('f2', 'rb')
461
        a6 = open('f3', 'rb')
462
        if self.readonly:
463
            _append('f5', a5.read())
464
            _append('f6', a6.read())
465
        else:
466
            t.append_multi([('f5', a5), ('f6', a6)])
467
468
        del a5, a6
469
470
        self.check_file_contents('f5',
471
                'here is some text\nand a bit more\n'
472
                'here is some text\nand a bit more\n'
473
                'adding more\ntext to two\n')
474
        self.check_file_contents('f6',
475
                'some text for the\nthird file created\n'
476
                'some text for the\nthird file created\n'
477
                'some garbage\nto put in three\n')
478
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
479
        a5 = open('f2', 'rb')
480
        a6 = open('f2', 'rb')
481
        a7 = open('f3', 'rb')
482
        if self.readonly:
483
            _append('c', a5.read())
484
            _append('a', a6.read())
485
            _append('d', a7.read())
486
        else:
487
            t.append('c', a5)
488
            t.append_multi([('a', a6), ('d', a7)])
489
        del a5, a6, a7
490
        self.check_file_contents('c', open('f2', 'rb').read())
491
        self.check_file_contents('d', open('f3', 'rb').read())
492
493
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
494
    def test_delete(self):
495
        # TODO: Test Transport.delete
1185.49.13 by John Arbash Meinel
Removed delayed setup, since it broke some tests. Fixed other small bugs. All tests pass.
496
        t = self.get_transport()
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
497
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
498
        # Not much to do with a readonly transport
499
        if self.readonly:
500
            return
501
502
        open('a', 'wb').write('a little bit of text\n')
503
        self.failUnless(t.has('a'))
504
        self.failUnlessExists('a')
505
        t.delete('a')
506
        self.failIf(os.path.lexists('a'))
507
508
        self.assertRaises(NoSuchFile, t.delete, 'a')
509
510
        open('a', 'wb').write('a text\n')
511
        open('b', 'wb').write('b text\n')
512
        open('c', 'wb').write('c text\n')
513
        self.assertEqual([True, True, True],
514
                list(t.has_multi(['a', 'b', 'c'])))
515
        t.delete_multi(['a', 'c'])
516
        self.assertEqual([False, True, False],
517
                list(t.has_multi(['a', 'b', 'c'])))
518
        self.failIf(os.path.lexists('a'))
519
        self.failUnlessExists('b')
520
        self.failIf(os.path.lexists('c'))
521
522
        self.assertRaises(NoSuchFile,
523
                t.delete_multi, ['a', 'b', 'c'])
524
525
        self.assertRaises(NoSuchFile,
526
                t.delete_multi, iter(['a', 'b', 'c']))
527
528
        open('a', 'wb').write('another a text\n')
529
        open('c', 'wb').write('another c text\n')
530
        t.delete_multi(iter(['a', 'b', 'c']))
531
532
        # We should have deleted everything
533
        # SftpServer creates control files in the
534
        # working directory, so we can just do a
535
        # plain "listdir".
536
        # self.assertEqual([], os.listdir('.'))
537
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
538
    def test_move(self):
1185.49.13 by John Arbash Meinel
Removed delayed setup, since it broke some tests. Fixed other small bugs. All tests pass.
539
        t = self.get_transport()
540
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
541
        if self.readonly:
542
            return
543
544
        # TODO: I would like to use os.listdir() to
545
        # make sure there are no extra files, but SftpServer
546
        # creates control files in the working directory
547
        # perhaps all of this could be done in a subdirectory
548
549
        open('a', 'wb').write('a first file\n')
550
        self.assertEquals([True, False], list(t.has_multi(['a', 'b'])))
551
552
        t.move('a', 'b')
553
        self.failUnlessExists('b')
554
        self.failIf(os.path.lexists('a'))
555
556
        self.check_file_contents('b', 'a first file\n')
557
        self.assertEquals([False, True], list(t.has_multi(['a', 'b'])))
558
559
        # Overwrite a file
560
        open('c', 'wb').write('c this file\n')
561
        t.move('c', 'b')
562
        self.failIf(os.path.lexists('c'))
563
        self.check_file_contents('b', 'c this file\n')
564
565
        # TODO: Try to write a test for atomicity
566
        # TODO: Test moving into a non-existant subdirectory
567
        # TODO: Test Transport.move_multi
568
1185.49.13 by John Arbash Meinel
Removed delayed setup, since it broke some tests. Fixed other small bugs. All tests pass.
569
    def test_copy(self):
570
        t = self.get_transport()
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
571
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
572
        if self.readonly:
573
            return
574
575
        open('a', 'wb').write('a file\n')
576
        t.copy('a', 'b')
577
        self.check_file_contents('b', 'a file\n')
578
579
        self.assertRaises(NoSuchFile, t.copy, 'c', 'd')
580
        os.mkdir('c')
581
        # What should the assert be if you try to copy a
582
        # file over a directory?
583
        #self.assertRaises(Something, t.copy, 'a', 'c')
584
        open('d', 'wb').write('text in d\n')
585
        t.copy('d', 'b')
586
        self.check_file_contents('b', 'text in d\n')
587
588
        # TODO: test copy_multi
589
1185.35.31 by Aaron Bentley
Throw ConnectionError instead of NoSuchFile except when we get a 404
590
    def test_connection_error(self):
591
        """ConnectionError is raised when connection is impossible"""
592
        if not hasattr(self, "get_bogus_transport"):
593
            return
594
        t = self.get_bogus_transport()
1185.31.19 by John Arbash Meinel
Updating transport test, to handle NoSuchFile if a bogus url accidentally resolves.
595
        try:
596
            t.get('.bzr/branch')
597
        except (ConnectionError, NoSuchFile), e:
598
            pass
599
        except (Exception), e:
600
            self.failIf(True, 'Wrong exception thrown: %s' % e)
601
        else:
602
            self.failIf(True, 'Did not get the expected exception.')
1442.1.44 by Robert Collins
Many transport related tweaks:
603
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
604
    def test_stat(self):
605
        # TODO: Test stat, just try once, and if it throws, stop testing
606
        from stat import S_ISDIR, S_ISREG
607
608
        t = self.get_transport()
609
610
        try:
611
            st = t.stat('.')
612
        except TransportNotPossible, e:
613
            # This transport cannot stat
614
            return
615
616
        paths = ['a', 'b/', 'b/c', 'b/d/', 'b/d/e']
617
        self.build_tree(paths)
618
619
        local_stats = []
620
621
        for p in paths:
622
            st = t.stat(p)
623
            local_st = os.stat(p)
624
            if p.endswith('/'):
625
                self.failUnless(S_ISDIR(st.st_mode))
626
            else:
627
                self.failUnless(S_ISREG(st.st_mode))
628
            self.assertEqual(local_st.st_size, st.st_size)
629
            self.assertEqual(local_st.st_mode, st.st_mode)
630
            local_stats.append(local_st)
631
632
        remote_stats = list(t.stat_multi(paths))
633
        remote_iter_stats = list(t.stat_multi(iter(paths)))
634
635
        for local, remote, remote_iter in \
636
            zip(local_stats, remote_stats, remote_iter_stats):
637
            self.assertEqual(local.st_mode, remote.st_mode)
638
            self.assertEqual(local.st_mode, remote_iter.st_mode)
639
640
            self.assertEqual(local.st_size, remote.st_size)
641
            self.assertEqual(local.st_size, remote_iter.st_size)
642
            # Should we test UID/GID?
643
644
        self.assertRaises(NoSuchFile, t.stat, 'q')
645
        self.assertRaises(NoSuchFile, t.stat, 'b/a')
646
647
        self.assertListRaises(NoSuchFile, t.stat_multi, ['a', 'c', 'd'])
648
        self.assertListRaises(NoSuchFile, t.stat_multi, iter(['a', 'c', 'd']))
649
650
    def test_list_dir(self):
651
        # TODO: Test list_dir, just try once, and if it throws, stop testing
652
        t = self.get_transport()
653
        
654
        if not t.listable():
655
            self.assertRaises(TransportNotPossible, t.list_dir, '.')
656
            return
657
1185.33.80 by Martin Pool
[merge] john
658
        def sorted_list(d):
659
            l = list(t.list_dir(d))
660
            l.sort()
661
            return l
662
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
663
        # SftpServer creates control files in the working directory
664
        # so lets move down a directory to be safe
665
        os.mkdir('wd')
666
        os.chdir('wd')
667
        t = t.clone('wd')
668
1185.33.80 by Martin Pool
[merge] john
669
        self.assertEqual([], sorted_list(u'.'))
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
670
        self.build_tree(['a', 'b', 'c/', 'c/d', 'c/e'])
671
1185.33.80 by Martin Pool
[merge] john
672
        self.assertEqual([u'a', u'b', u'c'], sorted_list(u'.'))
673
        self.assertEqual([u'd', u'e'], sorted_list(u'c'))
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
674
675
        os.remove('c/d')
676
        os.remove('b')
1185.33.80 by Martin Pool
[merge] john
677
        self.assertEqual([u'a', u'c'], sorted_list('.'))
678
        self.assertEqual([u'e'], sorted_list(u'c'))
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
679
680
        self.assertListRaises(NoSuchFile, t.list_dir, 'q')
681
        self.assertListRaises(NoSuchFile, t.list_dir, 'c/f')
682
        self.assertListRaises(NoSuchFile, t.list_dir, 'a')
683
684
    def test_clone(self):
685
        # TODO: Test that clone moves up and down the filesystem
686
        t1 = self.get_transport()
687
688
        self.build_tree(['a', 'b/', 'b/c'])
689
690
        self.failUnless(t1.has('a'))
691
        self.failUnless(t1.has('b/c'))
692
        self.failIf(t1.has('c'))
693
694
        t2 = t1.clone('b')
695
        self.failUnless(t2.has('c'))
696
        self.failIf(t2.has('a'))
697
698
        t3 = t2.clone('..')
699
        self.failUnless(t3.has('a'))
700
        self.failIf(t3.has('c'))
701
702
        self.failIf(t1.has('b/d'))
703
        self.failIf(t2.has('d'))
704
        self.failIf(t3.has('b/d'))
705
706
        if self.readonly:
707
            open('b/d', 'wb').write('newfile\n')
708
        else:
709
            t2.put('d', 'newfile\n')
710
711
        self.failUnless(t1.has('b/d'))
712
        self.failUnless(t2.has('d'))
713
        self.failUnless(t3.has('b/d'))
714
1185.35.31 by Aaron Bentley
Throw ConnectionError instead of NoSuchFile except when we get a 404
715
        
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
716
class LocalTransportTest(TestCaseInTempDir, TestTransportMixIn):
717
    def get_transport(self):
718
        from bzrlib.transport.local import LocalTransport
1185.33.66 by Martin Pool
[patch] use unicode literals for all hardcoded paths (Alexander Belchenko)
719
        return LocalTransport(u'.')
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
720
1442.1.44 by Robert Collins
Many transport related tweaks:
721
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
722
class HttpTransportTest(TestCaseWithWebserver, TestTransportMixIn):
1442.1.44 by Robert Collins
Many transport related tweaks:
723
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
724
    readonly = True
1442.1.44 by Robert Collins
Many transport related tweaks:
725
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
726
    def get_transport(self):
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
727
        from bzrlib.transport.http import HttpTransport
1185.33.66 by Martin Pool
[patch] use unicode literals for all hardcoded paths (Alexander Belchenko)
728
        url = self.get_remote_url(u'.')
1185.11.22 by John Arbash Meinel
Major refactoring of testtransport.
729
        return HttpTransport(url)
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
730
1185.35.31 by Aaron Bentley
Throw ConnectionError instead of NoSuchFile except when we get a 404
731
    def get_bogus_transport(self):
732
        from bzrlib.transport.http import HttpTransport
1185.33.17 by Martin Pool
[merge] aaron, various fixes
733
        return HttpTransport('http://jasldkjsalkdjalksjdkljasd')
1185.35.31 by Aaron Bentley
Throw ConnectionError instead of NoSuchFile except when we get a 404
734
1442.1.44 by Robert Collins
Many transport related tweaks:
735
736
class TestMemoryTransport(TestCase):
737
738
    def test_get_transport(self):
739
        memory.MemoryTransport()
740
741
    def test_clone(self):
742
        transport = memory.MemoryTransport()
743
        self.failUnless(transport.clone() is transport)
744
745
    def test_abspath(self):
746
        transport = memory.MemoryTransport()
747
        self.assertEqual("in-memory:relpath", transport.abspath('relpath'))
748
749
    def test_relpath(self):
750
        transport = memory.MemoryTransport()
751
752
    def test_append_and_get(self):
753
        transport = memory.MemoryTransport()
754
        transport.append('path', StringIO('content'))
755
        self.assertEqual(transport.get('path').read(), 'content')
756
        transport.append('path', StringIO('content'))
757
        self.assertEqual(transport.get('path').read(), 'contentcontent')
758
759
    def test_put_and_get(self):
760
        transport = memory.MemoryTransport()
761
        transport.put('path', StringIO('content'))
762
        self.assertEqual(transport.get('path').read(), 'content')
763
        transport.put('path', StringIO('content'))
764
        self.assertEqual(transport.get('path').read(), 'content')
765
766
    def test_append_without_dir_fails(self):
767
        transport = memory.MemoryTransport()
768
        self.assertRaises(NoSuchFile,
769
                          transport.append, 'dir/path', StringIO('content'))
770
771
    def test_put_without_dir_fails(self):
772
        transport = memory.MemoryTransport()
773
        self.assertRaises(NoSuchFile,
774
                          transport.put, 'dir/path', StringIO('content'))
775
776
    def test_get_missing(self):
777
        transport = memory.MemoryTransport()
778
        self.assertRaises(NoSuchFile, transport.get, 'foo')
779
780
    def test_has_missing(self):
781
        transport = memory.MemoryTransport()
782
        self.assertEquals(False, transport.has('foo'))
783
784
    def test_has_present(self):
785
        transport = memory.MemoryTransport()
786
        transport.append('foo', StringIO('content'))
787
        self.assertEquals(True, transport.has('foo'))
788
789
    def test_mkdir(self):
790
        transport = memory.MemoryTransport()
791
        transport.mkdir('dir')
792
        transport.append('dir/path', StringIO('content'))
793
        self.assertEqual(transport.get('dir/path').read(), 'content')
794
795
    def test_mkdir_missing_parent(self):
796
        transport = memory.MemoryTransport()
797
        self.assertRaises(NoSuchFile,
798
                          transport.mkdir, 'dir/dir')
799
800
    def test_mkdir_twice(self):
801
        transport = memory.MemoryTransport()
802
        transport.mkdir('dir')
803
        self.assertRaises(FileExists, transport.mkdir, 'dir')
804
805
    def test_parameters(self):
806
        transport = memory.MemoryTransport()
807
        self.assertEqual(True, transport.listable())
808
        self.assertEqual(False, transport.should_cache())
809
810
    def test_iter_files_recursive(self):
811
        transport = memory.MemoryTransport()
812
        transport.mkdir('dir')
813
        transport.put('dir/foo', StringIO('content'))
814
        transport.put('dir/bar', StringIO('content'))
815
        transport.put('bar', StringIO('content'))
816
        paths = set(transport.iter_files_recursive())
817
        self.assertEqual(set(['dir/foo', 'dir/bar', 'bar']), paths)
818
819
    def test_stat(self):
820
        transport = memory.MemoryTransport()
821
        transport.put('foo', StringIO('content'))
822
        transport.put('bar', StringIO('phowar'))
823
        self.assertEqual(7, transport.stat('foo').st_size)
824
        self.assertEqual(6, transport.stat('bar').st_size)
1185.35.31 by Aaron Bentley
Throw ConnectionError instead of NoSuchFile except when we get a 404
825