diff --git a/Makefile b/Makefile index 885bc44..ba62e1c 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ # -- # http://www.steve.org.uk/ # -# $Id: Makefile,v 1.111 2007-07-31 17:33:27 steve Exp $ +# $Id: Makefile,v 1.112 2007-09-01 19:23:09 steve Exp $ # @@ -211,14 +211,14 @@ release: fixup-perms update-version update-modules clean changelog # Run the test suite. # test: - prove --shuffle tests/ + prove --shuffle t/ # # Run the test suite verbosely. # test-verbose: - prove --shuffle --verbose tests/ + prove --shuffle --verbose t/ @@ -261,7 +261,7 @@ update: # cases to ensure that all required modules are available. # update-modules: - cd tests && make modules + cd t && make modules # diff --git a/t/argument-check.t b/t/argument-check.t new file mode 100755 index 0000000..f2649c6 --- /dev/null +++ b/t/argument-check.t @@ -0,0 +1,141 @@ +#!/usr/bin/perl -w +# +# Test that the arguments in etc/xen-tools.conf match those used in +# xen-create-image. +# +# Steve +# -- +# $Id: argument-check.t,v 1.1 2007-09-01 19:23:10 steve Exp $ +# + +use strict; +use Test::More qw( no_plan ); + +# +# Open and parse the xen-tools.conf configuration file. +# +my %OPTIONS; +%OPTIONS = parseConfigFile( "etc/xen-tools.conf" ); + +# +# Test we got something back. +# +ok( %OPTIONS, "Options successfully parsed" ); + + +# +# Now open and read the file "xen-create-image" +# +my @lines = readFile( "bin/xen-create-image" ); +ok ( @lines, "We read the 'xen-create-image' script" ); + + +# +# For each option we found we want to make sure it is +# contained in the script, via the documentation. +# +foreach my $key ( sort keys %OPTIONS ) +{ + my $found = 0; + + foreach my $line ( @lines ) + { + if ( $line =~ /--$key/ ) + { + $found = 1; + } + } + + next if ( $key =~ /mirror_/i ); + next if ( $key =~ /_options/i ); + + is( $found, 1 , " Found documentation for '$key'" ); +} + + + + +=head2 parseConfigFile + + Parse the 'key=value' configuration file passed to us, and + return a hash of the reults. + +=cut + +sub parseConfigFile +{ + my ($file) = ( @_ ); + + # + # Options we read + # + my %CONFIG; + + open( FILE, "<", $file ) or die "Cannot read file '$file' - $!"; + + my $line = ""; + + while (defined($line = ) ) + { + chomp $line; + if ($line =~ s/\\$//) + { + $line .= ; + redo unless eof(FILE); + } + + # Skip blank lines + next if ( length( $line ) < 1 ); + + # skip false positive + next if ( $line =~ /Otherwise/ ); + + # Find variable settings + if ( $line =~ /([^=]+)=([^\n]+)/ ) + { + my $key = $1; + my $val = $2; + + if ( $key =~ /([ \t#]*)(.*)/ ) + { + $key = $2; + } + + + # Strip leading and trailing whitespace. + $key =~ s/^\s+//; + $key =~ s/\s+$//; + $val =~ s/^\s+//; + $val =~ s/\s+$//; + + next if ( $key =~ /--/ ); + + # Store value. + $CONFIG{ $key } = $val; + } + } + + close( FILE ); + + return( %CONFIG ); +} + + + + +=head2 readFile + + Read a named file and return an array of its contents. + +=cut + +sub readFile +{ + my ($file) = ( @_ ); + + open( FILE, "<", $file ) or die "Cannot read file '$file' - $!"; + my @LINES = ; + close( FILE ); + + return( @LINES ); +} diff --git a/t/getopt.t b/t/getopt.t new file mode 100755 index 0000000..d6be6bb --- /dev/null +++ b/t/getopt.t @@ -0,0 +1,143 @@ +#!/usr/bin/perl -w +# +# Test that every perl script accepts and processes each of the options +# documented in its POD. +# +# Cute test :) +# +# Steve +# -- +# $Id: getopt.t,v 1.1 2007-09-01 19:23:10 steve Exp $ + + +use strict; +use File::Find; +use Test::More qw( no_plan ); + + +# +# Test each file +# +foreach my $file ( sort( glob "./bin/*-*" ) ) +{ + testFile( $file ); +} + + +# +# Check that the given file implements all the option processing it +# is supposed to. +# +# +sub testFile +{ + my ($file ) = (@_); + is( -e $file, 1, "File exists: $file" ); + is( -x $file, 1, "File is executable" ); + + # + # Run the file with "--help" and capture the output. + # + my $output = `$file --help`; + + # + # Parse out the options we accept + # + my @documented = (); + + foreach my $line ( split( /\n/, $output ) ) + { + if ( $line =~ /[ \t]*--([a-z-_]+)/ ) + { + push @documented, $1 unless( $line =~ /NOP/i ); + } + } + + # + # Test we discovered some documented options. + # + ok( $#documented > 1, "We found some options documented." ); + + + + # + # Now read the input file so that we can see if these advertised + # options are actually used. + # + open( IN, "<", $file ) or die "Failed to open file for reading $file - $!"; + my @LINES = ; + close( IN ); + + # + # Options accepted + # + my %accepted; + + # + # Do minimal parsing to find the options we process with + # Getopt::Long; + # + my $complete = join( "\n", @LINES ); + if ( $complete =~ /GetOptions\(([^\)]+)\)/mi ) + { + # + # Multi-line text which should have all the options we've + # invoked GetOptions with. + # + my $opt = $1; + + # + # Process each one. + # + foreach my $o ( split( /\n/, $opt ) ) + { +#print "O: $o "; + # + # Strip trailing comments. + # + if ( $o =~ /([^#]+)#/ ) + { + $o = $1; + } +#print " - strip comments : $o "; + + # + # Remove "" from around it. + # + if ( $o =~ /"([^"]+)"/ ) + { + $o = $1; + } +#print " - remove quotes : $o "; + # + # Discard anything after "=", or " " + # + if ( $o =~ /(.*)[ \t=]+(.*)/ ) + { + $o = $1; + } +#print " - remove = : $o "; + # + # Now avoid blank lines. + # + next if ( $o =~ /^[ \t]*$/ ); + + + # + # Phew. Now we're done. + # + # This option '$o' is something we call GetOptions with. + # + $accepted{$o} = 1; + } + } + + # + # Now we want to make sure that each documented option is + # present in the list of options we pass to getopt. + # + foreach my $argument ( @documented ) + { + is( $accepted{$argument}, 1, "Option '--$argument' accepted: $file" ); + } +} diff --git a/t/hook-daemons.t b/t/hook-daemons.t new file mode 100755 index 0000000..063c07b --- /dev/null +++ b/t/hook-daemons.t @@ -0,0 +1,106 @@ +#!/usr/bin/perl -w +# +# Test that our policy-rc.d file is created and removed as we expect in our hooks. +# +# Steve +# -- +# $Id: hook-daemons.t,v 1.1 2007-09-01 19:23:10 steve Exp $ +# + + +use strict; +use Test::More qw( no_plan ); +use File::Temp; + + + + +# +# Rather than having a hardwired list of distributions to test +# against we look for subdirectories beneath hooks/ and test each +# one. +# +foreach my $dir ( glob( "hooks/*" ) ) +{ + next if ( $dir =~ /CVS/i ); + next if ( ! -d $dir ); + + if ( $dir =~ /hooks\/(.*)/ ) + { + my $dist = $1; + + maybeCallHook( $dist ); + } +} + + + +# +# If the given distribution has the following two files test them: +# +# 01-disable-daemons +# 99-enable-daemons +# +sub maybeCallHook +{ + my( $dist ) = (@_); + + # + # Do the two files exist? + # + foreach my $file ( qw/ 01-disable-daemons 99-enable-daemons / ) + { + return if ( ! -e "./hooks/$dist/$file" ); + } + + # + # Call the test on the given distribution + # + testHook( $dist ); +} + + + +# +# Test that the two hooks work. +# +sub testHook +{ + my ( $dist ) = ( @_ ); + + # + # Output + # + ok( $dist, "Testing: $dist" ); + + # + # Create a temporary directory. + # + my $dir = File::Temp::tempdir( CLEANUP => 1 ); + + # + # Test we got a directory and there is no /usr/sbin there. + # + ok( -d $dir, "Temporary directory created OK" ); + ok( ! -d $dir . "/usr/sbin", "There is no /usr/sbin directory there. yet" );; + + + # + # Call the first hook + # + `./hooks/$dist/01-disable-daemons $dir`; + + # + # Now /usr/sbin should exist. + # + ok( -d $dir . "/usr/sbin", "The /usr/sbin directory was created" ); + ok( -x $dir . "/usr/sbin/policy-rc.d", "The policy-rc.d file was created" ); + + # + # Now call the second hook + # + `./hooks/$dist/99-enable-daemons $dir`; + + ok( ! -x $dir . "/usr/sbin/policy-rc.d", "The policy-rc.d file was correctly removed" ); +} + diff --git a/t/hook-hostname.t b/t/hook-hostname.t new file mode 100755 index 0000000..01cd006 --- /dev/null +++ b/t/hook-hostname.t @@ -0,0 +1,107 @@ +#!/usr/bin/perl -w +# +# Test that we get an /etc/hosts etc file created when DHCP is used. +# +# Steve +# -- +# $Id: hook-hostname.t,v 1.1 2007-09-01 19:23:10 steve Exp $ +# + + +use strict; +use Test::More qw( no_plan ); +use File::Temp; + + + + +# +# Rather than having a hardwired list of distributions to test +# against we look for subdirectories beneath hooks/ and test each +# one. +# +foreach my $dir ( glob( "hooks/*" ) ) +{ + next if ( $dir =~ /CVS/i ); + next if ( ! -d $dir ); + + if ( $dir =~ /hooks\/(.*)/ ) + { + my $dist = $1; + + testHostCreation( $dist ) unless ( $dist =~ /fedora/i ); + } +} + + + + +# +# Test that the creation succeeds. +# +sub testHostCreation +{ + my ( $dist ) = ( @_ ); + + # + # Setup the environment. + # + $ENV{'hostname'} = "steve"; + $ENV{'dhcp'} = 1; + + # + # Create a temporary directory. + # + my $dir = File::Temp::tempdir( CLEANUP => 1 ); + mkdir( $dir . "/etc", 0777 ); + + # + # Gentoo + # + if ( $dist =~ /gentoo/i ) + { + mkdir( $dir . "/etc/conf.d", 0777 ); + } + + ok( -d $dir, "Temporary directory created OK" ); + ok( -d $dir . "/etc/conf.d" , "Temporary directory created OK" ) if ( $dist =~ /gentoo/i ); + + # + # Make sure there are no files. + # + ok( -d $dir . "/etc/", "Temporary directory created OK" ); + ok( ! -e $dir . "/etc/hosts", " There is no hosts file present" ); + ok( ! -e $dir . "/etc/mailname", " There is no mailname file present" ); + ok( ! -e $dir . "/etc/hostname", " There is no hostname file present" ); + + # + # Make sure we have the distro-specific hook directory, and + # TLS-disabling hook script. + # + ok( -d "hooks/$dist", "There is a hook directory for the distro $dist" ); + + ok( -e "hooks/$dist/50-setup-hostname", "There is a hook for setting up hostname stuff." ); + + # + # Call the hook + # + `hooks/$dist/50-setup-hostname $dir`; + + ok( -e $dir . "/etc/hosts", " There is now a hosts file present" ); + + # + # These files are not used in Gentoo + # + if ( $dist =~ /gentoo/i ) + { + ok( -e $dir . "/etc/conf.d/domainname", " There is now a domainname file present" ); + ok( -e $dir . "/etc/conf.d/hostname", " There is now a hostname file present" ); + + } + else + { + ok( -e $dir . "/etc/mailname", " There is now a mailname file present" ); + ok( -e $dir . "/etc/hostname", " There is now a hostname file present" ); + } +} + diff --git a/t/hook-inittab.t b/t/hook-inittab.t new file mode 100755 index 0000000..776fdcf --- /dev/null +++ b/t/hook-inittab.t @@ -0,0 +1,108 @@ +#!/usr/bin/perl -w +# +# Test that the /etc/inittab file is modified as we expect. +# +# Steve +# -- +# $Id: hook-inittab.t,v 1.1 2007-09-01 19:23:10 steve Exp $ +# + +use strict; +use Test::More qw( no_plan ); +use File::Temp; +use File::Copy; + + +# +# Sanity check. +# +ok( -e "/etc/inittab", "/etc/inittab exists." ); + + + +# +# Rather than having a hardwired list of distributions to test +# against we look for subdirectories beneath hooks/ and test each +# one. +# +foreach my $dir ( glob( "hooks/*" ) ) +{ + next if ( $dir =~ /CVS/i ); + next if ( ! -d $dir ); + + if ( $dir =~ /hooks\/(.*)/ ) + { + my $dist = $1; + + next if ( $dist =~ /(edgy|dapper|ubuntu)/i ); + + testHook( $dist ); + } +} + + + + +sub testHook +{ + my ( $dist ) = ( @_ ); + + # + # Create a temporary directory, and copy our inittab into it. + # + my $dir = File::Temp::tempdir( CLEANUP => 1 ); + mkdir( $dir . "/etc", 0777 ); + File::Copy::cp( "/etc/inittab", $dir . "/etc" ); + + # + # Make sure that worked. + # + ok( -d $dir, "Temporary directory created OK" ); + ok( -e $dir . "/etc/inittab", "/etc/inittab copied correctly." ); + + ok( -e "hooks/$dist/30-disable-gettys", "$dist inittab fixing hook exists" ); + ok( -x "hooks/$dist/30-disable-gettys", "$dist inittab fixing hook is executable" ); + + # + # Call the hook + # + `hooks/$dist/30-disable-gettys $dir`; + + # + # Now we read the new file, and make sure it looks like we expect. + # + open( INIT, "<", $dir . "/etc/inittab" ) + or die "Failed to open modified inittab."; + my @lines = ; + close( INIT ); + + # + # Test we read some lines. + # + ok( $#lines > 0, "We read the new inittab." ); + + # + # Now test that the lines look like they should. + # + my $count = 0; + foreach my $line ( @lines ) + { + if ( $line =~ /^([1-9])(.*) (.*)$/ ) + { + # + # This should be our only line: + # + # 1:2345:respawn:/sbin/getty 38400 console + # + ok( $1 eq "1", "We found the first getty line." ); + ok( $3 eq "tty1", "Which does uses the correct driver: $3" ); + } + + if ( $line =~ /^(.).*getty/ ) + { + $count += 1 if ( $1 ne "#" ); + } + } + + ok( $count = 1, "Only found one uncommented getty line" ); +} diff --git a/t/hook-tls.t b/t/hook-tls.t new file mode 100755 index 0000000..2eeb8e1 --- /dev/null +++ b/t/hook-tls.t @@ -0,0 +1,87 @@ +#!/usr/bin/perl +# +# Test that the tls-disabling hook works. +# +# Steve +# -- +# $Id: hook-tls.t,v 1.1 2007-09-01 19:23:10 steve Exp $ +# + +use Config qw(config_vars); +use Test::More; +use File::Temp; + + +if ( $Config::Config{archname} =~ /64/ ) +{ + plan skip_all => "This test will fail upon 64 bit systems" ; +} +else +{ + plan no_plan; +} + +# +# Rather than having a hardwired list of distributions to test +# against we look for subdirectories beneath hooks/ and test each +# one. +# +foreach my $dir ( glob( "hooks/*" ) ) +{ + next if ( $dir =~ /CVS/i ); + next if ( ! -d $dir ); + + if ( $dir =~ /hooks\/(.*)/ ) + { + my $dist = $1; + + testTLSDisabling( $dist ) unless ( $dist =~ /(dapper|edgy|ubuntu|debian)/i ); + } +} + + + + +# +# Test that there is a hook for the given distribution, and that +# it successfully disables a faked TLS. +# +sub testTLSDisabling +{ + my ( $dist ) = ( @_ ); + + # + # Create a temporary directory. + # + my $dir = File::Temp::tempdir( CLEANUP => 1 ); + + mkdir( $dir . "/lib", 0777 ); + mkdir( $dir . "/lib/tls", 0777 ); + mkdir( $dir . "/lib/tls/foo", 0777 ); + + ok( -d $dir, "Temporary directory created OK" ); + ok( -d $dir . "/lib/tls", "TLS directory OK" ); + ok( -d $dir . "/lib/tls/foo", "TLS directory is non-empty" ); + + + # + # Make sure we have the distro-specific hook directory, and + # TLS-disabling hook script. + # + ok( -d "hooks/$dist", "There is a hook directory for the distro $dist" ); + + ok( -e "hooks/$dist/10-disable-tls", "TLS Disabling hook exists" ); + ok( -x "hooks/$dist/10-disable-tls", "TLS Disabling hook is executable" ); + + # + # Call the hook + # + `hooks/$dist/10-disable-tls $dir`; + + # + # Make sure the the TLS directory is empty + # + ok( ! -e "$dir/lib/tls/foo", "The fake library from /lib/tls is gone" ); + ok( -e "$dir/lib/tls.disabled/foo", "The fake library ended up in /lib/tls.disabled" ); + ok( -d "$dir/lib/tls", "There is a new /lib/tls directory" ); +} diff --git a/t/hooks.t b/t/hooks.t new file mode 100755 index 0000000..1e15741 --- /dev/null +++ b/t/hooks.t @@ -0,0 +1,54 @@ +#!/usr/bin/perl -w +# +# Test that all the hook files we install are executable. +# +# Steve +# -- +# $Id: hooks.t,v 1.1 2007-09-01 19:23:10 steve Exp $ +# + +use strict; +use Test::More qw( no_plan ); + + +# +# Rather than having a hardwired list of distributions to test +# against we look for subdirectories beneath hooks/ and test each +# one. +# +foreach my $dir ( glob( "hooks/*" ) ) +{ + next if ( $dir =~ /CVS/i ); + next if ( ! -d $dir ); + + if ( $dir =~ /hooks\/(.*)/ ) + { + my $dist = $1; + testDistroHooks( $dist ); + } +} + + + +sub testDistroHooks +{ + my ( $dist ) = ( @_ ); + + # + # Make sure we have a distro-specific hook directory. + # + ok( -d "hooks/$dist", "There is a hook directory for distro $dist" ); + + # + # Now make sure we just have files, and that they are executable. + # + foreach my $file ( glob( "hooks/$dist/*" ) ) + { + if ( ! -d $file ) + { + ok( -e $file, "$file" ); + ok( -x $file, " File is executable: $file" ); + } + } +} + diff --git a/t/modules.sh b/t/modules.sh new file mode 100755 index 0000000..ddba712 --- /dev/null +++ b/t/modules.sh @@ -0,0 +1,35 @@ +#!/bin/sh +# +# Automatically attempt to create a test which ensures all the modules +# used in the code are availabe. +# +# Steve +# -- +# http://www.steve.org.uk/ +# +# $Id: modules.sh,v 1.1 2007-09-01 19:23:10 steve Exp $ +# + +cat < \&checkFile, no_chdir => 1 }, '.' ); + + + +# +# Check a file. +# +# +sub checkFile +{ + # The file. + my $file = $File::Find::name; + + # We don't care about directories + return if ( ! -f $file ); + + # Nor about backup files. + return if ( $file =~ /~$/ ); + + # Nor about files which start with ./debian/ + return if ( $file =~ /^\.\/debian\// ); + + # See if it is a shell/perl file. + my $isShell = 0; + my $isPerl = 0; + + # Read the file. + open( INPUT, "<", $file ); + foreach my $line ( ) + { + if ( ( $line =~ /\/bin\/sh/ ) || + ( $line =~ /\/bin\/bash/ ) ) + { + $isShell = 1; + } + if ( $line =~ /\/usr\/bin\/perl/ ) + { + $isPerl = 1; + } + } + close( INPUT ); + + # + # Return if it wasn't a perl file. + # + if ( $isShell || $isPerl ) + { + # + # Count TAB characters + # + my $count = countTabCharacters( $file ); + + is( $count, 0, "Script has no tab characters: $file" ); + } +} + + +=head2 countTabCharacters + +=cut + +sub countTabCharacters +{ + my ( $file ) = (@_); + + my $count = 0; + + open( FILE, "<", $file ) + or die "Cannot open $file - $!"; + foreach my $line ( ) + { + while( $line =~ /(.*)\t(.*)/ ) + { + $count += 1; + $line = $1 . $2; + } + } + close( FILE ); + + return( $count ); +} diff --git a/t/perl-syntax.t b/t/perl-syntax.t new file mode 100755 index 0000000..46d7a06 --- /dev/null +++ b/t/perl-syntax.t @@ -0,0 +1,73 @@ +#!/usr/bin/perl -w +# +# Test that every perl file we have passes the syntax check. +# +# Steve +# -- +# $Id: perl-syntax.t,v 1.1 2007-09-01 19:23:10 steve Exp $ + + +use strict; +use File::Find; +use Test::More qw( no_plan ); + + +# +# Find all the files beneath the current directory, +# and call 'checkFile' with the name. +# +find( { wanted => \&checkFile, no_chdir => 1 }, '.' ); + + + +# +# Check a file. +# +# If this is a perl file then call "perl -c $name", otherwise +# return +# +sub checkFile +{ + # The file. + my $file = $File::Find::name; + + # We don't care about directories + return if ( ! -f $file ); + + # `modules.sh` is a false positive. + return if ( $file =~ /modules.sh$/ ); + + # `tests/hook-tls.t` is too. + return if ( $file =~ /hook-tls.t$/ ); + + # See if it is a perl file. + my $isPerl = 0; + + # Read the file. + open( INPUT, "<", $file ); + foreach my $line ( ) + { + if ( $line =~ /\/usr\/bin\/perl/ ) + { + $isPerl = 1; + } + } + close( INPUT ); + + # + # Return if it wasn't a perl file. + # + return if ( ! $isPerl ); + + # + # Now run 'perl -c $file' to see if we pass the syntax + # check. We add a couple of parameters to make sure we're + # really OK. + # + # use strict "vars"; + # use strict "subs"; + # + my $retval = system( "perl -Mstrict=subs -Mstrict=vars -c $file 2>/dev/null >/dev/null" ); + + is( $retval, 0, "Perl file passes our syntax check: $file" ); +} diff --git a/t/plugin-checks.t b/t/plugin-checks.t new file mode 100755 index 0000000..1181107 --- /dev/null +++ b/t/plugin-checks.t @@ -0,0 +1,98 @@ +#!/usr/bin/perl -w +# +# Test that the plugins each refer to environmental variables, +# not the perl config hash. +# +# Steve +# -- +# $Id: plugin-checks.t,v 1.1 2007-09-01 19:23:10 steve Exp $ +# + + +use strict; +use Test::More qw( no_plan ); + + +# +# Rather than having a hardwired list of distributions to test +# against we look for subdirectories beneath hooks/ and test each +# one. +# +foreach my $dir ( glob( "hooks/*" ) ) +{ + next if ( $dir =~ /CVS/i ); + next if ( ! -d $dir ); + + if ( $dir =~ /hooks\/(.*)/ ) + { + my $dist = $1; + testPlugins( $dist ); + } +} + + + +=head2 testPlugins + + Test each plugin associated with the given directory. + +=cut + +sub testPlugins +{ + my ( $dist ) = ( @_ ); + + # + # Make sure there is a hook directory for the named distro + # + ok( -d "hooks/$dist/", "There is a hook directory for the distro $dist" ); + + # + # Make sure the plugins are OK. + # + foreach my $file ( glob( "hooks/$dist/*" ) ) + { + ok( -e $file, "$file" ); + + if ( -f $file ) + { + ok( -x $file, "File is executable" ); + + # + # Make sure the file is OK + # + my $result = testFile( $file ); + is( $result, 0, " File contains no mention of the config hash" ); + + } + } + +} + + + +# +# Test that the named file contains no mention of '$CONFIG{'xx'};' +# +sub testFile +{ + my ( $file ) = ( @_ ); + + open( FILY, "<", $file ) or die "Failed to open $file - $!"; + + foreach my $line ( ) + { + if ( $line =~ /\$CONFIG{[ \t'"]+(.*)[ \t'"]+}/ ) + { + close( FILY ); + return $line; + } + } + close( FILY ); + + # + # Success + # + return 0; +} + diff --git a/t/pod-check.t b/t/pod-check.t new file mode 100755 index 0000000..f56b657 --- /dev/null +++ b/t/pod-check.t @@ -0,0 +1,33 @@ +#!/usr/bin/perl -w +# +# Test that the POD we include in our scripts is valid, via the external +# podcheck command. +# +# Steve +# -- +# $Id: pod-check.t,v 1.1 2007-09-01 19:23:10 steve Exp $ +# + +use strict; +use Test::More qw( no_plan ); + +foreach my $file ( glob( "bin/*-*" ) ) +{ + ok( -e $file, "$file" ); + ok( -x $file, " File is executable: $file" ); + ok( ! -d $file, " File is not a directory: $file" ); + + if ( ( -x $file ) && ( ! -d $file ) ) + { + # + # Execute the command giving STDERR to STDOUT where we + # can capture it. + # + my $cmd = "podchecker $file"; + my $output = `$cmd 2>&1`; + chomp( $output ); + + is( $output, "$file pod syntax OK.", " File has correct POD syntax: $file" ); + } +} + diff --git a/t/pod.t b/t/pod.t new file mode 100755 index 0000000..ee581f3 --- /dev/null +++ b/t/pod.t @@ -0,0 +1,17 @@ +#!/usr/bin/perl -w + +# +# Test that the POD we use in our modules is valid. +# + + +use strict; +use Test::More; +eval "use Test::Pod 1.00"; +plan skip_all => "Test::Pod 1.00 required for testing POD" if $@; + +# +# Run the test(s). +# +my @poddirs = qw( bin ); +all_pod_files_ok( all_pod_files( @poddirs ) ); diff --git a/t/portable-shell.t b/t/portable-shell.t new file mode 100755 index 0000000..8c78079 --- /dev/null +++ b/t/portable-shell.t @@ -0,0 +1,88 @@ +#!/usr/bin/perl -w +# +# Test that we don't use non-portable shell syntax in our hooks. +# +# Specifically we test for: +# +# 1. "[[" & "]]" around tests. +# +# 2. The "function" keyword +# +# Steve +# -- +# $Id: portable-shell.t,v 1.1 2007-09-01 19:23:10 steve Exp $ + + +use strict; +use File::Find; +use Test::More qw( no_plan ); + + +# +# Find all the files beneath the current directory, +# and call 'checkFile' with the name. +# +find( { wanted => \&checkFile, no_chdir => 1 }, '.' ); + + + +# +# Check a file. +# +# If this is a shell script then call "sh -n $name", otherwise +# return +# +sub checkFile +{ + # The file. + my $file = $File::Find::name; + + # We don't care about directories + return if ( ! -f $file ); + + # We're only testing things beneath hooks + return if ( $file !~ /hooks/ ); + + # See if it is a shell script. + my $isShell = 0; + + # Read the file. + open( INPUT, "<", $file ); + foreach my $line ( ) + { + if ( ( $line =~ /\/bin\/sh/ ) || + ( $line =~ /\/bin\/bash/ ) ) + { + $isShell = 1; + } + } + close( INPUT ); + + # + # Return if it wasn't a shell script. + # + return if ( ! $isShell ); + + + # The result + my $result = 0; + + # + # Open the file and read it. + # + open( INPUT, "<", $file ) + or die "Failed to open '$file' - $!"; + + while( my $line = ) + { + # [[ or ]] + $result += 1 if ( $line =~ /\[\[/ ); + $result += 1 if ( $line =~ /\]\]/ ); + + # function + $result += 1 if ( $line =~ /^[ \t]*function/ ); + } + close( INPUT ); + + is( $result, 0, "Shell script passes our portability check: $file" ); +} diff --git a/t/programs.t b/t/programs.t new file mode 100755 index 0000000..ae7126e --- /dev/null +++ b/t/programs.t @@ -0,0 +1,43 @@ +#!/usr/bin/perl -w -I.. +# +# Test that we have several required programs present. +# +# Steve +# -- +# + +use Test::More qw( no_plan ); + + +# +# Files that we want to use. +# +my @required = qw( /usr/sbin/debootstrap /bin/ls /bin/dd /bin/mount /bin/cp /bin/tar ); + +# +# Files that we might wish to use. +# +my @optional = qw( /usr/bin/rpmstrap /usr/sbin/xm /sbin/mkfs.ext3 /sbin/mkfs.xfs/sbin/mkfs.reiserfs ); + + + +# +# Test required programs +# +foreach my $file ( @required ) +{ + ok( -x $file , "Required binary installed: $file" ); +} + +# +# Test optional programs - if they exist then we ensure they are +# executable. If they don't we'll not complain since they are optional. +# +foreach my $file ( @optional ) +{ + if ( -e $file ) + { + ok( -x $file , "Optional binary installed: $file" ); + } +} + diff --git a/t/shell-syntax.t b/t/shell-syntax.t new file mode 100755 index 0000000..d32435b --- /dev/null +++ b/t/shell-syntax.t @@ -0,0 +1,64 @@ +#!/usr/bin/perl -w +# +# Test that every shell script we have passes a syntax check. +# +# Steve +# -- +# $Id: shell-syntax.t,v 1.1 2007-09-01 19:23:10 steve Exp $ + + +use strict; +use File::Find; +use Test::More qw( no_plan ); + + +# +# Find all the files beneath the current directory, +# and call 'checkFile' with the name. +# +find( { wanted => \&checkFile, no_chdir => 1 }, '.' ); + + + +# +# Check a file. +# +# If this is a shell script then call "sh -n $name", otherwise +# return +# +sub checkFile +{ + # The file. + my $file = $File::Find::name; + + # We don't care about directories + return if ( ! -f $file ); + + # See if it is a shell script. + my $isShell = 0; + + # Read the file. + open( INPUT, "<", $file ); + foreach my $line ( ) + { + if ( ( $line =~ /\/bin\/sh/ ) || + ( $line =~ /\/bin\/bash/ ) ) + { + $isShell = 1; + } + } + close( INPUT ); + + # + # Return if it wasn't a perl file. + # + return if ( ! $isShell ); + + # + # Now run 'sh -n $file' to see if we pass the syntax + # check + # + my $retval = system( "sh -n $file 2>/dev/null >/dev/null" ); + + is( $retval, 0, "Shell script passes our syntax check: $file" ); +} diff --git a/t/test-trailing-whitespace.t b/t/test-trailing-whitespace.t new file mode 100755 index 0000000..d152397 --- /dev/null +++ b/t/test-trailing-whitespace.t @@ -0,0 +1,61 @@ +#!/usr/bin/perl -w +# +# Test that every script in ./bin/ has no trailing whitespace. +# +# Steve +# -- +# $Id: test-trailing-whitespace.t,v 1.1 2007-09-01 19:23:10 steve Exp $ + + +use strict; +use File::Find; +use Test::More qw( no_plan ); + + +# +# Find our bin/ directory. +# +my $dir = undef; + +$dir = "./bin/" if ( -d "./bin/" ); +$dir = "../bin/" if ( -d "../bin/" ); + +plan skip_all => "No bin directory found" if (!defined( $dir ) ); + + +# +# Process each file. +# +foreach my $file (sort( glob ( $dir . "*" ) ) ) +{ + # skip backups, and directories. + next if ( $file =~ /~$/ ); + next if ( -d $file ); + + ok( -e $file, "Found file : $file" ); + + checkFile( $file ); +} + + +# +# Check a file. +# +# +sub checkFile +{ + my( $file ) = (@_); + + my $trailing = 0; + + # Read the file. + open( INPUT, "<", $file ); + foreach my $line ( ) + { + $trailing = 1 if ( $line =~ /^(.*)[ \t]+$/ ) + } + close( INPUT ); + + is( $trailing, 0, "File has no trailing whitespace" ); +} + diff --git a/t/xen-delete-image.t b/t/xen-delete-image.t new file mode 100755 index 0000000..2c035a4 --- /dev/null +++ b/t/xen-delete-image.t @@ -0,0 +1,94 @@ +#!/usr/bin/perl -w +# +# Test that the xen-delete-image script will delete an images +# contents correctly. +# +# Steve +# -- +# $Id: xen-delete-image.t,v 1.1 2007-09-01 19:23:10 steve Exp $ +# + + +use strict; +use Test::More qw( no_plan ); +use File::Temp; + + +# +# Create a temporary directory. +# +my $dir = File::Temp::tempdir( CLEANUP => 1 ); +my $domains = $dir . "/domains"; + +# +# Test that we can make the directory. +# +ok ( -d $dir, "The temporary directory was created: $dir" ); + +# +# Create the domains directory. +# +ok ( ! -d $domains, "The temp directory doesn't have a domains directory." ); +mkdir( $domains, 0777 ); +ok ( -d $domains, "The temp directory now has a domains directory." ); + + +# +# Generate a random hostname. +# +my $hostname = join ( '', map {('a'..'z')[rand 26]} 0..17 ); +ok( ! -d $domains . "/" . $hostname, "The virtual hostname doesnt exist." ); + +# +# Make the hostname directory +# +mkdir( $domains . "/" . $hostname, 0777 ); +ok( -d $domains . "/" . $hostname, "The virtual hostname now exists." ); + + +# +# Create a stub disk image +# +open( IMAGE, ">", $domains . "/" . $hostname . "/" . "disk.img" ) + or warn "Failed to open disk image : $!"; +print IMAGE "Test"; +close( IMAGE ); + + +# +# Create a stub swap image +# +open( IMAGE, ">", $domains . "/" . $hostname . "/" . "swap.img" ) + or warn "Failed to open swap image : $!"; +print IMAGE "Test"; +close( IMAGE ); + + +# +# Now we have : +# +# $dir/ +# $dir/domains/ +# $dir/domains/$hostname +# $dir/domains/$hostname/disk.img +# $dir/domains/$hostname/swap.img +# +# So we need to run the deletion script and verify the images +# are removed correctly. +# +`./bin/xen-delete-image --test --dir=$dir $hostname`; + + +# +# If the deletion worked our images are gone. +# +ok( ! -e $domains . "/" . $hostname . "/" . "disk.img", + "Disk image deleted successfully." ); +ok( ! -e $domains . "/" . $hostname . "/" . "swap.img", + "Swap image deleted successfully." ); + +# +# And the hostname directory should have gone too. +# +ok( ! -d $domains . "/" . $hostname, + "The hostname directory was removed" ); diff --git a/t/xen-lists-images.t b/t/xen-lists-images.t new file mode 100755 index 0000000..07ae7ef --- /dev/null +++ b/t/xen-lists-images.t @@ -0,0 +1,137 @@ +#!/usr/bin/perl -w +# +# Test that the xen-list-images script can process two "fake" +# installations which we construct manually. +# +# +# Steve +# -- +# $Id: xen-lists-images.t,v 1.1 2007-09-01 19:23:10 steve Exp $ +# + + +use strict; +use Test::More qw( no_plan ); +use File::Temp; + + +# +# Test some random instances. +# +testRandomInstance( "foo.my.flat", 0 ); +testRandomInstance( "foo.my.flat", 1 ); + +testRandomInstance( "bar.my.flat", 0 ); +testRandomInstance( "bar.my.flat", 1 ); + +testRandomInstance( "baz.my.flat", 0 ); +testRandomInstance( "baz.my.flat", 1 ); + + + +=head2 testRandomInstance + + Create a fake Xen configuration file and test that the xen-list-images + script can work with it. + +=cut + +sub testRandomInstance +{ + my ( $name, $dhcp ) = ( @_ ); + + # Create a temporary directory. + my $dir = File::Temp::tempdir( CLEANUP => 1 ); + ok ( -d $dir, "The temporary directory was created for test: $name" ); + + + # + # Generate a random amount of memory + # + my $memory = int( rand( 4096 ) ); + + # + # Generate a random IP address. + # + my $ip = ''; + my $count = 0; + while( $count < 4 ) + { + $ip .= int( rand( 256 ) ) . "."; + + $count += 1; + } + + + # + # Write a xen configuration file to the temporary directory. + # + open( TMP, ">", $dir . "/foo.cfg" ); + + if ( $dhcp ) + { + print TMP < 128, dhcp => 1, dir => '/tmp' ); +noMentionOf( "ip=", + memory => 128, dhcp => 1, dir => '/tmp' ); + + +# +# Look for an IP address when specifying one, and make sure there +# is no mention of DHCP. +# +testOutputContains( "ip=192.168.1.1", + memory => 128, ip1 => '192.168.1.1', dir => '/tmp' ); +noMentionOf( "dhcp", + memory => 128, ip1 => '192.168.1.1', dir => '/tmp' ); + +# +# SCSI based systems: +# +testOutputContains( "sda1", + memory => 128, ip1 => '192.168.1.1', dir => '/tmp' ); +testOutputContains( "/dev/sda1 ro", + memory => 128, ip1 => '192.168.1.1', dir => '/tmp' ); +noMentionOf( "hda1", + memory => 128, ip1 => '192.168.1.1', dir => '/tmp' ); + + +# +# IDE based systems +# +testOutputContains( "hda1", + memory => 128, ip1 => '192.168.1.1', dir => '/tmp', ide => 1 ); +testOutputContains( "/dev/hda1 ro", + memory => 128, ip1 => '192.168.1.1', dir => '/tmp', ide => 1 ); + + + +# +# Test memory size. +# +testOutputContains( "128", + memory => 128, dhcp => 1, dir => '/tmp' ); +testOutputContains( "211", + memory => 211, dhcp => 1, dir => '/tmp' ); +testOutputContains( "912", + memory => 912, dhcp => 1, lvm => 'skx-vg0' ); + + +# +# Test LVM stuff. +# +testOutputContains( "phy:", + memory => 128, dhcp => 1, lvm => 'skx-vg0' ); +testOutputContains( "skx-vg0", + memory => 128, dhcp => 1, lvm => 'skx-vg0' ); +noMentionOf( "/tmp", + memory => 128, dhcp => 1, lvm => 'skx-vg0' ); +noMentionOf( "domains", + memory => 128, dhcp => 1, lvm => 'skx-vg0' ); + + +# +# Now test the loopback devices. +# +testOutputContains( "/tmp", + memory => 128, dhcp => 1, dir => '/tmp' ); +testOutputContains( "/tmp/domains", + memory => 128, dhcp => 1, dir => '/tmp' ); +testOutputContains( "/tmp/domains/foo.my.flat", + memory => 128, dhcp => 1, dir => '/tmp' ); +noMentionOf( "phy:", + memory => 128, dhcp => 1, dir => '/tmp' ); + + + + + + + + +=head2 runCreateCommand + + Run the xt-create-xen-config command and return the output. + + This involves setting up the environment and running the command, + once complete return the text which has been written to the xen + configuration file. + +=cut + +sub runCreateCommand +{ + my ( %params ) = ( @_ ); + + # + # Force a hostname + # + $params{'hostname'} = 'foo.my.flat'; + $params{'noswap'} = 1; + + # + # Create a temporary directory, and make sure it is present. + # + my $dir = File::Temp::tempdir( CLEANUP => 0 ); + ok ( -d $dir, "The temporary directory was created: $dir" ); + + # + # Save the environment. + # + my %SAFE_ENV = %ENV; + + # + # Update the environment with our parameters. + # + foreach my $p ( keys %params ) + { + $ENV{$p} = $params{$p}; + } + + # + # Run the command + # + system( "./bin/xt-create-xen-config --output=$dir --template=./etc/xm.tmpl" ); + + # + # Reset the environment + # + %ENV = %SAFE_ENV; + + + + # + # Read the Xen configuration file which the xt-creaat... + # command wrote and return it to the caller. + # + open( OUTPUT, "<", $dir . "/foo.my.flat.cfg" ); + my @LINES = ; + close( OUTPUT ); + + return( join( "\n", @LINES ) ); +} + + + +=head2 testOutputContains + + Run the xt-create-xen-config and ensure that the output + contains the text we're looking for. + +=cut + +sub testOutputContains +{ + my ( $text, %params ) = ( @_ ); + + # Get the output of running the command. + my $output = runCreateCommand( %params ); + + # + # Look to see if we got the text. + # + my $found = 0; + if ( $output =~ /\Q$text\E/ ) + { + $found += 1; + } + + ok( $found > 0, "We found the output we wanted: $text" ); +} + + +=head2 noMentionOf + + Make sure that the creation of a given Xen configuration + file contains no mention of the given string. + +=cut + +sub noMentionOf +{ + my ( $text, %params ) = ( @_ ); + + # Get the output of running the command. + my $output = runCreateCommand( %params ); + + # + # Look to see if we got the text. + # + my $found = 0; + if ( $output =~ /\Q$text\E/ ) + { + $found += 1; + } + + ok( $found == 0, "The output didn't contain the excluded text: $text" ); + +}