test_server.py 37 KB

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