test_server.py 19 KB

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