add unit test and module compatibility lib

Add modules compat lib to support both python 2 and 3, and
add unit test to detect regressions.

Tito uses two modules that have changed significantly between
python 2 and 3:

* commands module merged into subprocess module
* ConfigParser module renamed as configparser
This commit is contained in:
Paul Morgan 2014-02-15 23:19:29 +00:00
parent 1182b13713
commit 03255001d6
13 changed files with 97 additions and 42 deletions

View file

@ -19,7 +19,6 @@ and rpms.
import os import os
import sys import sys
import re import re
import commands
from pkg_resources import require from pkg_resources import require
from distutils.version import LooseVersion as loose_version from distutils.version import LooseVersion as loose_version
from tempfile import mkdtemp from tempfile import mkdtemp
@ -685,7 +684,7 @@ class CvsBuilder(NoTgzBuilder):
if not reuse_cvs_checkout: if not reuse_cvs_checkout:
self._verify_cvs_module_not_already_checked_out() self._verify_cvs_module_not_already_checked_out()
commands.getoutput("mkdir -p %s" % self.cvs_workdir) getoutput("mkdir -p %s" % self.cvs_workdir)
cvs_releaser = CvsReleaser(self) cvs_releaser = CvsReleaser(self)
cvs_releaser.cvs_checkout_module() cvs_releaser.cvs_checkout_module()
cvs_releaser.cvs_verify_branches_exist() cvs_releaser.cvs_verify_branches_exist()
@ -879,7 +878,7 @@ class UpstreamBuilder(NoTgzBuilder):
debug("Generating patch with: %s" % patch_command) debug("Generating patch with: %s" % patch_command)
output = run_command(patch_command) output = run_command(patch_command)
print(output) print(output)
(status, output) = commands.getstatusoutput( (status, output) = getstatusoutput(
"grep 'Binary files .* differ' %s " % patch_file) "grep 'Binary files .* differ' %s " % patch_file)
if status == 0 and output != "": if status == 0 and output != "":
error_out("You are doomed. Diff contains binary files. You can not use this builder") error_out("You are doomed. Diff contains binary files. You can not use this builder")
@ -915,7 +914,7 @@ class UpstreamBuilder(NoTgzBuilder):
with just the package release being incremented on rebuilds. with just the package release being incremented on rebuilds.
""" """
# Use upstreamversion if defined in the spec file: # Use upstreamversion if defined in the spec file:
(status, output) = commands.getstatusoutput( (status, output) = getstatusoutput(
"cat %s | grep 'define upstreamversion' | " "cat %s | grep 'define upstreamversion' | "
"awk '{ print $3 ; exit }'" % self.spec_file) "awk '{ print $3 ; exit }'" % self.spec_file)
if status == 0 and output != "": if status == 0 and output != "":

View file

