From 1fe0501df0521c0657d59006e5a0bfd71f58491c Mon Sep 17 00:00:00 2001 From: Markus Lehtonen Date: Tue, 25 Feb 2014 12:20:24 +0200 Subject: [PATCH] rpm-ch: add unit tests for the command line tool Signed-off-by: Markus Lehtonen --- tests/component/rpm/data | 2 +- tests/component/rpm/test_rpm_ch.py | 401 +++++++++++++++++++++++++++++++++++++ 2 files changed, 402 insertions(+), 1 deletion(-) create mode 100644 tests/component/rpm/test_rpm_ch.py diff --git a/tests/component/rpm/data b/tests/component/rpm/data index b505e80..e59a3ef 160000 --- a/tests/component/rpm/data +++ b/tests/component/rpm/data @@ -1 +1 @@ -Subproject commit b505e8016f5a5003fdfa12619600df91e1a8ae97 +Subproject commit e59a3efb43993c24c5d7c2e2f354806ec419ed90 diff --git a/tests/component/rpm/test_rpm_ch.py b/tests/component/rpm/test_rpm_ch.py new file mode 100644 index 0000000..968cfd4 --- /dev/null +++ b/tests/component/rpm/test_rpm_ch.py @@ -0,0 +1,401 @@ +# vim: set fileencoding=utf-8 : +# +# (C) 2013 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +"""Tests for the git-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.component.rpm import RpmRepoTestBase + +# Disable "Method could be a function warning" +# pylint: disable=R0201 + + +def mock_ch(args): + """Wrapper for git-rpm-ch""" + + return rpm_ch(['arg0', '--packaging-branch=master', + '--spawn-editor=never'] + args) + +class TestRpmCh(RpmRepoTestBase): + """Basic tests for git-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 git-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 git-rpm-ch when not in a git repository""" + eq_(mock_ch([]), 1) + self._check_log(0, 'gbp:error: No Git repository at ') + + def test_invalid_config_file(self): + """Test invalid config file""" + # Create dummy invalid config file and run git-rpm-ch + GitRepository.create('.') + with open('.gbp.conf', 'w') as conffd: + conffd.write('foobar\n') + eq_(mock_ch([]), 1) + self._check_log(0, 'gbp:error: invalid config file: File contains no ' + 'section headers.') + + 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': ['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': ['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': ['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(), {'??': ['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_all(self): + """Test the --all cmdline option""" + repo = self.init_test_repo('gbp-test2') + + eq_(mock_ch(['--changelog-file=CHANGES', '--all']), 0) + content = self.read_file('packaging/gbp-test2.changes') + # Should contain N+2 lines (header, N commits and an empty line) + commit_cnt = len(repo.get_commits(since=None, until='master')) + eq_(len(content), commit_cnt + 2) + + 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': ['packaging/gbp-test-native.changes']}) + + # Use spec file as changelog + eq_(mock_ch(['--changelog-file=SPEC', '--since=HEAD^']), 0) + eq_(repo.status(), {' M': ['packaging/gbp-test-native.changes', + 'packaging/gbp-test-native.spec']}) + + # Arbitrary name + eq_(mock_ch(['--changelog-file=foo.changes', '--since=HEAD^']), 0) + eq_(repo.status(), {' M': ['packaging/gbp-test-native.changes', + 'packaging/gbp-test-native.spec'], + '??': ['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=auto']), 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': ['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': ['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 + 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) + + 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_tagging(self): + """Test commiting/tagging""" + repo = self.init_test_repo('gbp-test-native') + + # Update and commit+tag + eq_(mock_ch(['--tag', '--packaging-tag=new-tag', '--since=HEAD^']), 0) + ok_(repo.has_tag('new-tag')) + sha = repo.rev_parse('HEAD') + eq_(sha, repo.rev_parse('new-tag^0')) + + # Should fail if the tag already exists + eq_(mock_ch(['--tag', '--packaging-tag=new-tag', '--since=HEAD^']), 1) + + # Update and commit+tag + eq_(mock_ch(['--tag', '--packaging-tag=new-tag', '--since=HEAD^', + '--retag']), 0) + ok_(repo.has_tag('new-tag')) + sha2 = repo.rev_parse('HEAD') + ok_(sha2 != sha) + eq_(sha2, repo.rev_parse('new-tag^0')) + + def test_tagging2(self): + """Test commiting/tagging spec file""" + repo = self.init_test_repo('gbp-test2') + + # Check unclean repo + with open('untracked-file', 'w') as fobj: + fobj.write('this file is not tracked\n') + with open('README', 'a') as fobj: + fobj.write('some new content\n') + + # Unstaged file (README) -> failure + eq_(mock_ch(['--tag', '--packaging-tag=new-tag', '--since=HEAD^']), 1) + self._check_log(-1, 'gbp:error: Please commit or stage your changes') + + # Add file, update and commit+tag, untracked file should be ignored + repo.add_files('README') + eq_(mock_ch(['--tag', '--packaging-tag=new-tag', '--since=HEAD^']), 0) + ok_(repo.has_tag('new-tag')) + sha = repo.rev_parse('HEAD') + eq_(sha, repo.rev_parse('new-tag^0')) + + 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': ['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_option_message(self): + """Test the --message cmdline option""" + self.init_test_repo('gbp-test-native') + orig_content = self.read_file('packaging/gbp-test-native.changes') + + eq_(mock_ch(['--message', 'my entry\nanother entry']), 0) + content = self.read_file('packaging/gbp-test-native.changes') + # Added header, two entries and a blank line + eq_(len(content), len(orig_content) + 4) + eq_(content[2], '- another entry\n') + + 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 + with open('new-file.txt', 'w') as fobj: + fobj.write('this is new content\n') + repo.add_files('new-file.txt') + repo.commit_staged('Add new file') + + # 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""" + repo = 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") + -- 2.7.4