test_server.py 23 KB

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