~ubuntu-branches/ubuntu/natty/dulwich/natty

« back to all changes in this revision

Viewing changes to dulwich/tests/test_server.py

  • Committer: Bazaar Package Importer
  • Author(s): Jelmer Vernooij
  • Date: 2010-03-03 16:43:41 UTC
  • mfrom: (1.2.9 upstream) (8.1.4 sid)
  • Revision ID: james.westby@ubuntu.com-20100303164341-a8ktsiinmh9s0adk
Tags: 0.5.0-1
* New upstream release.
* Switch to dpkg-source 3.0 (quilt) format.
* Bump standards version to 3.8.4.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# test_server.py -- Tests for the git server
 
2
# Copyright (C) 2010 Google, Inc.
 
3
#
 
4
# This program is free software; you can redistribute it and/or
 
5
# modify it under the terms of the GNU General Public License
 
6
# as published by the Free Software Foundation; version 2
 
7
# or (at your option) any later version of the License.
 
8
#
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU General Public License
 
15
# along with this program; if not, write to the Free Software
 
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
17
# MA  02110-1301, USA.
 
18
 
 
19
 
 
20
"""Tests for the smart protocol server."""
 
21
 
 
22
 
 
23
from cStringIO import StringIO
 
24
from unittest import TestCase
 
25
 
 
26
from dulwich.errors import (
 
27
    GitProtocolError,
 
28
    )
 
29
from dulwich.server import (
 
30
    UploadPackHandler,
 
31
    ProtocolGraphWalker,
 
32
    SingleAckGraphWalkerImpl,
 
33
    MultiAckGraphWalkerImpl,
 
34
    MultiAckDetailedGraphWalkerImpl,
 
35
    )
 
36
 
 
37
from dulwich.protocol import (
 
38
    SINGLE_ACK,
 
39
    MULTI_ACK,
 
40
    )
 
41
 
 
42
ONE = '1' * 40
 
43
TWO = '2' * 40
 
44
THREE = '3' * 40
 
45
FOUR = '4' * 40
 
46
FIVE = '5' * 40
 
47
 
 
48
class TestProto(object):
 
49
    def __init__(self):
 
50
        self._output = []
 
51
        self._received = {0: [], 1: [], 2: [], 3: []}
 
52
 
 
53
    def set_output(self, output_lines):
 
54
        self._output = ['%s\n' % line.rstrip() for line in output_lines]
 
55
 
 
56
    def read_pkt_line(self):
 
57
        if self._output:
 
58
            return self._output.pop(0)
 
59
        else:
 
60
            return None
 
61
 
 
62
    def write_sideband(self, band, data):
 
63
        self._received[band].append(data)
 
64
 
 
65
    def write_pkt_line(self, data):
 
66
        if data is None:
 
67
            data = 'None'
 
68
        self._received[0].append(data)
 
69
 
 
70
    def get_received_line(self, band=0):
 
71
        lines = self._received[band]
 
72
        if lines:
 
73
            return lines.pop(0)
 
74
        else:
 
75
            return None
 
76
 
 
77
 
 
78
class UploadPackHandlerTestCase(TestCase):
 
79
    def setUp(self):
 
80
        self._handler = UploadPackHandler(None, None, None)
 
81
 
 
82
    def test_set_client_capabilities(self):
 
83
        try:
 
84
            self._handler.set_client_capabilities([])
 
85
        except GitProtocolError:
 
86
            self.fail()
 
87
 
 
88
        try:
 
89
            self._handler.set_client_capabilities([
 
90
                'multi_ack', 'side-band-64k', 'thin-pack', 'ofs-delta'])
 
91
        except GitProtocolError:
 
92
            self.fail()
 
93
 
 
94
    def test_set_client_capabilities_error(self):
 
95
        self.assertRaises(GitProtocolError,
 
96
                          self._handler.set_client_capabilities,
 
97
                          ['weird_ack_level', 'ofs-delta'])
 
98
        try:
 
99
            self._handler.set_client_capabilities(['include-tag'])
 
100
        except GitProtocolError:
 
101
            self.fail()
 
102
 
 
103
 
 
104
class TestCommit(object):
 
105
    def __init__(self, sha, parents, commit_time):
 
