test_util.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. # -*- coding: utf-8 -*-
  2. # Copyright (c) 2015 Hewlett-Packard Development Company, L.P. (HP)
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  5. # not use this file except in compliance with the License. You may obtain
  6. # 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, WITHOUT
  12. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. # License for the specific language governing permissions and limitations
  14. # under the License.
  15. import io
  16. import tempfile
  17. import textwrap
  18. import six
  19. from six.moves import configparser
  20. import sys
  21. from pbr.tests import base
  22. from pbr import util
  23. def config_from_ini(ini):
  24. config = {}
  25. ini = textwrap.dedent(six.u(ini))
  26. if sys.version_info >= (3, 2):
  27. parser = configparser.ConfigParser()
  28. parser.read_file(io.StringIO(ini))
  29. else:
  30. parser = configparser.SafeConfigParser()
  31. parser.readfp(io.StringIO(ini))
  32. for section in parser.sections():
  33. config[section] = dict(parser.items(section))
  34. return config
  35. class TestBasics(base.BaseTestCase):
  36. def test_basics(self):
  37. self.maxDiff = None
  38. config_text = """
  39. [metadata]
  40. name = foo
  41. version = 1.0
  42. author = John Doe
  43. author_email = jd@example.com
  44. maintainer = Jim Burke
  45. maintainer_email = jb@example.com
  46. home_page = http://example.com
  47. summary = A foobar project.
  48. description = Hello, world. This is a long description.
  49. download_url = http://opendev.org/x/pbr
  50. classifier =
  51. Development Status :: 5 - Production/Stable
  52. Programming Language :: Python
  53. platform =
  54. any
  55. license = Apache 2.0
  56. requires_dist =
  57. Sphinx
  58. requests
  59. setup_requires_dist =
  60. docutils
  61. python_requires = >=3.6
  62. provides_dist =
  63. bax
  64. provides_extras =
  65. bar
  66. obsoletes_dist =
  67. baz
  68. [files]
  69. packages_root = src
  70. packages =
  71. foo
  72. package_data =
  73. "" = *.txt, *.rst
  74. foo = *.msg
  75. namespace_packages =
  76. hello
  77. data_files =
  78. bitmaps =
  79. bm/b1.gif
  80. bm/b2.gif
  81. config =
  82. cfg/data.cfg
  83. scripts =
  84. scripts/hello-world.py
  85. modules =
  86. mod1
  87. """
  88. expected = {
  89. 'name': u'foo',
  90. 'version': u'1.0',
  91. 'author': u'John Doe',
  92. 'author_email': u'jd@example.com',
  93. 'maintainer': u'Jim Burke',
  94. 'maintainer_email': u'jb@example.com',
  95. 'url': u'http://example.com',
  96. 'description': u'A foobar project.',
  97. 'long_description': u'Hello, world. This is a long description.',
  98. 'download_url': u'http://opendev.org/x/pbr',
  99. 'classifiers': [
  100. u'Development Status :: 5 - Production/Stable',
  101. u'Programming Language :: Python',
  102. ],
  103. 'platforms': [u'any'],
  104. 'license': u'Apache 2.0',
  105. 'install_requires': [
  106. u'Sphinx',
  107. u'requests',
  108. ],
  109. 'setup_requires': [u'docutils'],
  110. 'python_requires': u'>=3.6',
  111. 'provides': [u'bax'],
  112. 'provides_extras': [u'bar'],
  113. 'obsoletes': [u'baz'],
  114. 'extras_require': {},
  115. 'package_dir': {'': u'src'},
  116. 'packages': [u'foo'],
  117. 'package_data': {
  118. '': ['*.txt,', '*.rst'],
  119. 'foo': ['*.msg'],
  120. },
  121. 'namespace_packages': [u'hello'],
  122. 'data_files': [
  123. ('bitmaps', ['bm/b1.gif', 'bm/b2.gif']),
  124. ('config', ['cfg/data.cfg']),
  125. ],
  126. 'scripts': [u'scripts/hello-world.py'],
  127. 'py_modules': [u'mod1'],
  128. }
  129. config = config_from_ini(config_text)
  130. actual = util.setup_cfg_to_setup_kwargs(config)
  131. self.assertDictEqual(expected, actual)
  132. class TestExtrasRequireParsingScenarios(base.BaseTestCase):
  133. scenarios = [
  134. ('simple_extras', {
  135. 'config_text': """
  136. [extras]
  137. first =
  138. foo
  139. bar==1.0
  140. second =
  141. baz>=3.2
  142. foo
  143. """,
  144. 'expected_extra_requires': {
  145. 'first': ['foo', 'bar==1.0'],
  146. 'second': ['baz>=3.2', 'foo'],
  147. 'test': ['requests-mock'],
  148. "test:(python_version=='2.6')": ['ordereddict'],
  149. }
  150. }),
  151. ('with_markers', {
  152. 'config_text': """
  153. [extras]
  154. test =
  155. foo:python_version=='2.6'
  156. bar
  157. baz<1.6 :python_version=='2.6'
  158. zaz :python_version>'1.0'
  159. """,
  160. 'expected_extra_requires': {
  161. "test:(python_version=='2.6')": ['foo', 'baz<1.6'],
  162. "test": ['bar', 'zaz']}}),
  163. ('no_extras', {
  164. 'config_text': """
  165. [metadata]
  166. long_description = foo
  167. """,
  168. 'expected_extra_requires':
  169. {}
  170. })]
  171. def test_extras_parsing(self):
  172. config = config_from_ini(self.config_text)
  173. kwargs = util.setup_cfg_to_setup_kwargs(config)
  174. self.assertEqual(self.expected_extra_requires,
  175. kwargs['extras_require'])
  176. class TestInvalidMarkers(base.BaseTestCase):
  177. def test_invalid_marker_raises_error(self):
  178. config = {'extras': {'test': "foo :bad_marker>'1.0'"}}
  179. self.assertRaises(SyntaxError, util.setup_cfg_to_setup_kwargs, config)
  180. class TestMapFieldsParsingScenarios(base.BaseTestCase):
  181. scenarios = [
  182. ('simple_project_urls', {
  183. 'config_text': """
  184. [metadata]
  185. project_urls =
  186. Bug Tracker = https://bugs.launchpad.net/pbr/
  187. Documentation = https://docs.openstack.org/pbr/
  188. Source Code = https://opendev.org/openstack/pbr
  189. """, # noqa: E501
  190. 'expected_project_urls': {
  191. 'Bug Tracker': 'https://bugs.launchpad.net/pbr/',
  192. 'Documentation': 'https://docs.openstack.org/pbr/',
  193. 'Source Code': 'https://opendev.org/openstack/pbr',
  194. },
  195. }),
  196. ('query_parameters', {
  197. 'config_text': """
  198. [metadata]
  199. project_urls =
  200. Bug Tracker = https://bugs.launchpad.net/pbr/?query=true
  201. Documentation = https://docs.openstack.org/pbr/?foo=bar
  202. Source Code = https://git.openstack.org/cgit/openstack-dev/pbr/commit/?id=hash
  203. """, # noqa: E501
  204. 'expected_project_urls': {
  205. 'Bug Tracker': 'https://bugs.launchpad.net/pbr/?query=true',
  206. 'Documentation': 'https://docs.openstack.org/pbr/?foo=bar',
  207. 'Source Code': 'https://git.openstack.org/cgit/openstack-dev/pbr/commit/?id=hash', # noqa: E501
  208. },
  209. }),
  210. ]
  211. def test_project_url_parsing(self):
  212. config = config_from_ini(self.config_text)
  213. kwargs = util.setup_cfg_to_setup_kwargs(config)
  214. self.assertEqual(self.expected_project_urls, kwargs['project_urls'])
  215. class TestKeywordsParsingScenarios(base.BaseTestCase):
  216. scenarios = [
  217. ('keywords_list', {
  218. 'config_text': """
  219. [metadata]
  220. keywords =
  221. one
  222. two
  223. three
  224. """, # noqa: E501
  225. 'expected_keywords': ['one', 'two', 'three'],
  226. },
  227. ),
  228. ('inline_keywords', {
  229. 'config_text': """
  230. [metadata]
  231. keywords = one, two, three
  232. """, # noqa: E501
  233. 'expected_keywords': ['one, two, three'],
  234. }),
  235. ]
  236. def test_keywords_parsing(self):
  237. config = config_from_ini(self.config_text)
  238. kwargs = util.setup_cfg_to_setup_kwargs(config)
  239. self.assertEqual(self.expected_keywords, kwargs['keywords'])
  240. class TestProvidesExtras(base.BaseTestCase):
  241. def test_provides_extras(self):
  242. ini = """
  243. [metadata]
  244. provides_extras = foo
  245. bar
  246. """
  247. config = config_from_ini(ini)
  248. kwargs = util.setup_cfg_to_setup_kwargs(config)
  249. self.assertEqual(['foo', 'bar'], kwargs['provides_extras'])
  250. class TestDataFilesParsing(base.BaseTestCase):
  251. scenarios = [
  252. ('data_files', {
  253. 'config_text': """
  254. [files]
  255. data_files =
  256. 'i like spaces/' =
  257. 'dir with space/file with spc 2'
  258. 'dir with space/file with spc 1'
  259. """,
  260. 'data_files': [
  261. ('i like spaces/', ['dir with space/file with spc 2',
  262. 'dir with space/file with spc 1'])
  263. ]
  264. })]
  265. def test_handling_of_whitespace_in_data_files(self):
  266. config = config_from_ini(self.config_text)
  267. kwargs = util.setup_cfg_to_setup_kwargs(config)
  268. self.assertEqual(self.data_files, kwargs['data_files'])
  269. class TestUTF8DescriptionFile(base.BaseTestCase):
  270. def test_utf8_description_file(self):
  271. _, path = tempfile.mkstemp()
  272. ini_template = """
  273. [metadata]
  274. description_file = %s
  275. """
  276. # Two \n's because pbr strips the file content and adds \n\n
  277. # This way we can use it directly as the assert comparison
  278. unicode_description = u'UTF8 description: é"…-ʃŋ\'\n\n'
  279. ini = ini_template % path
  280. with io.open(path, 'w', encoding='utf8') as f:
  281. f.write(unicode_description)
  282. config = config_from_ini(ini)
  283. kwargs = util.setup_cfg_to_setup_kwargs(config)
  284. self.assertEqual(unicode_description, kwargs['long_description'])