Add no-progress capability support to UploadPackHandler.
[jelmer/dulwich-libgit2.git] / dulwich / tests / test_server.py
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     Handler,
32     ProtocolGraphWalker,
33     SingleAckGraphWalkerImpl,
34     MultiAckGraphWalkerImpl,
35     MultiAckDetailedGraphWalkerImpl,
36     )
37
38 from dulwich.protocol import (
39     SINGLE_ACK,
40     MULTI_ACK,
41     )
42
43 ONE = '1' * 40
44 TWO = '2' * 40
45 THREE = '3' * 40
46 FOUR = '4' * 40
47 FIVE = '5' * 40
48
49 class TestProto(object):
50     def __init__(self):
51         self._output = []
52         self._received = {0: [], 1: [], 2: [], 3: []}
53
54     def set_output(self, output_lines):
55         self._output = ['%s\n' % line.rstrip() for line in output_lines]
56
57     def read_pkt_line(self):
58         if self._output:
59             return self._output.pop(0)
60         else:
61             return None
62
63     def write_sideband(self, band, data):
64         self._received[band].append(data)
65
66     def write_pkt_line(self, data):
67         if data is None:
68             data = 'None'
69         self._received[0].append(data)
70
71     def get_received_line(self, band=0):
72         lines = self._received[band]
73         if lines:
74             return lines.pop(0)
75         else:
76             return None
77
78
79 class HandlerTestCase(TestCase):
80     def setUp(self):
81         self._handler = Handler(None, None, None)
82         self._handler.capabilities = lambda: ('cap1', 'cap2', 'cap3')
83
84     def assertSucceeds(self, func, *args, **kwargs):
85         try:
86             func(*args, **kwargs)
87         except GitProtocolError:
88             self.fail()
89
90     def test_capability_line(self):
91         self.assertEquals('cap1 cap2 cap3', self._handler.capability_line())
92
93     def test_set_client_capabilities(self):
94         set_caps = self._handler.set_client_capabilities
95         self.assertSucceeds(set_caps, [])
96         self.assertSucceeds(set_caps, ['cap2'])
97         self.assertSucceeds(set_caps, ['cap1', 'cap2'])
98         # different order
99         self.assertSucceeds(set_caps, ['cap3', 'cap1', 'cap2'])
100         self.assertRaises(GitProtocolError, set_caps, ['capxxx', 'cap1'])
101
102     def test_has_capability(self):
103         self.assertRaises(GitProtocolError, self._handler.has_capability, 'cap')
104         caps = self._handler.capabilities()
105         self._handler.set_client_capabilities(caps)
106         for cap in caps:
107             self.assertTrue(self._handler.has_capability(cap))
108         self.assertFalse(self._handler.has_capability('capxxx'))
109
110
111 class UploadPackHandlerTestCase(TestCase):
112     def setUp(self):
113         self._handler = UploadPackHandler(None, None, None)
114         self._handler.proto = TestProto()
115
116     def test_progress(self):
117         self._handler.set_client_capabilities([])
118         self._handler.progress('first message')
119         self._handler.progress('second message')
120         self.assertEqual('first message',
121                          self._handler.proto.get_received_line(2))
122         self.assertEqual('second message',
123                          self._handler.proto.get_received_line(2))
124         self.assertEqual(None, self._handler.proto.get_received_line(2))
125
126     def test_no_progress(self):
127         self._handler.set_client_capabilities(['no-progress'])
128         self._handler.progress('first message')
129         self._handler.progress('second message')
130         self.assertEqual(None, self._handler.proto.get_received_line(2))
131
132
133 class TestCommit(object):
134     def __init__(self, sha, parents, commit_time):
135         self.id = sha
136         self._parents = parents
137         self.commit_time = commit_time
138
139     def get_parents(self):
140         return self._parents
141
142     def __repr__(self):
143         return '%s(%s)' % (self.__class__.__name__, self._sha)
144
145
146 class TestBackend(object):
147     def __init__(self, objects):
148         self.object_store = objects
149
150
151 class TestUploadPackHandler(Handler):
152     def __init__(self, objects, proto):
153         self.backend = TestBackend(objects)
154         self.proto = proto
155         self.stateless_rpc = False
156         self.advertise_refs = False
157
158     def capabilities(self):
159         return ('multi_ack',)
160
161
162 class ProtocolGraphWalkerTestCase(TestCase):
163     def setUp(self):
164         # Create the following commit tree:
165         #   3---5
166         #  /
167         # 1---2---4
168         self._objects = {
169             ONE: TestCommit(ONE, [], 111),
170             TWO: TestCommit(TWO, [ONE], 222),
171             THREE: TestCommit(THREE, [ONE], 333),
172             FOUR: TestCommit(FOUR, [TWO], 444),
173             FIVE: TestCommit(FIVE, [THREE], 555),
174             }
175         self._walker = ProtocolGraphWalker(
176             TestUploadPackHandler(self._objects, TestProto()))
177
178     def test_is_satisfied_no_haves(self):
179         self.assertFalse(self._walker._is_satisfied([], ONE, 0))
180         self.assertFalse(self._walker._is_satisfied([], TWO, 0))
181         self.assertFalse(self._walker._is_satisfied([], THREE, 0))
182
183     def test_is_satisfied_have_root(self):
184         self.assertTrue(self._walker._is_satisfied([ONE], ONE, 0))
185         self.assertTrue(self._walker._is_satisfied([ONE], TWO, 0))
186         self.assertTrue(self._walker._is_satisfied([ONE], THREE, 0))
187
188     def test_is_satisfied_have_branch(self):
189         self.assertTrue(self._walker._is_satisfied([TWO], TWO, 0))
190         # wrong branch
191         self.assertFalse(self._walker._is_satisfied([TWO], THREE, 0))
192
193     def test_all_wants_satisfied(self):
194         self._walker.set_wants([FOUR, FIVE])
195         # trivial case: wants == haves
196         self.assertTrue(self._walker.all_wants_satisfied([FOUR, FIVE]))
197         # cases that require walking the commit tree
198         self.assertTrue(self._walker.all_wants_satisfied([ONE]))
199         self.assertFalse(self._walker.all_wants_satisfied([TWO]))
200         self.assertFalse(self._walker.all_wants_satisfied([THREE]))
201         self.assertTrue(self._walker.all_wants_satisfied([TWO, THREE]))
202
203     def test_read_proto_line(self):
204         self._walker.proto.set_output([
205             'want %s' % ONE,
206             'want %s' % TWO,
207             'have %s' % THREE,
208             'foo %s' % FOUR,
209             'bar',
210             'done',
211             ])
212         self.assertEquals(('want', ONE), self._walker.read_proto_line())
213         self.assertEquals(('want', TWO), self._walker.read_proto_line())
214         self.assertEquals(('have', THREE), self._walker.read_proto_line())
215         self.assertRaises(GitProtocolError, self._walker.read_proto_line)
216         self.assertRaises(GitProtocolError, self._walker.read_proto_line)
217         self.assertEquals(('done', None), self._walker.read_proto_line())
218         self.assertEquals((None, None), self._walker.read_proto_line())
219
220     def test_determine_wants(self):
221         self.assertRaises(GitProtocolError, self._walker.determine_wants, {})
222
223         self._walker.proto.set_output([
224             'want %s multi_ack' % ONE,
225             'want %s' % TWO,
226             ])
227         heads = {'ref1': ONE, 'ref2': TWO, 'ref3': THREE}
228         self.assertEquals([ONE, TWO], self._walker.determine_wants(heads))
229
230         self._walker.proto.set_output(['want %s multi_ack' % FOUR])
231         self.assertRaises(GitProtocolError, self._walker.determine_wants, heads)
232
233         self._walker.proto.set_output([])
234         self.assertEquals([], self._walker.determine_wants(heads))
235
236         self._walker.proto.set_output(['want %s multi_ack' % ONE, 'foo'])
237         self.assertRaises(GitProtocolError, self._walker.determine_wants, heads)
238
239         self._walker.proto.set_output(['want %s multi_ack' % FOUR])
240         self.assertRaises(GitProtocolError, self._walker.determine_wants, heads)
241
242     # TODO: test commit time cutoff
243
244
245 class TestProtocolGraphWalker(object):
246     def __init__(self):
247         self.acks = []
248         self.lines = []
249         self.done = False
250         self.stateless_rpc = False
251         self.advertise_refs = False
252
253     def read_proto_line(self):
254         return self.lines.pop(0)
255
256     def send_ack(self, sha, ack_type=''):
257         self.acks.append((sha, ack_type))
258
259     def send_nak(self):
260         self.acks.append((None, 'nak'))
261
262     def all_wants_satisfied(self, haves):
263         return self.done
264
265     def pop_ack(self):
266         if not self.acks:
267             return None
268         return self.acks.pop(0)
269
270
271 class AckGraphWalkerImplTestCase(TestCase):
272     """Base setup and asserts for AckGraphWalker tests."""
273     def setUp(self):
274         self._walker = TestProtocolGraphWalker()
275         self._walker.lines = [
276             ('have', TWO),
277             ('have', ONE),
278             ('have', THREE),
279             ('done', None),
280             ]
281         self._impl = self.impl_cls(self._walker)
282
283     def assertNoAck(self):
284         self.assertEquals(None, self._walker.pop_ack())
285
286     def assertAcks(self, acks):
287         for sha, ack_type in acks:
288             self.assertEquals((sha, ack_type), self._walker.pop_ack())
289         self.assertNoAck()
290
291     def assertAck(self, sha, ack_type=''):
292         self.assertAcks([(sha, ack_type)])
293
294     def assertNak(self):
295         self.assertAck(None, 'nak')
296
297     def assertNextEquals(self, sha):
298         self.assertEquals(sha, self._impl.next())
299
300
301 class SingleAckGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
302     impl_cls = SingleAckGraphWalkerImpl
303
304     def test_single_ack(self):
305         self.assertNextEquals(TWO)
306         self.assertNoAck()
307
308         self.assertNextEquals(ONE)
309         self._walker.done = True
310         self._impl.ack(ONE)
311         self.assertAck(ONE)
312
313         self.assertNextEquals(THREE)
314         self._impl.ack(THREE)
315         self.assertNoAck()
316
317         self.assertNextEquals(None)
318         self.assertNoAck()
319
320     def test_single_ack_flush(self):
321         # same as ack test but ends with a flush-pkt instead of done
322         self._walker.lines[-1] = (None, None)
323
324         self.assertNextEquals(TWO)
325         self.assertNoAck()
326
327         self.assertNextEquals(ONE)
328         self._walker.done = True
329         self._impl.ack(ONE)
330         self.assertAck(ONE)
331
332         self.assertNextEquals(THREE)
333         self.assertNoAck()
334
335         self.assertNextEquals(None)
336         self.assertNoAck()
337
338     def test_single_ack_nak(self):
339         self.assertNextEquals(TWO)
340         self.assertNoAck()
341
342         self.assertNextEquals(ONE)
343         self.assertNoAck()
344
345         self.assertNextEquals(THREE)
346         self.assertNoAck()
347
348         self.assertNextEquals(None)
349         self.assertNak()
350
351     def test_single_ack_nak_flush(self):
352         # same as nak test but ends with a flush-pkt instead of done
353         self._walker.lines[-1] = (None, None)
354
355         self.assertNextEquals(TWO)
356         self.assertNoAck()
357
358         self.assertNextEquals(ONE)
359         self.assertNoAck()
360
361         self.assertNextEquals(THREE)
362         self.assertNoAck()
363
364         self.assertNextEquals(None)
365         self.assertNak()
366
367 class MultiAckGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
368     impl_cls = MultiAckGraphWalkerImpl
369
370     def test_multi_ack(self):
371         self.assertNextEquals(TWO)
372         self.assertNoAck()
373
374         self.assertNextEquals(ONE)
375         self._walker.done = True
376         self._impl.ack(ONE)
377         self.assertAck(ONE, 'continue')
378
379         self.assertNextEquals(THREE)
380         self._impl.ack(THREE)
381         self.assertAck(THREE, 'continue')
382
383         self.assertNextEquals(None)
384         self.assertAck(THREE)
385
386     def test_multi_ack_partial(self):
387         self.assertNextEquals(TWO)
388         self.assertNoAck()
389
390         self.assertNextEquals(ONE)
391         self._impl.ack(ONE)
392         self.assertAck(ONE, 'continue')
393
394         self.assertNextEquals(THREE)
395         self.assertNoAck()
396
397         self.assertNextEquals(None)
398         # done, re-send ack of last common
399         self.assertAck(ONE)
400
401     def test_multi_ack_flush(self):
402         self._walker.lines = [
403             ('have', TWO),
404             (None, None),
405             ('have', ONE),
406             ('have', THREE),
407             ('done', None),
408             ]
409         self.assertNextEquals(TWO)
410         self.assertNoAck()
411
412         self.assertNextEquals(ONE)
413         self.assertNak() # nak the flush-pkt
414
415         self._walker.done = True
416         self._impl.ack(ONE)
417         self.assertAck(ONE, 'continue')
418
419         self.assertNextEquals(THREE)
420         self._impl.ack(THREE)
421         self.assertAck(THREE, 'continue')
422
423         self.assertNextEquals(None)
424         self.assertAck(THREE)
425
426     def test_multi_ack_nak(self):
427         self.assertNextEquals(TWO)
428         self.assertNoAck()
429
430         self.assertNextEquals(ONE)
431         self.assertNoAck()
432
433         self.assertNextEquals(THREE)
434         self.assertNoAck()
435
436         self.assertNextEquals(None)
437         self.assertNak()
438
439 class MultiAckDetailedGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
440     impl_cls = MultiAckDetailedGraphWalkerImpl
441
442     def test_multi_ack(self):
443         self.assertNextEquals(TWO)
444         self.assertNoAck()
445
446         self.assertNextEquals(ONE)
447         self._walker.done = True
448         self._impl.ack(ONE)
449         self.assertAcks([(ONE, 'common'), (ONE, 'ready')])
450
451         self.assertNextEquals(THREE)
452         self._impl.ack(THREE)
453         self.assertAck(THREE, 'ready')
454
455         self.assertNextEquals(None)
456         self.assertAck(THREE)
457
458     def test_multi_ack_partial(self):
459         self.assertNextEquals(TWO)
460         self.assertNoAck()
461
462         self.assertNextEquals(ONE)
463         self._impl.ack(ONE)
464         self.assertAck(ONE, 'common')
465
466         self.assertNextEquals(THREE)
467         self.assertNoAck()
468
469         self.assertNextEquals(None)
470         # done, re-send ack of last common
471         self.assertAck(ONE)
472
473     def test_multi_ack_flush(self):
474         # same as ack test but contains a flush-pkt in the middle
475         self._walker.lines = [
476             ('have', TWO),
477             (None, None),
478             ('have', ONE),
479             ('have', THREE),
480             ('done', None),
481             ]
482         self.assertNextEquals(TWO)
483         self.assertNoAck()
484
485         self.assertNextEquals(ONE)
486         self.assertNak() # nak the flush-pkt
487
488         self._walker.done = True
489         self._impl.ack(ONE)
490         self.assertAcks([(ONE, 'common'), (ONE, 'ready')])
491
492         self.assertNextEquals(THREE)
493         self._impl.ack(THREE)
494         self.assertAck(THREE, 'ready')
495
496         self.assertNextEquals(None)
497         self.assertAck(THREE)
498
499     def test_multi_ack_nak(self):
500         self.assertNextEquals(TWO)
501         self.assertNoAck()
502
503         self.assertNextEquals(ONE)
504         self.assertNoAck()
505
506         self.assertNextEquals(THREE)
507         self.assertNoAck()
508
509         self.assertNextEquals(None)
510         self.assertNak()
511
512     def test_multi_ack_nak_flush(self):
513         # same as nak test but contains a flush-pkt in the middle
514         self._walker.lines = [
515             ('have', TWO),
516             (None, None),
517             ('have', ONE),
518             ('have', THREE),
519             ('done', None),
520             ]
521         self.assertNextEquals(TWO)
522         self.assertNoAck()
523
524         self.assertNextEquals(ONE)
525         self.assertNak()
526
527         self.assertNextEquals(THREE)
528         self.assertNoAck()
529
530         self.assertNextEquals(None)
531         self.assertNak()
532
533     def test_multi_ack_stateless(self):
534         # transmission ends with a flush-pkt
535         self._walker.lines[-1] = (None, None)
536         self._walker.stateless_rpc = True
537
538         self.assertNextEquals(TWO)
539         self.assertNoAck()
540
541         self.assertNextEquals(ONE)
542         self.assertNoAck()
543
544         self.assertNextEquals(THREE)
545         self.assertNoAck()
546
547         self.assertNextEquals(None)
548         self.assertNak()