@ -17,12 +17,11 @@ Tito's Command Line Interface
import sys import sys
import os import os
import random import random
import commands
import ConfigParser
from optparse import OptionParser from optparse import OptionParser
from tito.common import * from tito.common import *
from tito.compat import *
from tito.exception import * from tito.exception import *
# Hack for Python 2.4, seems to require we import these so they get compiled # Hack for Python 2.4, seems to require we import these so they get compiled
@ -195,7 +194,7 @@ class BaseCliModule(object):
# Load the global config. Later, when we know what tag/package we're # Load the global config. Later, when we know what tag/package we're
# building, we may also load that and potentially override some global # building, we may also load that and potentially override some global
# settings. # settings.
config = ConfigParser.ConfigParser() config = ConfigParser()
config.read(filename) config.read(filename)
# Verify the config contains what we need from it: # Verify the config contains what we need from it:
@ -262,13 +261,13 @@ class BaseCliModule(object):
cmd = "git show %s:%s%s" % (tag, relative_dir, cmd = "git show %s:%s%s" % (tag, relative_dir,
BUILD_PROPS_FILENAME) BUILD_PROPS_FILENAME)
debug(cmd) debug(cmd)
(status, output) = commands.getstatusoutput(cmd) (status, output) = getstatusoutput(cmd)
if status > 0: if status > 0:
# Give it another try looking for legacy props filename: # Give it another try looking for legacy props filename:
cmd = "git show %s:%s%s" % (tag, relative_dir, cmd = "git show %s:%s%s" % (tag, relative_dir,
"build.py.props") "build.py.props")
debug(cmd) debug(cmd)
(status, output) = commands.getstatusoutput(cmd) (status, output) = getstatusoutput(cmd)
temp_filename = "%s-%s" % (random.randint(1, 10000), temp_filename = "%s-%s" % (random.randint(1, 10000),
BUILD_PROPS_FILENAME) BUILD_PROPS_FILENAME)
@ -287,7 +286,7 @@ class BaseCliModule(object):
cmd = "git show %s:%s%s | grep NO_TAR_GZ" % \ cmd = "git show %s:%s%s | grep NO_TAR_GZ" % \
(tag, relative_dir, "Makefile") (tag, relative_dir, "Makefile")
debug(cmd) debug(cmd)
(status, output) = commands.getstatusoutput(cmd) (status, output) = getstatusoutput(cmd)
if status == 0 and output != "": if status == 0 and output != "":
properties_file = temp_props_file properties_file = temp_props_file
debug("Found Makefile with NO_TAR_GZ") debug("Found Makefile with NO_TAR_GZ")
@ -507,7 +506,7 @@ class ReleaseModule(BaseCliModule):
""" """
rel_eng_dir = os.path.join(find_git_root(), "rel-eng") rel_eng_dir = os.path.join(find_git_root(), "rel-eng")
filename = os.path.join(rel_eng_dir, RELEASERS_CONF_FILENAME) filename = os.path.join(rel_eng_dir, RELEASERS_CONF_FILENAME)
config = ConfigParser.ConfigParser() config = ConfigParser()
config.read(filename) config.read(filename)
return config return config
@ -736,7 +735,7 @@ class InitModule(BaseCliModule):
propsfile = os.path.join(rel_eng_dir, GLOBAL_BUILD_PROPS_FILENAME) propsfile = os.path.join(rel_eng_dir, GLOBAL_BUILD_PROPS_FILENAME)
if not os.path.exists(propsfile): if not os.path.exists(propsfile):
if not os.path.exists(rel_eng_dir): if not os.path.exists(rel_eng_dir):
commands.getoutput("mkdir -p %s" % rel_eng_dir) getoutput("mkdir -p %s" % rel_eng_dir)
print(" - created %s" % rel_eng_dir) print(" - created %s" % rel_eng_dir)
# write out tito.props # write out tito.props
@ -750,7 +749,7 @@ class InitModule(BaseCliModule):
out_f.close() out_f.close()
print(" - wrote %s" % GLOBAL_BUILD_PROPS_FILENAME) print(" - wrote %s" % GLOBAL_BUILD_PROPS_FILENAME)
commands.getoutput('git add %s' % propsfile) getoutput('git add %s' % propsfile)
should_commit = True should_commit = True
# prep the packages metadata directory # prep the packages metadata directory
@ -759,7 +758,7 @@ class InitModule(BaseCliModule):
if not os.path.exists(readme): if not os.path.exists(readme):
if not os.path.exists(pkg_dir): if not os.path.exists(pkg_dir):
commands.getoutput("mkdir -p %s" % pkg_dir) getoutput("mkdir -p %s" % pkg_dir)
print(" - created %s" % pkg_dir) print(" - created %s" % pkg_dir)
# write out readme file explaining what pkg_dir is for # write out readme file explaining what pkg_dir is for
@ -771,11 +770,11 @@ class InitModule(BaseCliModule):
out_f.close() out_f.close()
print(" - wrote %s" % readme) print(" - wrote %s" % readme)
commands.getoutput('git add %s' % readme) getoutput('git add %s' % readme)
should_commit = True should_commit = True
if should_commit: if should_commit:
commands.getoutput('git commit -m "Initialized to use tito. "') getoutput('git commit -m "Initialized to use tito. "')
print(" - committed to git") print(" - committed to git")
print("Done!") print("Done!")

View file

@ -17,9 +17,9 @@ Common operations.
import os import os
import re import re
import sys import sys
import commands
import traceback import traceback
from tito.compat import *
from tito.exception import RunCommandException from tito.exception import RunCommandException
DEFAULT_BUILD_DIR = "/tmp/tito" DEFAULT_BUILD_DIR = "/tmp/tito"
@ -194,7 +194,7 @@ def find_git_root():
Returned as a full path. Returned as a full path.
""" """
(status, cdup) = commands.getstatusoutput("git rev-parse --show-cdup") (status, cdup) = getstatusoutput("git rev-parse --show-cdup")
if status > 0: if status > 0:
error_out(["%s does not appear to be within a git checkout." % error_out(["%s does not appear to be within a git checkout." %
os.getcwd()]) os.getcwd()])
@ -219,12 +219,12 @@ def run_command(command, print_on_success=False):
If print_on_success is True, print status and output even If print_on_success is True, print status and output even
when command succeeds. when command succeeds.
""" """
(status, output) = commands.getstatusoutput(command) (status, output) = getstatusoutput(command)
return output return output
def tag_exists_locally(tag): def tag_exists_locally(tag):
(status, output) = commands.getstatusoutput("git tag | grep %s" % tag) (status, output) = getstatusoutput("git tag | grep %s" % tag)
if status > 0: if status > 0:
return False return False
else: else:

