test_vcs.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. import pytest
  2. from mock import Mock, patch
  3. from pip._vendor.packaging.version import parse as parse_version
  4. from pip._internal.vcs import RevOptions, VersionControl
  5. from pip._internal.vcs.bazaar import Bazaar
  6. from pip._internal.vcs.git import Git, looks_like_hash
  7. from pip._internal.vcs.mercurial import Mercurial
  8. from pip._internal.vcs.subversion import Subversion
  9. from tests.lib import pyversion
  10. if pyversion >= '3':
  11. VERBOSE_FALSE = False
  12. else:
  13. VERBOSE_FALSE = 0
  14. def test_rev_options_repr():
  15. rev_options = RevOptions(Git(), 'develop')
  16. assert repr(rev_options) == "<RevOptions git: rev='develop'>"
  17. @pytest.mark.parametrize(('vcs', 'expected1', 'expected2', 'kwargs'), [
  18. # First check VCS-specific RevOptions behavior.
  19. (Bazaar(), [], ['-r', '123'], {}),
  20. (Git(), ['HEAD'], ['123'], {}),
  21. (Mercurial(), [], ['123'], {}),
  22. (Subversion(), [], ['-r', '123'], {}),
  23. # Test extra_args. For this, test using a single VersionControl class.
  24. (Git(), ['HEAD', 'opt1', 'opt2'], ['123', 'opt1', 'opt2'],
  25. dict(extra_args=['opt1', 'opt2'])),
  26. ])
  27. def test_rev_options_to_args(vcs, expected1, expected2, kwargs):
  28. """
  29. Test RevOptions.to_args().
  30. """
  31. assert RevOptions(vcs, **kwargs).to_args() == expected1
  32. assert RevOptions(vcs, '123', **kwargs).to_args() == expected2
  33. def test_rev_options_to_display():
  34. """
  35. Test RevOptions.to_display().
  36. """
  37. # The choice of VersionControl class doesn't matter here since
  38. # the implementation is the same for all of them.
  39. vcs = Git()
  40. rev_options = RevOptions(vcs)
  41. assert rev_options.to_display() == ''
  42. rev_options = RevOptions(vcs, 'master')
  43. assert rev_options.to_display() == ' (to revision master)'
  44. def test_rev_options_make_new():
  45. """
  46. Test RevOptions.make_new().
  47. """
  48. # The choice of VersionControl class doesn't matter here since
  49. # the implementation is the same for all of them.
  50. vcs = Git()
  51. rev_options = RevOptions(vcs, 'master', extra_args=['foo', 'bar'])
  52. new_options = rev_options.make_new('develop')
  53. assert new_options is not rev_options
  54. assert new_options.extra_args == ['foo', 'bar']
  55. assert new_options.rev == 'develop'
  56. assert new_options.vcs is vcs
  57. @pytest.fixture
  58. def git():
  59. git_url = 'https://github.com/pypa/pip-test-package'
  60. sha = '5547fa909e83df8bd743d3978d6667497983a4b7'
  61. git = Git()
  62. git.get_url = Mock(return_value=git_url)
  63. git.get_revision = Mock(return_value=sha)
  64. return git
  65. @pytest.fixture
  66. def dist():
  67. dist = Mock()
  68. dist.egg_name = Mock(return_value='pip_test_package')
  69. return dist
  70. def test_looks_like_hash():
  71. assert looks_like_hash(40 * 'a')
  72. assert looks_like_hash(40 * 'A')
  73. # Test a string containing all valid characters.
  74. assert looks_like_hash(18 * 'a' + '0123456789abcdefABCDEF')
  75. assert not looks_like_hash(40 * 'g')
  76. assert not looks_like_hash(39 * 'a')
  77. @pytest.mark.network
  78. def test_git_get_src_requirements(git, dist):
  79. ret = git.get_src_requirement(dist, location='.')
  80. assert ret == ''.join([
  81. 'git+https://github.com/pypa/pip-test-package',
  82. '@5547fa909e83df8bd743d3978d6667497983a4b7',
  83. '#egg=pip_test_package'
  84. ])
  85. @patch('pip._internal.vcs.git.Git.get_revision_sha')
  86. def test_git_resolve_revision_rev_exists(get_sha_mock):
  87. get_sha_mock.return_value = '123456'
  88. git = Git()
  89. rev_options = git.make_rev_options('develop')
  90. url = 'git+https://git.example.com'
  91. new_options = git.resolve_revision('.', url, rev_options)
  92. assert new_options.rev == '123456'
  93. @patch('pip._internal.vcs.git.Git.get_revision_sha')
  94. def test_git_resolve_revision_rev_not_found(get_sha_mock):
  95. get_sha_mock.return_value = None
  96. git = Git()
  97. rev_options = git.make_rev_options('develop')
  98. url = 'git+https://git.example.com'
  99. new_options = git.resolve_revision('.', url, rev_options)
  100. assert new_options.rev == 'develop'
  101. @patch('pip._internal.vcs.git.Git.get_revision_sha')
  102. def test_git_resolve_revision_not_found_warning(get_sha_mock, caplog):
  103. get_sha_mock.return_value = None
  104. git = Git()
  105. url = 'git+https://git.example.com'
  106. sha = 40 * 'a'
  107. rev_options = git.make_rev_options(sha)
  108. new_options = git.resolve_revision('.', url, rev_options)
  109. assert new_options.rev == sha
  110. rev_options = git.make_rev_options(sha[:6])
  111. new_options = git.resolve_revision('.', url, rev_options)
  112. assert new_options.rev == 'aaaaaa'
  113. # Check that a warning got logged only for the abbreviated hash.
  114. messages = [r.getMessage() for r in caplog.records]
  115. messages = [msg for msg in messages if msg.startswith('Did not find ')]
  116. assert messages == [
  117. "Did not find branch or tag 'aaaaaa', assuming revision or ref."
  118. ]
  119. @pytest.mark.parametrize('rev_name,result', (
  120. ('5547fa909e83df8bd743d3978d6667497983a4b7', True),
  121. ('5547fa909', False),
  122. ('5678', False),
  123. ('abc123', False),
  124. ('foo', False),
  125. (None, False),
  126. ))
  127. def test_git_is_commit_id_equal(git, rev_name, result):
  128. """
  129. Test Git.is_commit_id_equal().
  130. """
  131. assert git.is_commit_id_equal('/path', rev_name) is result
  132. # The non-SVN backends all use the same get_netloc_and_auth(), so only test
  133. # Git as a representative.
  134. @pytest.mark.parametrize('args, expected', [
  135. # Test a basic case.
  136. (('example.com', 'https'), ('example.com', (None, None))),
  137. # Test with username and password.
  138. (('user:pass@example.com', 'https'),
  139. ('user:pass@example.com', (None, None))),
  140. ])
  141. def test_git__get_netloc_and_auth(args, expected):
  142. """
  143. Test VersionControl.get_netloc_and_auth().
  144. """
  145. netloc, scheme = args
  146. actual = Git().get_netloc_and_auth(netloc, scheme)
  147. assert actual == expected
  148. @pytest.mark.parametrize('args, expected', [
  149. # Test https.
  150. (('example.com', 'https'), ('example.com', (None, None))),
  151. # Test https with username and no password.
  152. (('user@example.com', 'https'), ('example.com', ('user', None))),
  153. # Test https with username and password.
  154. (('user:pass@example.com', 'https'), ('example.com', ('user', 'pass'))),
  155. # Test ssh with username and password.
  156. (('user:pass@example.com', 'ssh'),
  157. ('user:pass@example.com', (None, None))),
  158. ])
  159. def test_subversion__get_netloc_and_auth(args, expected):
  160. """
  161. Test Subversion.get_netloc_and_auth().
  162. """
  163. netloc, scheme = args
  164. actual = Subversion().get_netloc_and_auth(netloc, scheme)
  165. assert actual == expected
  166. def test_git__get_url_rev__idempotent():
  167. """
  168. Check that Git.get_url_rev_and_auth() is idempotent for what the code calls
  169. "stub URLs" (i.e. URLs that don't contain "://").
  170. Also check that it doesn't change self.url.
  171. """
  172. url = 'git+git@git.example.com:MyProject#egg=MyProject'
  173. vcs = Git(url)
  174. result1 = vcs.get_url_rev_and_auth(url)
  175. assert vcs.url == url
  176. result2 = vcs.get_url_rev_and_auth(url)
  177. expected = ('git@git.example.com:MyProject', None, (None, None))
  178. assert result1 == expected
  179. assert result2 == expected
  180. @pytest.mark.parametrize('url, expected', [
  181. ('svn+https://svn.example.com/MyProject',
  182. ('https://svn.example.com/MyProject', None, (None, None))),
  183. # Test a "+" in the path portion.
  184. ('svn+https://svn.example.com/My+Project',
  185. ('https://svn.example.com/My+Project', None, (None, None))),
  186. ])
  187. def test_version_control__get_url_rev_and_auth(url, expected):
  188. """
  189. Test the basic case of VersionControl.get_url_rev_and_auth().
  190. """
  191. actual = VersionControl().get_url_rev_and_auth(url)
  192. assert actual == expected
  193. @pytest.mark.parametrize('url', [
  194. 'https://svn.example.com/MyProject',
  195. # Test a URL containing a "+" (but not in the scheme).
  196. 'https://svn.example.com/My+Project',
  197. ])
  198. def test_version_control__get_url_rev_and_auth__missing_plus(url):
  199. """
  200. Test passing a URL to VersionControl.get_url_rev_and_auth() with a "+"
  201. missing from the scheme.
  202. """
  203. with pytest.raises(ValueError) as excinfo:
  204. VersionControl().get_url_rev_and_auth(url)
  205. assert 'malformed VCS url' in str(excinfo.value)
  206. @pytest.mark.parametrize('url, expected', [
  207. # Test http.
  208. ('bzr+http://bzr.myproject.org/MyProject/trunk/#egg=MyProject',
  209. 'http://bzr.myproject.org/MyProject/trunk/'),
  210. # Test https.
  211. ('bzr+https://bzr.myproject.org/MyProject/trunk/#egg=MyProject',
  212. 'https://bzr.myproject.org/MyProject/trunk/'),
  213. # Test ftp.
  214. ('bzr+ftp://bzr.myproject.org/MyProject/trunk/#egg=MyProject',
  215. 'ftp://bzr.myproject.org/MyProject/trunk/'),
  216. # Test sftp.
  217. ('bzr+sftp://bzr.myproject.org/MyProject/trunk/#egg=MyProject',
  218. 'sftp://bzr.myproject.org/MyProject/trunk/'),
  219. # Test launchpad.
  220. ('bzr+lp:MyLaunchpadProject#egg=MyLaunchpadProject',
  221. 'lp:MyLaunchpadProject'),
  222. # Test ssh (special handling).
  223. ('bzr+ssh://bzr.myproject.org/MyProject/trunk/#egg=MyProject',
  224. 'bzr+ssh://bzr.myproject.org/MyProject/trunk/'),
  225. ])
  226. def test_bazaar__get_url_rev_and_auth(url, expected):
  227. """
  228. Test Bazaar.get_url_rev_and_auth().
  229. """
  230. bzr = Bazaar(url=url)
  231. actual = bzr.get_url_rev_and_auth(url)
  232. assert actual == (expected, None, (None, None))
  233. @pytest.mark.parametrize('url, expected', [
  234. # Test an https URL.
  235. ('svn+https://svn.example.com/MyProject#egg=MyProject',
  236. ('https://svn.example.com/MyProject', None, (None, None))),
  237. # Test an https URL with a username and password.
  238. ('svn+https://user:pass@svn.example.com/MyProject#egg=MyProject',
  239. ('https://svn.example.com/MyProject', None, ('user', 'pass'))),
  240. # Test an ssh URL.
  241. ('svn+ssh://svn.example.com/MyProject#egg=MyProject',
  242. ('svn+ssh://svn.example.com/MyProject', None, (None, None))),
  243. # Test an ssh URL with a username.
  244. ('svn+ssh://user@svn.example.com/MyProject#egg=MyProject',
  245. ('svn+ssh://user@svn.example.com/MyProject', None, (None, None))),
  246. ])
  247. def test_subversion__get_url_rev_and_auth(url, expected):
  248. """
  249. Test Subversion.get_url_rev_and_auth().
  250. """
  251. actual = Subversion().get_url_rev_and_auth(url)
  252. assert actual == expected
  253. # The non-SVN backends all use the same make_rev_args(), so only test
  254. # Git as a representative.
  255. @pytest.mark.parametrize('username, password, expected', [
  256. (None, None, []),
  257. ('user', None, []),
  258. ('user', 'pass', []),
  259. ])
  260. def test_git__make_rev_args(username, password, expected):
  261. """
  262. Test VersionControl.make_rev_args().
  263. """
  264. actual = Git().make_rev_args(username, password)
  265. assert actual == expected
  266. @pytest.mark.parametrize('username, password, expected', [
  267. (None, None, []),
  268. ('user', None, ['--username', 'user']),
  269. ('user', 'pass', ['--username', 'user', '--password', 'pass']),
  270. ])
  271. def test_subversion__make_rev_args(username, password, expected):
  272. """
  273. Test Subversion.make_rev_args().
  274. """
  275. actual = Subversion().make_rev_args(username, password)
  276. assert actual == expected
  277. def test_subversion__get_url_rev_options():
  278. """
  279. Test Subversion.get_url_rev_options().
  280. """
  281. url = 'svn+https://user:pass@svn.example.com/MyProject@v1.0#egg=MyProject'
  282. url, rev_options = Subversion().get_url_rev_options(url)
  283. assert url == 'https://svn.example.com/MyProject'
  284. assert rev_options.rev == 'v1.0'
  285. assert rev_options.extra_args == (
  286. ['--username', 'user', '--password', 'pass']
  287. )
  288. def test_get_git_version():
  289. git_version = Git().get_git_version()
  290. assert git_version >= parse_version('1.0.0')