# vim: set fileencoding=utf-8 : # # (C) 2013-2015 Intel Corporation # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, please see # """Tests for the gbp-rpm-ch tool""" import os import re from nose.tools import assert_raises, eq_, ok_ # pylint: disable=E0611 from gbp.scripts.rpm_ch import main as rpm_ch from gbp.git import GitRepository from tests.testutils import capture_stderr from tests.component.rpm import RpmRepoTestBase # Disable "Method could be a function warning" # pylint: disable=R0201 def mock_ch(args): """Wrapper for gbp-rpm-ch""" with capture_stderr(): return rpm_ch(['arg0', '--packaging-branch=master', '--spawn-editor=never'] + args) class TestRpmCh(RpmRepoTestBase): """Basic tests for gbp-rpm-ch""" def setUp(self): """Test case setup""" super(TestRpmCh, self).setUp() # Set environment so that commits succeed without git config os.environ['GIT_AUTHOR_NAME'] = 'My Name' os.environ['GIT_COMMITTER_NAME'] = 'My Name' os.environ['EMAIL'] = 'me@example.com' @staticmethod def read_file(filename): """Read file to a list""" with open(filename) as fobj: return fobj.readlines() def test_invalid_args(self): """See that gbp-rpm-ch fails gracefully when called with invalid args""" GitRepository.create('.') with assert_raises(SystemExit): mock_ch(['--invalid-opt']) def test_import_outside_repo(self): """Run gbp-rpm-ch when not in a git repository""" eq_(mock_ch([]), 1) self._check_log(0, 'gbp:error: No Git repository at ') def test_update_spec_changelog(self): """Test updating changelog in spec""" repo = self.init_test_repo('gbp-test') eq_(mock_ch([]), 0) eq_(repo.status(), {' M': [b'gbp-test.spec']}) def test_update_changes_file(self): """Test updating a separate changes file""" repo = self.init_test_repo('gbp-test-native') eq_(mock_ch([]), 0) eq_(repo.status(), {' M': [b'packaging/gbp-test-native.changes']}) def test_create_spec_changelog(self): """Test creating changelog in spec file""" repo = self.init_test_repo('gbp-test2') orig_content = self.read_file('packaging/gbp-test2.spec') # Fails if no starting point is given eq_(mock_ch([]), 1) self._check_log(-1, "gbp:error: Couldn't determine starting point") # Give starting point eq_(mock_ch(['--since=HEAD^']), 0) eq_(repo.status(), {' M': [b'packaging/gbp-test2.spec']}) content = self.read_file('packaging/gbp-test2.spec') # Should contain 4 lines (%changelog, header, 1 entry and an empty line) eq_(len(content), len(orig_content) + 4) def test_create_changes_file(self): """Test creating a separate changes file""" repo = self.init_test_repo('gbp-test2') # Fails if no starting point is given eq_(mock_ch(['--changelog-file=CHANGES']), 1) self._check_log(-1, "gbp:error: Couldn't determine starting point") # Give starting point eq_(mock_ch(['--since=HEAD^', '--changelog-file=CHANGES']), 0) eq_(repo.status(), {'??': [b'packaging/gbp-test2.changes']}) content = self.read_file('packaging/gbp-test2.changes') # Should contain 3 lines (header, 1 entry and an empty line) eq_(len(content), 3) def test_option_changelog_file(self): """Test the --changelog-file cmdline option""" repo = self.init_test_repo('gbp-test-native') # Guess changelog file eq_(mock_ch(['--changelog-file=CHANGES']), 0) eq_(repo.status(), {' M': [b'packaging/gbp-test-native.changes']}) # Use spec file as changelog eq_(mock_ch(['--changelog-file=SPEC', '--since=HEAD^']), 0) eq_(repo.status(), {' M': [b'packaging/gbp-test-native.changes', b'packaging/gbp-test-native.spec']}) # Arbitrary name eq_(mock_ch(['--changelog-file=foo.changes', '--since=HEAD^']), 0) eq_(repo.status(), {' M': [b'packaging/gbp-test-native.changes', b'packaging/gbp-test-native.spec'], '??': [b'foo.changes']}) def test_option_spec_file(self): """Test the --spec-file cmdline option""" repo = self.init_test_repo('gbp-test2') eq_(mock_ch(['--spec-file=foo.spec']), 1) self._check_log(-1, "gbp:error: Unable to read spec file") eq_(mock_ch(['--spec-file=']), 1) self._check_log(-1, "gbp:error: Multiple spec files found") eq_(mock_ch(['--spec-file=packaging/gbp-test2.spec', '--since=HEAD^']), 0) eq_(repo.status(), {' M': [b'packaging/gbp-test2.spec']}) def test_option_packaging_dir(self): """Test the --packaging-dir cmdline option""" repo = self.init_test_repo('gbp-test-native') eq_(mock_ch(['--packaging-dir=foo']), 1) self._check_log(-1, "gbp:error: No spec file found") # Packaging dir should be taken from spec file if it is defined eq_(mock_ch(['--packaging-dir', 'foo', '--spec-file', 'packaging/gbp-test-native.spec']), 0) eq_(repo.status(), {' M': [b'packaging/gbp-test-native.changes']}) def test_branch_options(self): """Test the --packaging-branch and --ignore-branch cmdline options""" self.init_test_repo('gbp-test-native') eq_(mock_ch(['--packaging-branch=foo']), 1) self._check_log(-2, "gbp:error: You are not on branch 'foo'") eq_(mock_ch(['--packaging-branch=foo', '--ignore-branch']), 0) def test_option_no_release(self): """Test the --no-release cmdline option""" self.init_test_repo('gbp-test-native') orig_content = self.read_file('packaging/gbp-test-native.changes') eq_(mock_ch(['--no-release']), 0) content = self.read_file('packaging/gbp-test-native.changes') # Only one line (entry) added eq_(len(content), len(orig_content) + 1) def test_author(self): """Test determining the author name/email""" repo = self.init_test_repo('gbp-test-native') # Test taking email address from env os.environ['EMAIL'] = 'user@host.com' eq_(mock_ch([]), 0) header = self.read_file('packaging/gbp-test-native.changes')[0] ok_(re.match(r'.+ .+', header)) # Missing git config setting should not cause a failure del os.environ['EMAIL'] del os.environ['GIT_AUTHOR_NAME'] os.environ['GIT_CONFIG_NOSYSTEM'] = '1' os.environ['HOME'] = os.path.abspath('.') eq_(mock_ch(['--git-author', '--since=HEAD^1']), 0) # Test the --git-author option saved_author = os.environ.get('GIT_AUTHOR_NAME') saved_email = os.environ.get('GIT_AUTHOR_EMAIL') os.environ['GIT_AUTHOR_NAME'] = 'John Doe' os.environ['GIT_AUTHOR_EMAIL'] = 'jd@host.com' with open(os.path.join(repo.git_dir, 'config'), 'a') as fobj: fobj.write('[user]\n name=John Doe\n email=jd@host.com\n') eq_(mock_ch(['--git-author', '--since=HEAD^']), 0) header = self.read_file('packaging/gbp-test-native.changes')[0] ok_(re.match(r'.+ John Doe .+', header), header) if saved_author: os.environ['GIT_AUTHOR_NAME'] = saved_author if saved_email: os.environ['GIT_AUTHOR_EMAIL'] = saved_email def test_option_full(self): """Test the --full cmdline option""" repo = self.init_test_repo('gbp-test-native') orig_content = self.read_file('packaging/gbp-test-native.changes') eq_(mock_ch(['--full', '--since=HEAD^']), 0) commit_msg_body = repo.get_commit_info('HEAD')['body'] full_msg = [line for line in commit_msg_body.splitlines() if line] content = self.read_file('packaging/gbp-test-native.changes') # New lines: header, 1 entry "header", entry "body" from commit message # and one empty line eq_(len(content), len(orig_content) + 3 + len(full_msg)) def test_option_ignore_regex(self): """Test the --ignore-regex cmdline option""" repo = self.init_test_repo('gbp-test-native') orig_content = self.read_file('packaging/gbp-test-native.changes') eq_(mock_ch(['--full', '--since', 'HEAD^', '--ignore-regex', 'Signed-off-by:.*']), 0) commit_msg_body = repo.get_commit_info('HEAD')['body'] full_msg = [line for line in commit_msg_body.splitlines() if (line and not line.startswith('Signed-off-by:'))] content = self.read_file('packaging/gbp-test-native.changes') # New lines: header, 1 entry "header", filtered entry "body" from # commit message and one empty line eq_(len(content), len(orig_content) + 3 + len(full_msg)) def test_option_id_len(self): """Test the --id-len cmdline option""" repo = self.init_test_repo('gbp-test-native') eq_(mock_ch(['--id-len=10']), 0) commit_id = repo.rev_parse('HEAD', 10) content = self.read_file('packaging/gbp-test-native.changes') ok_(content[1].startswith('- [%s] ' % commit_id)) def test_option_changelog_revision(self): """Test the --id-len cmdline option""" self.init_test_repo('gbp-test-native') # Test invalid format (unknown field) eq_(mock_ch(['--changelog-revision=%(unknown_field)s']), 1) self._check_log(-1, 'gbp:error: Unable to construct revision field') # Test acceptable format eq_(mock_ch(['--changelog-revision=foobar']), 0) header = self.read_file('packaging/gbp-test-native.changes')[0] ok_(re.match(r'.+ foobar$', header)) def test_option_editor_cmd(self): """Test the --editor-cmd and --spawn-editor cmdline options""" repo = self.init_test_repo('gbp-test-native') eq_(mock_ch(['--spawn-editor=release', '--editor-cmd=rm']), 0) eq_(repo.status(), {' D': [b'packaging/gbp-test-native.changes']}) repo.force_head('HEAD', hard=True) ok_(repo.is_clean()) os.environ['EDITOR'] = 'rm' eq_(mock_ch(['--spawn-editor=always', '--editor-cmd=']), 0) def test_user_customizations(self): """Test the user customizations""" repo = self.init_test_repo('gbp-test-native') # Non-existent customization file eq_(mock_ch(['--customizations=customizations.py']), 1) # Create user customizations file with open('customizations.py', 'w') as fobj: fobj.write("class ChangelogEntryFormatter(object):\n") fobj.write(" @classmethod\n") fobj.write(" def compose(cls, commit_info, **kwargs):\n") fobj.write(" return ['- %s' % commit_info['id']]\n") eq_(mock_ch(['--customizations=customizations.py']), 0) entry = self.read_file('packaging/gbp-test-native.changes')[1] sha = repo.rev_parse('HEAD') eq_(entry, '- %s\n' % sha) def test_paths(self): """Test tracking of certain paths only""" repo = self.init_test_repo('gbp-test-native') orig_content = self.read_file('packaging/gbp-test-native.changes') # Add new commit with known content self.add_file(repo, 'new-file.txt', 'this is new content\n') # Only track a non-existent file eq_(mock_ch(['--since=HEAD^', 'non-existent-path']), 0) content = self.read_file('packaging/gbp-test-native.changes') # New lines: header and one empty line, no entries eq_(len(content), len(orig_content) + 2) # Track existing file repo.force_head('HEAD', hard=True) eq_(mock_ch(['--since=HEAD^', 'new-file.txt']), 0) content = self.read_file('packaging/gbp-test-native.changes') # New lines: header, one entry line and one empty line eq_(len(content), len(orig_content) + 3) def test_commit_guessing(self): """Basic tests for guessing the starting point""" repo = self.init_test_repo('gbp-test-native') # Check 'tagname' that is not found eq_(mock_ch(['--changelog-revision=%(tagname)s']), 0) self._check_log(0, 'gbp:warning: Changelog points to tagname') # Check 'upstreamversion' and 'release' fields repo.force_head('HEAD', hard=True) eq_(mock_ch(['--changelog-revision=%(upstreamversion)s-%(release)s']), 0) def test_commit_guessing_fail(self): """Test for failure of start commit guessing""" self.init_test_repo('gbp-test-native') # Add "very old" header to changelog with open('packaging/gbp-test-native.changes', 'w') as ch_fp: ch_fp.write('* Sat Jan 01 2000 User 123\n- foo\n') # rpm-ch should fail by not being able to find any commits before the # last changelog section eq_(mock_ch([]), 1) self._check_log(-1, "gbp:error: Couldn't determine starting point")