49
src/tito/compat.py Normal file
View file

@ -0,0 +1,49 @@
# Copyright (c) 2008-2010 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
"""
Compatibility library for Python 2.4 up through Python 3.
"""
import sys
PY2 = sys.version_info[0] == 2
if PY2:
import commands
from ConfigParser import ConfigParser
from ConfigParser import NoOptionError
from ConfigParser import RawConfigParser
else:
import subprocess
from configparser import ConfigParser
from configparser import NoOptionError
from configparser import RawConfigParser
def getstatusoutput(cmd):
"""
Returns (status, output) of executing cmd in a shell.
Supports Python 2.4 and 3.x.
"""
if PY2:
return commands.getstatusoutput(cmd)
else:
return subprocess.getstatusoutput(cmd)
def getoutput(cmd):
"""
Returns output of executing cmd in a shell.
Supports Python 2.4 and 3.x.
"""
if PY2:
return commands.getoutput(cmd)
else:
return subprocess.getoutput(cmd)

View file

@ -2,7 +2,7 @@ import os
from tito.builder import UpstreamBuilder from tito.builder import UpstreamBuilder
from tito.common import debug, run_command, error_out from tito.common import debug, run_command, error_out
import commands from tito.compat import *
class DistributionBuilder(UpstreamBuilder): class DistributionBuilder(UpstreamBuilder):
@ -32,7 +32,7 @@ class DistributionBuilder(UpstreamBuilder):
% (self.rpmbuild_gitcopy, self.project_name, self.upstream_version, self.build_version, self.git_commit_id)) % (self.rpmbuild_gitcopy, self.project_name, self.upstream_version, self.build_version, self.git_commit_id))
self.patch_files = output.split("\n") self.patch_files = output.split("\n")
for p_file in self.patch_files: for p_file in self.patch_files:
(status, output) = commands.getstatusoutput( (status, output) = getstatusoutput(
"grep 'Binary files .* differ' %s/%s " % (self.rpmbuild_gitcopy, p_file)) "grep 'Binary files .* differ' %s/%s " % (self.rpmbuild_gitcopy, p_file))
if status == 0 and output != "": if status == 0 and output != "":
error_out("You are doomed. Diff contains binary files. You can not use this builder") error_out("You are doomed. Diff contains binary files. You can not use this builder")

View file

@ -12,12 +12,12 @@
# in this software or its documentation. # in this software or its documentation.
import os import os
import commands
import tempfile import tempfile
import subprocess import subprocess
import sys import sys
from tito.common import run_command, debug, extract_bzs from tito.common import run_command, debug, extract_bzs
from tito.compat import *
from tito.release import Releaser from tito.release import Releaser
@ -49,7 +49,7 @@ class ObsReleaser(Releaser):
self.dry_run = dry_run self.dry_run = dry_run
self.no_build = no_build self.no_build = no_build
commands.getoutput("mkdir -p %s" % self.working_dir) getoutput("mkdir -p %s" % self.working_dir)
os.chdir(self.working_dir) os.chdir(self.working_dir)
run_command("%s co %s %s" % (self.cli_tool, self.obs_project_name, self.obs_package_name)) run_command("%s co %s %s" % (self.cli_tool, self.obs_project_name, self.obs_package_name))
@ -109,7 +109,7 @@ class ObsReleaser(Releaser):
os.chdir(project_checkout) os.chdir(project_checkout)
(status, diff_output) = commands.getstatusoutput("%s diff" % self.cli_tool) (status, diff_output) = getstatusoutput("%s diff" % self.cli_tool)
if diff_output.strip() == "": if diff_output.strip() == "":
print("No changes in main branch, skipping commit.") print("No changes in main branch, skipping commit.")
@ -138,7 +138,7 @@ class ObsReleaser(Releaser):
os.unlink(commit_msg_file) os.unlink(commit_msg_file)
if self.no_build: if self.no_build:
commands.getstatusoutput("%s abortbuild %s %s" % ( getstatusoutput("%s abortbuild %s %s" % (
self.cli_tool, self.obs_project_name, self.obs_package_name)) self.cli_tool, self.obs_project_name, self.obs_package_name))
print("Aborting automatic rebuild because --no-build has been specified.") print("Aborting automatic rebuild because --no-build has been specified.")

View file