106
        self.id = sha
 
107
        self._parents = parents
 
108
        self.commit_time = commit_time
 
109
 
 
110
    def get_parents(self):
 
111
        return self._parents
 
112
 
 
113
    def __repr__(self):
 
114
        return '%s(%s)' % (self.__class__.__name__, self._sha)
 
115
 
 
116
 
 
117
class TestBackend(object):
 
118
    def __init__(self, objects):
 
119
        self.object_store = objects
 
120
 
 
121
 
 
122
class TestHandler(object):
 
123
    def __init__(self, objects, proto):
 
124
        self.backend = TestBackend(objects)
 
125
        self.proto = proto
 
126
        self.stateless_rpc = False
 
127
        self.advertise_refs = False
 
128
 
 
129
    def capabilities(self):
 
130
        return 'multi_ack'
 
131
 
 
132
 
 
133
class ProtocolGraphWalkerTestCase(TestCase):
 
134
    def setUp(self):
 
135
        # Create the following commit tree:
 
136
        #   3---5
 
137
        #  /
 
138
        # 1---2---4
 
139
        self._objects = {
 
140
            ONE: TestCommit(ONE, [], 111),
 
141
            TWO: TestCommit(TWO, [ONE], 222),
 
142
            THREE: TestCommit(THREE, [ONE], 333),
 
143
            FOUR: TestCommit(FOUR, [TWO], 444),
 
144
            FIVE: TestCommit(FIVE, [THREE], 555),
 
145
            }
 
146
        self._walker = ProtocolGraphWalker(
 
147
            TestHandler(self._objects, TestProto()))
 
148
 
 
149
    def test_is_satisfied_no_haves(self):
 
150
        self.assertFalse(self._walker._is_satisfied([], ONE, 0))
 
151
        self.assertFalse(self._walker._is_satisfied([], TWO, 0))
 
152
        self.assertFalse(self._walker._is_satisfied([], THREE, 0))
 
153
 
 
154
    def test_is_satisfied_have_root(self):
 
155
        self.assertTrue(self._walker._is_satisfied([ONE], ONE, 0))
 
156
        self.assertTrue(self._walker._is_satisfied([ONE], TWO, 0))
 
157
        self.assertTrue(self._walker._is_satisfied([ONE], THREE, 0))
 
158
 
 
159
    def test_is_satisfied_have_branch(self):
 
160
        self.assertTrue(self._walker._is_satisfied([TWO], TWO, 0))
 
161
        # wrong branch
 
162
        self.assertFalse(self._walker._is_satisfied([TWO], THREE, 0))
 
163
 
 
164
    def test_all_wants_satisfied(self):
 
165
        self._walker.set_wants([FOUR, FIVE])
 
166
        # trivial case: wants == haves
 
167
        self.assertTrue(self._walker.all_wants_satisfied([FOUR, FIVE]))
 
168
        # cases that require walking the commit tree
 
169
        self.assertTrue(self._walker.all_wants_satisfied([ONE]))
 
170
        self.assertFalse(self._walker.all_wants_satisfied([TWO]))
 
171
        self.assertFalse(self._walker.all_wants_satisfied([THREE]))
 
172
        self.assertTrue(self._walker.all_wants_satisfied([TWO, THREE]))
 
173
 
 
174
    def test_read_proto_line(self):
 
175
        self._walker.proto.set_output([
 
176
            'want %s' % ONE,
 
177
            'want %s' % TWO,
 
178
            'have %s' % THREE,
 
179
            'foo %s' % FOUR,
 
180
            'bar',
 
181
            'done',
 
182
            ])
 
183
        self.assertEquals(('want', ONE), self._walker.read_proto_line())
 
184
        self.assertEquals(('want', TWO), self._walker.read_proto_line())
 
185
        self.assertEquals(('have', THREE), self._walker.read_proto_line())
 
186
        self.assertRaises(GitProtocolError, self._walker.read_proto_line)
 
187
        self.assertRaises(GitProtocolError, self._walker.read_proto_line)
 
188
        self.assertEquals(('done', None), self._walker.read_proto_line())
 
189
        self.assertEquals((None, None), self._walker.read_proto_line())
 
190
 
 
191
    def test_determine_wants(self):
 
