2
0

test_server.py 37 KB

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