@ -17,7 +17,6 @@ Code for submitting builds for release.
import copy import copy
import os import os
import sys import sys
import commands
import tempfile import tempfile
import subprocess import subprocess
import rpm import rpm
@ -26,6 +25,7 @@ from tempfile import mkdtemp
import shutil import shutil
from tito.common import * from tito.common import *
from tito.compat import *
from tito.buildparser import BuildTargetParser from tito.buildparser import BuildTargetParser
from tito.exception import TitoException from tito.exception import TitoException
from tito.config_object import ConfigObject from tito.config_object import ConfigObject
@ -522,7 +522,7 @@ class FedoraGitReleaser(Releaser):
def _git_release(self): def _git_release(self):
commands.getoutput("mkdir -p %s" % self.working_dir) getoutput("mkdir -p %s" % self.working_dir)
os.chdir(self.working_dir) os.chdir(self.working_dir)
run_command("%s clone %s" % (self.cli_tool, self.project_name)) run_command("%s clone %s" % (self.cli_tool, self.project_name))
@ -588,10 +588,10 @@ class FedoraGitReleaser(Releaser):
os.chdir(project_checkout) os.chdir(project_checkout)
# Newer versions of git don't seem to want --cached here? Try both: # Newer versions of git don't seem to want --cached here? Try both:
(status, diff_output) = commands.getstatusoutput("git diff --cached") (status, diff_output) = getstatusoutput("git diff --cached")
if diff_output.strip() == "": if diff_output.strip() == "":
debug("git diff --cached returned nothing, falling back to git diff.") debug("git diff --cached returned nothing, falling back to git diff.")
(status, diff_output) = commands.getstatusoutput("git diff") (status, diff_output) = getstatusoutput("git diff")
if diff_output.strip() == "": if diff_output.strip() == "":
print("No changes in main branch, skipping commit for: %s" % main_branch) print("No changes in main branch, skipping commit for: %s" % main_branch)
@ -680,7 +680,7 @@ class FedoraGitReleaser(Releaser):
return return
print("Submitting build: %s" % build_cmd) print("Submitting build: %s" % build_cmd)
(status, output) = commands.getstatusoutput(build_cmd) (status, output) = getstatusoutput(build_cmd)
if status > 0: if status > 0:
if "already been built" in output: if "already been built" in output:
print("Build has been submitted previously, continuing...") print("Build has been submitted previously, continuing...")
@ -797,7 +797,7 @@ class CvsReleaser(Releaser):
self._verify_cvs_module_not_already_checked_out() self._verify_cvs_module_not_already_checked_out()
print("Building release in CVS...") print("Building release in CVS...")
commands.getoutput("mkdir -p %s" % self.working_dir) getoutput("mkdir -p %s" % self.working_dir)
debug("cvs_branches = %s" % self.cvs_branches) debug("cvs_branches = %s" % self.cvs_branches)
self.cvs_checkout_module() self.cvs_checkout_module()
@ -873,7 +873,7 @@ class CvsReleaser(Releaser):
# For entirely new files we need to cvs add: # For entirely new files we need to cvs add:
for add_file in new: for add_file in new:
commands.getstatusoutput("cvs add %s" % add_file) getstatusoutput("cvs add %s" % add_file)
# Cleanup obsolete files: # Cleanup obsolete files:
for cleanup_file in old: for cleanup_file in old:
@ -913,7 +913,7 @@ class CvsReleaser(Releaser):
print("") print("")
os.chdir(self.package_workdir) os.chdir(self.package_workdir)
(status, diff_output) = commands.getstatusoutput("cvs diff -u") (status, diff_output) = getstatusoutput("cvs diff -u")
print(diff_output) print(diff_output)
print("") print("")
@ -978,7 +978,7 @@ class CvsReleaser(Releaser):
branch_dir = os.path.join(self.working_dir, self.project_name, branch_dir = os.path.join(self.working_dir, self.project_name,
branch) branch)
os.chdir(branch_dir) os.chdir(branch_dir)
(status, output) = commands.getstatusoutput(cmd) (status, output) = getstatusoutput(cmd)
print(output) print(output)
if status > 1: if status > 1:
self.cleanup() self.cleanup()

View file

