PageRenderTime 39ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/tests/python/pants_test/build_graph/test_build_file_address_mapper.py

https://gitlab.com/Ivy001/pants
Python | 281 lines | 269 code | 7 blank | 5 comment | 0 complexity | 98fa501dcdfc4e68e868fe1f7a3c1976 MD5 | raw file
  1. # coding=utf-8
  2. # Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).
  3. # Licensed under the Apache License, Version 2.0 (see LICENSE).
  4. from __future__ import (absolute_import, division, generators, nested_scopes, print_function,
  5. unicode_literals, with_statement)
  6. import os
  7. import re
  8. from textwrap import dedent
  9. from pants.base.cmd_line_spec_parser import CmdLineSpecParser
  10. from pants.base.specs import DescendantAddresses
  11. from pants.build_graph.address import Address, BuildFileAddress
  12. from pants.build_graph.address_lookup_error import AddressLookupError
  13. from pants.build_graph.build_file_address_mapper import BuildFileAddressMapper
  14. from pants.build_graph.target import Target
  15. from pants_test.base_test import BaseTest
  16. # TODO(Eric Ayers) There are methods in BuildFileAddressMapper that are missing
  17. # explicit unit tests: addresses_in_spec_path, spec_to_address, spec_to_addresses
  18. class BuildFileAddressMapperTest(BaseTest):
  19. def test_resolve(self):
  20. build_file = self.add_to_build_file('BUILD', 'target(name="foo")')
  21. address, addressable = self.address_mapper.resolve(Address.parse('//:foo'))
  22. self.assertIsInstance(address, BuildFileAddress)
  23. self.assertEqual(build_file, address.build_file)
  24. self.assertEqual('foo', address.target_name)
  25. self.assertEqual(address.target_name, addressable.addressed_name)
  26. self.assertEqual(addressable.addressed_type, Target)
  27. def test_resolve_spec(self):
  28. self.add_to_build_file('BUILD', dedent("""
  29. target(name='foozle')
  30. target(name='baz')
  31. """))
  32. with self.assertRaises(AddressLookupError):
  33. self.address_mapper.resolve_spec('//:bad_spec')
  34. dependencies_addressable = self.address_mapper.resolve_spec('//:foozle')
  35. self.assertEqual(dependencies_addressable.addressed_type, Target)
  36. def test_scan_addresses(self):
  37. root_build_file = self.add_to_build_file('BUILD', 'target(name="foo")')
  38. subdir_build_file = self.add_to_build_file('subdir/BUILD', 'target(name="bar")')
  39. subdir_suffix_build_file = self.add_to_build_file('subdir/BUILD.suffix', 'target(name="baz")')
  40. with open(os.path.join(self.build_root, 'BUILD.invalid.suffix'), 'w') as invalid_build_file:
  41. invalid_build_file.write('target(name="foobar")')
  42. self.assertEquals({BuildFileAddress(root_build_file, 'foo'),
  43. BuildFileAddress(subdir_build_file, 'bar'),
  44. BuildFileAddress(subdir_suffix_build_file, 'baz')},
  45. self.address_mapper.scan_addresses())
  46. def test_scan_addresses_with_root(self):
  47. self.add_to_build_file('BUILD', 'target(name="foo")')
  48. subdir_build_file = self.add_to_build_file('subdir/BUILD', 'target(name="bar")')
  49. subdir_suffix_build_file = self.add_to_build_file('subdir/BUILD.suffix', 'target(name="baz")')
  50. subdir = os.path.join(self.build_root, 'subdir')
  51. self.assertEquals({BuildFileAddress(subdir_build_file, 'bar'),
  52. BuildFileAddress(subdir_suffix_build_file, 'baz')},
  53. self.address_mapper.scan_addresses(root=subdir))
  54. def test_scan_addresses_with_invalid_root(self):
  55. with self.assertRaises(BuildFileAddressMapper.InvalidRootError):
  56. self.address_mapper.scan_addresses(root='subdir')
  57. def test_raises_invalid_build_file_reference(self):
  58. # reference a BUILD file that doesn't exist
  59. with self.assertRaisesRegexp(BuildFileAddressMapper.InvalidBuildFileReference,
  60. '^.*/non-existent-path does not contain any BUILD files.'
  61. '\s+when translating spec //non-existent-path:a'):
  62. self.address_mapper.spec_to_address('//non-existent-path:a')
  63. with self.assertRaisesRegexp(BuildFileAddressMapper.InvalidBuildFileReference,
  64. '^Spec : has no name part\s+when translating spec :'):
  65. self.address_mapper.spec_to_address(':')
  66. def test_raises_address_not_in_one_build_file(self):
  67. self.add_to_build_file('BUILD', 'target(name="foo")')
  68. # Create an address that doesn't exist in an existing BUILD file
  69. address = Address.parse(':bar')
  70. with self.assertRaisesRegexp(BuildFileAddressMapper.AddressNotInBuildFile,
  71. '^bar was not found in BUILD files from .*. '
  72. 'Perhaps you meant:'
  73. '\s+:foo$'):
  74. self.address_mapper.resolve(address)
  75. def test_raises_address_not_in_two_build_files(self):
  76. self.add_to_build_file('BUILD.1', 'target(name="foo1")')
  77. self.add_to_build_file('BUILD.2', 'target(name="foo2")')
  78. # Create an address that doesn't exist in an existing BUILD file
  79. address = Address.parse(':bar')
  80. with self.assertRaisesRegexp(BuildFileAddressMapper.AddressNotInBuildFile,
  81. '^bar was not found in BUILD files from .*. '
  82. 'Perhaps you meant one of:'
  83. '\s+:foo1 \(from BUILD.1\)'
  84. '\s+:foo2 \(from BUILD.2\)$'):
  85. self.address_mapper.resolve(address)
  86. def test_raises_address_invalid_address_error(self):
  87. with self.assertRaises(BuildFileAddressMapper.InvalidAddressError):
  88. self.address_mapper.resolve_spec("../foo")
  89. def test_raises_empty_build_file_error(self):
  90. self.add_to_build_file('BUILD', 'pass')
  91. with self.assertRaises(BuildFileAddressMapper.EmptyBuildFileError):
  92. self.address_mapper.resolve_spec('//:foo')
  93. def test_address_lookup_error_hierarchy(self):
  94. self.assertIsInstance(BuildFileAddressMapper.AddressNotInBuildFile(), AddressLookupError)
  95. self.assertIsInstance(BuildFileAddressMapper.EmptyBuildFileError(), AddressLookupError)
  96. self.assertIsInstance(BuildFileAddressMapper.InvalidBuildFileReference(), AddressLookupError)
  97. self.assertIsInstance(BuildFileAddressMapper.InvalidAddressError(), AddressLookupError)
  98. self.assertIsInstance(BuildFileAddressMapper.BuildFileScanError(), AddressLookupError)
  99. def test_raises_wrong_dependencies_type(self):
  100. self.add_to_build_file('BUILD', 'target(name="foo", dependencies="bar")')
  101. address = Address.parse(':foo')
  102. with self.assertRaisesRegexp(AddressLookupError,
  103. '^Invalid target.*foo.*.'
  104. 'dependencies passed to Target constructors must be a sequence of strings'):
  105. self.address_mapper.resolve(address)
  106. class BuildFileAddressMapperWithIgnoreTest(BaseTest):
  107. @property
  108. def build_ignore_patterns(self):
  109. return ['subdir']
  110. def test_scan_from_address_mapper(self):
  111. root_build_file = self.add_to_build_file('BUILD', 'target(name="foo")')
  112. self.add_to_build_file('subdir/BUILD', 'target(name="bar")')
  113. self.assertEquals({BuildFileAddress(root_build_file, 'foo')}, self.address_mapper.scan_addresses())
  114. def test_scan_from_context(self):
  115. self.add_to_build_file('BUILD', 'target(name="foo")')
  116. self.add_to_build_file('subdir/BUILD', 'target(name="bar")')
  117. graph = self.context().scan()
  118. self.assertEquals([target.address.spec for target in graph.targets()], ['//:foo'])
  119. class BuildFileAddressMapperScanTest(BaseTest):
  120. NO_FAIL_FAST_RE = re.compile(r"""^--------------------
  121. .*
  122. Exception message: name 'a_is_bad' is not defined
  123. while executing BUILD file BuildFile\(bad/a/BUILD, FileSystemProjectTree\(.*\)\)
  124. Loading addresses from 'bad/a' failed\.
  125. .*
  126. Exception message: name 'b_is_bad' is not defined
  127. while executing BUILD file BuildFile\(bad/b/BUILD, FileSystemProjectTree\(.*\)\)
  128. Loading addresses from 'bad/b' failed\.
  129. Invalid BUILD files for \[::\]$""", re.DOTALL)
  130. FAIL_FAST_RE = """^name 'a_is_bad' is not defined
  131. while executing BUILD file BuildFile\(bad/a/BUILD\, FileSystemProjectTree\(.*\)\)
  132. Loading addresses from 'bad/a' failed.$"""
  133. def setUp(self):
  134. super(BuildFileAddressMapperScanTest, self).setUp()
  135. def add_target(path, name):
  136. self.add_to_build_file(path, 'target(name="{name}")\n'.format(name=name))
  137. add_target('BUILD', 'root')
  138. add_target('a', 'a')
  139. add_target('a', 'b')
  140. add_target('a/b', 'b')
  141. add_target('a/b', 'c')
  142. self._spec_parser = CmdLineSpecParser(self.build_root)
  143. def test_bad_build_files(self):
  144. self.add_to_build_file('bad/a', 'a_is_bad')
  145. self.add_to_build_file('bad/b', 'b_is_bad')
  146. with self.assertRaisesRegexp(AddressLookupError, self.NO_FAIL_FAST_RE):
  147. list(self.address_mapper.scan_specs([DescendantAddresses('')], fail_fast=False))
  148. def test_bad_build_files_fail_fast(self):
  149. self.add_to_build_file('bad/a', 'a_is_bad')
  150. self.add_to_build_file('bad/b', 'b_is_bad')
  151. with self.assertRaisesRegexp(AddressLookupError, self.FAIL_FAST_RE):
  152. list(self.address_mapper.scan_specs([DescendantAddresses('')], fail_fast=True))
  153. def test_normal(self):
  154. self.assert_scanned([':root'], expected=[':root'])
  155. self.assert_scanned(['//:root'], expected=[':root'])
  156. self.assert_scanned(['a'], expected=['a'])
  157. self.assert_scanned(['a:a'], expected=['a'])
  158. self.assert_scanned(['a/b'], expected=['a/b'])
  159. self.assert_scanned(['a/b:b'], expected=['a/b'])
  160. self.assert_scanned(['a/b:c'], expected=['a/b:c'])
  161. def test_sibling(self):
  162. self.assert_scanned([':'], expected=[':root'])
  163. self.assert_scanned(['//:'], expected=[':root'])
  164. self.assert_scanned(['a:'], expected=['a', 'a:b'])
  165. self.assert_scanned(['//a:'], expected=['a', 'a:b'])
  166. self.assert_scanned(['a/b:'], expected=['a/b', 'a/b:c'])
  167. self.assert_scanned(['//a/b:'], expected=['a/b', 'a/b:c'])
  168. def test_sibling_or_descendents(self):
  169. self.assert_scanned(['::'], expected=[':root', 'a', 'a:b', 'a/b', 'a/b:c'])
  170. self.assert_scanned(['//::'], expected=[':root', 'a', 'a:b', 'a/b', 'a/b:c'])
  171. self.assert_scanned(['a::'], expected=['a', 'a:b', 'a/b', 'a/b:c'])
  172. self.assert_scanned(['//a::'], expected=['a', 'a:b', 'a/b', 'a/b:c'])
  173. self.assert_scanned(['a/b::'], expected=['a/b', 'a/b:c'])
  174. self.assert_scanned(['//a/b::'], expected=['a/b', 'a/b:c'])
  175. def test_cmd_line_affordances(self):
  176. self.assert_scanned(['./:root'], expected=[':root'])
  177. self.assert_scanned(['//./:root'], expected=[':root'])
  178. self.assert_scanned(['//./a/../:root'], expected=[':root'])
  179. self.assert_scanned([os.path.join(self.build_root, './a/../:root')],
  180. expected=[':root'])
  181. self.assert_scanned(['a/'], expected=['a'])
  182. self.assert_scanned(['./a/'], expected=['a'])
  183. self.assert_scanned([os.path.join(self.build_root, './a/')], expected=['a'])
  184. self.assert_scanned(['a/b/:b'], expected=['a/b'])
  185. self.assert_scanned(['./a/b/:b'], expected=['a/b'])
  186. self.assert_scanned([os.path.join(self.build_root, './a/b/:b')], expected=['a/b'])
  187. def test_cmd_line_spec_list(self):
  188. self.assert_scanned(['a', 'a/b'], expected=['a', 'a/b'])
  189. self.assert_scanned(['::'], expected=[':root', 'a', 'a:b', 'a/b', 'a/b:c'])
  190. def test_does_not_exist(self):
  191. with self.assertRaises(AddressLookupError):
  192. self.assert_scanned(['c'], expected=[])
  193. with self.assertRaises(AddressLookupError):
  194. self.assert_scanned(['c:'], expected=[])
  195. with self.assertRaises(AddressLookupError):
  196. self.assert_scanned(['c::'], expected=[])
  197. def test_build_ignore_patterns(self):
  198. expected_specs = [':root', 'a', 'a:b', 'a/b', 'a/b:c']
  199. # This bogus BUILD file gets in the way of parsing.
  200. self.add_to_build_file('some/dir', 'COMPLETELY BOGUS BUILDFILE)\n')
  201. with self.assertRaises(AddressLookupError):
  202. self.assert_scanned(['::'], expected=expected_specs)
  203. address_mapper_with_ignore = BuildFileAddressMapper(self.build_file_parser,
  204. self.project_tree,
  205. build_ignore_patterns=['some'])
  206. self.assert_scanned(['::'], expected=expected_specs, address_mapper=address_mapper_with_ignore)
  207. def test_exclude_target_regexps(self):
  208. address_mapper_with_exclude = BuildFileAddressMapper(self.build_file_parser,
  209. self.project_tree,
  210. exclude_target_regexps=[r'.*:b.*'])
  211. self.assert_scanned(['::'], expected=[':root', 'a', 'a/b:c'],
  212. address_mapper=address_mapper_with_exclude)
  213. def assert_scanned(self, specs_strings, expected, address_mapper=None):
  214. """Parse and scan the given specs."""
  215. address_mapper = address_mapper or self.address_mapper
  216. def sort(addresses):
  217. return sorted(addresses, key=lambda address: address.spec)
  218. specs = [self._spec_parser.parse_spec(s) for s in specs_strings]
  219. self.assertEqual(sort(Address.parse(addr) for addr in expected),
  220. sort(address_mapper.scan_specs(specs)))