bh@296: # Copyright (C) 2007, 2008, 2009 by Intevation GmbH
bh@118: # Authors:
bh@118: # Bernhard Herzog <bh@intevation.de>
bh@118: #
bh@118: # This program is free software under the GPL (>=v2)
bh@118: # Read the file COPYING coming with the software for details.
bh@118: 
bh@118: """Tests for treepkg.builder"""
bh@118: 
bh@118: import sys
bh@118: import os
bh@118: import unittest
bh@170: import StringIO
bh@118: 
bh@118: from treepkg.builder import PBuilder
bh@121: from treepkg.run import call
bh@118: 
bh@118: from filesupport import FileTestMixin
bh@118: 
bh@118: # helper program to dump the command line arguments into a file so that
bh@126: # test cases can check them.  Also if the environment variable
bh@126: # TREEPKG_TEST is set, create the file named by it.
bh@118: dump_command_line_py = """
bh@126: import sys, os
bh@118: open(sys.argv[1], 'w').write(repr(sys.argv[2:]))
bh@126: value = os.environ.get('TREEPKG_TEST')
bh@126: if value:
bh@126:     open(value, 'w').close()
bh@118: """
bh@118: 
bh@121: class PBuilderTests(unittest.TestCase, FileTestMixin):
bh@118: 
bh@118:     def setUp(self):
bh@118:         self.dump_command_line = self.create_temp_file("dump_command_line.py",
bh@118:                                                        dump_command_line_py)
bh@118:         self.command_line_file = self.temp_file_name("command_line")
bh@118:         self.root_command = [sys.executable, self.dump_command_line,
bh@118:                              self.command_line_file]
bh@118: 
bh@118:     def check_command_line(self, args):
bh@195:         self.check_file_contents(self.command_line_file, repr(args))
bh@118: 
bh@121: 
bh@121: class TestPBuilder(PBuilderTests):
bh@121: 
bh@170:     def test_init_pbuilder(self):
bricks@344:         """Tests the PBuilder.init_builder method."""
bh@185:         basedir = self.create_temp_dir("pbuilder")
bh@170:         pbuilderrc = os.path.join(basedir, "pbuilderrc")
bh@170:         builder = PBuilder(pbuilderrc, self.root_command)
bh@170:         old_stdout = sys.stdout
bh@170:         sys.stdout = captured_stdout = StringIO.StringIO()
bh@170:         try:
bricks@344:             builder.init_builder(distribution="etch",
bh@170:                                   mirrorsite="http://example.com/debian",
bh@170:                                   extramirrors=None)
bh@170:         finally:
bh@170:             sys.stdout = old_stdout
bh@170: 
bh@170:         # check whether the necessary directories were created
bh@170:         missing = [dirname for dirname in ["base", "build", "result",
bh@170:                                            "aptcache", "extra-pkg"]
bh@170:                    if not os.path.isdir(os.path.join(basedir, dirname))]
bh@170:         if missing:
bricks@344:             self.fail("init_builder did not create these directories: %s"
bh@170:                       % " ".join(missing))
bh@170: 
bh@170:         # check the pbuilderrc.  This test is a little too strict
bh@170:         # because it checks the exact contents of the file.  Instread it
bh@170:         # should normalize the contents in some way and check that.
bh@170:         pbuilderrc_contents = (
bh@170:             "# This file was automatically generated by initpbuilder.py.\n"
bh@170:             "# for the possible settings see \"man pbuilderrc\"\n"
bh@170:             "\n"
bh@170:             "BASETGZ=%(basedir)s/base/base.tgz\n"
bh@170:             "BUILDPLACE=%(basedir)s/build\n"
bh@170:             "USEPROC=yes\n"
bh@170:             "USEDEVPTS=yes\n"
bh@170:             "BUILDRESULT=%(basedir)s/result\n"
bh@170:             "DISTRIBUTION=etch\n"
bh@173:             "APTCACHE=%(basedir)s/aptcache\n"
bh@170:             "APTCACHEHARDLINK=yes\n"
bh@170:             "REMOVEPACKAGES=lilo\n"
bh@170:             "MIRRORSITE=\"http://example.com/debian\"\n"
bh@170:             "OTHERMIRROR=\"deb file://%(basedir)s/extra-pkg ./\"\n"
bh@170:             "BINDMOUNTS=\"%(basedir)s/extra-pkg\"\n"
bh@170:             "PKGNAME_LOGFILE=yes\n" % locals())
bh@195:         self.check_file_contents(pbuilderrc, pbuilderrc_contents)
bh@170: 
bh@170:         # The Packages file is empty for now.
bh@195:         self.check_file_contents(os.path.join(basedir, "extra-pkg", "Packages"),
bh@195:                                  "")
bh@170:         # check the text written to stdout.  This test is a little too
bh@170:         # strict because it checks the exact output.
bh@170:         self.assertEquals(captured_stdout.getvalue(),
bh@170:                           "creating directory: '%(basedir_repr)s/base'\n"
bh@170:                           "creating directory: '%(basedir_repr)s/build'\n"
bh@170:                           "creating directory: '%(basedir_repr)s/result'\n"
bh@170:                           "creating directory: '%(basedir_repr)s/aptcache'\n"
bh@170:                           "creating directory: '%(basedir_repr)s/extra-pkg'\n"
bh@170:                           "creating pbuilderrc: '%(basedir_repr)s/pbuilderrc'\n"
bh@170:                           "turning the extra-pkg dir into a debian archive\n"
bh@170:                           "running pbuilder create\n"
bh@170:                           % dict(basedir_repr=repr(basedir)[1:-1]))
bh@170: 
bh@170:     def test_init_pbuilder_run_twice(self):
bricks@344:         """Tests whether PBuilder.init_builder prints an error when run twice.
bh@170:         """
bh@185:         basedir = self.create_temp_dir("pbuilder")
bh@170: 
bh@170:         # run it once
bh@170:         pbuilderrc = os.path.join(basedir, "pbuilderrc")
bh@170:         builder = PBuilder(pbuilderrc, self.root_command)
bh@170:         old_stdout = sys.stdout
bh@170:         sys.stdout = captured_stdout = StringIO.StringIO()
bh@170:         try:
bricks@344:             builder.init_builder(distribution="etch",
bh@170:                                   mirrorsite="http://example.com/debian",
bh@170:                                   extramirrors=None)
bh@170:         finally:
bh@170:             sys.stdout = old_stdout
bh@170: 
bh@170:         # running it again should not modify anything in the directory
bh@170:         # (which we don't check currently), it should print an error
bh@170:         # message and exit with exit code 1.
bh@170:         old_stderr = sys.stderr
bh@170:         sys.stderr = captured_stderr = StringIO.StringIO()
bh@170:         try:
bh@170:             try:
bricks@344:                 builder.init_builder(distribution="etch",
bh@170:                                       mirrorsite="http://example.com/debian",
bh@170:                                       extramirrors=None)
bh@170:             except SystemExit, exc:
bh@170:                 self.assertEquals(exc.code, 1)
bh@170:         finally:
bh@170:             sys.stderr = old_stderr
bh@170:         self.assertEquals("pbuilderrc %r already exists.\n"
bh@170:                           % os.path.join(basedir, "pbuilderrc"),
bh@170:                           captured_stderr.getvalue())
bh@170: 
bh@178:     def test_update(self):
bh@178:         """Tests the PBuilder.update method."""
bh@178:         builder = PBuilder("my_pbuilderrc", self.root_command)
bh@178:         builder.update()
bh@178:         self.check_command_line(['/usr/sbin/pbuilder', 'update',
bh@178:                                  '--configfile', 'my_pbuilderrc'])
bh@170: 
bh@118:     def test_build(self):
bh@118:         """Tests the PBuilder.build method.
bh@118:         The test checks whether the build method creates the binary_dir
bh@118:         and then checks the arguments with which the root command is
bh@118:         called.  The test is a little too strict because it expects a
bh@118:         specific order of the arguments even though the order of some of
bh@118:         the arguments doesn't matter.
bh@118: 
bh@118:         A more thorough test of the build method is implicity done by
bh@118:         the packager tests.
bh@118:         """
bh@118:         binary_dir_name = self.temp_file_name("binary")
bh@118:         if os.path.exists(binary_dir_name):
bh@118:             os.rmdir(binary_dir_name)
bh@118:         # sanity check: the binary directory must not exist yet.
bh@118:         self.failIf(os.path.exists(binary_dir_name))
bh@118: 
bh@186:         logfilename = self.temp_file_name("the_logfile")
bh@186: 
bh@118:         builder = PBuilder("my_pbuilderrc", self.root_command)
bh@186:         builder.build("my_dsc_file", binary_dir_name, logfilename)
bh@118:         self.check_command_line(['/usr/sbin/pbuilder', 'build',
bh@118:                                  '--configfile', 'my_pbuilderrc',
bh@186:                                  '--logfile', logfilename,
bh@118:                                  '--buildresult', binary_dir_name,
bh@171:                                  '--debbuildopts', '-b',
bh@118:                                  'my_dsc_file'])
bh@118:         self.failUnless(os.path.isdir(binary_dir_name))
bh@119: 
bh@122:     def test_build_with_bindmounts(self):
bh@122:         """Tests the PBuilder.build method with the bindmounts parameter"""
bh@122:         binary_dir_name = self.temp_file_name("binary")
bh@122:         if os.path.exists(binary_dir_name):
bh@122:             os.rmdir(binary_dir_name)
bh@122:         # sanity check: the binary directory must not exist yet.
bh@122:         self.failIf(os.path.exists(binary_dir_name))
bh@122: 
bh@186:         logfilename = self.temp_file_name("the_logfile")
bh@186: 
bh@122:         builder = PBuilder("my_pbuilderrc", self.root_command)
bh@186:         builder.build("my_dsc_file", binary_dir_name, logfilename,
bh@122:                       bindmounts=["/home/builder/tracks",
bh@122:                                   "/home/builder/pbuilder"])
bh@122:         self.check_command_line(['/usr/sbin/pbuilder', 'build',
bh@122:                                  '--configfile', 'my_pbuilderrc',
bh@209:                                  '--logfile', logfilename,
bh@209:                                  '--buildresult', binary_dir_name,
bh@122:                                  '--bindmounts', "/home/builder/tracks",
bh@122:                                  '--bindmounts', "/home/builder/pbuilder",
bh@171:                                  '--debbuildopts', '-b',
bh@122:                                  'my_dsc_file'])
bh@122:         self.failUnless(os.path.isdir(binary_dir_name))
bh@122: 
bh@122:     def test_build_with_extra_packages(self):
bh@122:         """Tests the PBuilder.build method with the extra_packages parameter"""
bh@122:         binary_dir_name = self.temp_file_name("binary")
bh@122:         if os.path.exists(binary_dir_name):
bh@122:             os.rmdir(binary_dir_name)
bh@122:         # sanity check: the binary directory must not exist yet.
bh@122:         self.failIf(os.path.exists(binary_dir_name))
bh@122: 
bh@186:         logfilename = self.temp_file_name("the_logfile")
bh@186: 
bh@122:         builder = PBuilder("my_pbuilderrc", self.root_command)
bh@186:         builder.build("my_dsc_file", binary_dir_name, logfilename,
bh@122:                       extra_packages=["subversion", "texinfo"])
bh@122:         self.check_command_line(['/usr/sbin/pbuilder', 'build',
bh@122:                                  '--configfile', 'my_pbuilderrc',
bh@209:                                  '--logfile', logfilename,
bh@209:                                  '--buildresult', binary_dir_name,
bh@122:                                  '--extrapackages', "subversion",
bh@122:                                  '--extrapackages', "texinfo",
bh@171:                                  '--debbuildopts', '-b',
bh@122:                                  'my_dsc_file'])
bh@122:         self.failUnless(os.path.isdir(binary_dir_name))
bh@122: 
bh@126:     def test_build_with_extra_env(self):
bh@126:         """Tests the PBuilder.build method with the extra_env parameter"""
bh@126:         binary_dir_name = self.temp_file_name("binary")
bh@126:         if os.path.exists(binary_dir_name):
bh@126:             os.rmdir(binary_dir_name)
bh@185:         env_test_file = self.temp_file_name("envtest")
bh@126:         # sanity check: the binary directory must not exist yet.
bh@126:         self.failIf(os.path.exists(binary_dir_name))
bh@126:         # sanity check: the environment variable TREEPKG_TEST must not
bh@126:         # be set yet
bh@126:         self.failIf("TREEPKG_TEST" in os.environ)
bh@126: 
bh@186:         logfilename = self.temp_file_name("the_logfile")
bh@186: 
bh@126:         builder = PBuilder("my_pbuilderrc", self.root_command)
bh@186:         builder.build("my_dsc_file", binary_dir_name, logfilename,
bh@126:                       extra_env=dict(TREEPKG_TEST=env_test_file))
bh@126:         self.check_command_line(['/usr/sbin/pbuilder', 'build',
bh@126:                                  '--configfile', 'my_pbuilderrc',
bh@186:                                  '--logfile', logfilename,
bh@126:                                  '--buildresult', binary_dir_name,
bh@171:                                  '--debbuildopts', '-b',
bh@126:                                  'my_dsc_file'])
bh@126:         self.failUnless(os.path.isdir(binary_dir_name))
bh@126:         self.failUnless(os.path.exists(env_test_file))
bh@126: 
bh@119:     def test_run_script(self):
bh@186:         logfilename = self.temp_file_name("the_logfile")
bh@119:         builder = PBuilder("my_pbuilderrc", self.root_command)
bh@186:         builder.run_script(["my_script"], logfile=logfilename)
bh@119:         self.check_command_line(['/usr/sbin/pbuilder', 'execute',
bh@119:                                  '--configfile', 'my_pbuilderrc',
bh@186:                                  '--logfile', logfilename, '--',
bh@119:                                  'my_script'])
bh@119: 
bh@181:     def test_run_script_with_arguments(self):
bh@186:         logfilename = self.temp_file_name("the_logfile")
bh@181:         builder = PBuilder("my_pbuilderrc", self.root_command)
bh@186:         builder.run_script(["my_script", "--verbose"], logfile=logfilename)
bh@181:         self.check_command_line(['/usr/sbin/pbuilder', 'execute',
bh@181:                                  '--configfile', 'my_pbuilderrc',
bh@186:                                  '--logfile', logfilename, '--',
bh@181:                                  'my_script', '--verbose'])
bh@181: 
bh@119:     def test_run_script_with_bindmounts(self):
bh@186:         logfilename = self.temp_file_name("the_logfile")
bh@119:         builder = PBuilder("my_pbuilderrc", self.root_command)
bh@186:         builder.run_script(["my_script"], logfile=logfilename,
bh@119:                            bindmounts=("/home/builder/foo",
bh@119:                                        "/home/builder/treepkg"))
bh@119:         self.check_command_line(['/usr/sbin/pbuilder', 'execute',
bh@119:                                  '--configfile', 'my_pbuilderrc',
bh@186:                                  '--logfile', logfilename,
bh@119:                                  '--bindmounts', '/home/builder/foo',
bh@119:                                  '--bindmounts', '/home/builder/treepkg',
bh@183:                                  '--', 'my_script'])
bh@183: 
bh@183:     def test_run_script_save_after_exec(self):
bh@186:         logfilename = self.temp_file_name("the_logfile")
bh@183:         builder = PBuilder("my_pbuilderrc", self.root_command)
bh@186:         builder.run_script(["my_script", "--verbose"], logfilename,
bh@183:                            save_after_exec=True)
bh@183:         self.check_command_line(['/usr/sbin/pbuilder', 'execute',
bh@183:                                  '--configfile', 'my_pbuilderrc',
bh@186:                                  '--logfile', logfilename,
bh@183:                                  '--save-after-exec', '--',
bh@183:                                  'my_script', '--verbose'])
bh@121: 
bh@121: 
bh@121: class TestPBuilderWithBinaryPackage(PBuilderTests):
bh@121: 
bh@121:     minimal_package = [
bh@121:         ("debian",
bh@121:          [("control", """\
bh@121: Source: minimal
bh@121: Section: utils
bh@121: Priority: optional
bh@121: Maintainer: Bernhard Herzog <bh@intevation.de>
bh@121: Standards-Version: 3.7.2.2
bh@121: Build-Depends: debhelper
bh@121: 
bh@121: Package: minimal
bh@121: Architecture: any
bh@121: Depends: ${shlibs:Depends}
bh@121: Description: Minimal package for test purposes
bh@121: """),
bh@121:           ("rules", 0755, """\
bh@121: #!/usr/bin/make -f
bh@121: 
bh@121: build: build-stamp
bh@121: build-stamp:
bh@121: 	dh_testdir
bh@121: 	touch build-stamp
bh@121: 
bh@121: clean:
bh@121: 	dh_testdir
bh@121: 	dh_testroot
bh@121: 	rm -f build-stamp
bh@121: 	dh_clean
bh@121: 
bh@121: # Build architecture-dependent files here.
bh@121: binary-arch:
bh@121: 	dh_testdir
bh@121: 	dh_testroot
bh@121: 	dh_installdocs README
bh@121: 	dh_installchangelogs
bh@121: 	dh_fixperms
bh@121: 	dh_installdeb
bh@121: 	dh_gencontrol
bh@121: 	dh_md5sums
bh@121: 	dh_builddeb
bh@121: 
bh@121: binary: binary-arch
bh@121: .PHONY: build build-stamp clean binary-arch binary
bh@121: """),
bh@121:           ("changelog", """\
bh@121: minimal (1.0-1) unstable; urgency=low
bh@121: 
bh@121:   * Newly packaged
bh@121: 
bh@121:  -- Bernhard Herzog <bh@intevation.de>  Wed, 21 May 2008 16:10:29 +0200
bh@121: """)]),
bh@121:         ("README", """\
bh@121: This is a minimal debian package for test purposes
bh@121: """),
bh@121:         ]
bh@121: 
bh@121:     pbuilder_files = [("pbuilderrc", ""),
bh@121:                       ("extra-pkg", [])]
bh@121: 
bh@121:     def setUp(self):
bh@121:         PBuilderTests.setUp(self)
bh@185:         self.temp_base_dir = self.create_temp_dir("pbuilder")
bh@121:         self.minimal_packge_dir = os.path.join(self.temp_base_dir,
bh@121:                                                "minimal-1.0")
bh@121:         self.create_files(self.minimal_packge_dir, self.minimal_package)
bh@121:         call(["dpkg-buildpackage", "-rfakeroot", "-b", "-uc"],
bh@121:              cwd=self.minimal_packge_dir, suppress_output=True)
bh@121:         self.minimal_package_deb = os.path.join(self.temp_base_dir,
bh@121:                                                 "minimal_1.0-1_i386.deb")
bh@121:         self.pbuilder_basedir = os.path.join(self.temp_base_dir, "pbuilder")
bh@121:         self.create_files(self.pbuilder_basedir, self.pbuilder_files)
bh@121:         self.extra_pkg_dir = os.path.join(self.pbuilder_basedir, "extra-pkg")
bh@121:         self.pbuilderrc = os.path.join(self.pbuilder_basedir, "pbuilderrc")
bh@121: 
bh@121:     def test_add_binaries_to_extra_pkg(self):
bh@121:         """Tests the PBuilder.add_binaries_to_extra_pkg method"""
bh@121:         builder = PBuilder(self.pbuilderrc, self.root_command)
bh@121:         # sanity check: the extra-pkg directory should be empty now
bh@121:         self.assertEquals(os.listdir(self.extra_pkg_dir), [])
bh@121: 
bh@121:         builder.add_binaries_to_extra_pkg([self.minimal_package_deb])
bh@121: 
bh@121:         self.assertEquals(sorted(os.listdir(self.extra_pkg_dir)),
bh@296:                           ["Packages", "Release", "auto"])
bh@296:         self.assertEquals(sorted(os.listdir(os.path.join(self.extra_pkg_dir,
bh@296:                                                          "auto"))),
bh@296:                           ["minimal_1.0-1_i386.deb"])
bh@121:         self.check_command_line(['/usr/sbin/pbuilder', 'update',
bh@121:                                  '--configfile', self.pbuilderrc])