192
        self.assertRaises(GitProtocolError, self._walker.determine_wants, {})
 
193
 
 
194
        self._walker.proto.set_output([
 
195
            'want %s multi_ack' % ONE,
 
196
            'want %s' % TWO,
 
197
            ])
 
198
        heads = {'ref1': ONE, 'ref2': TWO, 'ref3': THREE}
 
199
        self.assertEquals([ONE, TWO], self._walker.determine_wants(heads))
 
200
 
 
201
        self._walker.proto.set_output(['want %s multi_ack' % FOUR])
 
202
        self.assertRaises(GitProtocolError, self._walker.determine_wants, heads)
 
203
 
 
204
        self._walker.proto.set_output([])
 
205
        self.assertEquals([], self._walker.determine_wants(heads))
 
206
 
 
207
        self._walker.proto.set_output(['want %s multi_ack' % ONE, 'foo'])
 
208
        self.assertRaises(GitProtocolError, self._walker.determine_wants, heads)
 
209
 
 
210
        self._walker.proto.set_output(['want %s multi_ack' % FOUR])
 
211
        self.assertRaises(GitProtocolError, self._walker.determine_wants, heads)
 
212
 
 
213
    # TODO: test commit time cutoff
 
214
 
 
215
 
 
216
class TestProtocolGraphWalker(object):
 
217
    def __init__(self):
 
218
        self.acks = []
 
219
        self.lines = []
 
220
        self.done = False
 
221
        self.stateless_rpc = False
 
222
        self.advertise_refs = False
 
223
 
 
224
    def read_proto_line(self):
 
225
        return self.lines.pop(0)
 
226
 
 
227
    def send_ack(self, sha, ack_type=''):
 
228
        self.acks.append((sha, ack_type))
 
229
 
 
230
    def send_nak(self):
 
231
        self.acks.append((None, 'nak'))
 
232
 
 
233
    def all_wants_satisfied(self, haves):
 
234
        return self.done
 
235
 
 
236
    def pop_ack(self):
 
237
        if not self.acks:
 
238
            return None
 
239
        return self.acks.pop(0)
 
240
 
 
241
 
 
242
class AckGraphWalkerImplTestCase(TestCase):
 
243
    """Base setup and asserts for AckGraphWalker tests."""
 
244
    def setUp(self):
 
245
        self._walker = TestProtocolGraphWalker()
 
246
        self._walker.lines = [
 
247
            ('have', TWO),
 
248
            ('have', ONE),
 
249
            ('have', THREE),
 
250
            ('done', None),
 
251
            ]
 
252
        self._impl = self.impl_cls(self._walker)
 
253
 
 
254
    def assertNoAck(self):
 
255
        self.assertEquals(None, self._walker.pop_ack())
 
256
 
 
257
    def assertAcks(self, acks):
 
258
        for sha, ack_type in acks:
 
259
            self.assertEquals((sha, ack_type), self._walker.pop_ack())
 
260
        self.assertNoAck()
 
261
 
 
262
    def assertAck(self, sha, ack_type=''):
 
263
        self.assertAcks([(sha, ack_type)])
 
264
 
 
265
    def assertNak(self):
 
266
        self.assertAck(None, 'nak')
 
267
 
 
268
    def assertNextEquals(self, sha):
 
269
        self.assertEquals(sha, self._impl.next())
 
270
 
 
271
 
 
272
class SingleAckGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
 
273
    impl_cls = SingleAckGraphWalkerImpl
 
274
 
 
275
    def test_single_ack(self):
 
276
        self.assertNextEquals(TWO)
 
277
        self.assertNoAck()
 
278
 
 
279
        self.assertNextEquals(ONE)
 
280
        self._walker.done = True
 
281
        self._impl.ack(ONE)
 
282
        self.assertAck(ONE)
 
283
 
 
284
        self.assertNextEquals(THREE)
 
285
        self._impl.ack(THREE)
 
286
        self.assertNoAck()
 
287
 
 
288
        self.assertNextEquals(None)
 
289
        self.assertNoAck()
 
290
 
 
291
    def test_single_ack_flush(self):
 
292
        # same as ack test but ends with a flush-pkt instead of done
 
293
        self._walker.lines[-1] = (None, None)
 
