PageRenderTime 52ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/buildutil/common_test.py

https://gitlab.com/adam.lukaitis/fplutil
Python | 407 lines | 205 code | 46 blank | 156 comment | 15 complexity | 1d9570affc4d121d109883df8cc75571 MD5 | raw file
  1. #!/usr/bin/python
  2. # Copyright 2014 Google Inc. All rights reserved.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. #
  16. import argparse
  17. import os
  18. import sys
  19. import unittest
  20. sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir))
  21. import buildutil.common as common
  22. class MockOsPathExists(object):
  23. """Mock of os.path.exists.
  24. Attributes:
  25. mock_path_exists: Value to return from __call__().
  26. """
  27. def __init__(self, mock_path_exists):
  28. """Initialize this instance.
  29. Args:
  30. mock_path_exists: Value to return from __call__().
  31. """
  32. self.mock_path_exists = mock_path_exists
  33. def __call__(self, unused_path):
  34. """Mock of os.path.exists.
  35. Returns:
  36. Value of mock_path_exists set in the constructor.
  37. """
  38. return self.mock_path_exists
  39. class MockFindExecutable(object):
  40. """Mock of common._find_executable
  41. Attribtues:
  42. mock_path: Path to return from __call__().
  43. """
  44. def __init__(self, mock_path):
  45. """Initialize the instance.
  46. Args:
  47. mock_path: Path to return from __call__().
  48. """
  49. self.mock_path = mock_path
  50. def __call__(self, unused_name):
  51. """Mock of common._find_executable().
  52. Returns:
  53. Returns the mock_path attribute.
  54. """
  55. return self.mock_path
  56. class RunCommandMock(object):
  57. """Callable object which verifies command lines.
  58. Attributes:
  59. expected_args: Expected command line arguments.
  60. expected_cwd: Expected working directory.
  61. expected_shell: Expected value of the shell argument.
  62. stdout: Standard output string that is the result of a call to this object.
  63. stderr: Standard error string that is the result of a call to this object.
  64. test_case: unittest.TestCase used to verify arguments.
  65. """
  66. def __init__(self, test_case, args=None, cwd=None, stdout=None, stderr=None,
  67. shell=None):
  68. """Callable object which verifies command lines.
  69. Args:
  70. test_case: unittest.TestCase used to verify arguments.
  71. args: Expected command line arguments.
  72. cwd: Expected working directory.
  73. stdout: Standard output string that is the result of a call to this
  74. object.
  75. stderr: Standard error string that is the result of a call to this
  76. object.
  77. shell: Expected value of the shell argument.
  78. """
  79. self.expected_args = args
  80. self.expected_cwd = cwd
  81. self.expected_shell = shell
  82. self.stdout = stdout
  83. self.stderr = stderr
  84. self.test_case = test_case
  85. def returns(self, stdout, stderr=None):
  86. """Set the strings values returned by __call__().
  87. Args:
  88. stdout: Standard output string that is the result of a call to this
  89. object.
  90. stderr: Standard error string that is the result of a call to this
  91. object.
  92. """
  93. self.stdout = stdout
  94. self.stderr = stderr
  95. def expect(self, args, cwd=None, shell=None):
  96. """Set the expected arguments when this class is called.
  97. Args:
  98. args: Expected arguments when this class is called.
  99. cwd: Expected current working directory when this class is called or
  100. None to ignore this value.
  101. shell: Expected value of the shell argument.
  102. """
  103. self.expected_args = args
  104. self.expected_cwd = cwd
  105. if shell is not None:
  106. self.expected_shell = shell
  107. def __call__(self, argv, capture=False, cwd=os.getcwd(), shell=False,
  108. stdin=None):
  109. """Mock of common.BuildEnvironment.run_subprocess().
  110. Args:
  111. argv: Arguments to compare with the expected arguments for this class.
  112. capture: Whether to return a tuple (self.stdout, self.stderr)
  113. cwd: Optional path relative to the project directory to run process in
  114. for commands that do not allow specifying this, such as ant.
  115. shell: Compared against expected_shell.
  116. stdin: Unused.
  117. Returns:
  118. (self.stdout, self.stderr) if capture is True, None otherwise.
  119. """
  120. if self.expected_cwd:
  121. self.test_case.assertEqual(self.expected_cwd, cwd)
  122. if self.expected_shell:
  123. self.test_case.assertEqual(self.expected_shell, shell)
  124. if issubclass(list, argv.__class__):
  125. self.test_case.assertListEqual(self.expected_args, argv)
  126. else:
  127. self.test_case.assertEqual(str(self.expected_args), argv)
  128. if capture:
  129. return (self.stdout, self.stderr)
  130. class RunCommandMockList(object):
  131. """List of RunCommandMock instances.
  132. Each call to this class pops the RunCommandMock instance from the top of
  133. the pending list and calls the object.
  134. Attributes:
  135. mock_list: List of RunCommandMock instances. This list of instances is
  136. called in sequence on subsequent calls to RunCommandMockList.__call__().
  137. """
  138. def __init__(self, mock_list):
  139. """Initialize this instance.
  140. Args:
  141. mock_list: List of RunCommandMock instances. This list of instances is
  142. called in sequence on subsequent calls to
  143. RunCommandMockList.__call__().
  144. """
  145. self.mock_list = mock_list
  146. def __call__(self, argv, capture=False, cwd=os.getcwd(), shell=False):
  147. """Mock of common.BuildEnvironment.run_subprocess().
  148. Args:
  149. argv: Arguments to compare with the expected arguments of the
  150. RunCommandMock instance at the head of mock_list.
  151. capture: Whether to return a tuple (self.stdout, self.stderr)
  152. cwd: Optional path relative to the project directory to run process in
  153. for commands that do not allow specifying this, such as ant.
  154. shell: Whether to run the command to run in a shell.
  155. Returns:
  156. (self.stdout, self.stderr) if capture is True, None otherwise.
  157. """
  158. return self.mock_list.pop(0)(argv, capture=capture, cwd=cwd, shell=shell)
  159. class MakeVerifier(RunCommandMock):
  160. """Callable object which verifies make command lines.
  161. Attributes:
  162. expected: Expected arguments for make.
  163. test_case: unittest.TestCase used to verify make arguments.
  164. """
  165. def __init__(self, test_case, expected):
  166. """Initialize this instance.
  167. Args:
  168. test_case: unittest.TestCase used to verify make arguments.
  169. expected: Expected arguments for make.
  170. """
  171. RunCommandMock.__init__(self, test_case)
  172. d = common.BuildEnvironment.build_defaults()
  173. # If the implementation changes arguments, this mock needs to be updated as
  174. # well.
  175. self.expect([d[common._MAKE_PATH], '-j', d[common._CPU_COUNT]] + expected)
  176. class CommonBuildUtilTest(unittest.TestCase):
  177. """Common base buildutil unit tests."""
  178. def setUp(self):
  179. self.git_clean_ran = False
  180. self.git_reset_ran = False
  181. self.os_path_exists = os.path.exists
  182. self.find_executable = common._find_executable
  183. common._find_executable = (
  184. lambda name, path=None: path if path else os.path.join('a', 'b', name))
  185. def tearDown(self):
  186. common._find_executable = self.find_executable
  187. os.path.exists = self.os_path_exists
  188. def test_build_defaults(self):
  189. d = common.BuildEnvironment.build_defaults()
  190. self.assertIn(common._PROJECT_DIR, d)
  191. self.assertIn(common._CPU_COUNT, d)
  192. self.assertIn(common._MAKE_PATH, d)
  193. self.assertIn(common._GIT_PATH, d)
  194. self.assertIn(common._MAKE_FLAGS, d)
  195. self.assertIn(common._GIT_CLEAN, d)
  196. self.assertIn(common._VERBOSE, d)
  197. self.assertIn(common._OUTPUT_DIR, d)
  198. self.assertIn(common._CLEAN, d)
  199. def test_init(self):
  200. d = common.BuildEnvironment.build_defaults()
  201. b = common.BuildEnvironment(d)
  202. self.assertEqual(b.project_directory, d[common._PROJECT_DIR])
  203. self.assertEqual(b.cpu_count, d[common._CPU_COUNT])
  204. self.assertEqual(b.make_path, d[common._MAKE_PATH])
  205. self.assertEqual(b.git_path, d[common._GIT_PATH])
  206. self.assertEqual(b.make_flags, d[common._MAKE_FLAGS])
  207. self.assertEqual(b.enable_git_clean, d[common._GIT_CLEAN])
  208. self.assertEqual(b.verbose, d[common._VERBOSE])
  209. self.assertEqual(b.output_directory, d[common._OUTPUT_DIR])
  210. self.assertEqual(b.clean, d[common._CLEAN])
  211. def test_add_arguments(self):
  212. p = argparse.ArgumentParser()
  213. common.BuildEnvironment.add_arguments(p)
  214. args = ['--' + common._PROJECT_DIR, 'a',
  215. '--' + common._CPU_COUNT, 'b',
  216. '--' + common._MAKE_PATH, 'c',
  217. '--' + common._GIT_PATH, 'd',
  218. '--' + common._MAKE_FLAGS, 'e',
  219. '--' + common._GIT_CLEAN,
  220. '--' + common._VERBOSE,
  221. '--' + common._OUTPUT_DIR, 'f',
  222. '--' + common._CLEAN]
  223. argobj = p.parse_args(args)
  224. d = vars(argobj)
  225. self.assertEqual('a', d[common._PROJECT_DIR])
  226. self.assertEqual('b', d[common._CPU_COUNT])
  227. self.assertEqual('c', d[common._MAKE_PATH])
  228. self.assertEqual('d', d[common._GIT_PATH])
  229. self.assertEqual('e', d[common._MAKE_FLAGS])
  230. self.assertTrue(d[common._GIT_CLEAN])
  231. self.assertTrue(d[common._VERBOSE])
  232. self.assertEqual('f', d[common._OUTPUT_DIR])
  233. self.assertTrue(d[common._CLEAN])
  234. def test_find_path_from_binary(self):
  235. test_data = [
  236. (os.path.join(os.path.sep, 'a', 'b', 'c'), 0,
  237. os.path.join(os.path.sep, 'a', 'b', 'c')),
  238. (os.path.join(os.path.sep, 'a', 'b', 'c'), 1,
  239. os.path.join(os.path.sep, 'a', 'b')),
  240. (os.path.join(os.path.sep, 'a', 'b', 'c'), 2,
  241. os.path.join(os.path.sep, 'a')),
  242. (os.path.join(os.path.sep, 'a', 'b', 'c'), 3, os.path.sep),
  243. (os.path.join(os.path.sep, 'a', 'b', 'c'), 4, None),
  244. (os.path.join(os.path.sep, 'a', 'b', 'c'), -1,
  245. os.path.join(os.path.sep, 'a', 'b', 'c')),
  246. (os.path.join(os.path.sep, 'a'), 0, os.path.join(os.path.sep, 'a')),
  247. (os.path.join(os.path.sep, 'a'), 1, os.path.sep),
  248. (os.path.join(os.path.sep, 'a'), 2, None)]
  249. for (path, levels, expect) in test_data:
  250. # reset in tearDown
  251. common._find_executable = MockFindExecutable(path)
  252. result = common.BuildEnvironment._find_path_from_binary('foo', levels)
  253. self.assertEqual(result, expect, '"%s" != "%s" (case: %s)' % (
  254. result, expect, str((path, levels, expect))))
  255. def test_find_binary_found(self):
  256. build_environment = common.BuildEnvironment(
  257. common.BuildEnvironment.build_defaults())
  258. common._find_executable = lambda name, path=None: None
  259. with self.assertRaises(common.ToolPathError):
  260. build_environment._find_binary(common.BuildEnvironment.MAKE)
  261. def test_find_binary_missing(self):
  262. build_environment = common.BuildEnvironment(
  263. common.BuildEnvironment.build_defaults())
  264. self.assertNotEquals(None, build_environment._find_binary(
  265. common.BuildEnvironment.GIT))
  266. def test_run_make(self):
  267. d = common.BuildEnvironment.build_defaults()
  268. b = common.BuildEnvironment(d)
  269. # Mock the call to run_subprocess.
  270. b.run_subprocess = MakeVerifier(self, ['-C', 'e', 'c', 'd'])
  271. b.make_flags = 'c d'
  272. b.project_directory = 'e'
  273. b.run_make()
  274. def test_run_make_clean(self):
  275. d = common.BuildEnvironment.build_defaults()
  276. b = common.BuildEnvironment(d)
  277. b.clean = True
  278. # Mock the call to run_subprocess.
  279. b.run_subprocess = MakeVerifier(self, ['-C', 'e', 'clean'])
  280. b.project_directory = 'e'
  281. b.run_make()
  282. def test_git_clean(self):
  283. d = common.BuildEnvironment.build_defaults()
  284. b = common.BuildEnvironment(d)
  285. # Mock the call to run_subprocess.
  286. b.run_subprocess = self.git_clean_verifier
  287. os.path.exists = MockOsPathExists(False) # reset in tearDown
  288. # first, unless enabled, git_clean() should do nothing.
  289. b.git_clean()
  290. self.assertFalse(self.git_clean_ran)
  291. self.assertFalse(self.git_reset_ran)
  292. b.enable_git_clean = True
  293. # next, should do nothing if not in git dir
  294. b.git_clean()
  295. self.assertFalse(self.git_clean_ran)
  296. self.assertFalse(self.git_reset_ran)
  297. os.path.exists = MockOsPathExists(True) # reset in tearDown
  298. # finally, should run
  299. b.project_directory = 'e'
  300. b.git_clean()
  301. self.assertTrue(self.git_clean_ran)
  302. self.assertTrue(self.git_reset_ran)
  303. def git_clean_verifier(self, args, cwd=None):
  304. """BuildEnvironment.run_subprocess mock for test_git_clean.
  305. Args:
  306. args: Argument list as normally passed to run_subprocess.
  307. cwd: Working directory arg as normally passed to run_subprocess.
  308. """
  309. d = common.BuildEnvironment.build_defaults()
  310. expected = None
  311. if 'clean' in args:
  312. self.git_clean_ran = True
  313. expected = [d[common._GIT_PATH], '-C', 'e', 'clean', '-d', '-f']
  314. else:
  315. if 'reset' in args:
  316. self.git_reset_ran = True
  317. expected = [d[common._GIT_PATH], '-C', 'e', 'reset', '--hard']
  318. self.assertIsNotNone(expected)
  319. self.assertListEqual(args, expected)
  320. if cwd:
  321. self.assertEqual(cwd, d[common._PROJECT_DIR])
  322. def test_get_project_directory(self):
  323. b = common.BuildEnvironment(common.BuildEnvironment.build_defaults())
  324. b.project_directory = os.path.join('examples', 'libfplutil_example')
  325. expected = os.path.join(os.getcwd(), b.project_directory)
  326. self.assertEquals(expected, b.get_project_directory())
  327. def test_get_project_directory_relative(self):
  328. b = common.BuildEnvironment(common.BuildEnvironment.build_defaults())
  329. directories = ('examples', 'libfplutil_example')
  330. b.project_directory = os.path.join(directories[0], directories[1])
  331. expected = os.path.join(os.getcwd(), directories[0])
  332. self.assertEquals(expected, b.get_project_directory(path='..'))
  333. # TBD, these are highly dependent high level functions that may need refactor
  334. # to unit-test well, as they are currently difficult to mock. At the moment
  335. # the examples serve as functional tests for these.
  336. def test_make_archive(self):
  337. pass
  338. def test_write_archive(self):
  339. pass
  340. if __name__ == '__main__':
  341. unittest.main()