test_server.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876
  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. """Tests for the smart protocol server."""
  19. from cStringIO import StringIO
  20. import os
  21. import tempfile
  22. from dulwich.errors import (
  23. GitProtocolError,
  24. NotGitRepository,
  25. UnexpectedCommandError,
  26. )
  27. from dulwich.objects import (
  28. Commit,
  29. Tag,
  30. )
  31. from dulwich.object_store import (
  32. MemoryObjectStore,
  33. )
  34. from dulwich.repo import (
  35. MemoryRepo,
  36. Repo,
  37. )
  38. from dulwich.server import (
  39. Backend,
  40. DictBackend,
  41. FileSystemBackend,
  42. Handler,
  43. MultiAckGraphWalkerImpl,
  44. MultiAckDetailedGraphWalkerImpl,
  45. _split_proto_line,
  46. serve_command,
  47. _find_shallow,
  48. ProtocolGraphWalker,
  49. ReceivePackHandler,
  50. SingleAckGraphWalkerImpl,
  51. UploadPackHandler,
  52. update_server_info,
  53. )
  54. from dulwich.tests import TestCase
  55. from dulwich.tests.utils import (
  56. make_commit,
  57. make_object,
  58. )
  59. from dulwich.protocol import (
  60. ZERO_SHA,
  61. )
  62. ONE = '1' * 40
  63. TWO = '2' * 40
  64. THREE = '3' * 40
  65. FOUR = '4' * 40
  66. FIVE = '5' * 40
  67. SIX = '6' * 40
  68. class TestProto(object):
  69. def __init__(self):
  70. self._output = []
  71. self._received = {0: [], 1: [], 2: [], 3: []}
  72. def set_output(self, output_lines):
  73. self._output = ['%s\n' % line.rstrip() for line in output_lines]
  74. def read_pkt_line(self):
  75. if self._output:
  76. return self._output.pop(0)
  77. else:
  78. return None
  79. def write_sideband(self, band, data):
  80. self._received[band].append(data)
  81. def write_pkt_line(self, data):
  82. self._received[0].append(data)
  83. def get_received_line(self, band=0):
  84. lines = self._received[band]
  85. return lines.pop(0)
  86. class TestGenericHandler(Handler):
  87. def __init__(self):
  88. Handler.__init__(self, Backend(), None)
  89. @classmethod
  90. def capabilities(cls):
  91. return ('cap1', 'cap2', 'cap3')
  92. @classmethod
  93. def required_capabilities(cls):
  94. return ('cap2',)
  95. class HandlerTestCase(TestCase):
  96. def setUp(self):
  97. super(HandlerTestCase, self).setUp()
  98. self._handler = TestGenericHandler()
  99. def assertSucceeds(self, func, *args, **kwargs):
  100. try:
  101. func(*args, **kwargs)
  102. except GitProtocolError, e:
  103. self.fail(e)
  104. def test_capability_line(self):
  105. self.assertEqual('cap1 cap2 cap3', self._handler.capability_line())
  106. def test_set_client_capabilities(self):
  107. set_caps = self._handler.set_client_capabilities
  108. self.assertSucceeds(set_caps, ['cap2'])
  109. self.assertSucceeds(set_caps, ['cap1', 'cap2'])
  110. # different order
  111. self.assertSucceeds(set_caps, ['cap3', 'cap1', 'cap2'])
  112. # error cases
  113. self.assertRaises(GitProtocolError, set_caps, ['capxxx', 'cap2'])
  114. self.assertRaises(GitProtocolError, set_caps, ['cap1', 'cap3'])
  115. # ignore innocuous but unknown capabilities
  116. self.assertRaises(GitProtocolError, set_caps, ['cap2', 'ignoreme'])
  117. self.assertFalse('ignoreme' in self._handler.capabilities())
  118. self._handler.innocuous_capabilities = lambda: ('ignoreme',)
  119. self.assertSucceeds(set_caps, ['cap2', 'ignoreme'])
  120. def test_has_capability(self):
  121. self.assertRaises(GitProtocolError, self._handler.has_capability, 'cap')
  122. caps = self._handler.capabilities()
  123. self._handler.set_client_capabilities(caps)
  124. for cap in caps:
  125. self.assertTrue(self._handler.has_capability(cap))
  126. self.assertFalse(self._handler.has_capability('capxxx'))
  127. class UploadPackHandlerTestCase(TestCase):
  128. def setUp(self):
  129. super(UploadPackHandlerTestCase, self).setUp()
  130. self._repo = MemoryRepo.init_bare([], {})
  131. backend = DictBackend({'/': self._repo})
  132. self._handler = UploadPackHandler(
  133. backend, ['/', 'host=lolcathost'], TestProto())
  134. def test_progress(self):
  135. caps = self._handler.required_capabilities()
  136. self._handler.set_client_capabilities(caps)
  137. self._handler.progress('first message')
  138. self._handler.progress('second message')
  139. self.assertEqual('first message',
  140. self._handler.proto.get_received_line(2))
  141. self.assertEqual('second message',
  142. self._handler.proto.get_received_line(2))
  143. self.assertRaises(IndexError, self._handler.proto.get_received_line, 2)
  144. def test_no_progress(self):
  145. caps = list(self._handler.required_capabilities()) + ['no-progress']
  146. self._handler.set_client_capabilities(caps)
  147. self._handler.progress('first message')
  148. self._handler.progress('second message')
  149. self.assertRaises(IndexError, self._handler.proto.get_received_line, 2)
  150. def test_get_tagged(self):
  151. refs = {
  152. 'refs/tags/tag1': ONE,
  153. 'refs/tags/tag2': TWO,
  154. 'refs/heads/master': FOUR, # not a tag, no peeled value
  155. }
  156. # repo needs to peel this object
  157. self._repo.object_store.add_object(make_commit(id=FOUR))
  158. self._repo.refs._update(refs)
  159. peeled = {
  160. 'refs/tags/tag1': '1234' * 10,
  161. 'refs/tags/tag2': '5678' * 10,
  162. }
  163. self._repo.refs._update_peeled(peeled)
  164. caps = list(self._handler.required_capabilities()) + ['include-tag']
  165. self._handler.set_client_capabilities(caps)
  166. self.assertEqual({'1234' * 10: ONE, '5678' * 10: TWO},
  167. self._handler.get_tagged(refs, repo=self._repo))
  168. # non-include-tag case
  169. caps = self._handler.required_capabilities()
  170. self._handler.set_client_capabilities(caps)
  171. self.assertEqual({}, self._handler.get_tagged(refs, repo=self._repo))
  172. class FindShallowTests(TestCase):
  173. def setUp(self):
  174. self._store = MemoryObjectStore()
  175. def make_commit(self, **attrs):
  176. commit = make_commit(**attrs)
  177. self._store.add_object(commit)
  178. return commit
  179. def make_linear_commits(self, n, message=''):
  180. commits = []
  181. parents = []
  182. for _ in xrange(n):
  183. commits.append(self.make_commit(parents=parents, message=message))
  184. parents = [commits[-1].id]
  185. return commits
  186. def assertSameElements(self, expected, actual):
  187. self.assertEqual(set(expected), set(actual))
  188. def test_linear(self):
  189. c1, c2, c3 = self.make_linear_commits(3)
  190. self.assertEqual((set([c3.id]), set([])),
  191. _find_shallow(self._store, [c3.id], 0))
  192. self.assertEqual((set([c2.id]), set([c3.id])),
  193. _find_shallow(self._store, [c3.id], 1))
  194. self.assertEqual((set([c1.id]), set([c2.id, c3.id])),
  195. _find_shallow(self._store, [c3.id], 2))
  196. self.assertEqual((set([]), set([c1.id, c2.id, c3.id])),
  197. _find_shallow(self._store, [c3.id], 3))
  198. def test_multiple_independent(self):
  199. a = self.make_linear_commits(2, message='a')
  200. b = self.make_linear_commits(2, message='b')
  201. c = self.make_linear_commits(2, message='c')
  202. heads = [a[1].id, b[1].id, c[1].id]
  203. self.assertEqual((set([a[0].id, b[0].id, c[0].id]), set(heads)),
  204. _find_shallow(self._store, heads, 1))
  205. def test_multiple_overlapping(self):
  206. # Create the following commit tree:
  207. # 1--2
  208. # \
  209. # 3--4
  210. c1, c2 = self.make_linear_commits(2)
  211. c3 = self.make_commit(parents=[c1.id])
  212. c4 = self.make_commit(parents=[c3.id])
  213. # 1 is shallow along the path from 4, but not along the path from 2.
  214. self.assertEqual((set([c1.id]), set([c1.id, c2.id, c3.id, c4.id])),
  215. _find_shallow(self._store, [c2.id, c4.id], 2))
  216. def test_merge(self):
  217. c1 = self.make_commit()
  218. c2 = self.make_commit()
  219. c3 = self.make_commit(parents=[c1.id, c2.id])
  220. self.assertEqual((set([c1.id, c2.id]), set([c3.id])),
  221. _find_shallow(self._store, [c3.id], 1))
  222. def test_tag(self):
  223. c1, c2 = self.make_linear_commits(2)
  224. tag = make_object(Tag, name='tag', message='',
  225. tagger='Tagger <test@example.com>',
  226. tag_time=12345, tag_timezone=0,
  227. object=(Commit, c2.id))
  228. self._store.add_object(tag)
  229. self.assertEqual((set([c1.id]), set([c2.id])),
  230. _find_shallow(self._store, [tag.id], 1))
  231. class TestUploadPackHandler(UploadPackHandler):
  232. @classmethod
  233. def required_capabilities(self):
  234. return ()
  235. class ReceivePackHandlerTestCase(TestCase):
  236. def setUp(self):
  237. super(ReceivePackHandlerTestCase, self).setUp()
  238. self._repo = MemoryRepo.init_bare([], {})
  239. backend = DictBackend({'/': self._repo})
  240. self._handler = ReceivePackHandler(
  241. backend, ['/', 'host=lolcathost'], TestProto())
  242. def test_apply_pack_del_ref(self):
  243. refs = {
  244. 'refs/heads/master': TWO,
  245. 'refs/heads/fake-branch': ONE}
  246. self._repo.refs._update(refs)
  247. update_refs = [[ONE, ZERO_SHA, 'refs/heads/fake-branch'], ]
  248. status = self._handler._apply_pack(update_refs)
  249. self.assertEqual(status[0][0], 'unpack')
  250. self.assertEqual(status[0][1], 'ok')
  251. self.assertEqual(status[1][0], 'refs/heads/fake-branch')
  252. self.assertEqual(status[1][1], 'ok')
  253. class ProtocolGraphWalkerTestCase(TestCase):
  254. def setUp(self):
  255. super(ProtocolGraphWalkerTestCase, self).setUp()
  256. # Create the following commit tree:
  257. # 3---5
  258. # /
  259. # 1---2---4
  260. commits = [
  261. make_commit(id=ONE, parents=[], commit_time=111),
  262. make_commit(id=TWO, parents=[ONE], commit_time=222),
  263. make_commit(id=THREE, parents=[ONE], commit_time=333),
  264. make_commit(id=FOUR, parents=[TWO], commit_time=444),
  265. make_commit(id=FIVE, parents=[THREE], commit_time=555),
  266. ]
  267. self._repo = MemoryRepo.init_bare(commits, {})
  268. backend = DictBackend({'/': self._repo})
  269. self._walker = ProtocolGraphWalker(
  270. TestUploadPackHandler(backend, ['/', 'host=lolcats'], TestProto()),
  271. self._repo.object_store, self._repo.get_peeled)
  272. def test_is_satisfied_no_haves(self):
  273. self.assertFalse(self._walker._is_satisfied([], ONE, 0))
  274. self.assertFalse(self._walker._is_satisfied([], TWO, 0))
  275. self.assertFalse(self._walker._is_satisfied([], THREE, 0))
  276. def test_is_satisfied_have_root(self):
  277. self.assertTrue(self._walker._is_satisfied([ONE], ONE, 0))
  278. self.assertTrue(self._walker._is_satisfied([ONE], TWO, 0))
  279. self.assertTrue(self._walker._is_satisfied([ONE], THREE, 0))
  280. def test_is_satisfied_have_branch(self):
  281. self.assertTrue(self._walker._is_satisfied([TWO], TWO, 0))
  282. # wrong branch
  283. self.assertFalse(self._walker._is_satisfied([TWO], THREE, 0))
  284. def test_all_wants_satisfied(self):
  285. self._walker.set_wants([FOUR, FIVE])
  286. # trivial case: wants == haves
  287. self.assertTrue(self._walker.all_wants_satisfied([FOUR, FIVE]))
  288. # cases that require walking the commit tree
  289. self.assertTrue(self._walker.all_wants_satisfied([ONE]))
  290. self.assertFalse(self._walker.all_wants_satisfied([TWO]))
  291. self.assertFalse(self._walker.all_wants_satisfied([THREE]))
  292. self.assertTrue(self._walker.all_wants_satisfied([TWO, THREE]))
  293. def test_split_proto_line(self):
  294. allowed = ('want', 'done', None)
  295. self.assertEqual(('want', ONE),
  296. _split_proto_line('want %s\n' % ONE, allowed))
  297. self.assertEqual(('want', TWO),
  298. _split_proto_line('want %s\n' % TWO, allowed))
  299. self.assertRaises(GitProtocolError, _split_proto_line,
  300. 'want xxxx\n', allowed)
  301. self.assertRaises(UnexpectedCommandError, _split_proto_line,
  302. 'have %s\n' % THREE, allowed)
  303. self.assertRaises(GitProtocolError, _split_proto_line,
  304. 'foo %s\n' % FOUR, allowed)
  305. self.assertRaises(GitProtocolError, _split_proto_line, 'bar', allowed)
  306. self.assertEqual(('done', None), _split_proto_line('done\n', allowed))
  307. self.assertEqual((None, None), _split_proto_line('', allowed))
  308. def test_determine_wants(self):
  309. self.assertEqual([], self._walker.determine_wants({}))
  310. self.assertEqual(None, self._walker.proto.get_received_line())
  311. self._walker.proto.set_output([
  312. 'want %s multi_ack' % ONE,
  313. 'want %s' % TWO,
  314. ])
  315. heads = {
  316. 'refs/heads/ref1': ONE,
  317. 'refs/heads/ref2': TWO,
  318. 'refs/heads/ref3': THREE,
  319. }
  320. self._repo.refs._update(heads)
  321. self.assertEqual([ONE, TWO], self._walker.determine_wants(heads))
  322. self._walker.advertise_refs = True
  323. self.assertEqual([], self._walker.determine_wants(heads))
  324. self._walker.advertise_refs = False
  325. self._walker.proto.set_output(['want %s multi_ack' % FOUR])
  326. self.assertRaises(GitProtocolError, self._walker.determine_wants, heads)
  327. self._walker.proto.set_output([])
  328. self.assertEqual([], self._walker.determine_wants(heads))
  329. self._walker.proto.set_output(['want %s multi_ack' % ONE, 'foo'])
  330. self.assertRaises(GitProtocolError, self._walker.determine_wants, heads)
  331. self._walker.proto.set_output(['want %s multi_ack' % FOUR])
  332. self.assertRaises(GitProtocolError, self._walker.determine_wants, heads)
  333. def test_determine_wants_advertisement(self):
  334. self._walker.proto.set_output([])
  335. # advertise branch tips plus tag
  336. heads = {
  337. 'refs/heads/ref4': FOUR,
  338. 'refs/heads/ref5': FIVE,
  339. 'refs/heads/tag6': SIX,
  340. }
  341. self._repo.refs._update(heads)
  342. self._repo.refs._update_peeled(heads)
  343. self._repo.refs._update_peeled({'refs/heads/tag6': FIVE})
  344. self._walker.determine_wants(heads)
  345. lines = []
  346. while True:
  347. line = self._walker.proto.get_received_line()
  348. if line is None:
  349. break
  350. # strip capabilities list if present
  351. if '\x00' in line:
  352. line = line[:line.index('\x00')]
  353. lines.append(line.rstrip())
  354. self.assertEqual([
  355. '%s refs/heads/ref4' % FOUR,
  356. '%s refs/heads/ref5' % FIVE,
  357. '%s refs/heads/tag6^{}' % FIVE,
  358. '%s refs/heads/tag6' % SIX,
  359. ], sorted(lines))
  360. # ensure peeled tag was advertised immediately following tag
  361. for i, line in enumerate(lines):
  362. if line.endswith(' refs/heads/tag6'):
  363. self.assertEqual('%s refs/heads/tag6^{}' % FIVE, lines[i+1])
  364. # TODO: test commit time cutoff
  365. def _handle_shallow_request(self, lines, heads):
  366. self._walker.proto.set_output(lines)
  367. self._walker._handle_shallow_request(heads)
  368. def assertReceived(self, expected):
  369. self.assertEquals(
  370. expected, list(iter(self._walker.proto.get_received_line, None)))
  371. def test_handle_shallow_request_no_client_shallows(self):
  372. self._handle_shallow_request(['deepen 1\n'], [FOUR, FIVE])
  373. self.assertEquals(set([TWO, THREE]), self._walker.shallow)
  374. self.assertReceived([
  375. 'shallow %s' % TWO,
  376. 'shallow %s' % THREE,
  377. ])
  378. def test_handle_shallow_request_no_new_shallows(self):
  379. lines = [
  380. 'shallow %s\n' % TWO,
  381. 'shallow %s\n' % THREE,
  382. 'deepen 1\n',
  383. ]
  384. self._handle_shallow_request(lines, [FOUR, FIVE])
  385. self.assertEquals(set([TWO, THREE]), self._walker.shallow)
  386. self.assertReceived([])
  387. def test_handle_shallow_request_unshallows(self):
  388. lines = [
  389. 'shallow %s\n' % TWO,
  390. 'deepen 2\n',
  391. ]
  392. self._handle_shallow_request(lines, [FOUR, FIVE])
  393. self.assertEquals(set([ONE]), self._walker.shallow)
  394. self.assertReceived([
  395. 'shallow %s' % ONE,
  396. 'unshallow %s' % TWO,
  397. # THREE is unshallow but was is not shallow in the client
  398. ])
  399. class TestProtocolGraphWalker(object):
  400. def __init__(self):
  401. self.acks = []
  402. self.lines = []
  403. self.done = False
  404. self.http_req = None
  405. self.advertise_refs = False
  406. def read_proto_line(self, allowed):
  407. command, sha = self.lines.pop(0)
  408. if allowed is not None:
  409. assert command in allowed
  410. return command, sha
  411. def send_ack(self, sha, ack_type=''):
  412. self.acks.append((sha, ack_type))
  413. def send_nak(self):
  414. self.acks.append((None, 'nak'))
  415. def all_wants_satisfied(self, haves):
  416. return self.done
  417. def pop_ack(self):
  418. if not self.acks:
  419. return None
  420. return self.acks.pop(0)
  421. class AckGraphWalkerImplTestCase(TestCase):
  422. """Base setup and asserts for AckGraphWalker tests."""
  423. def setUp(self):
  424. super(AckGraphWalkerImplTestCase, self).setUp()
  425. self._walker = TestProtocolGraphWalker()
  426. self._walker.lines = [
  427. ('have', TWO),
  428. ('have', ONE),
  429. ('have', THREE),
  430. ('done', None),
  431. ]
  432. self._impl = self.impl_cls(self._walker)
  433. def assertNoAck(self):
  434. self.assertEqual(None, self._walker.pop_ack())
  435. def assertAcks(self, acks):
  436. for sha, ack_type in acks:
  437. self.assertEqual((sha, ack_type), self._walker.pop_ack())
  438. self.assertNoAck()
  439. def assertAck(self, sha, ack_type=''):
  440. self.assertAcks([(sha, ack_type)])
  441. def assertNak(self):
  442. self.assertAck(None, 'nak')
  443. def assertNextEquals(self, sha):
  444. self.assertEqual(sha, self._impl.next())
  445. class SingleAckGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
  446. impl_cls = SingleAckGraphWalkerImpl
  447. def test_single_ack(self):
  448. self.assertNextEquals(TWO)
  449. self.assertNoAck()
  450. self.assertNextEquals(ONE)
  451. self._walker.done = True
  452. self._impl.ack(ONE)
  453. self.assertAck(ONE)
  454. self.assertNextEquals(THREE)
  455. self._impl.ack(THREE)
  456. self.assertNoAck()
  457. self.assertNextEquals(None)
  458. self.assertNoAck()
  459. def test_single_ack_flush(self):
  460. # same as ack test but ends with a flush-pkt instead of done
  461. self._walker.lines[-1] = (None, None)
  462. self.assertNextEquals(TWO)
  463. self.assertNoAck()
  464. self.assertNextEquals(ONE)
  465. self._walker.done = True
  466. self._impl.ack(ONE)
  467. self.assertAck(ONE)
  468. self.assertNextEquals(THREE)
  469. self.assertNoAck()
  470. self.assertNextEquals(None)
  471. self.assertNoAck()
  472. def test_single_ack_nak(self):
  473. self.assertNextEquals(TWO)
  474. self.assertNoAck()
  475. self.assertNextEquals(ONE)
  476. self.assertNoAck()
  477. self.assertNextEquals(THREE)
  478. self.assertNoAck()
  479. self.assertNextEquals(None)
  480. self.assertNak()
  481. def test_single_ack_nak_flush(self):
  482. # same as nak test but ends with a flush-pkt instead of done
  483. self._walker.lines[-1] = (None, None)
  484. self.assertNextEquals(TWO)
  485. self.assertNoAck()
  486. self.assertNextEquals(ONE)
  487. self.assertNoAck()
  488. self.assertNextEquals(THREE)
  489. self.assertNoAck()
  490. self.assertNextEquals(None)
  491. self.assertNak()
  492. class MultiAckGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
  493. impl_cls = MultiAckGraphWalkerImpl
  494. def test_multi_ack(self):
  495. self.assertNextEquals(TWO)
  496. self.assertNoAck()
  497. self.assertNextEquals(ONE)
  498. self._walker.done = True
  499. self._impl.ack(ONE)
  500. self.assertAck(ONE, 'continue')
  501. self.assertNextEquals(THREE)
  502. self._impl.ack(THREE)
  503. self.assertAck(THREE, 'continue')
  504. self.assertNextEquals(None)
  505. self.assertAck(THREE)
  506. def test_multi_ack_partial(self):
  507. self.assertNextEquals(TWO)
  508. self.assertNoAck()
  509. self.assertNextEquals(ONE)
  510. self._impl.ack(ONE)
  511. self.assertAck(ONE, 'continue')
  512. self.assertNextEquals(THREE)
  513. self.assertNoAck()
  514. self.assertNextEquals(None)
  515. # done, re-send ack of last common
  516. self.assertAck(ONE)
  517. def test_multi_ack_flush(self):
  518. self._walker.lines = [
  519. ('have', TWO),
  520. (None, None),
  521. ('have', ONE),
  522. ('have', THREE),
  523. ('done', None),
  524. ]
  525. self.assertNextEquals(TWO)
  526. self.assertNoAck()
  527. self.assertNextEquals(ONE)
  528. self.assertNak() # nak the flush-pkt
  529. self._walker.done = True
  530. self._impl.ack(ONE)
  531. self.assertAck(ONE, 'continue')
  532. self.assertNextEquals(THREE)
  533. self._impl.ack(THREE)
  534. self.assertAck(THREE, 'continue')
  535. self.assertNextEquals(None)
  536. self.assertAck(THREE)
  537. def test_multi_ack_nak(self):
  538. self.assertNextEquals(TWO)
  539. self.assertNoAck()
  540. self.assertNextEquals(ONE)
  541. self.assertNoAck()
  542. self.assertNextEquals(THREE)
  543. self.assertNoAck()
  544. self.assertNextEquals(None)
  545. self.assertNak()
  546. class MultiAckDetailedGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
  547. impl_cls = MultiAckDetailedGraphWalkerImpl
  548. def test_multi_ack(self):
  549. self.assertNextEquals(TWO)
  550. self.assertNoAck()
  551. self.assertNextEquals(ONE)
  552. self._walker.done = True
  553. self._impl.ack(ONE)
  554. self.assertAcks([(ONE, 'common'), (ONE, 'ready')])
  555. self.assertNextEquals(THREE)
  556. self._impl.ack(THREE)
  557. self.assertAck(THREE, 'ready')
  558. self.assertNextEquals(None)
  559. self.assertAck(THREE)
  560. def test_multi_ack_partial(self):
  561. self.assertNextEquals(TWO)
  562. self.assertNoAck()
  563. self.assertNextEquals(ONE)
  564. self._impl.ack(ONE)
  565. self.assertAck(ONE, 'common')
  566. self.assertNextEquals(THREE)
  567. self.assertNoAck()
  568. self.assertNextEquals(None)
  569. # done, re-send ack of last common
  570. self.assertAck(ONE)
  571. def test_multi_ack_flush(self):
  572. # same as ack test but contains a flush-pkt in the middle
  573. self._walker.lines = [
  574. ('have', TWO),
  575. (None, None),
  576. ('have', ONE),
  577. ('have', THREE),
  578. ('done', None),
  579. ]
  580. self.assertNextEquals(TWO)
  581. self.assertNoAck()
  582. self.assertNextEquals(ONE)
  583. self.assertNak() # nak the flush-pkt
  584. self._walker.done = True
  585. self._impl.ack(ONE)
  586. self.assertAcks([(ONE, 'common'), (ONE, 'ready')])
  587. self.assertNextEquals(THREE)
  588. self._impl.ack(THREE)
  589. self.assertAck(THREE, 'ready')
  590. self.assertNextEquals(None)
  591. self.assertAck(THREE)
  592. def test_multi_ack_nak(self):
  593. self.assertNextEquals(TWO)
  594. self.assertNoAck()
  595. self.assertNextEquals(ONE)
  596. self.assertNoAck()
  597. self.assertNextEquals(THREE)
  598. self.assertNoAck()
  599. self.assertNextEquals(None)
  600. self.assertNak()
  601. def test_multi_ack_nak_flush(self):
  602. # same as nak test but contains a flush-pkt in the middle
  603. self._walker.lines = [
  604. ('have', TWO),
  605. (None, None),
  606. ('have', ONE),
  607. ('have', THREE),
  608. ('done', None),
  609. ]
  610. self.assertNextEquals(TWO)
  611. self.assertNoAck()
  612. self.assertNextEquals(ONE)
  613. self.assertNak()
  614. self.assertNextEquals(THREE)
  615. self.assertNoAck()
  616. self.assertNextEquals(None)
  617. self.assertNak()
  618. def test_multi_ack_stateless(self):
  619. # transmission ends with a flush-pkt
  620. self._walker.lines[-1] = (None, None)
  621. self._walker.http_req = True
  622. self.assertNextEquals(TWO)
  623. self.assertNoAck()
  624. self.assertNextEquals(ONE)
  625. self.assertNoAck()
  626. self.assertNextEquals(THREE)
  627. self.assertNoAck()
  628. self.assertNextEquals(None)
  629. self.assertNak()
  630. class FileSystemBackendTests(TestCase):
  631. """Tests for FileSystemBackend."""
  632. def setUp(self):
  633. super(FileSystemBackendTests, self).setUp()
  634. self.path = tempfile.mkdtemp()
  635. self.repo = Repo.init(self.path)
  636. self.backend = FileSystemBackend()
  637. def test_nonexistant(self):
  638. self.assertRaises(NotGitRepository,
  639. self.backend.open_repository, "/does/not/exist/unless/foo")
  640. def test_absolute(self):
  641. repo = self.backend.open_repository(self.path)
  642. self.assertEqual(repo.path, self.repo.path)
  643. def test_child(self):
  644. self.assertRaises(NotGitRepository,
  645. self.backend.open_repository, os.path.join(self.path, "foo"))
  646. def test_bad_repo_path(self):
  647. repo = MemoryRepo.init_bare([], {})
  648. backend = DictBackend({'/': repo})
  649. self.assertRaises(NotGitRepository,
  650. lambda: backend.open_repository('/ups'))
  651. class ServeCommandTests(TestCase):
  652. """Tests for serve_command."""
  653. def setUp(self):
  654. super(ServeCommandTests, self).setUp()
  655. self.backend = DictBackend({})
  656. def serve_command(self, handler_cls, args, inf, outf):
  657. return serve_command(handler_cls, ["test"] + args, backend=self.backend,
  658. inf=inf, outf=outf)
  659. def test_receive_pack(self):
  660. commit = make_commit(id=ONE, parents=[], commit_time=111)
  661. self.backend.repos["/"] = MemoryRepo.init_bare(
  662. [commit], {"refs/heads/master": commit.id})
  663. outf = StringIO()
  664. exitcode = self.serve_command(ReceivePackHandler, ["/"], StringIO("0000"), outf)
  665. outlines = outf.getvalue().splitlines()
  666. self.assertEqual(2, len(outlines))
  667. self.assertEqual("1111111111111111111111111111111111111111 refs/heads/master",
  668. outlines[0][4:].split("\x00")[0])
  669. self.assertEqual("0000", outlines[-1])
  670. self.assertEqual(0, exitcode)
  671. class UpdateServerInfoTests(TestCase):
  672. """Tests for update_server_info."""
  673. def setUp(self):
  674. super(UpdateServerInfoTests, self).setUp()
  675. self.path = tempfile.mkdtemp()
  676. self.repo = Repo.init(self.path)
  677. def test_empty(self):
  678. update_server_info(self.repo)
  679. self.assertEqual("",
  680. open(os.path.join(self.path, ".git", "info", "refs"), 'r').read())
  681. self.assertEqual("",
  682. open(os.path.join(self.path, ".git", "objects", "info", "packs"), 'r').read())
  683. def test_simple(self):
  684. commit_id = self.repo.do_commit(
  685. message="foo",
  686. committer="Joe Example <joe@example.com>",
  687. ref="refs/heads/foo")
  688. update_server_info(self.repo)
  689. ref_text = open(os.path.join(self.path, ".git", "info", "refs"), 'r').read()
  690. self.assertEqual(ref_text, "%s\trefs/heads/foo\n" % commit_id)
  691. packs_text = open(os.path.join(self.path, ".git", "objects", "info", "packs"), 'r').read()
  692. self.assertEqual(packs_text, "")