294
 
 
295
        self.assertNextEquals(TWO)
 
296
        self.assertNoAck()
 
297
 
 
298
        self.assertNextEquals(ONE)
 
299
        self._walker.done = True
 
300
        self._impl.ack(ONE)
 
301
        self.assertAck(ONE)
 
302
 
 
303
        self.assertNextEquals(THREE)
 
304
        self.assertNoAck()
 
305
 
 
306
        self.assertNextEquals(None)
 
307
        self.assertNoAck()
 
308
 
 
309
    def test_single_ack_nak(self):
 
310
        self.assertNextEquals(TWO)
 
311
        self.assertNoAck()
 
312
 
 
313
        self.assertNextEquals(ONE)
 
314
        self.assertNoAck()
 
315
 
 
316
        self.assertNextEquals(THREE)
 
317
        self.assertNoAck()
 
318
 
 
319
        self.assertNextEquals(None)
 
320
        self.assertNak()
 
321
 
 
322
    def test_single_ack_nak_flush(self):
 
323
        # same as nak test but ends with a flush-pkt instead of done
 
324
        self._walker.lines[-1] = (None, None)
 
325
 
 
326
        self.assertNextEquals(TWO)
 
327
        self.assertNoAck()
 
328
 
 
329
        self.assertNextEquals(ONE)
 
330
        self.assertNoAck()
 
331
 
 
332
        self.assertNextEquals(THREE)
 
333
        self.assertNoAck()
 
334
 
 
335
        self.assertNextEquals(None)
 
336
        self.assertNak()
 
337
 
 
338
class MultiAckGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
 
339
    impl_cls = MultiAckGraphWalkerImpl
 
340
 
 
341
    def test_multi_ack(self):
 
342
        self.assertNextEquals(TWO)
 
343
        self.assertNoAck()
 
344
 
 
345
        self.assertNextEquals(ONE)
 
346
        self._walker.done = True
 
347
        self._impl.ack(ONE)
 
348
        self.assertAck(ONE, 'continue')
 
349
 
 
350
        self.assertNextEquals(THREE)
 
351
        self._impl.ack(THREE)
 
352
        self.assertAck(THREE, 'continue')
 
353
 
 
354
        self.assertNextEquals(None)
 
355
        self.assertAck(THREE)
 
356
 
 
357
    def test_multi_ack_partial(self):
 
358
        self.assertNextEquals(TWO)
 
359
        self.assertNoAck()
 
360
 
 
361
        self.assertNextEquals(ONE)
 
362
        self._impl.ack(ONE)
 
363
        self.assertAck(ONE, 'continue')
 
364
 
 
365
        self.assertNextEquals(THREE)
 
366
        self.assertNoAck()
 
367
 
 
368
        self.assertNextEquals(None)
 
369
        # done, re-send ack of last common
 
370
        self.assertAck(ONE)
 
371
 
 
372
    def test_multi_ack_flush(self):
 
373
        self._walker.lines = [
 
374
            ('have', TWO),
 
375
            (None, None),
 
376
            ('have', ONE),
 
377
            ('have', THREE),
 
378
            ('done', None),
 
379
            ]
 
380
        self.assertNextEquals(TWO)
 
381
        self.assertNoAck()
 
382
 
 
383
        self.assertNextEquals(ONE)
 
384
        self.assertNak() # nak the flush-pkt
 
385
 
 
386
        self._walker.done = True
 
387
        self._impl.ack(ONE)
 
388
        self.assertAck(ONE, 'continue')
 
389
 
 
390
        self.assertNextEquals(THREE)
 
391
        self._impl.ack(THREE)
 
392
        self.assertAck(THREE, 'continue')
 
393
 
 
394
        self.assertNextEquals(None)
 
395
        self.assertAck(THREE)
 
396
 
 
397
    def test_multi_ack_nak(self):
 
398
        self.assertNextEquals(TWO)
 
399
        self.assertNoAck()
 
400
 
 
401
        self.assertNextEquals(ONE)
 
402
        self.assertNoAck()
 
403
 
 
404
        self.assertNextEquals(THREE)
 
405
        self.assertNoAck()
 
406
 
 
407
        self.assertNextEquals(None)
 
