gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.app / org.gvsig.scripting.app.mainplugin / src / main / resources-plugin / scripting / lib / dulwich / tests / test_server.py @ 959
History | View | Annotate | Download (34.7 KB)
1 |
# test_server.py -- Tests for the git server
|
---|---|
2 |
# Copyright (C) 2010 Google, Inc.
|
3 |
#
|
4 |
# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
|
5 |
# General Public License as public by the Free Software Foundation; version 2.0
|
6 |
# or (at your option) any later version. You can redistribute it and/or
|
7 |
# modify it under the terms of either of these two licenses.
|
8 |
#
|
9 |
# Unless required by applicable law or agreed to in writing, software
|
10 |
# distributed under the License is distributed on an "AS IS" BASIS,
|
11 |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12 |
# See the License for the specific language governing permissions and
|
13 |
# limitations under the License.
|
14 |
#
|
15 |
# You should have received a copy of the licenses; if not, see
|
16 |
# <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
|
17 |
# and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
|
18 |
# License, Version 2.0.
|
19 |
#
|
20 |
|
21 |
"""Tests for the smart protocol server."""
|
22 |
|
23 |
from io import BytesIO |
24 |
import os |
25 |
import shutil |
26 |
import tempfile |
27 |
|
28 |
import sys |
29 |
|
30 |
from dulwich.errors import ( |
31 |
GitProtocolError, |
32 |
NotGitRepository, |
33 |
UnexpectedCommandError, |
34 |
HangupException, |
35 |
) |
36 |
from dulwich.object_store import ( |
37 |
MemoryObjectStore, |
38 |
) |
39 |
from dulwich.repo import ( |
40 |
MemoryRepo, |
41 |
Repo, |
42 |
) |
43 |
from dulwich.server import ( |
44 |
Backend, |
45 |
DictBackend, |
46 |
FileSystemBackend, |
47 |
MultiAckGraphWalkerImpl, |
48 |
MultiAckDetailedGraphWalkerImpl, |
49 |
PackHandler, |
50 |
_split_proto_line, |
51 |
serve_command, |
52 |
_find_shallow, |
53 |
ProtocolGraphWalker, |
54 |
ReceivePackHandler, |
55 |
SingleAckGraphWalkerImpl, |
56 |
UploadPackHandler, |
57 |
update_server_info, |
58 |
) |
59 |
from dulwich.tests import TestCase |
60 |
from dulwich.tests.utils import ( |
61 |
make_commit, |
62 |
make_tag, |
63 |
) |
64 |
from dulwich.protocol import ( |
65 |
ZERO_SHA, |
66 |
) |
67 |
|
68 |
ONE = b'1' * 40 |
69 |
TWO = b'2' * 40 |
70 |
THREE = b'3' * 40 |
71 |
FOUR = b'4' * 40 |
72 |
FIVE = b'5' * 40 |
73 |
SIX = b'6' * 40 |
74 |
|
75 |
|
76 |
class TestProto(object): |
77 |
|
78 |
def __init__(self): |
79 |
self._output = []
|
80 |
self._received = {0: [], 1: [], 2: [], 3: []} |
81 |
|
82 |
def set_output(self, output_lines): |
83 |
self._output = output_lines
|
84 |
|
85 |
def read_pkt_line(self): |
86 |
if self._output: |
87 |
data = self._output.pop(0) |
88 |
if data is not None: |
89 |
return data.rstrip() + b'\n' |
90 |
else:
|
91 |
# flush-pkt ('0000').
|
92 |
return None |
93 |
else:
|
94 |
raise HangupException()
|
95 |
|
96 |
def write_sideband(self, band, data): |
97 |
self._received[band].append(data)
|
98 |
|
99 |
def write_pkt_line(self, data): |
100 |
self._received[0].append(data) |
101 |
|
102 |
def get_received_line(self, band=0): |
103 |
lines = self._received[band]
|
104 |
return lines.pop(0) |
105 |
|
106 |
|
107 |
class TestGenericPackHandler(PackHandler): |
108 |
|
109 |
def __init__(self): |
110 |
PackHandler.__init__(self, Backend(), None) |
111 |
|
112 |
@classmethod
|
113 |
def capabilities(cls): |
114 |
return (b'cap1', b'cap2', b'cap3') |
115 |
|
116 |
@classmethod
|
117 |
def required_capabilities(cls): |
118 |
return (b'cap2',) |
119 |
|
120 |
|
121 |
class HandlerTestCase(TestCase): |
122 |
|
123 |
def setUp(self): |
124 |
super(HandlerTestCase, self).setUp() |
125 |
self._handler = TestGenericPackHandler()
|
126 |
|
127 |
def assertSucceeds(self, func, *args, **kwargs): |
128 |
try:
|
129 |
func(*args, **kwargs) |
130 |
except GitProtocolError as e: |
131 |
self.fail(e)
|
132 |
|
133 |
def test_capability_line(self): |
134 |
self.assertEqual(b' cap1 cap2 cap3', self._handler.capability_line()) |
135 |
|
136 |
def test_set_client_capabilities(self): |
137 |
set_caps = self._handler.set_client_capabilities
|
138 |
self.assertSucceeds(set_caps, [b'cap2']) |
139 |
self.assertSucceeds(set_caps, [b'cap1', b'cap2']) |
140 |
|
141 |
# different order
|
142 |
self.assertSucceeds(set_caps, [b'cap3', b'cap1', b'cap2']) |
143 |
|
144 |
# error cases
|
145 |
self.assertRaises(GitProtocolError, set_caps, [b'capxxx', b'cap2']) |
146 |
self.assertRaises(GitProtocolError, set_caps, [b'cap1', b'cap3']) |
147 |
|
148 |
# ignore innocuous but unknown capabilities
|
149 |
self.assertRaises(GitProtocolError, set_caps, [b'cap2', b'ignoreme']) |
150 |
self.assertFalse(b'ignoreme' in self._handler.capabilities()) |
151 |
self._handler.innocuous_capabilities = lambda: (b'ignoreme',) |
152 |
self.assertSucceeds(set_caps, [b'cap2', b'ignoreme']) |
153 |
|
154 |
def test_has_capability(self): |
155 |
self.assertRaises(GitProtocolError, self._handler.has_capability, b'cap') |
156 |
caps = self._handler.capabilities()
|
157 |
self._handler.set_client_capabilities(caps)
|
158 |
for cap in caps: |
159 |
self.assertTrue(self._handler.has_capability(cap)) |
160 |
self.assertFalse(self._handler.has_capability(b'capxxx')) |
161 |
|
162 |
|
163 |
class UploadPackHandlerTestCase(TestCase): |
164 |
|
165 |
def setUp(self): |
166 |
super(UploadPackHandlerTestCase, self).setUp() |
167 |
self._repo = MemoryRepo.init_bare([], {})
|
168 |
backend = DictBackend({b'/': self._repo}) |
169 |
self._handler = UploadPackHandler(
|
170 |
backend, [b'/', b'host=lolcathost'], TestProto()) |
171 |
|
172 |
def test_progress(self): |
173 |
caps = self._handler.required_capabilities()
|
174 |
self._handler.set_client_capabilities(caps)
|
175 |
self._handler.progress(b'first message') |
176 |
self._handler.progress(b'second message') |
177 |
self.assertEqual(b'first message', |
178 |
self._handler.proto.get_received_line(2)) |
179 |
self.assertEqual(b'second message', |
180 |
self._handler.proto.get_received_line(2)) |
181 |
self.assertRaises(IndexError, self._handler.proto.get_received_line, 2) |
182 |
|
183 |
def test_no_progress(self): |
184 |
caps = list(self._handler.required_capabilities()) + [b'no-progress'] |
185 |
self._handler.set_client_capabilities(caps)
|
186 |
self._handler.progress(b'first message') |
187 |
self._handler.progress(b'second message') |
188 |
self.assertRaises(IndexError, self._handler.proto.get_received_line, 2) |
189 |
|
190 |
def test_get_tagged(self): |
191 |
refs = { |
192 |
b'refs/tags/tag1': ONE,
|
193 |
b'refs/tags/tag2': TWO,
|
194 |
b'refs/heads/master': FOUR, # not a tag, no peeled value |
195 |
} |
196 |
# repo needs to peel this object
|
197 |
self._repo.object_store.add_object(make_commit(id=FOUR))
|
198 |
self._repo.refs._update(refs)
|
199 |
peeled = { |
200 |
b'refs/tags/tag1': b'1234' * 10, |
201 |
b'refs/tags/tag2': b'5678' * 10, |
202 |
} |
203 |
self._repo.refs._update_peeled(peeled)
|
204 |
|
205 |
caps = list(self._handler.required_capabilities()) + [b'include-tag'] |
206 |
self._handler.set_client_capabilities(caps)
|
207 |
self.assertEqual({b'1234' * 10: ONE, b'5678' * 10: TWO}, |
208 |
self._handler.get_tagged(refs, repo=self._repo)) |
209 |
|
210 |
# non-include-tag case
|
211 |
caps = self._handler.required_capabilities()
|
212 |
self._handler.set_client_capabilities(caps)
|
213 |
self.assertEqual({}, self._handler.get_tagged(refs, repo=self._repo)) |
214 |
|
215 |
|
216 |
class FindShallowTests(TestCase): |
217 |
|
218 |
def setUp(self): |
219 |
super(FindShallowTests, self).setUp() |
220 |
self._store = MemoryObjectStore()
|
221 |
|
222 |
def make_commit(self, **attrs): |
223 |
commit = make_commit(**attrs) |
224 |
self._store.add_object(commit)
|
225 |
return commit
|
226 |
|
227 |
def make_linear_commits(self, n, message=b''): |
228 |
commits = [] |
229 |
parents = [] |
230 |
for _ in range(n): |
231 |
commits.append(self.make_commit(parents=parents, message=message))
|
232 |
parents = [commits[-1].id]
|
233 |
return commits
|
234 |
|
235 |
def assertSameElements(self, expected, actual): |
236 |
self.assertEqual(set(expected), set(actual)) |
237 |
|
238 |
def test_linear(self): |
239 |
c1, c2, c3 = self.make_linear_commits(3) |
240 |
|
241 |
self.assertEqual((set([c3.id]), set([])), |
242 |
_find_shallow(self._store, [c3.id], 1)) |
243 |
self.assertEqual((set([c2.id]), set([c3.id])), |
244 |
_find_shallow(self._store, [c3.id], 2)) |
245 |
self.assertEqual((set([c1.id]), set([c2.id, c3.id])), |
246 |
_find_shallow(self._store, [c3.id], 3)) |
247 |
self.assertEqual((set([]), set([c1.id, c2.id, c3.id])), |
248 |
_find_shallow(self._store, [c3.id], 4)) |
249 |
|
250 |
def test_multiple_independent(self): |
251 |
a = self.make_linear_commits(2, message=b'a') |
252 |
b = self.make_linear_commits(2, message=b'b') |
253 |
c = self.make_linear_commits(2, message=b'c') |
254 |
heads = [a[1].id, b[1].id, c[1].id] |
255 |
|
256 |
self.assertEqual((set([a[0].id, b[0].id, c[0].id]), set(heads)), |
257 |
_find_shallow(self._store, heads, 2)) |
258 |
|
259 |
def test_multiple_overlapping(self): |
260 |
# Create the following commit tree:
|
261 |
# 1--2
|
262 |
# \
|
263 |
# 3--4
|
264 |
c1, c2 = self.make_linear_commits(2) |
265 |
c3 = self.make_commit(parents=[c1.id])
|
266 |
c4 = self.make_commit(parents=[c3.id])
|
267 |
|
268 |
# 1 is shallow along the path from 4, but not along the path from 2.
|
269 |
self.assertEqual((set([c1.id]), set([c1.id, c2.id, c3.id, c4.id])), |
270 |
_find_shallow(self._store, [c2.id, c4.id], 3)) |
271 |
|
272 |
def test_merge(self): |
273 |
c1 = self.make_commit()
|
274 |
c2 = self.make_commit()
|
275 |
c3 = self.make_commit(parents=[c1.id, c2.id])
|
276 |
|
277 |
self.assertEqual((set([c1.id, c2.id]), set([c3.id])), |
278 |
_find_shallow(self._store, [c3.id], 2)) |
279 |
|
280 |
def test_tag(self): |
281 |
c1, c2 = self.make_linear_commits(2) |
282 |
tag = make_tag(c2, name=b'tag')
|
283 |
self._store.add_object(tag)
|
284 |
|
285 |
self.assertEqual((set([c1.id]), set([c2.id])), |
286 |
_find_shallow(self._store, [tag.id], 2)) |
287 |
|
288 |
|
289 |
class TestUploadPackHandler(UploadPackHandler): |
290 |
@classmethod
|
291 |
def required_capabilities(self): |
292 |
return ()
|
293 |
|
294 |
class ReceivePackHandlerTestCase(TestCase): |
295 |
|
296 |
def setUp(self): |
297 |
super(ReceivePackHandlerTestCase, self).setUp() |
298 |
self._repo = MemoryRepo.init_bare([], {})
|
299 |
backend = DictBackend({b'/': self._repo}) |
300 |
self._handler = ReceivePackHandler(
|
301 |
backend, [b'/', b'host=lolcathost'], TestProto()) |
302 |
|
303 |
def test_apply_pack_del_ref(self): |
304 |
refs = { |
305 |
b'refs/heads/master': TWO,
|
306 |
b'refs/heads/fake-branch': ONE}
|
307 |
self._repo.refs._update(refs)
|
308 |
update_refs = [[ONE, ZERO_SHA, b'refs/heads/fake-branch'], ]
|
309 |
status = self._handler._apply_pack(update_refs)
|
310 |
self.assertEqual(status[0][0], b'unpack') |
311 |
self.assertEqual(status[0][1], b'ok') |
312 |
self.assertEqual(status[1][0], b'refs/heads/fake-branch') |
313 |
self.assertEqual(status[1][1], b'ok') |
314 |
|
315 |
|
316 |
class ProtocolGraphWalkerEmptyTestCase(TestCase): |
317 |
def setUp(self): |
318 |
super(ProtocolGraphWalkerEmptyTestCase, self).setUp() |
319 |
self._repo = MemoryRepo.init_bare([], {})
|
320 |
backend = DictBackend({b'/': self._repo}) |
321 |
self._walker = ProtocolGraphWalker(
|
322 |
TestUploadPackHandler(backend, [b'/', b'host=lolcats'], TestProto()), |
323 |
self._repo.object_store, self._repo.get_peeled) |
324 |
|
325 |
def test_empty_repository(self): |
326 |
# The server should wait for a flush packet.
|
327 |
self._walker.proto.set_output([])
|
328 |
self.assertRaises(HangupException, self._walker.determine_wants, {}) |
329 |
self.assertEqual(None, self._walker.proto.get_received_line()) |
330 |
|
331 |
self._walker.proto.set_output([None]) |
332 |
self.assertEqual([], self._walker.determine_wants({})) |
333 |
self.assertEqual(None, self._walker.proto.get_received_line()) |
334 |
|
335 |
|
336 |
|
337 |
class ProtocolGraphWalkerTestCase(TestCase): |
338 |
|
339 |
def setUp(self): |
340 |
super(ProtocolGraphWalkerTestCase, self).setUp() |
341 |
# Create the following commit tree:
|
342 |
# 3---5
|
343 |
# /
|
344 |
# 1---2---4
|
345 |
commits = [ |
346 |
make_commit(id=ONE, parents=[], commit_time=111),
|
347 |
make_commit(id=TWO, parents=[ONE], commit_time=222),
|
348 |
make_commit(id=THREE, parents=[ONE], commit_time=333),
|
349 |
make_commit(id=FOUR, parents=[TWO], commit_time=444),
|
350 |
make_commit(id=FIVE, parents=[THREE], commit_time=555),
|
351 |
] |
352 |
self._repo = MemoryRepo.init_bare(commits, {})
|
353 |
backend = DictBackend({b'/': self._repo}) |
354 |
self._walker = ProtocolGraphWalker(
|
355 |
TestUploadPackHandler(backend, [b'/', b'host=lolcats'], TestProto()), |
356 |
self._repo.object_store, self._repo.get_peeled) |
357 |
|
358 |
def test_all_wants_satisfied_no_haves(self): |
359 |
self._walker.set_wants([ONE])
|
360 |
self.assertFalse(self._walker.all_wants_satisfied([])) |
361 |
self._walker.set_wants([TWO])
|
362 |
self.assertFalse(self._walker.all_wants_satisfied([])) |
363 |
self._walker.set_wants([THREE])
|
364 |
self.assertFalse(self._walker.all_wants_satisfied([])) |
365 |
|
366 |
def test_all_wants_satisfied_have_root(self): |
367 |
self._walker.set_wants([ONE])
|
368 |
self.assertTrue(self._walker.all_wants_satisfied([ONE])) |
369 |
self._walker.set_wants([TWO])
|
370 |
self.assertTrue(self._walker.all_wants_satisfied([ONE])) |
371 |
self._walker.set_wants([THREE])
|
372 |
self.assertTrue(self._walker.all_wants_satisfied([ONE])) |
373 |
|
374 |
def test_all_wants_satisfied_have_branch(self): |
375 |
self._walker.set_wants([TWO])
|
376 |
self.assertTrue(self._walker.all_wants_satisfied([TWO])) |
377 |
# wrong branch
|
378 |
self._walker.set_wants([THREE])
|
379 |
self.assertFalse(self._walker.all_wants_satisfied([TWO])) |
380 |
|
381 |
def test_all_wants_satisfied(self): |
382 |
self._walker.set_wants([FOUR, FIVE])
|
383 |
# trivial case: wants == haves
|
384 |
self.assertTrue(self._walker.all_wants_satisfied([FOUR, FIVE])) |
385 |
# cases that require walking the commit tree
|
386 |
self.assertTrue(self._walker.all_wants_satisfied([ONE])) |
387 |
self.assertFalse(self._walker.all_wants_satisfied([TWO])) |
388 |
self.assertFalse(self._walker.all_wants_satisfied([THREE])) |
389 |
self.assertTrue(self._walker.all_wants_satisfied([TWO, THREE])) |
390 |
|
391 |
def test_split_proto_line(self): |
392 |
allowed = (b'want', b'done', None) |
393 |
self.assertEqual((b'want', ONE), |
394 |
_split_proto_line(b'want ' + ONE + b'\n', allowed)) |
395 |
self.assertEqual((b'want', TWO), |
396 |
_split_proto_line(b'want ' + TWO + b'\n', allowed)) |
397 |
self.assertRaises(GitProtocolError, _split_proto_line,
|
398 |
b'want xxxx\n', allowed)
|
399 |
self.assertRaises(UnexpectedCommandError, _split_proto_line,
|
400 |
b'have ' + THREE + b'\n', allowed) |
401 |
self.assertRaises(GitProtocolError, _split_proto_line,
|
402 |
b'foo ' + FOUR + b'\n', allowed) |
403 |
self.assertRaises(GitProtocolError, _split_proto_line, b'bar', allowed) |
404 |
self.assertEqual((b'done', None), _split_proto_line(b'done\n', allowed)) |
405 |
self.assertEqual((None, None), _split_proto_line(b'', allowed)) |
406 |
|
407 |
def test_determine_wants(self): |
408 |
self._walker.proto.set_output([None]) |
409 |
self.assertEqual([], self._walker.determine_wants({})) |
410 |
self.assertEqual(None, self._walker.proto.get_received_line()) |
411 |
|
412 |
self._walker.proto.set_output([
|
413 |
b'want ' + ONE + b' multi_ack', |
414 |
b'want ' + TWO,
|
415 |
None,
|
416 |
]) |
417 |
heads = { |
418 |
b'refs/heads/ref1': ONE,
|
419 |
b'refs/heads/ref2': TWO,
|
420 |
b'refs/heads/ref3': THREE,
|
421 |
} |
422 |
self._repo.refs._update(heads)
|
423 |
self.assertEqual([ONE, TWO], self._walker.determine_wants(heads)) |
424 |
|
425 |
self._walker.advertise_refs = True |
426 |
self.assertEqual([], self._walker.determine_wants(heads)) |
427 |
self._walker.advertise_refs = False |
428 |
|
429 |
self._walker.proto.set_output([b'want ' + FOUR + b' multi_ack', None]) |
430 |
self.assertRaises(GitProtocolError, self._walker.determine_wants, heads) |
431 |
|
432 |
self._walker.proto.set_output([None]) |
433 |
self.assertEqual([], self._walker.determine_wants(heads)) |
434 |
|
435 |
self._walker.proto.set_output([b'want ' + ONE + b' multi_ack', b'foo', None]) |
436 |
self.assertRaises(GitProtocolError, self._walker.determine_wants, heads) |
437 |
|
438 |
self._walker.proto.set_output([b'want ' + FOUR + b' multi_ack', None]) |
439 |
self.assertRaises(GitProtocolError, self._walker.determine_wants, heads) |
440 |
|
441 |
def test_determine_wants_advertisement(self): |
442 |
self._walker.proto.set_output([None]) |
443 |
# advertise branch tips plus tag
|
444 |
heads = { |
445 |
b'refs/heads/ref4': FOUR,
|
446 |
b'refs/heads/ref5': FIVE,
|
447 |
b'refs/heads/tag6': SIX,
|
448 |
} |
449 |
self._repo.refs._update(heads)
|
450 |
self._repo.refs._update_peeled(heads)
|
451 |
self._repo.refs._update_peeled({b'refs/heads/tag6': FIVE}) |
452 |
self._walker.determine_wants(heads)
|
453 |
lines = [] |
454 |
while True: |
455 |
line = self._walker.proto.get_received_line()
|
456 |
if line is None: |
457 |
break
|
458 |
# strip capabilities list if present
|
459 |
if b'\x00' in line: |
460 |
line = line[:line.index(b'\x00')]
|
461 |
lines.append(line.rstrip()) |
462 |
|
463 |
self.assertEqual([
|
464 |
FOUR + b' refs/heads/ref4',
|
465 |
FIVE + b' refs/heads/ref5',
|
466 |
FIVE + b' refs/heads/tag6^{}',
|
467 |
SIX + b' refs/heads/tag6',
|
468 |
], sorted(lines))
|
469 |
|
470 |
# ensure peeled tag was advertised immediately following tag
|
471 |
for i, line in enumerate(lines): |
472 |
if line.endswith(b' refs/heads/tag6'): |
473 |
self.assertEqual(FIVE + b' refs/heads/tag6^{}', lines[i+1]) |
474 |
|
475 |
# TODO: test commit time cutoff
|
476 |
|
477 |
def _handle_shallow_request(self, lines, heads): |
478 |
self._walker.proto.set_output(lines + [None]) |
479 |
self._walker._handle_shallow_request(heads)
|
480 |
|
481 |
def assertReceived(self, expected): |
482 |
self.assertEqual(
|
483 |
expected, list(iter(self._walker.proto.get_received_line, None))) |
484 |
|
485 |
def test_handle_shallow_request_no_client_shallows(self): |
486 |
self._handle_shallow_request([b'deepen 2\n'], [FOUR, FIVE]) |
487 |
self.assertEqual(set([TWO, THREE]), self._walker.shallow) |
488 |
self.assertReceived([
|
489 |
b'shallow ' + TWO,
|
490 |
b'shallow ' + THREE,
|
491 |
]) |
492 |
|
493 |
def test_handle_shallow_request_no_new_shallows(self): |
494 |
lines = [ |
495 |
b'shallow ' + TWO + b'\n', |
496 |
b'shallow ' + THREE + b'\n', |
497 |
b'deepen 2\n',
|
498 |
] |
499 |
self._handle_shallow_request(lines, [FOUR, FIVE])
|
500 |
self.assertEqual(set([TWO, THREE]), self._walker.shallow) |
501 |
self.assertReceived([])
|
502 |
|
503 |
def test_handle_shallow_request_unshallows(self): |
504 |
lines = [ |
505 |
b'shallow ' + TWO + b'\n', |
506 |
b'deepen 3\n',
|
507 |
] |
508 |
self._handle_shallow_request(lines, [FOUR, FIVE])
|
509 |
self.assertEqual(set([ONE]), self._walker.shallow) |
510 |
self.assertReceived([
|
511 |
b'shallow ' + ONE,
|
512 |
b'unshallow ' + TWO,
|
513 |
# THREE is unshallow but was is not shallow in the client
|
514 |
]) |
515 |
|
516 |
|
517 |
class TestProtocolGraphWalker(object): |
518 |
|
519 |
def __init__(self): |
520 |
self.acks = []
|
521 |
self.lines = []
|
522 |
self.wants_satisified = False |
523 |
self.http_req = None |
524 |
self.advertise_refs = False |
525 |
self._impl = None |
526 |
self.done_required = True |
527 |
self.done_received = False |
528 |
self._empty = False |
529 |
self.pack_sent = False |
530 |
|
531 |
def read_proto_line(self, allowed): |
532 |
command, sha = self.lines.pop(0) |
533 |
if allowed is not None: |
534 |
assert command in allowed |
535 |
return command, sha
|
536 |
|
537 |
def send_ack(self, sha, ack_type=b''): |
538 |
self.acks.append((sha, ack_type))
|
539 |
|
540 |
def send_nak(self): |
541 |
self.acks.append((None, b'nak')) |
542 |
|
543 |
def all_wants_satisfied(self, haves): |
544 |
if haves:
|
545 |
return self.wants_satisified |
546 |
|
547 |
def pop_ack(self): |
548 |
if not self.acks: |
549 |
return None |
550 |
return self.acks.pop(0) |
551 |
|
552 |
def handle_done(self): |
553 |
if not self._impl: |
554 |
return
|
555 |
# Whether or not PACK is sent after is determined by this, so
|
556 |
# record this value.
|
557 |
self.pack_sent = self._impl.handle_done(self.done_required, |
558 |
self.done_received)
|
559 |
return self.pack_sent |
560 |
|
561 |
def notify_done(self): |
562 |
self.done_received = True |
563 |
|
564 |
|
565 |
class AckGraphWalkerImplTestCase(TestCase): |
566 |
"""Base setup and asserts for AckGraphWalker tests."""
|
567 |
|
568 |
def setUp(self): |
569 |
super(AckGraphWalkerImplTestCase, self).setUp() |
570 |
self._walker = TestProtocolGraphWalker()
|
571 |
self._walker.lines = [
|
572 |
(b'have', TWO),
|
573 |
(b'have', ONE),
|
574 |
(b'have', THREE),
|
575 |
(b'done', None), |
576 |
] |
577 |
self._impl = self.impl_cls(self._walker) |
578 |
self._walker._impl = self._impl |
579 |
|
580 |
def assertNoAck(self): |
581 |
self.assertEqual(None, self._walker.pop_ack()) |
582 |
|
583 |
def assertAcks(self, acks): |
584 |
for sha, ack_type in acks: |
585 |
self.assertEqual((sha, ack_type), self._walker.pop_ack()) |
586 |
self.assertNoAck()
|
587 |
|
588 |
def assertAck(self, sha, ack_type=b''): |
589 |
self.assertAcks([(sha, ack_type)])
|
590 |
|
591 |
def assertNak(self): |
592 |
self.assertAck(None, b'nak') |
593 |
|
594 |
def assertNextEquals(self, sha): |
595 |
self.assertEqual(sha, next(self._impl)) |
596 |
|
597 |
def assertNextEmpty(self): |
598 |
# This is necessary because of no-done - the assumption that it
|
599 |
# it safe to immediately send out the final ACK is no longer
|
600 |
# true but the test is still needed for it. TestProtocolWalker
|
601 |
# does implement the handle_done which will determine whether
|
602 |
# the final confirmation can be sent.
|
603 |
self.assertRaises(IndexError, next, self._impl) |
604 |
self._walker.handle_done()
|
605 |
|
606 |
|
607 |
class SingleAckGraphWalkerImplTestCase(AckGraphWalkerImplTestCase): |
608 |
|
609 |
impl_cls = SingleAckGraphWalkerImpl |
610 |
|
611 |
def test_single_ack(self): |
612 |
self.assertNextEquals(TWO)
|
613 |
self.assertNoAck()
|
614 |
|
615 |
self.assertNextEquals(ONE)
|
616 |
self._impl.ack(ONE)
|
617 |
self.assertAck(ONE)
|
618 |
|
619 |
self.assertNextEquals(THREE)
|
620 |
self._impl.ack(THREE)
|
621 |
self.assertNoAck()
|
622 |
|
623 |
self.assertNextEquals(None) |
624 |
self.assertNoAck()
|
625 |
|
626 |
def test_single_ack_flush(self): |
627 |
# same as ack test but ends with a flush-pkt instead of done
|
628 |
self._walker.lines[-1] = (None, None) |
629 |
|
630 |
self.assertNextEquals(TWO)
|
631 |
self.assertNoAck()
|
632 |
|
633 |
self.assertNextEquals(ONE)
|
634 |
self._impl.ack(ONE)
|
635 |
self.assertAck(ONE)
|
636 |
|
637 |
self.assertNextEquals(THREE)
|
638 |
self.assertNoAck()
|
639 |
|
640 |
self.assertNextEquals(None) |
641 |
self.assertNoAck()
|
642 |
|
643 |
def test_single_ack_nak(self): |
644 |
self.assertNextEquals(TWO)
|
645 |
self.assertNoAck()
|
646 |
|
647 |
self.assertNextEquals(ONE)
|
648 |
self.assertNoAck()
|
649 |
|
650 |
self.assertNextEquals(THREE)
|
651 |
self.assertNoAck()
|
652 |
|
653 |
self.assertNextEquals(None) |
654 |
self.assertNextEmpty()
|
655 |
self.assertNak()
|
656 |
|
657 |
def test_single_ack_nak_flush(self): |
658 |
# same as nak test but ends with a flush-pkt instead of done
|
659 |
self._walker.lines[-1] = (None, None) |
660 |
|
661 |
self.assertNextEquals(TWO)
|
662 |
self.assertNoAck()
|
663 |
|
664 |
self.assertNextEquals(ONE)
|
665 |
self.assertNoAck()
|
666 |
|
667 |
self.assertNextEquals(THREE)
|
668 |
self.assertNoAck()
|
669 |
|
670 |
self.assertNextEquals(None) |
671 |
self.assertNextEmpty()
|
672 |
self.assertNak()
|
673 |
|
674 |
|
675 |
class MultiAckGraphWalkerImplTestCase(AckGraphWalkerImplTestCase): |
676 |
|
677 |
impl_cls = MultiAckGraphWalkerImpl |
678 |
|
679 |
def test_multi_ack(self): |
680 |
self.assertNextEquals(TWO)
|
681 |
self.assertNoAck()
|
682 |
|
683 |
self.assertNextEquals(ONE)
|
684 |
self._impl.ack(ONE)
|
685 |
self.assertAck(ONE, b'continue') |
686 |
|
687 |
self.assertNextEquals(THREE)
|
688 |
self._impl.ack(THREE)
|
689 |
self.assertAck(THREE, b'continue') |
690 |
|
691 |
self.assertNextEquals(None) |
692 |
self.assertNextEmpty()
|
693 |
self.assertAck(THREE)
|
694 |
|
695 |
def test_multi_ack_partial(self): |
696 |
self.assertNextEquals(TWO)
|
697 |
self.assertNoAck()
|
698 |
|
699 |
self.assertNextEquals(ONE)
|
700 |
self._impl.ack(ONE)
|
701 |
self.assertAck(ONE, b'continue') |
702 |
|
703 |
self.assertNextEquals(THREE)
|
704 |
self.assertNoAck()
|
705 |
|
706 |
self.assertNextEquals(None) |
707 |
self.assertNextEmpty()
|
708 |
self.assertAck(ONE)
|
709 |
|
710 |
def test_multi_ack_flush(self): |
711 |
self._walker.lines = [
|
712 |
(b'have', TWO),
|
713 |
(None, None), |
714 |
(b'have', ONE),
|
715 |
(b'have', THREE),
|
716 |
(b'done', None), |
717 |
] |
718 |
self.assertNextEquals(TWO)
|
719 |
self.assertNoAck()
|
720 |
|
721 |
self.assertNextEquals(ONE)
|
722 |
self.assertNak() # nak the flush-pkt |
723 |
|
724 |
self._impl.ack(ONE)
|
725 |
self.assertAck(ONE, b'continue') |
726 |
|
727 |
self.assertNextEquals(THREE)
|
728 |
self._impl.ack(THREE)
|
729 |
self.assertAck(THREE, b'continue') |
730 |
|
731 |
self.assertNextEquals(None) |
732 |
self.assertNextEmpty()
|
733 |
self.assertAck(THREE)
|
734 |
|
735 |
def test_multi_ack_nak(self): |
736 |
self.assertNextEquals(TWO)
|
737 |
self.assertNoAck()
|
738 |
|
739 |
self.assertNextEquals(ONE)
|
740 |
self.assertNoAck()
|
741 |
|
742 |
self.assertNextEquals(THREE)
|
743 |
self.assertNoAck()
|
744 |
|
745 |
self.assertNextEquals(None) |
746 |
self.assertNextEmpty()
|
747 |
self.assertNak()
|
748 |
|
749 |
|
750 |
class MultiAckDetailedGraphWalkerImplTestCase(AckGraphWalkerImplTestCase): |
751 |
|
752 |
impl_cls = MultiAckDetailedGraphWalkerImpl |
753 |
|
754 |
def test_multi_ack(self): |
755 |
self.assertNextEquals(TWO)
|
756 |
self.assertNoAck()
|
757 |
|
758 |
self.assertNextEquals(ONE)
|
759 |
self._impl.ack(ONE)
|
760 |
self.assertAck(ONE, b'common') |
761 |
|
762 |
self.assertNextEquals(THREE)
|
763 |
self._impl.ack(THREE)
|
764 |
self.assertAck(THREE, b'common') |
765 |
|
766 |
# done is read.
|
767 |
self._walker.wants_satisified = True |
768 |
self.assertNextEquals(None) |
769 |
self._walker.lines.append((None, None)) |
770 |
self.assertNextEmpty()
|
771 |
self.assertAcks([(THREE, b'ready'), (None, b'nak'), (THREE, b'')]) |
772 |
# PACK is sent
|
773 |
self.assertTrue(self._walker.pack_sent) |
774 |
|
775 |
def test_multi_ack_nodone(self): |
776 |
self._walker.done_required = False |
777 |
self.assertNextEquals(TWO)
|
778 |
self.assertNoAck()
|
779 |
|
780 |
self.assertNextEquals(ONE)
|
781 |
self._impl.ack(ONE)
|
782 |
self.assertAck(ONE, b'common') |
783 |
|
784 |
self.assertNextEquals(THREE)
|
785 |
self._impl.ack(THREE)
|
786 |
self.assertAck(THREE, b'common') |
787 |
|
788 |
# done is read.
|
789 |
self._walker.wants_satisified = True |
790 |
self.assertNextEquals(None) |
791 |
self._walker.lines.append((None, None)) |
792 |
self.assertNextEmpty()
|
793 |
self.assertAcks([(THREE, b'ready'), (None, b'nak'), (THREE, b'')]) |
794 |
# PACK is sent
|
795 |
self.assertTrue(self._walker.pack_sent) |
796 |
|
797 |
def test_multi_ack_flush_end(self): |
798 |
# transmission ends with a flush-pkt without a done but no-done is
|
799 |
# assumed.
|
800 |
self._walker.lines[-1] = (None, None) |
801 |
self.assertNextEquals(TWO)
|
802 |
self.assertNoAck()
|
803 |
|
804 |
self.assertNextEquals(ONE)
|
805 |
self._impl.ack(ONE)
|
806 |
self.assertAck(ONE, b'common') |
807 |
|
808 |
self.assertNextEquals(THREE)
|
809 |
self._impl.ack(THREE)
|
810 |
self.assertAck(THREE, b'common') |
811 |
|
812 |
# no done is read
|
813 |
self._walker.wants_satisified = True |
814 |
self.assertNextEmpty()
|
815 |
self.assertAcks([(THREE, b'ready'), (None, b'nak')]) |
816 |
# PACK is NOT sent
|
817 |
self.assertFalse(self._walker.pack_sent) |
818 |
|
819 |
def test_multi_ack_flush_end_nodone(self): |
820 |
# transmission ends with a flush-pkt without a done but no-done is
|
821 |
# assumed.
|
822 |
self._walker.lines[-1] = (None, None) |
823 |
self._walker.done_required = False |
824 |
self.assertNextEquals(TWO)
|
825 |
self.assertNoAck()
|
826 |
|
827 |
self.assertNextEquals(ONE)
|
828 |
self._impl.ack(ONE)
|
829 |
self.assertAck(ONE, b'common') |
830 |
|
831 |
self.assertNextEquals(THREE)
|
832 |
self._impl.ack(THREE)
|
833 |
self.assertAck(THREE, b'common') |
834 |
|
835 |
# no done is read, but pretend it is (last 'ACK 'commit_id' '')
|
836 |
self._walker.wants_satisified = True |
837 |
self.assertNextEmpty()
|
838 |
self.assertAcks([(THREE, b'ready'), (None, b'nak'), (THREE, b'')]) |
839 |
# PACK is sent
|
840 |
self.assertTrue(self._walker.pack_sent) |
841 |
|
842 |
def test_multi_ack_partial(self): |
843 |
self.assertNextEquals(TWO)
|
844 |
self.assertNoAck()
|
845 |
|
846 |
self.assertNextEquals(ONE)
|
847 |
self._impl.ack(ONE)
|
848 |
self.assertAck(ONE, b'common') |
849 |
|
850 |
self.assertNextEquals(THREE)
|
851 |
self.assertNoAck()
|
852 |
|
853 |
self.assertNextEquals(None) |
854 |
self.assertNextEmpty()
|
855 |
self.assertAck(ONE)
|
856 |
|
857 |
def test_multi_ack_flush(self): |
858 |
# same as ack test but contains a flush-pkt in the middle
|
859 |
self._walker.lines = [
|
860 |
(b'have', TWO),
|
861 |
(None, None), |
862 |
(b'have', ONE),
|
863 |
(b'have', THREE),
|
864 |
(b'done', None), |
865 |
(None, None), |
866 |
] |
867 |
self.assertNextEquals(TWO)
|
868 |
self.assertNoAck()
|
869 |
|
870 |
self.assertNextEquals(ONE)
|
871 |
self.assertNak() # nak the flush-pkt |
872 |
|
873 |
self._impl.ack(ONE)
|
874 |
self.assertAck(ONE, b'common') |
875 |
|
876 |
self.assertNextEquals(THREE)
|
877 |
self._impl.ack(THREE)
|
878 |
self.assertAck(THREE, b'common') |
879 |
|
880 |
self._walker.wants_satisified = True |
881 |
self.assertNextEquals(None) |
882 |
self.assertNextEmpty()
|
883 |
self.assertAcks([(THREE, b'ready'), (None, b'nak'), (THREE, b'')]) |
884 |
|
885 |
def test_multi_ack_nak(self): |
886 |
self.assertNextEquals(TWO)
|
887 |
self.assertNoAck()
|
888 |
|
889 |
self.assertNextEquals(ONE)
|
890 |
self.assertNoAck()
|
891 |
|
892 |
self.assertNextEquals(THREE)
|
893 |
self.assertNoAck()
|
894 |
|
895 |
# Done is sent here.
|
896 |
self.assertNextEquals(None) |
897 |
self.assertNextEmpty()
|
898 |
self.assertNak()
|
899 |
self.assertNextEmpty()
|
900 |
self.assertTrue(self._walker.pack_sent) |
901 |
|
902 |
def test_multi_ack_nak_nodone(self): |
903 |
self._walker.done_required = False |
904 |
self.assertNextEquals(TWO)
|
905 |
self.assertNoAck()
|
906 |
|
907 |
self.assertNextEquals(ONE)
|
908 |
self.assertNoAck()
|
909 |
|
910 |
self.assertNextEquals(THREE)
|
911 |
self.assertNoAck()
|
912 |
|
913 |
# Done is sent here.
|
914 |
self.assertFalse(self._walker.pack_sent) |
915 |
self.assertNextEquals(None) |
916 |
self.assertNextEmpty()
|
917 |
self.assertTrue(self._walker.pack_sent) |
918 |
self.assertNak()
|
919 |
self.assertNextEmpty()
|
920 |
|
921 |
def test_multi_ack_nak_flush(self): |
922 |
# same as nak test but contains a flush-pkt in the middle
|
923 |
self._walker.lines = [
|
924 |
(b'have', TWO),
|
925 |
(None, None), |
926 |
(b'have', ONE),
|
927 |
(b'have', THREE),
|
928 |
(b'done', None), |
929 |
] |
930 |
self.assertNextEquals(TWO)
|
931 |
self.assertNoAck()
|
932 |
|
933 |
self.assertNextEquals(ONE)
|
934 |
self.assertNak()
|
935 |
|
936 |
self.assertNextEquals(THREE)
|
937 |
self.assertNoAck()
|
938 |
|
939 |
self.assertNextEquals(None) |
940 |
self.assertNextEmpty()
|
941 |
self.assertNak()
|
942 |
|
943 |
def test_multi_ack_stateless(self): |
944 |
# transmission ends with a flush-pkt
|
945 |
self._walker.lines[-1] = (None, None) |
946 |
self._walker.http_req = True |
947 |
|
948 |
self.assertNextEquals(TWO)
|
949 |
self.assertNoAck()
|
950 |
|
951 |
self.assertNextEquals(ONE)
|
952 |
self.assertNoAck()
|
953 |
|
954 |
self.assertNextEquals(THREE)
|
955 |
self.assertNoAck()
|
956 |
|
957 |
self.assertFalse(self._walker.pack_sent) |
958 |
self.assertNextEquals(None) |
959 |
self.assertNak()
|
960 |
|
961 |
self.assertNextEmpty()
|
962 |
self.assertNoAck()
|
963 |
self.assertFalse(self._walker.pack_sent) |
964 |
|
965 |
def test_multi_ack_stateless_nodone(self): |
966 |
self._walker.done_required = False |
967 |
# transmission ends with a flush-pkt
|
968 |
self._walker.lines[-1] = (None, None) |
969 |
self._walker.http_req = True |
970 |
|
971 |
self.assertNextEquals(TWO)
|
972 |
self.assertNoAck()
|
973 |
|
974 |
self.assertNextEquals(ONE)
|
975 |
self.assertNoAck()
|
976 |
|
977 |
self.assertNextEquals(THREE)
|
978 |
self.assertNoAck()
|
979 |
|
980 |
self.assertFalse(self._walker.pack_sent) |
981 |
self.assertNextEquals(None) |
982 |
self.assertNak()
|
983 |
|
984 |
self.assertNextEmpty()
|
985 |
self.assertNoAck()
|
986 |
# PACK will still not be sent.
|
987 |
self.assertFalse(self._walker.pack_sent) |
988 |
|
989 |
|
990 |
class FileSystemBackendTests(TestCase): |
991 |
"""Tests for FileSystemBackend."""
|
992 |
|
993 |
def setUp(self): |
994 |
super(FileSystemBackendTests, self).setUp() |
995 |
self.path = tempfile.mkdtemp()
|
996 |
self.addCleanup(shutil.rmtree, self.path) |
997 |
self.repo = Repo.init(self.path) |
998 |
if sys.platform == 'win32': |
999 |
self.backend = FileSystemBackend(self.path[0] + ':' + os.sep) |
1000 |
else:
|
1001 |
self.backend = FileSystemBackend()
|
1002 |
|
1003 |
def test_nonexistant(self): |
1004 |
self.assertRaises(NotGitRepository,
|
1005 |
self.backend.open_repository, "/does/not/exist/unless/foo") |
1006 |
|
1007 |
def test_absolute(self): |
1008 |
repo = self.backend.open_repository(self.path) |
1009 |
self.assertEqual(
|
1010 |
os.path.normcase(os.path.abspath(repo.path)), |
1011 |
os.path.normcase(os.path.abspath(self.repo.path)))
|
1012 |
|
1013 |
def test_child(self): |
1014 |
self.assertRaises(NotGitRepository,
|
1015 |
self.backend.open_repository, os.path.join(self.path, "foo")) |
1016 |
|
1017 |
def test_bad_repo_path(self): |
1018 |
backend = FileSystemBackend() |
1019 |
|
1020 |
self.assertRaises(NotGitRepository,
|
1021 |
lambda: backend.open_repository('/ups')) |
1022 |
|
1023 |
|
1024 |
class DictBackendTests(TestCase): |
1025 |
"""Tests for DictBackend."""
|
1026 |
|
1027 |
def test_nonexistant(self): |
1028 |
repo = MemoryRepo.init_bare([], {}) |
1029 |
backend = DictBackend({b'/': repo})
|
1030 |
self.assertRaises(NotGitRepository,
|
1031 |
backend.open_repository, "/does/not/exist/unless/foo")
|
1032 |
|
1033 |
def test_bad_repo_path(self): |
1034 |
repo = MemoryRepo.init_bare([], {}) |
1035 |
backend = DictBackend({b'/': repo})
|
1036 |
|
1037 |
self.assertRaises(NotGitRepository,
|
1038 |
lambda: backend.open_repository('/ups')) |
1039 |
|
1040 |
|
1041 |
class ServeCommandTests(TestCase): |
1042 |
"""Tests for serve_command."""
|
1043 |
|
1044 |
def setUp(self): |
1045 |
super(ServeCommandTests, self).setUp() |
1046 |
self.backend = DictBackend({})
|
1047 |
|
1048 |
def serve_command(self, handler_cls, args, inf, outf): |
1049 |
return serve_command(handler_cls, [b"test"] + args, backend=self.backend, |
1050 |
inf=inf, outf=outf) |
1051 |
|
1052 |
def test_receive_pack(self): |
1053 |
commit = make_commit(id=ONE, parents=[], commit_time=111)
|
1054 |
self.backend.repos[b"/"] = MemoryRepo.init_bare( |
1055 |
[commit], {b"refs/heads/master": commit.id})
|
1056 |
outf = BytesIO() |
1057 |
exitcode = self.serve_command(ReceivePackHandler, [b"/"], BytesIO(b"0000"), outf) |
1058 |
outlines = outf.getvalue().splitlines() |
1059 |
self.assertEqual(2, len(outlines)) |
1060 |
self.assertEqual(b"1111111111111111111111111111111111111111 refs/heads/master", |
1061 |
outlines[0][4:].split(b"\x00")[0]) |
1062 |
self.assertEqual(b"0000", outlines[-1]) |
1063 |
self.assertEqual(0, exitcode) |
1064 |
|
1065 |
|
1066 |
class UpdateServerInfoTests(TestCase): |
1067 |
"""Tests for update_server_info."""
|
1068 |
|
1069 |
def setUp(self): |
1070 |
super(UpdateServerInfoTests, self).setUp() |
1071 |
self.path = tempfile.mkdtemp()
|
1072 |
self.addCleanup(shutil.rmtree, self.path) |
1073 |
self.repo = Repo.init(self.path) |
1074 |
|
1075 |
def test_empty(self): |
1076 |
update_server_info(self.repo)
|
1077 |
with open(os.path.join(self.path, ".git", "info", "refs"), 'rb') as f: |
1078 |
self.assertEqual(b'', f.read()) |
1079 |
with open(os.path.join(self.path, ".git", "objects", "info", "packs"), 'rb') as f: |
1080 |
self.assertEqual(b'', f.read()) |
1081 |
|
1082 |
def test_simple(self): |
1083 |
commit_id = self.repo.do_commit(
|
1084 |
message=b"foo",
|
1085 |
committer=b"Joe Example <joe@example.com>",
|
1086 |
ref=b"refs/heads/foo")
|
1087 |
update_server_info(self.repo)
|
1088 |
with open(os.path.join(self.path, ".git", "info", "refs"), 'rb') as f: |
1089 |
self.assertEqual(f.read(), commit_id + b'\trefs/heads/foo\n') |
1090 |
with open(os.path.join(self.path, ".git", "objects", "info", "packs"), 'rb') as f: |
1091 |
self.assertEqual(f.read(), b'') |