From f2f02f7fce5296e286f2b1a8e2c6f7dba1d8f292 Mon Sep 17 00:00:00 2001 From: Chenxiong Qi Date: Nov 16 2016 02:33:35 +0000 Subject: [PATCH 1/8] Remove unused code Signed-off-by: Chenxiong Qi --- diff --git a/pyrpkg/cli.py b/pyrpkg/cli.py index 246c4a4..75fc3e3 100644 --- a/pyrpkg/cli.py +++ b/pyrpkg/cli.py @@ -1633,37 +1633,3 @@ class TaskWatcher(object): return 'FAILED: %s' % self.get_failure() else: return koji.TASK_STATES[info['state']].lower() - - -if __name__ == '__main__': - client = cliClient() - client.do_imports() - client.parse_cmdline() - - if not client.args.path: - try: - client.args.path = os.getcwd() - except: - print('Could not get current path, have you deleted it?') - sys.exit(1) - - # setup the logger -- This logger will take things of INFO or DEBUG and - # log it to stdout. Anything above that (WARN, ERROR, CRITICAL) will go - # to stderr. Normal operation will show anything INFO and above. - # Quiet hides INFO, while Verbose exposes DEBUG. In all cases WARN or - # higher are exposed (via stderr). - log = client.site.log - client.setupLogging(log) - - if client.args.v: - log.setLevel(logging.DEBUG) - elif client.args.q: - log.setLevel(logging.WARNING) - else: - log.setLevel(logging.INFO) - - # Run the necessary command - try: - client.args.command() - except KeyboardInterrupt: - pass From 5c92ab58d7b69cd1350f66d1a7794b656dbe05fe Mon Sep 17 00:00:00 2001 From: Chenxiong Qi Date: Nov 21 2016 06:01:40 +0000 Subject: [PATCH 2/8] More tests to Commands and cliClient Signed-off-by: Chenxiong Qi --- diff --git a/pyrpkg/__init__.py b/pyrpkg/__init__.py index 94374de..48f0174 100644 --- a/pyrpkg/__init__.py +++ b/pyrpkg/__init__.py @@ -682,7 +682,7 @@ class Commands(object): self.log.debug('Creating repo object from %s', self.path) try: self._repo = git.Repo(self.path) - except git.InvalidGitRepositoryError: + except (git.InvalidGitRepositoryError, git.NoSuchPathError): raise rpkgError('%s is not a valid repo' % self.path) @property @@ -2406,7 +2406,7 @@ class Commands(object): # Create a list for unused patches unused = [] # Get the content of spec into memory for fast searching - with open(self.spec, 'r') as f: + with open(os.path.join(self.path, self.spec), 'r') as f: data = f.read() try: spec = data.decode('UTF-8') diff --git a/tests/test_cli.py b/tests/test_cli.py index d88949e..3fdabd4 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,17 +1,19 @@ # -*- coding: utf-8 -*- +import logging import os +import sys from os.path import exists from os.path import join from six.moves import configparser +from six.moves import StringIO import git import pyrpkg.cli from mock import patch from utils import CommandTestCase -from utils import run from pyrpkg import rpkgError @@ -27,7 +29,7 @@ License: GPL %description package demo for testing %changelog -* Mon Nov 07 2016 cqi@redhat.com +* Mon Nov 07 2016 tester@example.com - first release 0.1 - add new spec ''' @@ -40,15 +42,34 @@ class CliTestCase(CommandTestCase): config.read(config_file) client = pyrpkg.cli.cliClient(config, name='rpkg') + client.setupLogging(pyrpkg.log) + pyrpkg.log.setLevel(logging.CRITICAL) client.do_imports() client.parse_cmdline() return client - def make_changes(self): - cmds = (['touch', 'new-file.txt'], - ['git', 'add', 'new-file.txt']) - map(lambda cmd: run(cmd, cwd=self.cloned_repo_path), cmds) + def touch(self, filename, content=None): + """Touch a file with optional content""" + + _content = content if content else '' + with open(filename, 'w') as f: + f.write(_content) + + def make_changes(self, repo=None, untracked=None, commit=None, filename=None, content=''): + repo_path = repo or self.cloned_repo_path + _filename = filename or 'new-file.txt' + + self.write_file(os.path.join(repo_path, _filename), content) + + cmds = [] + if not untracked: + cmds.append(['git', 'add', _filename]) + if not untracked and commit: + cmds.append(['git', 'commit', '-m', 'Add new file {0}'.format(_filename)]) + + if cmds: + map(lambda cmd: self.run_cmd(cmd, cwd=repo_path), cmds) class TestClog(CliTestCase): @@ -56,32 +77,34 @@ class TestClog(CliTestCase): def setUp(self): super(TestClog, self).setUp() - self.make_changes() - def cli_clog(self): """Run clog command""" cli = self.new_cli() cli.clog() def test_clog(self): - with patch('sys.argv', ['rpkg', '--path', self.cloned_repo_path, 'clog']): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'clog'] + + with patch('sys.argv', new=cli_cmd): self.cli_clog() - clog_file = os.path.join(self.cloned_repo_path, 'clog') - self.assertTrue(os.path.exists(clog_file)) - with open(clog_file, 'r') as f: - clog = f.read().strip() - self.assertEqual('Initial version', clog) + clog_file = os.path.join(self.cloned_repo_path, 'clog') + self.assertTrue(os.path.exists(clog_file)) + with open(clog_file, 'r') as f: + clog = f.read().strip() + self.assertEqual('Initial version', clog) def test_raw_clog(self): - with patch('sys.argv', ['rpkg', '--path', self.cloned_repo_path, 'clog', '--raw']): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'clog', '--raw'] + + with patch('sys.argv', new=cli_cmd): self.cli_clog() - clog_file = os.path.join(self.cloned_repo_path, 'clog') - self.assertTrue(os.path.exists(clog_file)) - with open(clog_file, 'r') as f: - clog = f.read().strip() - self.assertEqual('- Initial version', clog) + clog_file = os.path.join(self.cloned_repo_path, 'clog') + self.assertTrue(os.path.exists(clog_file)) + with open(clog_file, 'r') as f: + clog = f.read().strip() + self.assertEqual('- Initial version', clog) class TestCommit(CliTestCase): @@ -100,99 +123,246 @@ class TestCommit(CliTestCase): cli.commit() def test_with_only_summary(self): - with patch('sys.argv', new=['rpkg', '--path', self.cloned_repo_path, - 'commit', '-m', 'new release']): + cli = ['rpkg', '--path', self.cloned_repo_path, 'commit', '-m', 'new release'] + + with patch('sys.argv', new=cli): self.cli_commit() - commit_msg = self.get_last_commit_message() - self.assertEqual('new release', commit_msg) + commit_msg = self.get_last_commit_message() + self.assertEqual('new release', commit_msg) def test_with_summary_and_changelog(self): - with patch('sys.argv', new=['rpkg', '--path', self.cloned_repo_path, - 'commit', '-m', 'new release', '--with-changelog']): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, + 'commit', '-m', 'new release', '--with-changelog'] + + with patch('sys.argv', new=cli_cmd): self.cli_commit() - commit_msg = self.get_last_commit_message() - expected_commit_msg = '''new release + commit_msg = self.get_last_commit_message() + expected_commit_msg = '''new release - Initial version''' - self.assertEqual(expected_commit_msg, commit_msg) - self.assertFalse(os.path.exists(os.path.join(self.cloned_repo_path, 'clog'))) - self.assertFalse(os.path.exists(os.path.join(self.cloned_repo_path, 'commit-message'))) + self.assertEqual(expected_commit_msg, commit_msg) + self.assertFalse(os.path.exists(os.path.join(self.cloned_repo_path, 'clog'))) + self.assertFalse(os.path.exists(os.path.join(self.cloned_repo_path, 'commit-message'))) def test_with_clog(self): - with patch('sys.argv', new=['rpkg', '--path', self.cloned_repo_path, 'commit', '--clog']): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'commit', '--clog'] + + with patch('sys.argv', new=cli_cmd): self.cli_commit() - commit_msg = self.get_last_commit_message() - expected_commit_msg = 'Initial version' - self.assertEqual(expected_commit_msg, commit_msg) - self.assertFalse(os.path.exists(os.path.join(self.cloned_repo_path, 'clog'))) + commit_msg = self.get_last_commit_message() + expected_commit_msg = 'Initial version' + self.assertEqual(expected_commit_msg, commit_msg) + self.assertFalse(os.path.exists(os.path.join(self.cloned_repo_path, 'clog'))) def test_with_raw_clog(self): - with patch('sys.argv', new=['rpkg', '--path', self.cloned_repo_path, - 'commit', '--clog', '--raw']): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'commit', '--clog', '--raw'] + with patch('sys.argv', new=cli_cmd): self.cli_commit() - commit_msg = self.get_last_commit_message() - expected_commit_msg = '- Initial version' - self.assertEqual(expected_commit_msg, commit_msg) - self.assertFalse(os.path.exists(os.path.join(self.cloned_repo_path, 'clog'))) + commit_msg = self.get_last_commit_message() + expected_commit_msg = '- Initial version' + self.assertEqual(expected_commit_msg, commit_msg) + self.assertFalse(os.path.exists(os.path.join(self.cloned_repo_path, 'clog'))) def test_cannot_use_with_changelog_without_a_summary(self): - with patch('sys.argv', new=['rpkg', '--path', self.cloned_repo_path, - 'commit', '--with-changelog']): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'commit', '--with-changelog'] + + with patch('sys.argv', new=cli_cmd): self.assertRaises(rpkgError, self.cli_commit) def test_push_after_commit(self): repo = git.Repo(self.cloned_repo_path) self.checkout_branch(repo, 'eng-rhel-6') - with patch('sys.argv', new=['rpkg', '--path', self.cloned_repo_path, - 'commit', '-m', 'new release', '--with-changelog', '--push']): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, + 'commit', '-m', 'new release', '--with-changelog', '--push'] + + with patch('sys.argv', new=cli_cmd): self.cli_commit() - diff_commits = repo.git.rev_list('origin/master...master') - self.assertEqual('', diff_commits) + diff_commits = repo.git.rev_list('origin/master...master') + self.assertEqual('', diff_commits) + + def test_signoff(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'commit', '-m', 'new release', '-s'] + + with patch('sys.argv', new=cli_cmd): + self.cli_commit() + + commit_msg = self.get_last_commit_message() + self.assertTrue('Signed-off-by:' in commit_msg) + + +class TestPull(CliTestCase): + + def test_pull(self): + self.make_changes(repo=self.repo_path, commit=True) + + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'pull'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + cli.pull() + + origin_last_commit = str(git.Repo(self.repo_path).iter_commits().next()) + cloned_last_commit = str(cli.cmd.repo.iter_commits().next()) + self.assertEqual(origin_last_commit, cloned_last_commit) + + def test_pull_rebase(self): + self.make_changes(repo=self.repo_path, commit=True) + self.make_changes(repo=self.cloned_repo_path, commit=True, + filename='README.rst', content='Hello teseting.') + + origin_last_commit = str(git.Repo(self.repo_path).iter_commits().next()) + + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'pull', '--rebase'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + cli.pull() + + commits = cli.cmd.repo.iter_commits() + commits.next() + fetched_commit = str(commits.next()) + self.assertEqual(origin_last_commit, fetched_commit) + self.assertEqual('', cli.cmd.repo.git.log('--merges')) class TestSrpm(CliTestCase): def test_srpm(self): - with patch('sys.argv', new=['rpkg', '--path', self.cloned_repo_path, - '--release', 'rhel-6', 'srpm']): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, '--release', 'rhel-6', 'srpm'] + + with patch('sys.argv', new=cli_cmd): cli = self.new_cli() cli.srpm() - self.assertTrue(os.path.exists(os.path.join(self.cloned_repo_path, - 'docpkg-1.2-2.el6.src.rpm'))) + self.assertTrue(os.path.exists(os.path.join(self.cloned_repo_path, + 'docpkg-1.2-2.el6.src.rpm'))) class TestCompile(CliTestCase): - def test_compile(self): - with patch('sys.argv', new=['rpkg', '--path', self.cloned_repo_path, - '--release', 'rhel-6', 'compile']): + def compile(self, cli_cmd): + with patch('sys.argv', new=cli_cmd): cli = self.new_cli() - cli.compile() + with patch('pyrpkg.Commands._run_command', new=self.redirect_cmd_output): + cli.compile() + + @patch('sys.stdout', new=StringIO()) + def test_compile(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, '--release', 'rhel-6', 'compile'] + self.compile(cli_cmd) + stdout = sys.stdout.getvalue() + + self.assertTrue('Executing(%prep):' in stdout) + self.assertTrue('Executing(%build):' in stdout) + + @patch('sys.stdout', new=StringIO()) + def test_compile_short_circuit(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, '--release', 'rhel-6', + 'compile', '--short-circuit'] + self.compile(cli_cmd) + stdout = sys.stdout.getvalue() + + self.assertTrue('Executing(%prep):' not in stdout) + self.assertTrue('Executing(%build):' in stdout) + + @patch('sys.stdout', new=StringIO()) + def test_compile_quiet(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, '--release', 'rhel-6', '-q', 'compile'] + self.compile(cli_cmd) + stdout = sys.stdout.getvalue() + + self.assertEqual('', stdout) + + @patch('sys.stdout', new=StringIO()) + def test_compile_arch(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, '--release', 'rhel-6', '-q', + 'compile', '--arch', 'i686'] + self.compile(cli_cmd) + stdout = sys.stdout.getvalue() + + self.assertTrue('''Building target platforms: i686 +Building for target i686''', stdout) class TestPrep(CliTestCase): - def test_compile(self): - with patch('sys.argv', new=['rpkg', '--path', self.cloned_repo_path, - '--release', 'rhel-6', 'prep']): + def prep(self, cli_cmd): + with patch('sys.argv', new=cli_cmd): cli = self.new_cli() - cli.prep() + with patch('pyrpkg.Commands._run_command', new=self.redirect_cmd_output): + cli.prep() + + @patch('sys.stdout', new=StringIO()) + def test_prep(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, '--release', 'rhel-6', 'prep'] + self.prep(cli_cmd) + stdout = sys.stdout.getvalue() + + self.assertTrue('Executing(%prep):' in stdout) + + @patch('sys.stdout', new=StringIO()) + def test_prep_arch(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, '--release', 'rhel-6', '-q', + 'compile', '--arch', 'i686'] + self.prep(cli_cmd) + stdout = sys.stdout.getvalue() + + self.assertTrue('''Building target platforms: i686 +Building for target i686''', stdout) class TestInstall(CliTestCase): - def test_compile(self): - with patch('sys.argv', new=['rpkg', '--path', self.cloned_repo_path, - '--release', 'rhel-6', 'install']): + def install(self, cli_cmd): + with patch('sys.argv', new=cli_cmd): cli = self.new_cli() - cli.install() + with patch('pyrpkg.Commands._run_command', new=self.redirect_cmd_output): + cli.install() + + @patch('sys.stdout', new=StringIO()) + def test_install(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, '--release', 'rhel-6', 'install'] + self.install(cli_cmd) + stdout = sys.stdout.getvalue() + + self.assertTrue('Executing(%prep):' in stdout) + self.assertTrue('Executing(%build):' in stdout) + self.assertTrue('Executing(%install):' in stdout) + self.assertTrue('Executing(%check):' in stdout) + self.assertTrue('Executing(%doc):' in stdout) + + @patch('sys.stdout', new=StringIO()) + def test_install_nocheck(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, '--release', 'rhel-6', + 'install', '--nocheck'] + self.install(cli_cmd) + stdout = sys.stdout.getvalue() + + self.assertTrue('Executing(%check):' not in stdout) + + @patch('sys.stdout', new=StringIO()) + def test_install_quiet(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, '--release', 'rhel-6', '-q', 'install'] + self.install(cli_cmd) + stdout = sys.stdout.getvalue() + + self.assertEqual('', stdout) + + @patch('sys.stdout', new=StringIO()) + def test_install_arch(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, '--release', + 'rhel-6', 'install', '--arch', 'i686'] + self.install(cli_cmd) + stdout = sys.stdout.getvalue() + + self.assertTrue('''Building target platforms: i686 +Building for target i686''', stdout) class TestLocal(CliTestCase): @@ -210,46 +380,356 @@ class TestLocal(CliTestCase): return translation.get(arch, arch) def test_local(self): - with patch('sys.argv', new=['rpkg', '--path', self.cloned_repo_path, - '--release', 'rhel-6', 'local']): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, '--release', 'rhel-6', 'local'] + + with patch('sys.argv', new=cli_cmd): cli = self.new_cli() cli.local() - self.assertTrue(exists(join(self.cloned_repo_path, 'docpkg-1.2-2.el6.src.rpm'))) - # This covers some special cases, e.g. building in copr, that is - # RPMs are not put in arch subdirectory even if %{_build_name_fmt} - # is %{ARCH}/%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}.rpm - arch = self.translate_arch(cli.cmd.localarch) - self.assertTrue( - exists(join(self.cloned_repo_path, 'docpkg-1.2-2.el6.{0}.rpm'.format(arch))) or - exists(join(self.cloned_repo_path, '{0}/docpkg-1.2-2.el6.{0}.rpm'.format(arch)))) + self.assertTrue(exists(join(self.cloned_repo_path, 'docpkg-1.2-2.el6.src.rpm'))) + # This covers some special cases, e.g. building in copr, that is + # RPMs are not put in arch subdirectory even if %{_build_name_fmt} + # is %{ARCH}/%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}.rpm + arch = self.translate_arch(cli.cmd.localarch) + self.assertTrue( + exists(join(self.cloned_repo_path, 'docpkg-1.2-2.el6.{0}.rpm'.format(arch))) or + exists(join(self.cloned_repo_path, '{0}/docpkg-1.2-2.el6.{0}.rpm'.format(arch)))) def test_local_with_arch(self): - with patch('sys.argv', new=['rpkg', '--path', self.cloned_repo_path, - '--release', 'rhel-6', 'local', '--arch', 'i686']): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, '--release', 'rhel-6', + 'local', '--arch', 'i686'] + + with patch('sys.argv', new=cli_cmd): cli = self.new_cli() cli.local() - self.assertTrue(exists(join(self.cloned_repo_path, 'docpkg-1.2-2.el6.src.rpm'))) - self.assertTrue( - exists(join(self.cloned_repo_path, 'docpkg-1.2-2.el6.i686.rpm')) or - exists(join(self.cloned_repo_path, 'i686/docpkg-1.2-2.el6.i686.rpm'))) + self.assertTrue(exists(join(self.cloned_repo_path, 'docpkg-1.2-2.el6.src.rpm'))) + self.assertTrue( + exists(join(self.cloned_repo_path, 'docpkg-1.2-2.el6.i686.rpm')) or + exists(join(self.cloned_repo_path, 'i686/docpkg-1.2-2.el6.i686.rpm'))) def test_local_with_builddir(self): custom_builddir = os.path.join(self.cloned_repo_path, 'this-builddir') - with patch('sys.argv', new=['rpkg', '--path', self.cloned_repo_path, - '--release', 'rhel-6', 'local', '--builddir', custom_builddir]): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, '--release', 'rhel-6', + 'local', '--builddir', custom_builddir] + + with patch('sys.argv', new=cli_cmd): cli = self.new_cli() cli.local() - self.assertFilesExists(('this-builddir/README.rst',)) + self.assertFilesExists(('this-builddir/README.rst',)) class TestVerifyFiles(CliTestCase): def test_verify_files(self): - with patch('sys.argv', new=['rpkg', '--path', self.cloned_repo_path, - '--release', 'rhel-6', 'verify-files']): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, '--release', 'rhel-6', 'verify-files'] + + with patch('sys.argv', new=cli_cmd): cli = self.new_cli() cli.verify_files() + + +class TestVerrel(CliTestCase): + + @patch('sys.stdout', new=StringIO()) + def test_verrel_get_module_name_from_spec(self): + cli_cmd = ['rpkg', '--path', self.repo_path, '--release', 'rhel-6', 'verrel'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + cli.verrel() + + output = sys.stdout.getvalue().strip() + self.assertEqual('docpkg-1.2-2.el6', output) + + @patch('sys.stdout', new=StringIO()) + def test_verrel(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, '--release', 'rhel-6', 'verrel'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + cli.verrel() + + module_name = os.path.basename(self.repo_path) + output = sys.stdout.getvalue().strip() + self.assertEqual('{0}-1.2-2.el6'.format(module_name), output) + + +class TestSwitchBranch(CliTestCase): + + @patch('sys.stdout', new=StringIO()) + def test_list_branches(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'switch-branch'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + cli.switch_branch() + + output = sys.stdout.getvalue() + + # Not test all branches listed, just test part of them. + strings = ('Locals', 'Remotes', 'eng-rhel-6', 'origin/eng-rhel-6') + for string in strings: + self.assertTrue(string in output) + + def test_switch_branch_tracking_remote_branch(self): + repo = git.Repo(self.cloned_repo_path) + + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'switch-branch', 'rhel-6.8'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + cli.switch_branch() + + self.assertEqual('rhel-6.8', repo.active_branch.name) + + # Ensure local branch is tracking remote branch + self.assertEqual('refs/heads/rhel-6.8', repo.git.config('branch.rhel-6.8.merge')) + + def test_switch_local_branch(self): + repo = git.Repo(self.cloned_repo_path) + self.checkout_branch(repo, 'master') + + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'switch-branch', 'eng-rhel-6'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + cli.switch_branch() + + self.assertEqual('eng-rhel-6', repo.active_branch.name) + + def test_fail_on_dirty_repo(self): + self.make_changes() + + repo = git.Repo(self.cloned_repo_path) + self.checkout_branch(repo, 'eng-rhel-6') + + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'switch-branch', 'master'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + try: + cli.switch_branch() + except rpkgError as e: + expected_msg = '{0} has uncommitted changes'.format(self.cloned_repo_path) + self.assertTrue(expected_msg in str(e)) + else: + self.fail('switch branch on dirty repo should fail.') + + self.assertEqual('eng-rhel-6', repo.active_branch.name) + + def test_fail_switch_unknown_remote_branch(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'switch-branch', + 'unknown-remote-branch'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + try: + cli.switch_branch() + except rpkgError as e: + self.assertEqual('Unknown remote branch origin/unknown-remote-branch', str(e)) + else: + self.fail('Switch to unknown remote branch should fail.') + + +class TestUnusedPatches(CliTestCase): + + def setUp(self): + super(TestUnusedPatches, self).setUp() + + self.patches = ( + os.path.join(self.cloned_repo_path, '0001-add-new-feature.patch'), + os.path.join(self.cloned_repo_path, '0002-hotfix.patch'), + ) + map(self.touch, self.patches) + git.Repo(self.cloned_repo_path).index.add(self.patches) + + @patch('sys.stdout', new=StringIO()) + def test_list_unused_patches(self): + self.checkout_branch(git.Repo(self.cloned_repo_path), 'eng-rhel-6') + + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'unused-patches'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + cli.unused_patches() + + output = sys.stdout.getvalue().strip() + expected_patches = [os.path.basename(patch_file) for patch_file in self.patches] + self.assertEqual('\n'.join(expected_patches), output) + + +class TestDiff(CliTestCase): + + def setUp(self): + super(TestDiff, self).setUp() + + with open(os.path.join(self.cloned_repo_path, self.spec_file), 'a') as f: + f.write('- upgrade dependencies') + + self.make_changes() + + def test_diff(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'diff'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + cli.diff() + + def test_diff_cached(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'diff', '--cached'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + cli.diff() + + +class TestGimmeSpec(CliTestCase): + + @patch('sys.stdout', new=StringIO()) + def test_gimmespec(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'gimmespec'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + cli.gimmespec() + + output = sys.stdout.getvalue().strip() + self.assertEqual('docpkg.spec', output) + + +class TestClean(CliTestCase): + + def test_dry_run(self): + self.make_changes(untracked=True) + + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'clean', '--dry-run'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + cli.clean() + + self.assertFilesExists(['new-file.txt']) + + def test_clean(self): + self.make_changes(untracked=True) + dirname = os.path.join(self.cloned_repo_path, 'temp-build') + os.mkdir(dirname) + + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'clean'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + cli.clean() + + self.assertFalse(os.path.exists(os.path.join(self.cloned_repo_path, 'new-file.txt'))) + self.assertFalse(os.path.exists(dirname)) + + # Ensure no tracked files and directories are removed. + self.assertFilesExists(['docpkg.spec', '.git']) + + +class TestLint(CliTestCase): + + @patch('sys.stdout', new=StringIO()) + def test_lint(self): + self.checkout_branch(git.Repo(self.cloned_repo_path), 'eng-rhel-7') + + cli_cmd = ['rpkg', '--module-name', 'docpkg', '--path', self.cloned_repo_path, 'lint'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + with patch('pyrpkg.Commands._run_command', new=self.redirect_cmd_output): + cli.lint() + + summary = sys.stdout.getvalue().strip().split('\n')[-1] + self.assertEqual( + '0 packages and 1 specfiles checked; 0 errors, 0 warnings.', summary) + + @patch('sys.stdout', new=StringIO()) + def test_lint_warning_detected(self): + self.checkout_branch(git.Repo(self.cloned_repo_path), 'eng-rhel-7') + + spec_file = os.path.join(self.cloned_repo_path, self.spec_file) + spec_content = self.read_file(spec_file).replace('%install', '') + self.write_file(spec_file, spec_content) + + cli_cmd = ['rpkg', '--module-name', 'docpkg', '--path', self.cloned_repo_path, 'lint'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + with patch('pyrpkg.Commands._run_command', new=self.redirect_cmd_output): + cli.lint() + + output = sys.stdout.getvalue() + self.assertTrue('W: no-%install-section' in output) + + @patch('sys.stdout', new=StringIO()) + def test_lint_warining_with_info(self): + self.checkout_branch(git.Repo(self.cloned_repo_path), 'eng-rhel-7') + + spec_file = os.path.join(self.cloned_repo_path, self.spec_file) + spec_content = self.read_file(spec_file).replace('%install', '') + self.write_file(spec_file, spec_content) + + cli_cmd = ['rpkg', '--module-name', 'docpkg', '--path', self.cloned_repo_path, + 'lint', '--info'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + with patch('pyrpkg.Commands._run_command', new=self.redirect_cmd_output): + cli.lint() + + warning_explanation = 'The spec file does not contain an %install section.' + output = sys.stdout.getvalue() + self.assertTrue('W: no-%install-section' in output) + self.assertTrue(warning_explanation in output) + + +class TestGitUrl(CliTestCase): + + @patch('sys.stdout', new=StringIO()) + def test_giturl(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'giturl'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + cli.giturl() + + last_commit = str(cli.cmd.repo.iter_commits().next()) + expected_giturl = '{0}?#{1}'.format( + cli.cmd.anongiturl % {'module': os.path.basename(self.repo_path)}, + last_commit) + output = sys.stdout.getvalue().strip() + self.assertEqual(expected_giturl, output) + + +class TestNew(CliTestCase): + + def test_no_tags_yet(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'new'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + try: + cli.new() + except rpkgError as e: + self.assertTrue('no tags' in str(e)) + else: + self.fail('Command new should fail due to no tags in the repo.') + + @patch('sys.stdout', new=StringIO()) + def test_get_diff(self): + self.run_cmd(['git', 'tag', '-m', 'New release v0.1', 'v0.1'], cwd=self.cloned_repo_path) + self.make_changes(repo=self.cloned_repo_path, commit=True, content='New change') + + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'new'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + cli.new() + + output = sys.stdout.getvalue() + self.assertTrue('+New change' in output) diff --git a/tests/test_commands.py b/tests/test_commands.py index 3998311..e558672 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -2,8 +2,10 @@ import os import shutil +import tempfile import git +import rpm from mock import patch from pyrpkg import rpkgError @@ -343,3 +345,162 @@ $what_is_this expected_lines = ['- add %changelog section\n', '- add new spec\n'] self.assertEqual(expected_lines, clog_lines) + + +class TestProperties(CommandTestCase): + + def setUp(self): + super(TestProperties, self).setUp() + self.invalid_repo = tempfile.mkdtemp() + + def tearDown(self): + shutil.rmtree(self.invalid_repo) + super(TestProperties, self).tearDown() + + def test_target(self): + cmd = self.make_commands() + self.checkout_branch(cmd.repo, 'eng-rhel-6') + self.assertEqual('eng-rhel-6-candidate', cmd.target) + + def test_spec(self): + cmd = self.make_commands() + self.assertEqual('docpkg.spec', cmd.spec) + + def test_nvr(self): + cmd = self.make_commands(dist='eng-rhel-6') + + module_name = os.path.basename(self.repo_path) + self.assertEqual('{0}-1.2-2.el6'.format(module_name), cmd.nvr) + + def test_nvr_cannot_get_module_name_from_push_url(self): + cmd = self.make_commands(path=self.repo_path, dist='eng-rhel-6') + self.assertEqual('docpkg-1.2-2.el6', cmd.nvr) + + def test_localarch(self): + expected_localarch = rpm.expandMacro('%{_arch}') + cmd = self.make_commands() + self.assertEqual(expected_localarch, cmd.localarch) + + def test_commithash(self): + cmd = self.make_commands(path=self.cloned_repo_path) + repo = git.Repo(self.cloned_repo_path) + expected_commit_hash = str(repo.iter_commits().next()) + self.assertEqual(expected_commit_hash, cmd.commithash) + + def test_dist(self): + repo = git.Repo(self.cloned_repo_path) + self.checkout_branch(repo, 'eng-rhel-7') + + cmd = self.make_commands(path=self.cloned_repo_path) + self.assertEqual('el7', cmd.disttag) + self.assertEqual('rhel', cmd.distvar) + self.assertEqual('7', cmd.distval) + self.assertEqual('0', cmd.epoch) + + def test_repo(self): + cmd = self.make_commands(path=self.cloned_repo_path) + cmd.load_repo() + self.assertEqual(self.cloned_repo_path, os.path.dirname(cmd._repo.git_dir)) + + cmd = self.make_commands(path=self.invalid_repo) + self.assertRaises(rpkgError, cmd.load_repo) + + cmd = self.make_commands(path='some-dir') + self.assertRaises(rpkgError, cmd.load_repo) + + def test_mockconfig(self): + cmd = self.make_commands(path=self.cloned_repo_path) + self.checkout_branch(cmd.repo, 'eng-rhel-7') + expected_localarch = rpm.expandMacro('%{_arch}') + self.assertEqual('eng-rhel-7-candidate-{0}'.format(expected_localarch), cmd.mockconfig) + + def test_get_ns_module_name(self): + cmd = self.make_commands(path=self.cloned_repo_path) + + tests = ( + ('http://localhost/rpms/docpkg.git', 'docpkg'), + ('http://localhost/docker/docpkg.git', 'docpkg'), + ('http://localhost/docpkg.git', 'docpkg'), + ('http://localhost/rpms/docpkg', 'docpkg'), + ) + for push_url, expected_ns_module_name in tests: + cmd._push_url = push_url + cmd.load_ns_module_name() + self.assertEqual(expected_ns_module_name, cmd._ns_module_name) + + cmd.distgit_namespaced = True + tests = ( + ('http://localhost/rpms/docpkg.git', 'rpms/docpkg'), + ('http://localhost/docker/docpkg.git', 'docker/docpkg'), + ('http://localhost/docpkg.git', 'rpms/docpkg'), + ('http://localhost/rpms/docpkg', 'rpms/docpkg'), + ) + for push_url, expected_ns_module_name in tests: + cmd._push_url = push_url + cmd.load_ns_module_name() + self.assertEqual(expected_ns_module_name, cmd._ns_module_name) + + +class TestNamespaced(CommandTestCase): + + def test_get_namespace_giturl(self): + cmd = self.make_commands() + cmd.gitbaseurl = 'ssh://%(user)s@localhost/%(module)s' + cmd.distgit_namespaced = False + + self.assertEqual(cmd.gitbaseurl % {'user': cmd.user, 'module': 'docpkg'}, + cmd._get_namespace_giturl('docpkg')) + self.assertEqual(cmd.gitbaseurl % {'user': cmd.user, 'module': 'docker/docpkg'}, + cmd._get_namespace_giturl('docker/docpkg')) + self.assertEqual(cmd.gitbaseurl % {'user': cmd.user, 'module': 'rpms/docpkg'}, + cmd._get_namespace_giturl('rpms/docpkg')) + + def test_get_namespace_giturl_namespaced_is_enabled(self): + cmd = self.make_commands() + cmd.gitbaseurl = 'ssh://%(user)s@localhost/%(module)s' + cmd.distgit_namespaced = True + + self.assertEqual(cmd.gitbaseurl % {'user': cmd.user, 'module': 'rpms/docpkg'}, + cmd._get_namespace_giturl('docpkg')) + self.assertEqual(cmd.gitbaseurl % {'user': cmd.user, 'module': 'docker/docpkg'}, + cmd._get_namespace_giturl('docker/docpkg')) + self.assertEqual(cmd.gitbaseurl % {'user': cmd.user, 'module': 'rpms/docpkg'}, + cmd._get_namespace_giturl('rpms/docpkg')) + + def test_get_namespace_anongiturl(self): + cmd = self.make_commands() + cmd.anongiturl = 'git://localhost/%(module)s' + cmd.distgit_namespaced = False + + self.assertEqual(cmd.anongiturl % {'module': 'docpkg'}, + cmd._get_namespace_anongiturl('docpkg')) + self.assertEqual(cmd.anongiturl % {'module': 'docker/docpkg'}, + cmd._get_namespace_anongiturl('docker/docpkg')) + self.assertEqual(cmd.anongiturl % {'module': 'rpms/docpkg'}, + cmd._get_namespace_anongiturl('rpms/docpkg')) + + def test_get_namespace_anongiturl_namespaced_is_enabled(self): + cmd = self.make_commands() + cmd.anongiturl = 'git://localhost/%(module)s' + cmd.distgit_namespaced = True + + self.assertEqual(cmd.anongiturl % {'module': 'rpms/docpkg'}, + cmd._get_namespace_anongiturl('docpkg')) + self.assertEqual(cmd.anongiturl % {'module': 'docker/docpkg'}, + cmd._get_namespace_anongiturl('docker/docpkg')) + self.assertEqual(cmd.anongiturl % {'module': 'rpms/docpkg'}, + cmd._get_namespace_anongiturl('rpms/docpkg')) + + +class TestGetLatestCommit(CommandTestCase): + + def test_get_latest_commit(self): + cmd = self.make_commands(path=self.cloned_repo_path) + # Repos used for running tests locates in local filesyste, refer to + # self.repo_path and self.cloned_repo_path. + cmd.anongiturl = '/tmp/%(module)s' + cmd.distgit_namespaced = False + + self.assertEqual(str(git.Repo(self.repo_path).iter_commits().next()), + cmd.get_latest_commit(os.path.basename(self.repo_path), + 'eng-rhel-6')) diff --git a/tests/utils.py b/tests/utils.py index 4d4946e..049d0f5 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -5,6 +5,7 @@ import subprocess import tempfile import unittest import shutil +import sys from pyrpkg import Commands @@ -29,7 +30,7 @@ License: GPL Group: Applications/Productivity BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) %description -This is a dummy description. +Dummy docpkg for tests %prep %check %build @@ -37,21 +38,16 @@ touch README.rst %clean rm -rf $$RPM_BUILD_ROOT %install +rm -rf $$RPM_BUILD_ROOT %files +%defattr(-,root,root,-) %doc README.rst %changelog -* Thu Apr 21 2006 Chenxiong Qi - 1.2-2 +* Thu Apr 21 2016 Tester - 1.2-2 - Initial version ''' -def run(cmd, **kwargs): - returncode = subprocess.call(cmd, **kwargs) - if returncode != 0: - raise RuntimeError('Command fails. Command: %s. Return code %d' % ( - ' '.join(cmd), returncode)) - - class Assertions(object): def assertFilesExists(self, filenames): @@ -64,13 +60,39 @@ class Assertions(object): self.assertTrue(os.path.exists(os.path.join(self.cloned_repo_path, filename))) -class CommandTestCase(Assertions, unittest.TestCase): +class Utils(object): + + def run_cmd(self, cmd, **kwargs): + returncode = subprocess.call(cmd, **kwargs) + if returncode != 0: + raise RuntimeError('Command fails. Command: %s. Return code %d' % ( + ' '.join(cmd), returncode)) + + def redirect_cmd_output(self, cmd, shell=False, env=None, pipe=[], cwd=None): + if shell: + cmd = ' '.join(cmd) + proc = subprocess.Popen(cmd, shell=shell, cwd=cwd, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = proc.communicate() + sys.stdout.write(stdout) + sys.stderr.write(stderr) + + def read_file(self, filename): + with open(filename, 'r') as f: + return f.read() + + def write_file(self, filename, content=''): + with open(filename, 'w') as f: + f.write(content) + + +class CommandTestCase(Assertions, Utils, unittest.TestCase): def setUp(self): # create a base repo self.repo_path = tempfile.mkdtemp(prefix='rpkg-commands-tests-') - self.spec_file = 'package.spec' + self.spec_file = 'docpkg.spec' # Add spec file to this repo and commit spec_file_path = os.path.join(self.repo_path, self.spec_file) @@ -86,13 +108,15 @@ class CommandTestCase(Assertions, unittest.TestCase): ['git', 'branch', 'eng-rhel-6'], ['git', 'branch', 'eng-rhel-6.5'], ['git', 'branch', 'eng-rhel-7'], + ['git', 'branch', 'rhel-6.8'], + ['git', 'branch', 'rhel-7'], ] for cmd in git_cmds: - run(cmd, cwd=self.repo_path) + self.run_cmd(cmd, cwd=self.repo_path) # Clone the repo self.cloned_repo_path = tempfile.mkdtemp(prefix='rpkg-commands-tests-cloned-') - run(['git', 'clone', self.repo_path, self.cloned_repo_path]) + self.run_cmd(['git', 'clone', self.repo_path, self.cloned_repo_path]) git_cmds = [ ['git', 'config', 'user.email', 'cqi@redhat.com'], ['git', 'config', 'user.name', 'Chenxiong Qi'], @@ -101,7 +125,7 @@ class CommandTestCase(Assertions, unittest.TestCase): ['git', 'branch', '--track', 'eng-rhel-7', 'origin/eng-rhel-7'], ] for cmd in git_cmds: - run(cmd, cwd=self.cloned_repo_path) + self.run_cmd(cmd, cwd=self.cloned_repo_path) def tearDown(self): shutil.rmtree(self.repo_path) From 259eb1e804ae3c4799f485cd737d4689f5470bc1 Mon Sep 17 00:00:00 2001 From: Chenxiong Qi Date: Nov 21 2016 06:01:40 +0000 Subject: [PATCH 3/8] Tests for lookaside related commands Signed-off-by: Chenxiong Qi --- diff --git a/tests/test_cli.py b/tests/test_cli.py index 3fdabd4..d91fcfb 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,8 +1,11 @@ # -*- coding: utf-8 -*- +import hashlib import logging import os +import shutil import sys +import tempfile from os.path import exists from os.path import join @@ -733,3 +736,136 @@ class TestNew(CliTestCase): output = sys.stdout.getvalue() self.assertTrue('+New change' in output) + + +class LookasideCacheMock(object): + + def init_lookaside_cache(self): + self.lookasidecache_storage = tempfile.mkdtemp('rpkg-tests-lookasidecache-storage-') + + def destroy_lookaside_cache(self): + shutil.rmtree(self.lookasidecache_storage) + + def lookasidecache_upload(self, module_name, filepath, hash): + filename = os.path.basename(filepath) + storage_filename = os.path.join(self.lookasidecache_storage, filename) + with open(storage_filename, 'w') as fout: + with open(filepath, 'r') as fin: + fout.write(fin.read()) + + def lookasidecache_download(self, name, filename, hash, outfile, hashtype=None, **kwargs): + with open(outfile, 'w') as f: + f.write('binary data') + + +class TestUpload(LookasideCacheMock, CliTestCase): + + def setUp(self): + super(TestUpload, self).setUp() + + self.init_lookaside_cache() + self.sources_file = os.path.join(self.cloned_repo_path, 'sources') + self.gitignore_file = os.path.join(self.cloned_repo_path, '.gitignore') + self.readme_patch = os.path.join(self.cloned_repo_path, 'readme.patch') + self.write_file(self.readme_patch, '+Hello world') + + def tearDown(self): + self.destroy_lookaside_cache() + super(TestUpload, self).tearDown() + + def hash_file(self, filename): + md5 = hashlib.md5() + with open(filename, 'r') as f: + md5.update(f.read()) + return md5.hexdigest() + + def test_upload(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'upload', self.readme_patch] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + with patch('pyrpkg.lookaside.CGILookasideCache.upload', new=self.lookasidecache_upload): + cli.upload() + + expected_sources_content = '{0} readme.patch'.format(self.hash_file(self.readme_patch)) + self.assertEqual(expected_sources_content, self.read_file(self.sources_file).strip()) + self.assertTrue('readme.patch' in self.read_file(self.gitignore_file).strip()) + + git_status = cli.cmd.repo.git.status() + self.assertTrue('Changes not staged for commit:' not in git_status) + self.assertTrue('Changes to be committed:' in git_status) + + def test_append_to_sources(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'upload', self.readme_patch] + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + with patch('pyrpkg.lookaside.CGILookasideCache.upload', new=self.lookasidecache_upload): + cli.upload() + + readme_rst = os.path.join(self.cloned_repo_path, 'README.rst') + self.make_changes(filename=readme_rst, content='# dockpkg', commit=True) + + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'upload', readme_rst] + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + with patch('pyrpkg.lookaside.CGILookasideCache.upload', new=self.lookasidecache_upload): + cli.upload() + + expected_sources_content = [ + '{0} {1}'.format(self.hash_file(self.readme_patch), + os.path.basename(self.readme_patch)), + '{0} {1}'.format(self.hash_file(readme_rst), + os.path.basename(readme_rst)), + ] + self.assertEqual(expected_sources_content, + self.read_file(self.sources_file).strip().split('\n')) + + +class TestSources(LookasideCacheMock, CliTestCase): + + def setUp(self): + super(TestSources, self).setUp() + self.init_lookaside_cache() + + # Uploading a file aims to run the loop in sources command. + self.readme_patch = os.path.join(self.cloned_repo_path, 'readme.patch') + self.write_file(self.readme_patch, content='+Welcome to README') + + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'upload', self.readme_patch] + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + with patch('pyrpkg.lookaside.CGILookasideCache.upload', new=self.lookasidecache_upload): + cli.upload() + + def tearDown(self): + # Tests may put a file readme.patch in current directory, so, let's remove it. + if os.path.exists('readme.patch'): + os.remove('readme.patch') + self.destroy_lookaside_cache() + super(TestSources, self).tearDown() + + def test_sources(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'sources'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + with patch('pyrpkg.lookaside.CGILookasideCache.download', + new=self.lookasidecache_download): + cli.sources() + + # NOTE: without --outdir, whatever to run sources command in package + # repository, sources file is downloaded into current working + # directory. Is this a bug, or need to improve? + self.assertTrue(os.path.exists('readme.patch')) + + def test_sources_to_outdir(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, + 'sources', '--outdir', self.cloned_repo_path] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + with patch('pyrpkg.lookaside.CGILookasideCache.download', + new=self.lookasidecache_download): + cli.sources() + + self.assertFilesExists(['readme.patch']) diff --git a/tests/utils.py b/tests/utils.py index 049d0f5..9d15628 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -56,6 +56,7 @@ class Assertions(object): :param filenames: a sequence of file names within package repository to be checked. :type filenames: list or tuple """ + assert isinstance(filenames, (tuple, list)) for filename in filenames: self.assertTrue(os.path.exists(os.path.join(self.cloned_repo_path, filename))) @@ -102,6 +103,7 @@ class CommandTestCase(Assertions, Utils, unittest.TestCase): git_cmds = [ ['git', 'init'], ['git', 'add', spec_file_path], + ['touch', 'sources'], ['git', 'config', 'user.email', 'cqi@redhat.com'], ['git', 'config', 'user.name', 'Chenxiong Qi'], ['git', 'commit', '-m', '"initial commit"'], From 01ba8b47e0543ae54bcc456ea90129a62bbd064f Mon Sep 17 00:00:00 2001 From: Chenxiong Qi Date: Nov 21 2016 09:25:16 +0000 Subject: [PATCH 4/8] Add tests for import_srpm Besides tests, also fix import_srpm, that it cannot work with --path option. Signed-off-by: Chenxiong Qi --- diff --git a/pyrpkg/__init__.py b/pyrpkg/__init__.py index 48f0174..f2de199 100644 --- a/pyrpkg/__init__.py +++ b/pyrpkg/__init__.py @@ -1498,14 +1498,13 @@ class Commands(object): Returns a list of files to upload. """ - + # bail if we're dirty + if self.repo.is_dirty(): + raise rpkgError('There are uncommitted changes in your repo') # see if the srpm even exists srpm = os.path.abspath(srpm) if not os.path.exists(srpm): raise rpkgError('File not found.') - # bail if we're dirty - if self.repo.is_dirty(): - raise rpkgError('There are uncommitted changes in your repo') # Get the details of the srpm name, files, uploadfiles = self._srpmdetails(srpm) @@ -1559,7 +1558,7 @@ class Commands(object): self.repo.index.add(files) # Return to the caller and let them take it from there. os.chdir(oldpath) - return(uploadfiles) + return [os.path.join(self.path, file) for file in uploadfiles] def list_tag(self, tagname='*'): """List all tags in the repository which match a given tagname. diff --git a/tests/test_cli.py b/tests/test_cli.py index d91fcfb..c87939e 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- +import gzip import hashlib import logging import os +import rpmfluff import shutil import sys import tempfile @@ -421,7 +423,7 @@ class TestLocal(CliTestCase): cli = self.new_cli() cli.local() - self.assertFilesExists(('this-builddir/README.rst',)) + self.assertFilesExist(('this-builddir/README.rst',), search_dir=self.cloned_repo_path) class TestVerifyFiles(CliTestCase): @@ -614,7 +616,7 @@ class TestClean(CliTestCase): cli = self.new_cli() cli.clean() - self.assertFilesExists(['new-file.txt']) + self.assertFilesExist(['new-file.txt'], search_dir=self.cloned_repo_path) def test_clean(self): self.make_changes(untracked=True) @@ -631,7 +633,7 @@ class TestClean(CliTestCase): self.assertFalse(os.path.exists(dirname)) # Ensure no tracked files and directories are removed. - self.assertFilesExists(['docpkg.spec', '.git']) + self.assertFilesExist(['docpkg.spec', '.git'], search_dir=self.cloned_repo_path) class TestLint(CliTestCase): @@ -757,6 +759,19 @@ class LookasideCacheMock(object): with open(outfile, 'w') as f: f.write('binary data') + def hash_file(self, filename): + md5 = hashlib.md5() + with open(filename, 'r') as f: + md5.update(f.read()) + return md5.hexdigest() + + def assertFilesUploaded(self, filenames): + assert isinstance(filenames, (tuple, list)) + for filename in filenames: + self.assertTrue( + os.path.exists(os.path.join(self.lookasidecache_storage, filename)), + '{0} is not uploaded. It is not in fake lookaside storage.'.format(filename)) + class TestUpload(LookasideCacheMock, CliTestCase): @@ -773,12 +788,6 @@ class TestUpload(LookasideCacheMock, CliTestCase): self.destroy_lookaside_cache() super(TestUpload, self).tearDown() - def hash_file(self, filename): - md5 = hashlib.md5() - with open(filename, 'r') as f: - md5.update(f.read()) - return md5.hexdigest() - def test_upload(self): cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'upload', self.readme_patch] @@ -868,4 +877,96 @@ class TestSources(LookasideCacheMock, CliTestCase): new=self.lookasidecache_download): cli.sources() - self.assertFilesExists(['readme.patch']) + self.assertFilesExist(['readme.patch'], search_dir=self.cloned_repo_path) + + +class TestFailureImportSrpm(CliTestCase): + + def test_import_nonexistent_srpm(self): + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'import', 'nonexistent-srpm'] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + try: + cli.import_srpm() + except rpkgError as e: + self.assertEqual('File not found.', str(e)) + else: + self.fail('import_srpm should fail if srpm does not exist.') + + def test_repo_is_dirty(self): + srpm_file = os.path.join(os.path.dirname(__file__), 'fixtures', 'docpkg-0.2-1.src.rpm') + self.make_changes() + cli_cmd = ['rpkg', '--path', self.cloned_repo_path, 'import', srpm_file] + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + try: + cli.import_srpm() + except rpkgError as e: + self.assertEqual('There are uncommitted changes in your repo', str(e)) + else: + self.fail('import_srpm should fail if package repository is dirty.') + + +class TestImportSrpm(LookasideCacheMock, CliTestCase): + + def setUp(self): + super(TestImportSrpm, self).setUp() + self.init_lookaside_cache() + + # Gzip file that will be added into the SRPM + self.docpkg_gz = os.path.join(self.cloned_repo_path, 'docpkg.gz') + gzf = gzip.open(self.docpkg_gz, 'w') + gzf.write('file content of docpkg') + gzf.close() + + # Build the SRPM + self.build = rpmfluff.SimpleRpmBuild(name='docpkg', version='0.2', release='1') + self.build.add_changelog_entry('- New release 0.2-1', version='0.2', release='1', + nameStr='tester ') + self.build.add_simple_payload_file() + self.build.add_source(rpmfluff.SourceFile('docpkg.gz', + gzip.open(self.docpkg_gz, 'r').read())) + self.build.make() + self.srpm_file = self.build.get_built_srpm() + + self.chaos_repo = tempfile.mkdtemp(prefix='rpkg-tests-chaos-repo-') + self.run_cmd(['git', 'init'], cwd=self.chaos_repo) + + def tearDown(self): + os.remove(self.docpkg_gz) + shutil.rmtree(self.build.get_base_dir()) + shutil.rmtree(self.chaos_repo) + self.destroy_lookaside_cache() + super(TestImportSrpm, self).tearDown() + + def assert_import_srpm(self, target_repo): + cli_cmd = ['rpkg', '--path', target_repo, '--module-name', 'docpkg', + 'import', '--skip-diffs', self.srpm_file] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + with patch('pyrpkg.lookaside.CGILookasideCache.upload', self.lookasidecache_upload): + cli.import_srpm() + + docpkg_gz = os.path.basename(self.docpkg_gz) + diff_cached = cli.cmd.repo.git.diff('--cached') + self.assertTrue('+- - New release 0.2-1' in diff_cached) + self.assertTrue('+hello world' in diff_cached) + self.assertFilesExist(['.gitignore', + 'sources', + 'docpkg.spec', + 'hello-world.txt', + docpkg_gz], search_dir=target_repo) + self.assertFilesNotExist(['CHANGELOG.rst'], search_dir=target_repo) + with open(os.path.join(target_repo, 'sources'), 'r') as f: + self.assertEqual( + '{0} {1}'.format(self.hash_file(os.path.join(target_repo, docpkg_gz)), docpkg_gz), + f.read().strip()) + with open(os.path.join(target_repo, '.gitignore'), 'r') as f: + self.assertEqual('/{0}'.format(docpkg_gz), f.read().strip()) + self.assertFilesUploaded([docpkg_gz]) + + def test_import(self): + self.assert_import_srpm(self.chaos_repo) + self.assert_import_srpm(self.cloned_repo_path) diff --git a/tests/utils.py b/tests/utils.py index 9d15628..b8d9794 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -50,15 +50,31 @@ rm -rf $$RPM_BUILD_ROOT class Assertions(object): - def assertFilesExists(self, filenames): + def get_exists_method(self, search_dir=None): + if search_dir is None: + def exists(filename): + return os.path.exists(filename) + else: + def exists(filename): + return os.path.exists(os.path.join(search_dir, filename)) + return exists + + def assertFilesExist(self, filenames, search_dir=None): """Assert existence of files within package repository :param filenames: a sequence of file names within package repository to be checked. :type filenames: list or tuple """ assert isinstance(filenames, (tuple, list)) + exists = self.get_exists_method(search_dir) for filename in filenames: - self.assertTrue(os.path.exists(os.path.join(self.cloned_repo_path, filename))) + self.assertTrue(exists(filename), 'Failure because {0} does not exist'.format(filename)) + + def assertFilesNotExist(self, filenames, search_dir=None): + assert isinstance(filenames, (tuple, list)) + exists = self.get_exists_method(search_dir) + for filename in filenames: + self.assertFalse(exists(filename), 'Failure because {0} exists.'.format(filename)) class Utils(object): @@ -102,8 +118,8 @@ class CommandTestCase(Assertions, Utils, unittest.TestCase): git_cmds = [ ['git', 'init'], - ['git', 'add', spec_file_path], - ['touch', 'sources'], + ['touch', 'sources', 'CHANGELOG.rst'], + ['git', 'add', spec_file_path, 'sources', 'CHANGELOG.rst'], ['git', 'config', 'user.email', 'cqi@redhat.com'], ['git', 'config', 'user.name', 'Chenxiong Qi'], ['git', 'commit', '-m', '"initial commit"'], From 40100243a48d78b9ead011084ace0ded1dbdca64 Mon Sep 17 00:00:00 2001 From: Chenxiong Qi Date: Nov 21 2016 14:39:06 +0000 Subject: [PATCH 5/8] Fix setUp of TestImportSrpm for EL6 In EL6, git (version 1.7.1) command must work with a valid HEAD. Signed-off-by: Chenxiong Qi --- diff --git a/tests/test_cli.py b/tests/test_cli.py index c87939e..281765f 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -931,7 +931,16 @@ class TestImportSrpm(LookasideCacheMock, CliTestCase): self.srpm_file = self.build.get_built_srpm() self.chaos_repo = tempfile.mkdtemp(prefix='rpkg-tests-chaos-repo-') - self.run_cmd(['git', 'init'], cwd=self.chaos_repo) + cmds = ( + ['git', 'init'], + ['touch', 'README.rst'], + ['git', 'add', 'README.rst'], + ['git', 'config', 'user.name', 'tester'], + ['git', 'config', 'user.email', 'tester@example.com'], + ['git', 'commit', '-m', '"Add README"'], + ) + for cmd in cmds: + self.run_cmd(cmd, cwd=self.chaos_repo) def tearDown(self): os.remove(self.docpkg_gz) From 605b40133cf7f74fd6296d20a7c2d7b590dcfeb3 Mon Sep 17 00:00:00 2001 From: Chenxiong Qi Date: Nov 23 2016 03:17:24 +0000 Subject: [PATCH 6/8] Remove unnecessary touch method Signed-off-by: Chenxiong Qi --- diff --git a/tests/test_cli.py b/tests/test_cli.py index 281765f..6ccd39d 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -54,13 +54,6 @@ class CliTestCase(CommandTestCase): return client - def touch(self, filename, content=None): - """Touch a file with optional content""" - - _content = content if content else '' - with open(filename, 'w') as f: - f.write(_content) - def make_changes(self, repo=None, untracked=None, commit=None, filename=None, content=''): repo_path = repo or self.cloned_repo_path _filename = filename or 'new-file.txt' @@ -548,7 +541,7 @@ class TestUnusedPatches(CliTestCase): os.path.join(self.cloned_repo_path, '0001-add-new-feature.patch'), os.path.join(self.cloned_repo_path, '0002-hotfix.patch'), ) - map(self.touch, self.patches) + map(self.write_file, self.patches) git.Repo(self.cloned_repo_path).index.add(self.patches) @patch('sys.stdout', new=StringIO()) From 87473baf2a6d370c984ab81bc0cbe98da0131221 Mon Sep 17 00:00:00 2001 From: Chenxiong Qi Date: Nov 23 2016 04:31:45 +0000 Subject: [PATCH 7/8] Use fake user info to config repository in tests Signed-off-by: Chenxiong Qi --- diff --git a/tests/commands/__init__.py b/tests/commands/__init__.py index ba4d3ce..2a8035e 100644 --- a/tests/commands/__init__.py +++ b/tests/commands/__init__.py @@ -60,10 +60,10 @@ class CommandTestCase(unittest.TestCase): clonedir = os.path.join(cloneroot, module.split('/')[-1]) open(os.path.join(clonedir, '.gitignore'), 'w').close() open(os.path.join(clonedir, 'sources'), 'w').close() - subprocess.check_call(['git', 'config', 'user.name', 'Chenxiong Qi'], + subprocess.check_call(['git', 'config', 'user.name', 'tester'], cwd=clonedir, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - subprocess.check_call(['git', 'config', 'user.email', 'cqi@redhat.com'], + subprocess.check_call(['git', 'config', 'user.email', 'tester@example.com'], cwd=clonedir, stdout=subprocess.PIPE, stderr=subprocess.PIPE) subprocess.check_call(['git', 'add', '.gitignore', 'sources'], diff --git a/tests/test_commands.py b/tests/test_commands.py index e558672..0d08eb1 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -315,12 +315,12 @@ License: GPL %description package demo for testing %changelog -* Mon Nov 07 2016 cqi@redhat.com +* Mon Nov 07 2016 tester@example.com - add %%changelog section - add new spec $what_is_this -* Mon Nov 06 2016 cqi@redhat.com +* Mon Nov 06 2016 tester@example.com - initial ''') diff --git a/tests/utils.py b/tests/utils.py index b8d9794..defbf20 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -120,8 +120,8 @@ class CommandTestCase(Assertions, Utils, unittest.TestCase): ['git', 'init'], ['touch', 'sources', 'CHANGELOG.rst'], ['git', 'add', spec_file_path, 'sources', 'CHANGELOG.rst'], - ['git', 'config', 'user.email', 'cqi@redhat.com'], - ['git', 'config', 'user.name', 'Chenxiong Qi'], + ['git', 'config', 'user.email', 'tester@example.com'], + ['git', 'config', 'user.name', 'tester'], ['git', 'commit', '-m', '"initial commit"'], ['git', 'branch', 'eng-rhel-6'], ['git', 'branch', 'eng-rhel-6.5'], @@ -136,8 +136,8 @@ class CommandTestCase(Assertions, Utils, unittest.TestCase): self.cloned_repo_path = tempfile.mkdtemp(prefix='rpkg-commands-tests-cloned-') self.run_cmd(['git', 'clone', self.repo_path, self.cloned_repo_path]) git_cmds = [ - ['git', 'config', 'user.email', 'cqi@redhat.com'], - ['git', 'config', 'user.name', 'Chenxiong Qi'], + ['git', 'config', 'user.email', 'tester@example.com'], + ['git', 'config', 'user.name', 'tester'], ['git', 'branch', '--track', 'eng-rhel-6', 'origin/eng-rhel-6'], ['git', 'branch', '--track', 'eng-rhel-6.5', 'origin/eng-rhel-6.5'], ['git', 'branch', '--track', 'eng-rhel-7', 'origin/eng-rhel-7'], From de518a51a22166ad9f94bb0cbb0671cc846c446f Mon Sep 17 00:00:00 2001 From: Chenxiong Qi Date: Nov 24 2016 05:39:03 +0000 Subject: [PATCH 8/8] Make rpmbuild run with local en_US.UTF-8 in tests This is necessary because different locales make rpmbuild output different text. For example, setting LANG=C, rpmbuild outputs Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.sRiekk However, setting LANG=cs_CZ, rpmbuild outputs following instead of Executing(%prep) Prov%prep): /bin/sh -e /var/tmp/rpm-tmp.E5Uk9p With this change, tests don't depend on the locale set in the host. Signed-off-by: Chenxiong Qi --- diff --git a/tests/utils.py b/tests/utils.py index defbf20..1898367 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -88,7 +88,10 @@ class Utils(object): def redirect_cmd_output(self, cmd, shell=False, env=None, pipe=[], cwd=None): if shell: cmd = ' '.join(cmd) - proc = subprocess.Popen(cmd, shell=shell, cwd=cwd, + proc_env = os.environ.copy() + proc_env.update(env or {}) + proc_env['LANG'] = 'en_US.UTF-8' + proc = subprocess.Popen(cmd, shell=shell, cwd=cwd, env=proc_env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate() sys.stdout.write(stdout)