408
        self.assertNak()
 
409
 
 
410
class MultiAckDetailedGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
 
411
    impl_cls = MultiAckDetailedGraphWalkerImpl
 
412
 
 
413
    def test_multi_ack(self):
 
414
        self.assertNextEquals(TWO)
 
415
        self.assertNoAck()
 
416
 
 
417
        self.assertNextEquals(ONE)
 
418
        self._walker.done = True
 
419
        self._impl.ack(ONE)
 
420
        self.assertAcks([(ONE, 'common'), (ONE, 'ready')])
 
421
 
 
422
        self.assertNextEquals(THREE)
 
423
        self._impl.ack(THREE)
 
424
        self.assertAck(THREE, 'ready')
 
425
 
 
426
        self.assertNextEquals(None)
 
427
        self.assertAck(THREE)
 
428
 
 
429
    def test_multi_ack_partial(self):
 
430
        self.assertNextEquals(TWO)
 
431
        self.assertNoAck()
 
432
 
 
433
        self.assertNextEquals(ONE)
 
434
        self._impl.ack(ONE)
 
435
        self.assertAck(ONE, 'common')
 
436
 
 
437
        self.assertNextEquals(THREE)
 
438
        self.assertNoAck()
 
439
 
 
440
        self.assertNextEquals(None)
 
441
        # done, re-send ack of last common
 
442
        self.assertAck(ONE)
 
443
 
 
444
    def test_multi_ack_flush(self):
 
445
        # same as ack test but contains a flush-pkt in the middle
 
446
        self._walker.lines = [
 
447
            ('have', TWO),
 
448
            (None, None),
 
449
            ('have', ONE),
 
450
            ('have', THREE),
 
451
            ('done', None),
 
452
            ]
 
453
        self.assertNextEquals(TWO)
 
454
        self.assertNoAck()
 
455
 
 
456
        self.assertNextEquals(ONE)
 
457
        self.assertNak() # nak the flush-pkt
 
458
 
 
459
        self._walker.done = True
 
460
        self._impl.ack(ONE)
 
461
        self.assertAcks([(ONE, 'common'), (ONE, 'ready')])
 
462
 
 
463
        self.assertNextEquals(THREE)
 
464
        self._impl.ack(THREE)
 
465
        self.assertAck(THREE, 'ready')
 
466
 
 
467
        self.assertNextEquals(None)
 
468
        self.assertAck(THREE)
 
469
 
 
470
    def test_multi_ack_nak(self):
 
471
        self.assertNextEquals(TWO)
 
472
        self.assertNoAck()
 
473
 
 
474
        self.assertNextEquals(ONE)
 
475
        self.assertNoAck()
 
476
 
 
477
        self.assertNextEquals(THREE)
 
478
        self.assertNoAck()
 
479
 
 
480
        self.assertNextEquals(None)
 
481
        self.assertNak()
 
482
 
 
483
    def test_multi_ack_nak_flush(self):
 
484
        # same as nak test but contains a flush-pkt in the middle
 
485
        self._walker.lines = [
 
486
            ('have', TWO),
 
487
            (None, None),
 
488
            ('have', ONE),
 
489
            ('have', THREE),
 
490
            ('done', None),
 
491
            ]
 
492
        self.assertNextEquals(TWO)
 
493
        self.assertNoAck()
 
494
 
 
495
        self.assertNextEquals(ONE)
 
496
        self.assertNak()
 
497
 
 
498
        self.assertNextEquals(THREE)
 
499
        self.assertNoAck()
 
500
 
 
501
        self.assertNextEquals(None)
 
502
        self.assertNak()
 
503
 
 
504
    def test_multi_ack_stateless(self):
 
505
        # transmission ends with a flush-pkt
 
506
        self._walker.lines[-1] = (None, None)
 
507
        self._walker.stateless_rpc = True
 
508
 
 
509
        self.assertNextEquals(TWO)
 
510
        self.assertNoAck()
 
511
 
 
512
        self.assertNextEquals(ONE)
 
513
        self.assertNoAck()
 
514
 
 
515
        self.assertNextEquals(THREE)
 
516
        self.assertNoAck()
 
517
 
 
518
        self.assertNextEquals(None)
 
519
        self.assertNak()