fetch_test.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. #!/usr/bin/env vpython3
  2. # coding=utf-8
  3. # Copyright (c) 2012 The Chromium Authors. All rights reserved.
  4. # Use of this source code is governed by a BSD-style license that can be
  5. # found in the LICENSE file.
  6. """Unit tests for fetch.py."""
  7. import contextlib
  8. import logging
  9. import argparse
  10. import os
  11. import subprocess
  12. import sys
  13. import tempfile
  14. import unittest
  15. import distutils
  16. if sys.version_info.major == 2:
  17. from StringIO import StringIO
  18. import mock
  19. else:
  20. from io import StringIO
  21. from unittest import mock
  22. sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
  23. import fetch
  24. class SystemExitMock(Exception):
  25. pass
  26. class TestUtilityFunctions(unittest.TestCase):
  27. """This test case is against utility functions"""
  28. def _usage_static_message(self, stdout):
  29. valid_fetch_config_text = 'Valid fetch configs:'
  30. self.assertIn(valid_fetch_config_text, stdout)
  31. # split[0] contains static text, whereas split[1] contains list of configs
  32. split = stdout.split(valid_fetch_config_text)
  33. self.assertEqual(2, len(split))
  34. # verify a few fetch_configs
  35. self.assertIn('foo', split[1])
  36. self.assertNotIn('bar', split[1])
  37. def test_handle_args_valid_usage(self):
  38. response = fetch.handle_args(['filename', 'foo'])
  39. self.assertEqual(argparse.Namespace(
  40. dry_run=False,
  41. nohooks=False,
  42. no_history=False,
  43. force=False,
  44. config='foo',
  45. protocol_override='https',
  46. props=[]), response)
  47. response = fetch.handle_args([
  48. 'filename', '-n', '--dry-run', '--nohooks', '--no-history', '--force',
  49. '--protocol-override', 'sso', 'foo', '--some-param=1', '--bar=2'
  50. ])
  51. self.assertEqual(argparse.Namespace(
  52. dry_run=True,
  53. nohooks=True,
  54. no_history=True,
  55. force=True,
  56. config='foo',
  57. protocol_override='sso',
  58. props=['--some-param=1', '--bar=2']), response)
  59. response = fetch.handle_args([
  60. 'filename', '-n', '--dry-run', '--nohooks', '--no-history', '--force',
  61. '-p', 'sso', 'foo', '--some-param=1', '--bar=2'
  62. ])
  63. self.assertEqual(argparse.Namespace(
  64. dry_run=True,
  65. nohooks=True,
  66. no_history=True,
  67. force=True,
  68. config='foo',
  69. protocol_override='sso',
  70. props=['--some-param=1', '--bar=2']), response)
  71. @mock.patch('os.path.exists', return_value=False)
  72. @mock.patch('sys.stdout', StringIO())
  73. @mock.patch('sys.exit', side_effect=SystemExitMock)
  74. def test_run_config_fetch_not_found(self, exit_mock, exists):
  75. with self.assertRaises(SystemExitMock):
  76. fetch.run_config_fetch('foo', [])
  77. exit_mock.assert_called_with(1)
  78. exists.assert_called_once()
  79. self.assertEqual(1, len(exists.call_args[0]))
  80. self.assertTrue(exists.call_args[0][0].endswith('foo.py'))
  81. stdout = sys.stdout.getvalue()
  82. self.assertEqual('Could not find a config for foo\n', stdout)
  83. def test_run_config_fetch_integration(self):
  84. config = fetch.run_config_fetch('depot_tools', [])
  85. url = 'https://chromium.googlesource.com/chromium/tools/depot_tools.git'
  86. spec = {
  87. 'type': 'gclient_git',
  88. 'gclient_git_spec': {
  89. 'solutions': [{
  90. 'url': url,
  91. 'managed': False,
  92. 'name': 'depot_tools',
  93. 'deps_file': 'DEPS',
  94. }],
  95. }
  96. }
  97. self.assertEqual((spec, 'depot_tools'), config)
  98. def test_checkout_factory(self):
  99. with self.assertRaises(KeyError):
  100. fetch.CheckoutFactory('invalid', {}, {}, "root")
  101. gclient = fetch.CheckoutFactory('gclient', {}, {}, "root")
  102. self.assertTrue(isinstance(gclient, fetch.GclientCheckout))
  103. class TestCheckout(unittest.TestCase):
  104. def setUp(self):
  105. mock.patch('sys.stdout', StringIO()).start()
  106. self.addCleanup(mock.patch.stopall)
  107. self.opts = argparse.Namespace(dry_run=False)
  108. self.checkout = fetch.Checkout(self.opts, {}, '')
  109. @contextlib.contextmanager
  110. def _temporary_file(self):
  111. """Creates a temporary file and removes it once it's out of scope"""
  112. name = tempfile.mktemp()
  113. try:
  114. with open(name, 'w+') as f:
  115. yield f
  116. finally:
  117. os.remove(name)
  118. def test_run_dry(self):
  119. self.opts.dry_run = True
  120. self.checkout.run(['foo-not-found'])
  121. self.assertEqual('Running: foo-not-found\n', sys.stdout.getvalue())
  122. def test_run_non_existing_command(self):
  123. with self.assertRaises(OSError):
  124. self.checkout.run(['foo-not-found'])
  125. self.assertEqual('Running: foo-not-found\n', sys.stdout.getvalue())
  126. def test_run_non_existing_command_return_stdout(self):
  127. with self.assertRaises(OSError):
  128. self.checkout.run(['foo-not-found'], return_stdout=True)
  129. self.assertEqual('Running: foo-not-found\n', sys.stdout.getvalue())
  130. @mock.patch('sys.stderr', StringIO())
  131. @mock.patch('sys.exit', side_effect=SystemExitMock)
  132. def test_run_wrong_param(self, exit_mock):
  133. # mocked version of sys.std* is not passed to subprocess, use temp files
  134. with self._temporary_file() as f:
  135. with self.assertRaises(subprocess.CalledProcessError):
  136. self.checkout.run([sys.executable, '-invalid-param'],
  137. return_stdout=True,
  138. stderr=f)
  139. f.seek(0)
  140. # Expect some message to stderr
  141. self.assertNotEqual('', f.read())
  142. self.assertEqual('', sys.stderr.getvalue())
  143. with self._temporary_file() as f:
  144. with self.assertRaises(SystemExitMock):
  145. self.checkout.run([sys.executable, '-invalid-param'], stderr=f)
  146. f.seek(0)
  147. # Expect some message to stderr
  148. self.assertNotEqual('', f.read())
  149. self.assertIn('Subprocess failed with return code', sys.stdout.getvalue())
  150. exit_mock.assert_called_once()
  151. def test_run_return_as_value(self):
  152. cmd = [sys.executable, '-c', 'print("foo")']
  153. response = self.checkout.run(cmd, return_stdout=True)
  154. # we expect no response other than information about command
  155. self.assertNotIn('foo', sys.stdout.getvalue().split('\n'))
  156. # this file should be included in response
  157. self.assertEqual('foo', response.strip())
  158. def test_run_print_to_stdout(self):
  159. cmd = [sys.executable, '-c', 'print("foo")']
  160. # mocked version of sys.std* is not passed to subprocess, use temp files
  161. with self._temporary_file() as stdout:
  162. with self._temporary_file() as stderr:
  163. response = self.checkout.run(cmd, stdout=stdout, stderr=stderr)
  164. stdout.seek(0)
  165. stderr.seek(0)
  166. self.assertEqual('foo\n', stdout.read())
  167. self.assertEqual('', stderr.read())
  168. stdout = sys.stdout.getvalue()
  169. self.assertEqual('', response)
  170. class TestGClientCheckout(unittest.TestCase):
  171. def setUp(self):
  172. self.run = mock.patch('fetch.Checkout.run').start()
  173. self.opts = argparse.Namespace(dry_run=False)
  174. self.checkout = fetch.GclientCheckout(self.opts, {}, '/root')
  175. self.addCleanup(mock.patch.stopall)
  176. @mock.patch('distutils.spawn.find_executable', return_value=True)
  177. def test_run_gclient_executable_found(self, find_executable):
  178. self.checkout.run_gclient('foo', 'bar', baz='qux')
  179. find_executable.assert_called_once_with('gclient')
  180. self.run.assert_called_once_with(('gclient', 'foo', 'bar'), baz='qux')
  181. @mock.patch('distutils.spawn.find_executable', return_value=False)
  182. def test_run_gclient_executable_not_found(self, find_executable):
  183. self.checkout.run_gclient('foo', 'bar', baz='qux')
  184. find_executable.assert_called_once_with('gclient')
  185. args = self.run.call_args[0][0]
  186. kargs = self.run.call_args[1]
  187. self.assertEqual(4, len(args))
  188. self.assertEqual(sys.executable, args[0])
  189. self.assertTrue(args[1].endswith('gclient.py'))
  190. self.assertEqual(('foo', 'bar'), args[2:])
  191. self.assertEqual({'baz': 'qux'}, kargs)
  192. class TestGclientGitCheckout(unittest.TestCase):
  193. def setUp(self):
  194. self.run_gclient = mock.patch('fetch.GclientCheckout.run_gclient').start()
  195. self.run_git = mock.patch('fetch.GitCheckout.run_git').start()
  196. self.opts = argparse.Namespace(
  197. dry_run=False, nohooks=True, no_history=False)
  198. specs = {
  199. 'solutions': [{
  200. 'foo': 'bar',
  201. 'baz': 1
  202. }, {
  203. 'foo': False
  204. }],
  205. 'with_branch_heads': True,
  206. }
  207. self.checkout = fetch.GclientGitCheckout(self.opts, specs, '/root')
  208. self.addCleanup(mock.patch.stopall)
  209. def test_init(self):
  210. self.checkout.init()
  211. self.assertEqual(2, self.run_gclient.call_count)
  212. self.assertEqual(3, self.run_git.call_count)
  213. # Verify only expected commands and ignore arguments to avoid copying
  214. # commands from fetch.py
  215. self.assertEqual(['config', 'sync'],
  216. [a[0][0] for a in self.run_gclient.call_args_list])
  217. self.assertEqual(['submodule', 'config', 'config'],
  218. [a[0][0] for a in self.run_git.call_args_list])
  219. # First call to gclient, format spec is expected to be called so "foo" is
  220. # expected to be present
  221. args = self.run_gclient.call_args_list[0][0]
  222. self.assertEqual('config', args[0])
  223. self.assertIn('foo', args[2])
  224. if __name__ == '__main__':
  225. logging.basicConfig(
  226. level=logging.DEBUG if '-v' in sys.argv else logging.ERROR)
  227. unittest.main()