@ -17,7 +17,6 @@ Code for tagging Spacewalk/Satellite packages.
import os import os
import re import re
import rpm import rpm
import commands
import StringIO import StringIO
import shutil import shutil
import subprocess import subprocess
@ -34,6 +33,7 @@ from tito.common import (debug, error_out, run_command,
get_script_path, get_spec_version_and_release, replace_version, get_script_path, get_spec_version_and_release, replace_version,
tag_exists_locally, tag_exists_remotely, head_points_to_tag, undo_tag, tag_exists_locally, tag_exists_remotely, head_points_to_tag, undo_tag,
increase_version, reset_release, increase_zstream) increase_version, reset_release, increase_zstream)
from tito.compat import *
from tito.exception import TitoException from tito.exception import TitoException
from tito.config_object import ConfigObject from tito.config_object import ConfigObject
@ -470,7 +470,7 @@ class VersionTagger(ConfigObject):
print(" Push: git push && git push origin %s" % new_tag) print(" Push: git push && git push origin %s" % new_tag)
def _check_tag_does_not_exist(self, new_tag): def _check_tag_does_not_exist(self, new_tag):
status, output = commands.getstatusoutput( status, output = getstatusoutput(
'git tag -l %s|grep ""' % new_tag) 'git tag -l %s|grep ""' % new_tag)
if status == 0: if status == 0:
raise Exception("Tag %s already exists!" % new_tag) raise Exception("Tag %s already exists!" % new_tag)

View file

@ -15,7 +15,6 @@
Functional Tests for the FetchBuilder. Functional Tests for the FetchBuilder.
""" """
import ConfigParser
import glob import glob
import os import os
import shutil import shutil
@ -24,6 +23,7 @@ import tempfile
from os.path import join from os.path import join
from tito.common import run_command from tito.common import run_command
from tito.compat import *
from fixture import TitoGitTestFixture, tito from fixture import TitoGitTestFixture, tito
EXT_SRC_PKG = "extsrc" EXT_SRC_PKG = "extsrc"
@ -44,7 +44,7 @@ class FetchBuilderTests(TitoGitTestFixture):
spec = join(os.path.dirname(__file__), "specs/extsrc.spec") spec = join(os.path.dirname(__file__), "specs/extsrc.spec")
# Setup test config: # Setup test config:
self.config = ConfigParser.RawConfigParser() self.config = RawConfigParser()
self.config.add_section("buildconfig") self.config.add_section("buildconfig")
self.config.set("buildconfig", "builder", self.config.set("buildconfig", "builder",
"tito.builder.FetchBuilder") "tito.builder.FetchBuilder")

View file

@ -15,7 +15,6 @@
Functional Tests for the FetchBuilder. Functional Tests for the FetchBuilder.
""" """
import ConfigParser
import glob import glob
import os import os
import shutil import shutil
@ -25,6 +24,8 @@ from os.path import join
from fixture import TitoGitTestFixture, tito from fixture import TitoGitTestFixture, tito
from tito.compat import *
PKG_NAME = "releaseme" PKG_NAME = "releaseme"
RELEASER_CONF = """ RELEASER_CONF = """
@ -42,7 +43,7 @@ class YumReleaserTests(TitoGitTestFixture):
self.create_project(PKG_NAME) self.create_project(PKG_NAME)
# Setup test config: # Setup test config:
self.config = ConfigParser.RawConfigParser() self.config = RawConfigParser()
self.config.add_section("buildconfig") self.config.add_section("buildconfig")
self.config.set("buildconfig", "builder", self.config.set("buildconfig", "builder",
"tito.builder.Builder") "tito.builder.Builder")

View file

@ -14,6 +14,8 @@
import os import os
import unittest import unittest
from tito.compat import *
UNIT_DIR = os.path.abspath(os.path.dirname(__file__)) UNIT_DIR = os.path.abspath(os.path.dirname(__file__))
REPO_DIR = os.path.join(UNIT_DIR, '..', '..') REPO_DIR = os.path.join(UNIT_DIR, '..', '..')

View file

@ -84,3 +84,8 @@ class UglyHackishTest(TitoUnitTestFixture):
cmd = "find . -type f -regex '.*\.py$' -exec egrep %s {} + | wc -l" % regex cmd = "find . -type f -regex '.*\.py$' -exec egrep %s {} + | wc -l" % regex
result = int(getoutput(cmd)) result = int(getoutput(cmd))
self.assertEqual(result, 0, "Found except clause not supported in Python 3") self.assertEqual(result, 0, "Found except clause not supported in Python 3")
def test_import_commands(self):
cmd = "find . -type f -regex '.*\.py$' -exec egrep '^(import|from) commands\.' {} + | grep -v 'compat\.py' | wc -l"
result = int(getoutput(cmd))
self.assertEqual(result, 0, "Found commands module (not supported in Python 3)")

View file

@ -1,7 +1,7 @@
import unittest import unittest
from tito.buildparser import BuildTargetParser from tito.buildparser import BuildTargetParser
from ConfigParser import ConfigParser from tito.compat import *
from tito.exception import TitoException from tito.exception import TitoException