]> git.donarmstrong.com Git - debhelper.git/commitdiff
Add a test suite for build systems.
authorModestas Vainius <modestas@vainius.eu>
Thu, 11 Jun 2009 00:01:58 +0000 (03:01 +0300)
committerModestas Vainius <modestas@vainius.eu>
Thu, 11 Jun 2009 11:10:08 +0000 (14:10 +0300)
* Tests for core Buildsystem API (mostly path API).
* Tests for check_auto_configure() for each buildsystem.
* Build system autoselection tests under "typical" conditions for each buildsystem.
* DH_AUTO_OPTIONS and command line argument parsing tests.
* Real dh_auto_* tests for autoconf/makefile build systems with emulated.
  autoconf behaviour under in both in source and out of source tree scenarios.

Signed-off-by: Modestas Vainius <modestas@vainius.eu>
t/buildsystems/autoconf/configure [new file with mode: 0755]
t/buildsystems/buildsystem_tests [new file with mode: 0755]
t/buildsystems/debian/changelog [new file with mode: 0644]
t/buildsystems/debian/compat [new file with mode: 0644]
t/buildsystems/debian/control [new file with mode: 0644]

diff --git a/t/buildsystems/autoconf/configure b/t/buildsystems/autoconf/configure
new file mode 100755 (executable)
index 0000000..adea14e
--- /dev/null
@@ -0,0 +1,74 @@
+#!/usr/bin/perl
+
+# Emulate autoconf behaviour and do some checks
+
+use strict;
+use warnings;
+
+my @OPTIONS=qw(
+   ^--build=.*$
+   ^--prefix=/usr$
+   ^--includedir=\$\{prefix\}/include$
+   ^--mandir=\$\{prefix\}/share/man$
+   ^--infodir=\$\{prefix\}/share/info$
+   ^--sysconfdir=/etc$
+   ^--localstatedir=/var$
+   ^--libexecdir=\$\{prefix\}/lib/.*$
+   ^--disable-maintainer-mode$
+   ^--disable-dependency-tracking$
+);
+
+# Verify if all command line arguments were passed
+my @options = map { { regex => qr/$_/,
+                      str => $_,
+                      found => 0 } } @OPTIONS;
+my @extra_args;
+ARGV_LOOP: foreach my $arg (@ARGV) {
+       foreach my $opt (@options) {
+               if ($arg =~ $opt->{regex}) {
+                       $opt->{found} = 1;
+                       next ARGV_LOOP;
+               }
+       }
+       # Extra / unrecognized argument
+       push @extra_args, $arg;
+}
+
+my @notfound = grep { ! $_->{found} and $_ } @options;
+if (@notfound) {
+       print STDERR "Error: the following default options were NOT passed\n";
+       print STDERR "  ", $_->{str}, "\n" foreach (@notfound);
+       exit 1;
+}
+
+# Create a simple Makefile
+open(MAKEFILE, ">", "Makefile");
+print MAKEFILE <<EOF;
+CONFIGURE := $0
+all: stamp_configure \$(CONFIGURE)
+       \@echo Package built > stamp_build
+
+# Tests if dh_auto_test executes 'check' target if 'test' does not exist
+check: \$(CONFIGURE) stamp_build
+       \@echo Tested > stamp_test
+
+install: stamp_build
+       \@echo DESTDIR=\$(DESTDIR) > stamp_install
+
+# Tests whether dh_auto_clean executes distclean but does not touch
+# this target
+clean:
+       echo "This should not have been executed" >&2 && exit 1
+
+distclean:
+       \@rm -f stamp_* Makefile
+
+.PHONY: all check install clean distclean
+EOF
+close MAKEFILE;
+
+open(STAMP, ">", "stamp_configure");
+print STAMP $_, "\n" foreach (@extra_args);
+close STAMP;
+
+exit 0;
diff --git a/t/buildsystems/buildsystem_tests b/t/buildsystems/buildsystem_tests
new file mode 100755 (executable)
index 0000000..a2b451a
--- /dev/null
@@ -0,0 +1,430 @@
+#!/usr/bin/perl
+
+use Test::More tests => 224;
+
+use strict;
+use warnings;
+use IPC::Open2;
+use Cwd ();
+use File::Temp qw(tempfile tempdir);
+use File::Basename ();
+
+# Let the tests to be run from anywhere but currect directory
+# is expected to be the one where this test lives in.
+chdir File::Basename::dirname($0) or die "Unable to chdir to ".File::Basename::dirname($0);
+
+use_ok( 'Debian::Debhelper::Dh_Lib' );
+use_ok( 'Debian::Debhelper::Buildsystem' );
+use_ok( 'Debian::Debhelper::Dh_Buildsystems' );
+
+my $TOPDIR = "../..";
+my @STEPS = qw(configure build test install clean);
+my @BUILDSYSTEMS = qw(autoconf perl_makemaker makefile python_distutils perl_build cmake);
+my $BS_CLASS = 'Debian::Debhelper::Buildsystem';
+
+my ($bs, @bs, %bs);
+my ($tmp, @tmp, %tmp);
+my ($tmpdir, $builddir, $default_builddir);
+
+### Common subs ####
+sub touch {
+       my $file=shift;
+       my $chmod=shift;
+       open FILE, ">", $file and close FILE or die "Unable to touch $file";
+       chmod $chmod, $file if defined $chmod;
+}
+
+sub cleandir {
+       my $dir=shift;
+       system ("find", $dir, "-type", "f", "-delete");
+}
+sub readlines {
+       my $h=shift;
+       my @lines = <$h>;
+       close $h;
+       chop @lines;
+       return \@lines;
+}
+
+sub process_stdout {
+       my ($cmdline, $stdin) = @_;
+       my ($reader, $writer);
+
+       open2($reader, $writer, $cmdline) or die "Unable to exec $cmdline";
+       print $writer $stdin if $stdin;
+       close $writer;
+       return readlines($reader);
+}
+
+### Test Buildsystem class API methods
+is( $BS_CLASS->_canonpath("path/to/the/./nowhere/../../somewhere"),
+    "path/to/somewhere", "_canonpath no1" );
+is( $BS_CLASS->_canonpath("path/to/../forward/../../somewhere"),
+    "somewhere","_canonpath no2" );
+is( $BS_CLASS->_canonpath("path/to/../../../somewhere"),
+    "../somewhere","_canonpath no3" );
+is( $BS_CLASS->_rel2rel("path/my/file", "path/my"),
+    "file", "_rel2rel no1" );
+is( $BS_CLASS->_rel2rel("path/dir/file", "path/my"),
+    "../dir/file", "_rel2rel no2" );
+is( $BS_CLASS->_rel2rel("file", "/root/path/my", "/root"),
+    "../../file", "_rel2rel no3" );
+
+### Test Buildsystem class path API methods under different configurations
+sub test_buildsystem_paths_api {
+       my ($bs, $config, $expected)=@_;
+
+       my $api_is = sub {
+               my ($got, $name)=@_;
+               is( $got, $expected->{$name}, "paths API ($config): $name")
+       };
+
+       &$api_is( $bs->get_sourcedir(), 'get_sourcedir()' );
+       &$api_is( $bs->get_sourcepath("a/b"), 'get_sourcepath(a/b)' );
+       &$api_is( $bs->get_builddir(), 'get_builddir()' );
+       &$api_is( $bs->get_buildpath(), 'get_buildpath()' );
+       &$api_is( $bs->get_buildpath("a/b"), 'get_buildpath(a/b)' );
+       &$api_is( $bs->get_source_rel2builddir(), 'get_source_rel2builddir()' );
+       &$api_is( $bs->get_source_rel2builddir("a/b"), 'get_source_rel2builddir(a/b)' );
+       &$api_is( $bs->get_build_rel2sourcedir(), 'get_build_rel2sourcedir()' );
+       &$api_is( $bs->get_build_rel2sourcedir("a/b"), 'get_build_rel2sourcedir(a/b)' );
+}
+
+# Defaults
+$bs = $BS_CLASS->new();
+$default_builddir = $bs->DEFAULT_BUILD_DIRECTORY();
+%tmp = (
+       "get_sourcedir()" => ".",
+       "get_sourcepath(a/b)" => "./a/b",
+       "get_builddir()" => undef,
+       "get_buildpath()" => ".",
+       "get_buildpath(a/b)" =>  "./a/b",
+       "get_source_rel2builddir()" => ".",
+       "get_source_rel2builddir(a/b)" => "./a/b",
+       "get_build_rel2sourcedir()" => ".",
+       "get_build_rel2sourcedir(a/b)" => "./a/b",
+);
+test_buildsystem_paths_api($bs, "no builddir, no sourcedir", \%tmp);
+
+# builddir=bld/dir
+$bs = $BS_CLASS->new(builddir => "bld/dir");
+%tmp = (
+       "get_sourcedir()" => ".",
+       "get_sourcepath(a/b)" => "./a/b",
+       "get_builddir()" => "bld/dir",
+       "get_buildpath()" => "bld/dir",
+       "get_buildpath(a/b)" =>  "bld/dir/a/b",
+       "get_source_rel2builddir()" => "../..",
+       "get_source_rel2builddir(a/b)" => "../../a/b",
+       "get_build_rel2sourcedir()" => "bld/dir",
+       "get_build_rel2sourcedir(a/b)" => "bld/dir/a/b",
+);
+test_buildsystem_paths_api($bs, "builddir=bld/dir, no sourcedir", \%tmp);
+
+# Default builddir, sourcedir=autoconf
+$bs = $BS_CLASS->new(builddir => undef, sourcedir => "autoconf");
+%tmp = (
+       "get_sourcedir()" => "autoconf",
+       "get_sourcepath(a/b)" => "autoconf/a/b",
+       "get_builddir()" => "autoconf/$default_builddir",
+       "get_buildpath()" => "autoconf/$default_builddir",
+       "get_buildpath(a/b)" =>  "autoconf/$default_builddir/a/b",
+       "get_source_rel2builddir()" => "..",
+       "get_source_rel2builddir(a/b)" => "../a/b",
+       "get_build_rel2sourcedir()" => "$default_builddir",
+       "get_build_rel2sourcedir(a/b)" => "$default_builddir/a/b",
+);
+test_buildsystem_paths_api($bs, "default builddir, sourcedir=autoconf", \%tmp);
+
+# Enforced out of source tree building
+# sourcedir=builddir=autoconf hence default builddir is implied
+$bs = $BS_CLASS->new(builddir => "./autoconf", sourcedir => "autoconf/");
+$bs->enforce_out_of_source_building();
+test_buildsystem_paths_api($bs, "out of source enforced, sourcedir=autoconf/", \%tmp);
+
+# sourcedir=autoconf (builddir should be dropped)
+$bs = $BS_CLASS->new(builddir => ".", sourcedir => "autoconf");
+%tmp = (
+       "get_sourcedir()" => "autoconf",
+       "get_sourcepath(a/b)" => "autoconf/a/b",
+       "get_builddir()" => undef,
+       "get_buildpath()" => "autoconf",
+       "get_buildpath(a/b)" =>  "autoconf/a/b",
+       "get_source_rel2builddir()" => ".",
+       "get_source_rel2builddir(a/b)" => "./a/b",
+       "get_build_rel2sourcedir()" => ".",
+       "get_build_rel2sourcedir(a/b)" => "./a/b",
+);
+test_buildsystem_paths_api($bs, "no builddir, sourcedir=autoconf", \%tmp);
+
+# builddir=bld/dir, sourcedir=autoconf. Should be the same as sourcedir=autoconf.
+$bs = $BS_CLASS->new(builddir => "bld/dir", sourcedir => "autoconf");
+$bs->enforce_in_source_building();
+test_buildsystem_paths_api($bs, "in source enforced, sourcedir=autoconf", \%tmp);
+
+# builddir=../bld/dir (relative to the sourcedir)
+$bs = $BS_CLASS->new(builddir => "../bld/dir/", sourcedir => "autoconf");
+%tmp = (
+       "get_sourcedir()" => "autoconf",
+       "get_sourcepath(a/b)" => "autoconf/a/b",
+       "get_builddir()" => "bld/dir",
+       "get_buildpath()" => "bld/dir",
+       "get_buildpath(a/b)" =>  "bld/dir/a/b",
+       "get_source_rel2builddir()" => "../../autoconf",
+       "get_source_rel2builddir(a/b)" => "../../autoconf/a/b",
+       "get_build_rel2sourcedir()" => "../bld/dir",
+       "get_build_rel2sourcedir(a/b)" => "../bld/dir/a/b",
+);
+test_buildsystem_paths_api($bs, "builddir=../bld/dir, sourcedir=autoconf", \%tmp);
+
+# Builddir relative to the pwd (same path as above).
+$bs = $BS_CLASS->new(builddir => "./bld/dir", sourcedir => "autoconf");
+test_buildsystem_paths_api($bs, "builddir=./bld/dir, sourcedir=autoconf", \%tmp);
+
+### Test if all buildsystems can be loaded
+@bs = load_all_buildsystems([ $INC[0] ]);
+@tmp = map { $_->NAME() } @bs;
+is_deeply( \@tmp, \@BUILDSYSTEMS, "load_all_buildsystems() loads all built-in buildsystems" );
+
+### Test check_auto_buildable() of each buildsystem
+sub test_check_auto_buildable {
+       my $bs=shift;
+       my $config=shift;
+       my $expected=shift;
+       my @steps=@_ || @STEPS;
+
+       if (! ref $expected) {
+               my %all_steps;
+               $all_steps{$_} = $expected foreach (@steps);
+               $expected = \%all_steps;
+       }
+       for my $step (@steps) {
+               my $e = 0;
+               if (exists $expected->{$step}) {
+                       $e = $expected->{$step};
+               } elsif (exists $expected->{default}) {
+                       $e = $expected->{default};
+               }
+               if ($e) {
+                       ok( $bs->check_auto_buildable($step),
+                           $bs->NAME() . "($config): check_auto_buildable($step)" );
+               }
+               else {
+                       ok( ! $bs->check_auto_buildable($step),
+                           $bs->NAME() . "($config): ! check_auto_buildable($step)" );
+               }
+       }
+}
+
+$tmpdir = tempdir("tmp.XXXXXX");
+$builddir = "$tmpdir/builddir";
+mkdir $builddir;
+%tmp = (
+       builddir => 'builddir',
+       sourcedir => $tmpdir
+);
+
+$bs{autoconf} = load_buildsystem("autoconf", undef, %tmp);
+$bs{cmake} = load_buildsystem("cmake", undef, %tmp);
+$bs{perl_mm} = load_buildsystem("perl_makemaker", undef, %tmp);
+$bs = load_buildsystem("makefile", undef, %tmp);
+
+test_check_auto_buildable($bs{autoconf}, "no configure", 0);
+test_check_auto_buildable($bs{cmake}, "no CMakeLists.txt", 0);
+test_check_auto_buildable($bs{perl_mm}, "no Makefile.PL", 0);
+test_check_auto_buildable($bs, "no Makefile", 0);
+
+touch "$tmpdir/configure", 0755;
+test_check_auto_buildable($bs{autoconf}, "configure", { configure => 1 });
+
+touch "$tmpdir/CMakeLists.txt";
+test_check_auto_buildable($bs{cmake}, "CMakeLists.txt", { configure => 1 });
+
+touch "$tmpdir/Makefile.PL";
+test_check_auto_buildable($bs{perl_mm}, "Makefile.PL",
+    { configure => 1, install => 1 });
+
+# With Makefile
+touch "$builddir/Makefile";
+test_check_auto_buildable($bs, "Makefile", { configure => 0, default => 1 });
+test_check_auto_buildable($bs{autoconf}, "configure+Makefile", { configure => 1 });
+test_check_auto_buildable($bs{cmake}, "CMakeLists.txt+Makefile", 1);
+
+# Makefile.PL forces in-source
+#(see note in check_auto_buildable() why always 1 here)
+unlink "$builddir/Makefile";
+touch "$tmpdir/Makefile";
+test_check_auto_buildable($bs{perl_mm}, "Makefile.PL+Makefile", 1);
+
+# Perl Build.PL - handles always
+$bs = load_buildsystem("perl_build", undef, %tmp);
+test_check_auto_buildable($bs, "no Build.PL", 0);
+touch "$tmpdir/Build.PL";
+test_check_auto_buildable($bs, "Build.PL", { configure => 1 });
+touch "$tmpdir/Build"; # forced in source
+test_check_auto_buildable($bs, "Build.PL+Build", 1);
+
+# Python Distutils
+$bs = load_buildsystem("python_distutils", undef, %tmp);
+test_check_auto_buildable($bs, "no setup.py", 0);
+touch "$tmpdir/setup.py";
+test_check_auto_buildable($bs, "setup.py", 1);
+
+cleandir($tmpdir);
+
+### Now test if it can autoselect a proper buildsystem for a typical package
+sub test_autoselection {
+       my $system=shift;
+       my $expected=shift;
+       for my $step (@STEPS) {
+               my $bs = load_buildsystem(undef, $step, @_);
+               my $e = $expected;
+               $e = $expected->{$step} if ref $expected;
+               if (defined $bs) {
+                       is( $bs->NAME(), $e, "autoselection($system): $step=".((defined $e)?$e:'undef') );
+               }
+               else {
+                       is ( undef, $e, "autoselection($system): $step=".((defined $e)?$e:'undef') );
+               }
+       }
+}
+
+# Autoconf
+touch "$tmpdir/configure", 0755;
+touch "$builddir/Makefile";
+test_autoselection("autoconf",
+    { configure => "autoconf", build => "makefile",
+      test => "makefile", install => "makefile", clean => "makefile" }, %tmp);
+cleandir $tmpdir;
+
+# Perl Makemaker (build, test, clean fail with builddir set [not supported])
+touch "$tmpdir/Makefile.PL";
+touch "$tmpdir/Makefile";
+test_autoselection("perl_makemaker", "perl_makemaker", %tmp);
+cleandir $tmpdir;
+
+# Makefile
+touch "$builddir/Makefile";
+test_autoselection("makefile", { build => "makefile", test => "makefile",
+               install => "makefile", clean => "makefile" }, %tmp);
+cleandir $tmpdir;
+
+# Python Distutils
+touch "$tmpdir/setup.py";
+test_autoselection("python_distutils", "python_distutils", %tmp);
+cleandir $tmpdir;
+
+# Perl Build
+touch "$tmpdir/Build.PL";
+touch "$tmpdir/Build";
+test_autoselection("perl_build", "perl_build", %tmp);
+cleandir $tmpdir;
+
+# CMake
+touch "$tmpdir/CMakeLists.txt";
+touch "$builddir/Makefile";
+test_autoselection("cmake",
+    { configure => "cmake", build => "makefile",
+      test => "makefile", install => "makefile", clean => "makefile" }, %tmp);
+cleandir $tmpdir;
+
+### Test buildsystems_init() and commandline/env argument handling
+sub get_load_bs_source {
+       my ($system, $step)=@_;
+       $step = (defined $step) ? "'$step'" : 'undef';
+       $system = (defined $system) ? "'$system'" : 'undef';
+
+return <<EOF;
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Buildsystems;
+
+buildsystems_init();
+my \$bs = load_buildsystem($system, $step);
+if (defined \$bs) {
+       print 'NAME=', \$bs->NAME(), "\\n";
+       print \$_, "=", (defined \$bs->{\$_}) ? \$bs->{\$_} : 'undef', "\\n"
+           foreach (sort keys \%\$bs);
+}
+EOF
+}
+
+is_deeply( process_stdout("DH_AUTO_OPTIONS='--builddirectory=bld\\ dir --sourcedirectory autoconf' $^X -- -",
+                          get_load_bs_source(undef, "configure")),
+    [ 'NAME=autoconf', 'builddir=autoconf/bld dir', 'makecmd=make', 'sourcedir=autoconf' ],
+    "dh_auto_options w/space, autoconf autoselection and sourcedir/builddir" );
+
+is_deeply( process_stdout("$^X -- - -cautoconf -d autoconf", get_load_bs_source("autoconf", "build")),
+    [ 'NAME=autoconf', 'builddir=undef', 'makecmd=make', 'sourcedir=autoconf' ],
+    "forced autoconf and sourcedir" );
+
+is_deeply( process_stdout("$^X -- - -b -cautoconf", get_load_bs_source("autoconf", "build")),
+    [ 'NAME=autoconf', "builddir=$default_builddir", 'makecmd=make', 'sourcedir=.' ],
+    "forced autoconf and default build directory" );
+
+# Build the autoconf test package
+sub dh_auto_do_autoconf {
+       my $sourcedir=shift;
+       my $builddir=shift;
+       my %args=@_;
+
+       my (@lines, @extra_args);
+       my $buildpath = $sourcedir;
+       my @dh_auto_args = ("-d", $sourcedir);
+       my $dh_auto_str = "-d $sourcedir";
+       if ($builddir) {
+               push @dh_auto_args, "-b", $builddir;
+               $dh_auto_str .= " -b $builddir";
+               $buildpath .= "/$builddir";
+       }
+
+       my $do_dh_auto = sub {
+               my $step=shift;
+               my @extra_args;
+               my $extra_str = "";
+               if (exists $args{"${step}_args"}) {
+                       push @extra_args, @{$args{"${step}_args"}};
+                       $extra_str .= " $_" foreach (@extra_args);
+               }
+               is ( system("$TOPDIR/dh_auto_$step", @dh_auto_args, "--", @extra_args), 0,
+                        "dh_auto_$step $dh_auto_str$extra_str" );
+               return @extra_args;
+       };
+       
+       @extra_args = &$do_dh_auto('configure');
+       ok ( -f "$buildpath/Makefile", "$buildpath/Makefile exists" );
+       @lines=();
+       if (ok( open(FILE, "$buildpath/stamp_configure"), "$buildpath/stamp_configure exists") ) {
+               @lines = @{readlines(\*FILE)};
+       }
+       is_deeply( \@lines, \@extra_args, "$buildpath/stamp_configure contains extra args" );
+
+       &$do_dh_auto('build');
+       ok ( -f "$buildpath/stamp_build", "$buildpath/stamp_build exists" );
+       &$do_dh_auto('test');
+       ok ( -f "$buildpath/stamp_test", "$buildpath/stamp_test exists" );
+       &$do_dh_auto('install');
+       @lines=();
+       if ( ok(open(FILE, "$buildpath/stamp_install"), "$buildpath/stamp_install exists") ) {
+               @lines = @{readlines(\*FILE)};
+       } 
+       is_deeply( \@lines, [ "DESTDIR=".Cwd::getcwd()."/debian/testpackage" ],
+           "$buildpath/stamp_install contains DESTDIR" );
+       &$do_dh_auto('clean');
+       if ($builddir) {
+               ok ( ! -e "$buildpath", "builddir $buildpath was removed" );
+       }
+       else {
+               ok ( ! -e "$buildpath/Makefile" && ! -e "$buildpath/stamp_configure", "Makefile and stamps gone" );
+       }
+       ok ( -x "$sourcedir/configure", "configure script renamins after clean" );
+}
+
+dh_auto_do_autoconf('autoconf');
+dh_auto_do_autoconf('autoconf', 'bld/dir', configure_args => [ "--extra-autoconf-configure-arg" ]);
+ok ( ! -e 'autoconf/bld', "autoconf/bld got deleted too" );
+
+END {
+       system("rm", "-rf", $tmpdir);
+       system("$TOPDIR/dh_clean");
+}
diff --git a/t/buildsystems/debian/changelog b/t/buildsystems/debian/changelog
new file mode 100644 (file)
index 0000000..f902d89
--- /dev/null
@@ -0,0 +1,5 @@
+testpackage (1.0-1) unstable; urgency=low
+
+  * Initial release. (Closes: #XXXXXX)
+
+ -- Test <testing@nowhere>  Tue, 09 Jun 2009 15:35:32 +0300
diff --git a/t/buildsystems/debian/compat b/t/buildsystems/debian/compat
new file mode 100644 (file)
index 0000000..7f8f011
--- /dev/null
@@ -0,0 +1 @@
+7
diff --git a/t/buildsystems/debian/control b/t/buildsystems/debian/control
new file mode 100644 (file)
index 0000000..7edd806
--- /dev/null
@@ -0,0 +1,10 @@
+Source: testsrcpackage
+Section: devel
+Priority: optional
+Maintainer: Test <testing@nowhere>
+Standards-Version: 3.8.1
+
+Package: testpackage
+Architecture: all
+Description: short description
+ Long description