1 # Copyright (c) 2011 OpenStack Foundation
2 # Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
5 # Licensed under the Apache License, Version 2.0 (the "License"); you may
6 # not use this file except in compliance with the License. You may obtain
7 # a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 # License for the specific language governing permissions and limitations
17 from __future__ import print_function
22 import cStringIO as io
31 from pbr import options
32 from pbr import packaging
33 from pbr.tests import base
36 class SkipFileWrites(base.BaseTestCase):
39 ('changelog_option_true',
40 dict(option_key='skip_changelog', option_value='True',
41 env_key='SKIP_WRITE_GIT_CHANGELOG', env_value=None,
42 pkg_func=git.write_git_changelog, filename='ChangeLog')),
43 ('changelog_option_false',
44 dict(option_key='skip_changelog', option_value='False',
45 env_key='SKIP_WRITE_GIT_CHANGELOG', env_value=None,
46 pkg_func=git.write_git_changelog, filename='ChangeLog')),
47 ('changelog_env_true',
48 dict(option_key='skip_changelog', option_value='False',
49 env_key='SKIP_WRITE_GIT_CHANGELOG', env_value='True',
50 pkg_func=git.write_git_changelog, filename='ChangeLog')),
51 ('changelog_both_true',
52 dict(option_key='skip_changelog', option_value='True',
53 env_key='SKIP_WRITE_GIT_CHANGELOG', env_value='True',
54 pkg_func=git.write_git_changelog, filename='ChangeLog')),
55 ('authors_option_true',
56 dict(option_key='skip_authors', option_value='True',
57 env_key='SKIP_GENERATE_AUTHORS', env_value=None,
58 pkg_func=git.generate_authors, filename='AUTHORS')),
59 ('authors_option_false',
60 dict(option_key='skip_authors', option_value='False',
61 env_key='SKIP_GENERATE_AUTHORS', env_value=None,
62 pkg_func=git.generate_authors, filename='AUTHORS')),
64 dict(option_key='skip_authors', option_value='False',
65 env_key='SKIP_GENERATE_AUTHORS', env_value='True',
66 pkg_func=git.generate_authors, filename='AUTHORS')),
68 dict(option_key='skip_authors', option_value='True',
69 env_key='SKIP_GENERATE_AUTHORS', env_value='True',
70 pkg_func=git.generate_authors, filename='AUTHORS')),
74 super(SkipFileWrites, self).setUp()
75 self.temp_path = self.useFixture(fixtures.TempDir()).path
76 self.root_dir = os.path.abspath(os.path.curdir)
77 self.git_dir = os.path.join(self.root_dir, ".git")
78 if not os.path.exists(self.git_dir):
79 self.skipTest("%s is missing; skipping git-related checks"
82 self.filename = os.path.join(self.temp_path, self.filename)
83 self.option_dict = dict()
84 if self.option_key is not None:
85 self.option_dict[self.option_key] = ('setup.cfg',
88 fixtures.EnvironmentVariable(self.env_key, self.env_value))
91 self.pkg_func(git_dir=self.git_dir,
92 dest_dir=self.temp_path,
93 option_dict=self.option_dict)
95 not os.path.exists(self.filename),
96 (self.option_value.lower() in options.TRUE_VALUES
97 or self.env_value is not None))
99 _changelog_content = """7780758\x00Break parser\x00 (tag: refs/tags/1_foo.1)
100 04316fe\x00Make python\x00 (refs/heads/review/monty_taylor/27519)
101 378261a\x00Add an integration test script.\x00
102 3c373ac\x00Merge "Lib\x00 (HEAD, tag: refs/tags/2013.2.rc2, tag: refs/tags/2013.2, refs/heads/mile-proposed)
103 182feb3\x00Fix pip invocation for old versions of pip.\x00 (tag: refs/tags/0.5.17)
104 fa4f46e\x00Remove explicit depend on distribute.\x00 (tag: refs/tags/0.5.16)
105 d1c53dd\x00Use pip instead of easy_install for installation.\x00
106 a793ea1\x00Merge "Skip git-checkout related tests when .git is missing"\x00
107 6c27ce7\x00Skip git-checkout related tests when .git is missing\x00
108 451e513\x00Bug fix: create_stack() fails when waiting\x00
109 4c8cfe4\x00Improve test coverage: network delete API\x00 (tag: refs/tags/(evil))
110 d7e6167\x00Bug fix: Fix pass thru filtering in list_networks\x00 (tag: refs/tags/ev()il)
111 c47ec15\x00Consider 'in-use' a non-pending volume for caching\x00 (tag: refs/tags/ev)il)
112 8696fbd\x00Improve test coverage: private extension API\x00 (tag: refs/tags/ev(il)
113 f0440f8\x00Improve test coverage: hypervisor list\x00 (tag: refs/tags/e(vi)l)
114 04984a5\x00Refactor hooks file.\x00 (HEAD, tag: 0.6.7,b, tag: refs/tags/(12), refs/heads/master)
115 a65e8ee\x00Remove jinja pin.\x00 (tag: refs/tags/0.5.14, tag: refs/tags/0.5.13)
119 def _make_old_git_changelog_format(line):
120 """Convert post-1.8.1 git log format to pre-1.8.1 git log format"""
124 sha, msg, refname = line.split('\x00')
125 refname = refname.replace('tag: ', '')
126 return '\x00'.join((sha, msg, refname))
128 _old_git_changelog_content = '\n'.join(
129 _make_old_git_changelog_format(line)
130 for line in _changelog_content.split('\n'))
133 class GitLogsTest(base.BaseTestCase):
136 ('pre1.8.3', {'changelog': _old_git_changelog_content}),
137 ('post1.8.3', {'changelog': _changelog_content}),
141 super(GitLogsTest, self).setUp()
142 self.temp_path = self.useFixture(fixtures.TempDir()).path
143 self.root_dir = os.path.abspath(os.path.curdir)
144 self.git_dir = os.path.join(self.root_dir, ".git")
146 fixtures.EnvironmentVariable('SKIP_GENERATE_AUTHORS'))
148 fixtures.EnvironmentVariable('SKIP_WRITE_GIT_CHANGELOG'))
150 def test_write_git_changelog(self):
151 self.useFixture(fixtures.FakePopen(lambda _: {
152 "stdout": BytesIO(self.changelog.encode('utf-8'))
155 git.write_git_changelog(git_dir=self.git_dir,
156 dest_dir=self.temp_path)
158 with open(os.path.join(self.temp_path, "ChangeLog"), "r") as ch_fh:
159 changelog_contents = ch_fh.read()
160 self.assertIn("2013.2", changelog_contents)
161 self.assertIn("0.5.17", changelog_contents)
162 self.assertIn("------", changelog_contents)
163 self.assertIn("Refactor hooks file", changelog_contents)
165 "Bug fix: create\_stack() fails when waiting",
167 self.assertNotIn("Refactor hooks file.", changelog_contents)
168 self.assertNotIn("182feb3", changelog_contents)
169 self.assertNotIn("review/monty_taylor/27519", changelog_contents)
170 self.assertNotIn("0.5.13", changelog_contents)
171 self.assertNotIn("0.6.7", changelog_contents)
172 self.assertNotIn("12", changelog_contents)
173 self.assertNotIn("(evil)", changelog_contents)
174 self.assertNotIn("ev()il", changelog_contents)
175 self.assertNotIn("ev(il", changelog_contents)
176 self.assertNotIn("ev)il", changelog_contents)
177 self.assertNotIn("e(vi)l", changelog_contents)
178 self.assertNotIn('Merge "', changelog_contents)
179 self.assertNotIn('1\_foo.1', changelog_contents)
181 def test_generate_authors(self):
182 author_old = u"Foo Foo <email@foo.com>"
183 author_new = u"Bar Bar <email@bar.com>"
184 co_author = u"Foo Bar <foo@bar.com>"
185 co_author_by = u"Co-authored-by: " + co_author
188 "git --git-dir=%s log --format=%%aN <%%aE>"
190 git_co_log_cmd = ("git --git-dir=%s log" % self.git_dir)
191 git_top_level = "git rev-parse --show-toplevel"
193 git_log_cmd: author_new,
194 git_co_log_cmd: co_author_by,
195 git_top_level: self.root_dir,
198 exist_files = [self.git_dir,
199 os.path.join(self.temp_path, "AUTHORS.in")]
200 self.useFixture(fixtures.MonkeyPatch(
202 lambda path: os.path.abspath(path) in exist_files))
204 def _fake_run_shell_command(cmd, **kwargs):
205 return cmd_map[" ".join(cmd)]
207 self.useFixture(fixtures.MonkeyPatch(
208 "pbr.git._run_shell_command",
209 _fake_run_shell_command))
211 with open(os.path.join(self.temp_path, "AUTHORS.in"), "w") as auth_fh:
212 auth_fh.write("%s\n" % author_old)
214 git.generate_authors(git_dir=self.git_dir,
215 dest_dir=self.temp_path)
217 with open(os.path.join(self.temp_path, "AUTHORS"), "r") as auth_fh:
218 authors = auth_fh.read()
219 self.assertTrue(author_old in authors)
220 self.assertTrue(author_new in authors)
221 self.assertTrue(co_author in authors)
224 class _SphinxConfig(object):
228 class BaseSphinxTest(base.BaseTestCase):
231 super(BaseSphinxTest, self).setUp()
233 # setup_command requires the Sphinx instance to have some
234 # attributes that aren't set normally with the way we use the
235 # class (because we replace the constructor). Add default
236 # values directly to the class definition.
237 import sphinx.application
238 sphinx.application.Sphinx.messagelog = []
239 sphinx.application.Sphinx.statuscode = 0
241 self.useFixture(fixtures.MonkeyPatch(
242 "sphinx.application.Sphinx.__init__", lambda *a, **kw: None))
243 self.useFixture(fixtures.MonkeyPatch(
244 "sphinx.application.Sphinx.build", lambda *a, **kw: None))
245 self.useFixture(fixtures.MonkeyPatch(
246 "sphinx.application.Sphinx.config", _SphinxConfig))
247 self.useFixture(fixtures.MonkeyPatch(
248 "sphinx.config.Config.init_values", lambda *a: None))
249 self.useFixture(fixtures.MonkeyPatch(
250 "sphinx.config.Config.__init__", lambda *a: None))
251 from distutils import dist
252 self.distr = dist.Distribution()
253 self.distr.packages = ("fake_package",)
254 self.distr.command_options["build_sphinx"] = {
255 "source_dir": ["a", "."]}
256 pkg_fixture = fixtures.PythonPackage(
257 "fake_package", [("fake_module.py", b""),
258 ("another_fake_module_for_testing.py", b""),
259 ("fake_private_module.py", b"")])
260 self.useFixture(pkg_fixture)
261 self.useFixture(base.DiveDir(pkg_fixture.base))
262 self.distr.command_options["pbr"] = {}
263 if hasattr(self, "excludes"):
264 self.distr.command_options["pbr"]["autodoc_exclude_modules"] = (
266 "fake_package.fake_private_module\n"
267 "fake_package.another_fake_*\n"
268 "fake_package.unknown_module")
269 if hasattr(self, 'has_opt') and self.has_opt:
270 options = self.distr.command_options["pbr"]
271 options["autodoc_index_modules"] = ('setup.cfg', self.autodoc)
274 class BuildSphinxTest(BaseSphinxTest):
277 ('true_autodoc_caps',
278 dict(has_opt=True, autodoc='True', has_autodoc=True)),
279 ('true_autodoc_caps_with_excludes',
280 dict(has_opt=True, autodoc='True', has_autodoc=True,
281 excludes="fake_package.fake_private_module\n"
282 "fake_package.another_fake_*\n"
283 "fake_package.unknown_module")),
284 ('true_autodoc_lower',
285 dict(has_opt=True, autodoc='true', has_autodoc=True)),
287 dict(has_opt=True, autodoc='False', has_autodoc=False)),
289 dict(has_opt=False, autodoc='False', has_autodoc=False)),
292 def test_build_doc(self):
293 build_doc = packaging.LocalBuildDoc(self.distr)
297 os.path.exists("api/autoindex.rst") == self.has_autodoc)
300 "api/fake_package.fake_module.rst") == self.has_autodoc)
301 if not self.has_autodoc or hasattr(self, "excludes"):
302 assertion = self.assertFalse
304 assertion = self.assertTrue
307 "api/fake_package.fake_private_module.rst"))
310 "api/fake_package.another_fake_module_for_testing.rst"))
312 def test_builders_config(self):
313 build_doc = packaging.LocalBuildDoc(self.distr)
314 build_doc.finalize_options()
316 self.assertEqual(1, len(build_doc.builders))
317 self.assertIn('html', build_doc.builders)
319 build_doc = packaging.LocalBuildDoc(self.distr)
320 build_doc.builders = ''
321 build_doc.finalize_options()
323 self.assertEqual('', build_doc.builders)
325 build_doc = packaging.LocalBuildDoc(self.distr)
326 build_doc.builders = 'man'
327 build_doc.finalize_options()
329 self.assertEqual(1, len(build_doc.builders))
330 self.assertIn('man', build_doc.builders)
332 build_doc = packaging.LocalBuildDoc(self.distr)
333 build_doc.builders = 'html,man,doctest'
334 build_doc.finalize_options()
336 self.assertIn('html', build_doc.builders)
337 self.assertIn('man', build_doc.builders)
338 self.assertIn('doctest', build_doc.builders)
340 def test_cmd_builder_override(self):
343 self.distr.command_options["pbr"] = {
344 "autodoc_index_modules": ('setup.cfg', self.autodoc)
347 self.distr.command_options["build_sphinx"]["builder"] = (
348 "command line", "non-existing-builder")
350 build_doc = packaging.LocalBuildDoc(self.distr)
351 self.assertNotIn('non-existing-builder', build_doc.builders)
352 self.assertIn('html', build_doc.builders)
354 # process command line options which should override config
355 build_doc.finalize_options()
357 self.assertIn('non-existing-builder', build_doc.builders)
358 self.assertNotIn('html', build_doc.builders)
360 def test_cmd_builder_override_multiple_builders(self):
363 self.distr.command_options["pbr"] = {
364 "autodoc_index_modules": ('setup.cfg', self.autodoc)
367 self.distr.command_options["build_sphinx"]["builder"] = (
368 "command line", "builder1,builder2")
370 build_doc = packaging.LocalBuildDoc(self.distr)
371 build_doc.finalize_options()
373 self.assertEqual(["builder1", "builder2"], build_doc.builders)
376 class APIAutoDocTest(base.BaseTestCase):
379 super(APIAutoDocTest, self).setUp()
381 # setup_command requires the Sphinx instance to have some
382 # attributes that aren't set normally with the way we use the
383 # class (because we replace the constructor). Add default
384 # values directly to the class definition.
385 import sphinx.application
386 sphinx.application.Sphinx.messagelog = []
387 sphinx.application.Sphinx.statuscode = 0
389 self.useFixture(fixtures.MonkeyPatch(
390 "sphinx.application.Sphinx.__init__", lambda *a, **kw: None))
391 self.useFixture(fixtures.MonkeyPatch(
392 "sphinx.application.Sphinx.build", lambda *a, **kw: None))
393 self.useFixture(fixtures.MonkeyPatch(
394 "sphinx.application.Sphinx.config", _SphinxConfig))
395 self.useFixture(fixtures.MonkeyPatch(
396 "sphinx.config.Config.init_values", lambda *a: None))
397 self.useFixture(fixtures.MonkeyPatch(
398 "sphinx.config.Config.__init__", lambda *a: None))
399 from distutils import dist
400 self.distr = dist.Distribution()
401 self.distr.packages = ("fake_package",)
402 self.distr.command_options["build_sphinx"] = {
403 "source_dir": ["a", "."]}
404 self.sphinx_options = self.distr.command_options["build_sphinx"]
405 pkg_fixture = fixtures.PythonPackage(
406 "fake_package", [("fake_module.py", b""),
407 ("another_fake_module_for_testing.py", b""),
408 ("fake_private_module.py", b"")])
409 self.useFixture(pkg_fixture)
410 self.useFixture(base.DiveDir(pkg_fixture.base))
411 self.pbr_options = self.distr.command_options.setdefault('pbr', {})
412 self.pbr_options["autodoc_index_modules"] = ('setup.cfg', 'True')
414 def test_default_api_build_dir(self):
415 build_doc = packaging.LocalBuildDoc(self.distr)
418 print('PBR OPTIONS:', self.pbr_options)
419 print('DISTR OPTIONS:', self.distr.command_options)
421 self.assertTrue(os.path.exists("api/autoindex.rst"))
422 self.assertTrue(os.path.exists("api/fake_package.fake_module.rst"))
425 "api/fake_package.fake_private_module.rst"))
428 "api/fake_package.another_fake_module_for_testing.rst"))
430 def test_different_api_build_dir(self):
431 # Options have to come out of the settings dict as a tuple
432 # showing the source and the value.
433 self.pbr_options['api_doc_dir'] = (None, 'contributor/api')
434 build_doc = packaging.LocalBuildDoc(self.distr)
437 print('PBR OPTIONS:', self.pbr_options)
438 print('DISTR OPTIONS:', self.distr.command_options)
440 self.assertTrue(os.path.exists("contributor/api/autoindex.rst"))
442 os.path.exists("contributor/api/fake_package.fake_module.rst"))
445 "contributor/api/fake_package.fake_private_module.rst"))